diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 482ee6633..8d669033a 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -1019,6 +1019,7 @@ set (PCH_SOURCES build/src/mdsprite.cpp build/src/polymost.cpp + core/am_map.cpp core/actorinfo.cpp core/zcc_compile_raze.cpp core/vmexports.cpp diff --git a/source/common/statusbar/base_sbar.h b/source/common/statusbar/base_sbar.h index 972cbf0df..39d2f97e1 100644 --- a/source/common/statusbar/base_sbar.h +++ b/source/common/statusbar/base_sbar.h @@ -193,4 +193,9 @@ public: void Fill(PalEntry color, double x, double y, double w, double h, int flags = 0); void SetClipRect(double x, double y, double w, double h, int flags = 0); -}; \ No newline at end of file + int GetTopOfStatusbar() const + { + return SBarTop; + } + +}; diff --git a/source/core/am_map.cpp b/source/core/am_map.cpp new file mode 100644 index 000000000..082d36112 --- /dev/null +++ b/source/core/am_map.cpp @@ -0,0 +1,2711 @@ +//----------------------------------------------------------------------------- +// +// Copyright 1993-1996 id Software +// Copyright 1994-1996 Raven Software +// Copyright 1999-2016 Randy Heit +// Copyright 2002-2016 Christoph Oelckers +// +// 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 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//----------------------------------------------------------------------------- +// +// +// DESCRIPTION: the automap code +// +//----------------------------------------------------------------------------- + +#include +#include + +#include "filesystem.h" +#include "d_event.h" +#include "gi.h" +#include "c_bind.h" +#include "serializer.h" +#include "v_video.h" +#include "v_draw.h" +#include "c_dispatch.h" +#include "statusbar.h" +#include "i_net.h" +#include "fcolormap.h" +#include "gamestate.h" + +// Data. +#include "gstrings.h" + +#include "am_map.h" +#include "c_buttons.h" +#include "texturemanager.h" +#include "v_font.h" +#include "printf.h" +#include "coreactor.h" +#include "gamefuncs.h" +#include "sectorgeometry.h" +#include "hw_sections.h" + +#include "gamecontrol.h" + +//============================================================================= +// +// Global state +// +//============================================================================= + +enum +{ + AM_NUMMARKPOINTS = 10, +}; + +// C++ cannot do static const floats in a class, so these need to be global... +static const double PLAYERRADIUS = 16.; // player radius for automap checking +static const double M_ZOOMIN = 2; // how much zoom-in per second +static const double M_ZOOMOUT = 0.2; // how much zoom-out per second +static const double M_OLDZOOMIN = (1.02); // for am_zoom +static const double M_OLDZOOMOUT = (1 / 1.02); + +static FTextureID marknums[AM_NUMMARKPOINTS]; // numbers used for marking by the automap +bool automapactive = false; + +//============================================================================= +// +// Types +// +//============================================================================= + +struct fpoint_t +{ + int x, y; +}; + +struct fline_t +{ + fpoint_t a, b; +}; + +struct mpoint_t +{ + double x, y; +}; + +struct mline_t +{ + mpoint_t a, b; +}; + +struct islope_t +{ + double slp, islp; +}; + +//============================================================================= +// +// CVARs +// +//============================================================================= + +CVAR(Bool, am_textured, false, CVAR_ARCHIVE) +CVAR(Float, am_linealpha, 1.0f, CVAR_ARCHIVE) +CVAR(Int, am_linethickness, 1, CVAR_ARCHIVE) +CVAR(Bool, am_thingrenderstyles, true, CVAR_ARCHIVE) + + +EXTERN_CVAR(Bool, sv_cheats) +CUSTOM_CVAR(Int, am_cheat, 0, 0) +{ + // No automap cheat in net games when cheats are disabled! + if (netgame && !sv_cheats && self != 0) + { + self = 0; + } +} + +CVAR(Int, am_rotate, 0, CVAR_ARCHIVE); +CVAR(Int, am_overlay, 0, CVAR_ARCHIVE); +CVAR(Bool, am_showsecrets, true, CVAR_ARCHIVE); +CVAR(Bool, am_showmonsters, true, CVAR_ARCHIVE); +CVAR(Bool, am_showtime, true, CVAR_ARCHIVE); +CVAR(Bool, am_showtotaltime, false, CVAR_ARCHIVE); +CVAR(Int, am_colorset, 0, CVAR_ARCHIVE); +CVAR(Bool, am_customcolors, true, CVAR_ARCHIVE); +CVAR(Int, am_map_secrets, 1, CVAR_ARCHIVE); +CVAR(Int, am_drawmapback, 1, CVAR_ARCHIVE); +CVAR(Bool, am_showkeys, true, CVAR_ARCHIVE); +CVAR(Int, am_showthingsprites, 0, CVAR_ARCHIVE); +CVAR (Bool, am_showkeys_always, false, CVAR_ARCHIVE); + +DAutomapBase* automap; + +CUSTOM_CVAR(Int, am_emptyspacemargin, 0, CVAR_ARCHIVE) +{ + if (self < 0) + { + self = 0; + } + else if (self > 90) + { + self = 90; + } + + if (automap) + { + automap->NewResolution(); + } +} + +//============================================================================= +// +// map functions +// +//============================================================================= + +CVAR(Bool, am_followplayer, true, CVAR_ARCHIVE) +CVAR(Bool, am_portaloverlay, true, CVAR_ARCHIVE) +CVAR(Bool, am_showgrid, false, CVAR_ARCHIVE) +CVAR(Float, am_zoomdir, 0, CVAR_ARCHIVE) + +static const char *const DEFAULT_FONT_NAME = "AMMNUMx"; +CVAR(String, am_markfont, DEFAULT_FONT_NAME, CVAR_ARCHIVE) +CVAR(Int, am_markcolor, CR_GREY, CVAR_ARCHIVE) + +CCMD(am_togglefollow) +{ + am_followplayer = !am_followplayer; + if (automap) + automap->ResetFollowLocation(); + Printf("%s\n", GStrings(am_followplayer ? "AMSTR_FOLLOWON" : "AMSTR_FOLLOWOFF")); +} + +CCMD(am_togglegrid) +{ + am_showgrid = !am_showgrid; + Printf("%s\n", GStrings(am_showgrid ? "AMSTR_GRIDON" : "AMSTR_GRIDOFF")); +} + +CCMD(am_toggletexture) +{ + am_textured = !am_textured; + Printf("%s\n", GStrings(am_textured ? "AMSTR_TEXON" : "AMSTR_TEXOFF")); +} + +CCMD(am_setmark) +{ + if (automap) + { + int m = automap->addMark(); + if (m >= 0) + { + Printf("%s %d\n", GStrings("AMSTR_MARKEDSPOT"), m); + } + } +} + +CCMD(am_clearmarks) +{ + if (automap && automap->clearMarks()) + { + Printf("%s\n", GStrings("AMSTR_MARKSCLEARED")); + } +} + +CCMD(am_gobig) +{ + if (automap) + automap->GoBig(); +} + +CCMD(togglemap) +{ + if (gameaction == ga_nothing) + { + gameaction = ga_togglemap; + } +} + +CCMD(am_zoom) +{ + if (argv.argc() >= 2) + { + am_zoomdir = (float)atof(argv[1]); + } +} + + +//============================================================================= +// +// Automap colors +// +//============================================================================= + +CVAR (Color, am_backcolor, 0x6c5440, CVAR_ARCHIVE); +CVAR (Color, am_yourcolor, 0xfce8d8, CVAR_ARCHIVE); +CVAR (Color, am_wallcolor, 0x2c1808, CVAR_ARCHIVE); +CVAR (Color, am_secretwallcolor, 0x000000, CVAR_ARCHIVE); +CVAR (Color, am_specialwallcolor, 0xffffff, CVAR_ARCHIVE); +CVAR (Color, am_tswallcolor, 0x888888, CVAR_ARCHIVE); +CVAR (Color, am_fdwallcolor, 0x887058, CVAR_ARCHIVE); +CVAR (Color, am_cdwallcolor, 0x4c3820, CVAR_ARCHIVE); +CVAR (Color, am_efwallcolor, 0x665555, CVAR_ARCHIVE); +CVAR (Color, am_thingcolor, 0xfcfcfc, CVAR_ARCHIVE); +CVAR (Color, am_gridcolor, 0x8b5a2b, CVAR_ARCHIVE); +CVAR (Color, am_xhaircolor, 0x808080, CVAR_ARCHIVE); +CVAR (Color, am_notseencolor, 0x6c6c6c, CVAR_ARCHIVE); +CVAR (Color, am_lockedcolor, 0x007800, CVAR_ARCHIVE); +CVAR (Color, am_intralevelcolor, 0x0000ff, CVAR_ARCHIVE); +CVAR (Color, am_interlevelcolor, 0xff0000, CVAR_ARCHIVE); +CVAR (Color, am_secretsectorcolor, 0xff00ff, CVAR_ARCHIVE); +CVAR (Color, am_unexploredsecretcolor, 0xff00ff, CVAR_ARCHIVE); +CVAR (Color, am_thingcolor_friend, 0xfcfcfc, CVAR_ARCHIVE); +CVAR (Color, am_thingcolor_monster, 0xfcfcfc, CVAR_ARCHIVE); +CVAR (Color, am_thingcolor_ncmonster, 0xfcfcfc, CVAR_ARCHIVE); +CVAR (Color, am_thingcolor_item, 0xfcfcfc, CVAR_ARCHIVE); +CVAR (Color, am_thingcolor_citem, 0xfcfcfc, CVAR_ARCHIVE); +CVAR (Color, am_portalcolor, 0x404040, CVAR_ARCHIVE); + +CVAR (Color, am_ovyourcolor, 0xfce8d8, CVAR_ARCHIVE); +CVAR (Color, am_ovwallcolor, 0x00ff00, CVAR_ARCHIVE); +CVAR (Color, am_ovsecretwallcolor, 0x008844, CVAR_ARCHIVE); +CVAR (Color, am_ovspecialwallcolor, 0xffffff, CVAR_ARCHIVE); +CVAR (Color, am_ovotherwallscolor, 0x008844, CVAR_ARCHIVE); +CVAR (Color, am_ovlockedcolor, 0x008844, CVAR_ARCHIVE); +CVAR (Color, am_ovefwallcolor, 0x008844, CVAR_ARCHIVE); +CVAR (Color, am_ovfdwallcolor, 0x008844, CVAR_ARCHIVE); +CVAR (Color, am_ovcdwallcolor, 0x008844, CVAR_ARCHIVE); +CVAR (Color, am_ovunseencolor, 0x00226e, CVAR_ARCHIVE); +CVAR (Color, am_ovtelecolor, 0xffff00, CVAR_ARCHIVE); +CVAR (Color, am_ovinterlevelcolor, 0xffff00, CVAR_ARCHIVE); +CVAR (Color, am_ovsecretsectorcolor,0x00ffff, CVAR_ARCHIVE); +CVAR (Color, am_ovunexploredsecretcolor,0x00ffff, CVAR_ARCHIVE); +CVAR (Color, am_ovthingcolor, 0xe88800, CVAR_ARCHIVE); +CVAR (Color, am_ovthingcolor_friend, 0xe88800, CVAR_ARCHIVE); +CVAR (Color, am_ovthingcolor_monster, 0xe88800, CVAR_ARCHIVE); +CVAR (Color, am_ovthingcolor_ncmonster, 0xe88800, CVAR_ARCHIVE); +CVAR (Color, am_ovthingcolor_item, 0xe88800, CVAR_ARCHIVE); +CVAR (Color, am_ovthingcolor_citem, 0xe88800, CVAR_ARCHIVE); +CVAR (Color, am_ovportalcolor, 0x004022, CVAR_ARCHIVE); + +//============================================================================= +// +// internal representation of a single color +// +//============================================================================= + +struct AMColor +{ + uint32_t RGB; + + void FromCVar(FColorCVar & cv) + { + RGB = uint32_t(cv) | MAKEARGB(255, 0, 0, 0); + } + + void FromRGB(int r,int g, int b) + { + RGB = MAKEARGB(255, r, g, b); + } + + void setInvalid() + { + RGB = 0; + } + + bool isValid() const + { + return RGB != 0; + } +}; + +//============================================================================= +// +// a complete color set +// +//============================================================================= + +static const char *ColorNames[] = { + "Background", + "YourColor", + "WallColor", + "TwoSidedWallColor", + "FloorDiffWallColor", + "CeilingDiffWallColor", + "ExtraFloorWallColor", + "ThingColor", + "ThingColor_Item", + "ThingColor_CountItem", + "ThingColor_Monster", + "ThingColor_NocountMonster", + "ThingColor_Friend", + "SpecialWallColor", + "SecretWallColor", + "GridColor", + "XHairColor", + "NotSeenColor", + "LockedColor", + "IntraTeleportColor", + "InterTeleportColor", + "SecretSectorColor", + "UnexploredSecretColor", + "PortalColor", + "AlmostBackgroundColor", + nullptr +}; + +struct AMColorset +{ + enum + { + Background, + YourColor, + WallColor, + TSWallColor, + FDWallColor, + CDWallColor, + EFWallColor, + ThingColor, + ThingColor_Item, + ThingColor_CountItem, + ThingColor_Monster, + ThingColor_NocountMonster, + ThingColor_Friend, + SpecialWallColor, + SecretWallColor, + GridColor, + XHairColor, + NotSeenColor, + LockedColor, + IntraTeleportColor, + InterTeleportColor, + SecretSectorColor, + UnexploredSecretColor, + PortalColor, + AlmostBackgroundColor, + AM_NUM_COLORS + }; + + AMColor c[AM_NUM_COLORS]; + bool displayLocks; + bool forcebackground; + bool defined; // only for mod specific colorsets: must be true to be usable + + void initFromCVars(FColorCVar **values) + { + for(int i=0;iResetToDefault(); + } + for (unsigned i = 0; i < countof(cv_overlay); i++) + { + cv_overlay[i]->ResetToDefault(); + } +} + + + +#define NOT_USED 1,0,0 // use almost black as indicator for an unused color + +static unsigned char DoomColors[]= { + 0x00,0x00,0x00, // background + 0xff,0xff,0xff, // yourcolor + 0xfc,0x00,0x00, // wallcolor + 0x80,0x80,0x80, // tswallcolor + 0xbc,0x78,0x48, // fdwallcolor + 0xfc,0xfc,0x00, // cdwallcolor + 0xbc,0x78,0x48, // efwallcolor + 0x74,0xfc,0x6c, // thingcolor + 0x74,0xfc,0x6c, // thingcolor_item + 0x74,0xfc,0x6c, // thingcolor_citem + 0x74,0xfc,0x6c, // thingcolor_monster + 0x74,0xfc,0x6c, // thingcolor_ncmonster + 0x74,0xfc,0x6c, // thingcolor_friend + NOT_USED, // specialwallcolor + NOT_USED, // secretwallcolor + 0x4c,0x4c,0x4c, // gridcolor + 0x80,0x80,0x80, // xhaircolor + 0x6c,0x6c,0x6c, // notseencolor + 0xfc,0xfc,0x00, // lockedcolor + NOT_USED, // intrateleport + NOT_USED, // interteleport + NOT_USED, // secretsector + NOT_USED, // unexploredsecretsector + 0x10,0x10,0x10, // almostbackground + 0x40,0x40,0x40 // portal +}; + +static unsigned char StrifeColors[]= { + 0x00,0x00,0x00, // background + 239, 239, 0, // yourcolor + 199, 195, 195, // wallcolor + 119, 115, 115, // tswallcolor + 55, 59, 91, // fdwallcolor + 119, 115, 115, // cdwallcolor + 55, 59, 91, // efwallcolor + 187, 59, 0, // thingcolor + 219, 171, 0, // thingcolor_item + 219, 171, 0, // thingcolor_citem + 0xfc,0x00,0x00, // thingcolor_monster + 0xfc,0x00,0x00, // thingcolor_ncmonster + 0xfc,0x00,0x00, // thingcolor_friend + NOT_USED, // specialwallcolor + NOT_USED, // secretwallcolor + 0x4c,0x4c,0x4c, // gridcolor + 0x80,0x80,0x80, // xhaircolor + 0x6c,0x6c,0x6c, // notseencolor + 119, 115, 115, // lockedcolor + NOT_USED, // intrateleport + NOT_USED, // interteleport + NOT_USED, // secretsector + NOT_USED, // unexploredsecretsector + 0x10,0x10,0x10, // almostbackground + 0x40,0x40,0x40 // portal +}; + +static unsigned char RavenColors[]= { + 0x6c,0x54,0x40, // background + 0xff,0xff,0xff, // yourcolor + 75, 50, 16, // wallcolor + 88, 93, 86, // tswallcolor + 208, 176, 133, // fdwallcolor + 103, 59, 31, // cdwallcolor + 208, 176, 133, // efwallcolor + 236, 236, 236, // thingcolor + 236, 236, 236, // thingcolor_item + 236, 236, 236, // thingcolor_citem + 236, 236, 236, // thingcolor_monster + 236, 236, 236, // thingcolor_ncmonster + 236, 236, 236, // thingcolor_friend + NOT_USED, // specialwallcolor + NOT_USED, // secretwallcolor + 75, 50, 16, // gridcolor + 0x00,0x00,0x00, // xhaircolor + 0x00,0x00,0x00, // notseencolor + 103, 59, 31, // lockedcolor + NOT_USED, // intrateleport + NOT_USED, // interteleport + NOT_USED, // secretsector + NOT_USED, // unexploredsecretsector + 0x10,0x10,0x10, // almostbackground + 0x50,0x50,0x50 // portal +}; + +#undef NOT_USED + +static AMColorset AMColors; +static AMColorset AMMod; +static AMColorset AMModOverlay; + + +void AM_ClearColorsets() +{ + AMModOverlay.defined = false; + AMMod.defined = false; +} + +//============================================================================= +// +// +// +//============================================================================= + +static void AM_initColors(bool overlayed) +{ + if (overlayed) + { + if (am_customcolors && AMModOverlay.defined) + { + AMColors = AMModOverlay; + } + else + { + AMColors.initFromCVars(cv_overlay); + } + } + else if (am_customcolors && AMMod.defined) + { + AMColors = AMMod; + } + else switch (am_colorset) + { + default: + /* Use the custom colors in the am_* cvars */ + AMColors.initFromCVars(cv_standard); + break; + + case 1: // Doom + // Use colors corresponding to the original Doom's + AMColors.initFromColors(DoomColors, false); + break; + + case 2: // Strife + // Use colors corresponding to the original Strife's + AMColors.initFromColors(StrifeColors, false); + break; + + case 3: // Raven + // Use colors corresponding to the original Raven's + AMColors.initFromColors(RavenColors, true); + break; + + } +} + +#if 0 +//============================================================================= +// +// custom color parser +// +//============================================================================= + +void FMapInfoParser::ParseAMColors(bool overlay) +{ + bool colorset = false; + + AMColorset &cset = overlay? AMModOverlay : AMMod; + + cset.setWhite(); + cset.defined = true; + sc.MustGetToken('{'); + while(sc.GetToken()) + { + if (sc.TokenType == '}') return; + + sc.TokenMustBe(TK_Identifier); + FString nextKey = sc.String; + sc.MustGetToken('='); + + if (nextKey.CompareNoCase("base") == 0) + { + if (colorset) sc.ScriptError("'base' must be specified before the first color"); + sc.MustGetToken(TK_StringConst); + if (sc.Compare("doom")) + { + cset.initFromColors(DoomColors, false); + } + else if (sc.Compare("raven")) + { + cset.initFromColors(RavenColors, true); + } + else if (sc.Compare("strife")) + { + cset.initFromColors(StrifeColors, false); + } + else + { + sc.ScriptError("Unknown value for 'base'. Must be 'Doom', 'Strife' or 'Raven'."); + } + } + else if (nextKey.CompareNoCase("showlocks") == 0) + { + if(sc.CheckToken(TK_False)) + cset.displayLocks = false; + else + { + sc.MustGetToken(TK_True); + cset.displayLocks = true; + } + } + else + { + int i; + for (i = 0; ColorNames[i] != nullptr; i++) + { + if (nextKey.CompareNoCase(ColorNames[i]) == 0) + { + sc.MustGetToken(TK_StringConst); + FString color = sc.String; + FString colorName = V_GetColorStringByName(color); + if(!colorName.IsEmpty()) color = colorName; + int colorval = V_GetColorFromString(color); + cset.c[i].FromRGB(RPART(colorval), GPART(colorval), BPART(colorval)); + colorset = true; + break; + } + } + if (ColorNames[i]== nullptr) + { + sc.ScriptError("Unknown key '%s'", nextKey.GetChars()); + } + } + } +} +#endif + +//============================================================================= +// +// The vector graphics for the automap. +// A line drawing of the player pointing right, +// starting from the middle. +// +//============================================================================= + +static TArray MapArrow; +static TArray CheatMapArrow; +static TArray CheatKey; +static TArray EasyKey; + +static std::array thintriangle_guy = { { + {{-.5,-.7}, {1,0}}, + {{1,0}, {-.5,.7}}, + {{-.5,.7}, {-.5,-.7}} +} }; + +//============================================================================= +// +// vector graphics +// +//============================================================================= + +static void AM_ParseArrow(TArray &Arrow, const char *lumpname) +{ + const int R = int((8 * PLAYERRADIUS) / 7); + FScanner sc; + int lump = fileSystem.CheckNumForFullName(lumpname, true); + if (lump >= 0) + { + sc.OpenLumpNum(lump); + sc.SetCMode(true); + while (sc.GetToken()) + { + mline_t line; + sc.TokenMustBe('('); + sc.MustGetFloat(); + line.a.x = sc.Float*R; + sc.MustGetToken(','); + sc.MustGetFloat(); + line.a.y = sc.Float*R; + sc.MustGetToken(')'); + sc.MustGetToken(','); + sc.MustGetToken('('); + sc.MustGetFloat(); + line.b.x = sc.Float*R; + sc.MustGetToken(','); + sc.MustGetFloat(); + line.b.y = sc.Float*R; + sc.MustGetToken(')'); + Arrow.Push(line); + } + } +} + +void AM_StaticInit() +{ + MapArrow.Clear(); + CheatMapArrow.Clear(); + CheatKey.Clear(); + EasyKey.Clear(); + + if (gameinfo.mMapArrow.IsNotEmpty()) AM_ParseArrow(MapArrow, gameinfo.mMapArrow); + if (gameinfo.mCheatMapArrow.IsNotEmpty()) AM_ParseArrow(CheatMapArrow, gameinfo.mCheatMapArrow); + AM_ParseArrow(CheatKey, gameinfo.mCheatKey); + AM_ParseArrow(EasyKey, gameinfo.mEasyKey); + if (MapArrow.Size() == 0) I_FatalError("No automap arrow defined"); + + char namebuf[9]; + + for (int i = 0; i < AM_NUMMARKPOINTS; i++) + { + mysnprintf(namebuf, countof(namebuf), "AMMNUM%d", i); + marknums[i] = TexMan.CheckForTexture(namebuf, ETextureType::MiscPatch); + } +} + + +//============================================================================= +// +// the actual automap class definition +// +//============================================================================= + +IMPLEMENT_CLASS(DAutomapBase, true, false); + +class DAutomap :public DAutomapBase +{ + DECLARE_CLASS(DAutomap, DAutomapBase) + + double F_PANINC; + + //FLevelLocals *Level; + // scale on entry + // used by MTOF to scale from map-to-frame-buffer coords + double scale_mtof = .2; + // used by FTOM to scale from frame-buffer-to-map coords (=1/scale_mtof) + double scale_ftom; + + int bigstate; + int MapPortalGroup; + + // Disable the ML_DONTDRAW line flag if x% of all lines in a map are flagged with it + // (To counter annoying mappers who think they are smart by making the automap unusable) + bool am_showallenabled; + + // location of window on screen + int f_x; + int f_y; + + // size of window on screen + int f_w; + int f_h; + + int amclock; + + mpoint_t m_paninc; // how far the window pans each tic (map coords) + double mtof_zoommul; // how far the window zooms in each tic (map coords) + + double m_x, m_y; // LL x,y where the window is on the map (map coords) + double m_x2, m_y2; // UR x,y where the window is on the map (map coords) + + // + // width/height of window on map (map coords) + // + double m_w; + double m_h; + + // based on level size + double min_x, min_y, max_x, max_y; + + double max_w; // max_x-min_x, + double max_h; // max_y-min_y + + // based on player size + double min_w; + double min_h; + + + double min_scale_mtof; // used to tell when to stop zooming out + double max_scale_mtof; // used to tell when to stop zooming in + + // old stuff for recovery later + double old_m_w, old_m_h; + double old_m_x, old_m_y; + + // old location used by the Follower routine + mpoint_t f_oldloc; + + mpoint_t markpoints[AM_NUMMARKPOINTS]; // where the points are + int markpointnum = 0; // next point to be assigned + + FTextureID mapback; // the automap background + double mapystart = 0; // y-value for the start of the map bitmap...used in the parallax stuff. + double mapxstart = 0; //x-value for the bitmap. + + TArray points; + + // translates between frame-buffer and map distances + double FTOM(double x) + { + return x * scale_ftom; + } + + double MTOF(double x) + { + return x * scale_mtof; + } + + // translates between frame-buffer and map coordinates + int CXMTOF(double x) + { + return int(MTOF((x)-m_x)/* - f_x*/); + } + + int CYMTOF(double y) + { + return int(f_h - MTOF((y)-m_y)/* + f_y*/); + } + + void calcMinMaxMtoF(); + + void DrawMarker(FGameTexture *tex, double x, double y, int yadjust, + INTBOOL flip, double xscale, double yscale, int translation, double alpha, uint32_t fillcolor, FRenderStyle renderstyle); + + void rotatePoint(double *x, double *y); + void rotate(double *x, double *y, DAngle an); + void doFollowPlayer(); + void saveScaleAndLoc(); + void restoreScaleAndLoc(); + void minOutWindowScale(); + void activateNewScale(); + void findMinMaxBoundaries(); + void ClipRotatedExtents(double pivotx, double pivoty); + void ScrollParchment(double dmapx, double dmapy); + void changeWindowLoc(); + void maxOutWindowScale(); + void changeWindowScale(double delta); + void clearFB(const AMColor &color); + bool clipMline(mline_t *ml, fline_t *fl); + void drawMline(mline_t *ml, const AMColor &color); + void drawMline(mline_t *ml, int colorindex); + void drawGrid(int color); + void drawSubsectors(); + void drawWalls(bool allmap); + void drawLineCharacter(const mline_t *lineguy, size_t lineguylines, double scale, DAngle angle, const AMColor &color, double x, double y); + void drawPlayers(); + void drawKeys(); + void drawThings(); + void drawMarks(); + void drawAuthorMarkers(); + void drawCrosshair(const AMColor &color); + + +public: + DAutomap() { F_PANINC = 140 / GameTicRate; } + bool Responder(event_t* ev, bool last) override; + void Ticker(void) override; + void Drawer(int bottom) override; + void NewResolution() override; + void LevelInit() override; + void UpdateShowAllLines() override; + void Serialize(FSerializer &arc) override; + void GoBig() override; + void ResetFollowLocation() override; + int addMark() override; + bool clearMarks() override; + DVector2 GetPosition() override; + void startDisplay() override; + +}; + +IMPLEMENT_CLASS(DAutomap, false, false) + + +//============================================================================= +// +// +// +//============================================================================= + + + + +//============================================================================= +// +// called by the coordinate drawer +// +//============================================================================= + +DVector2 DAutomap::GetPosition() +{ + return DVector2((m_x + m_w / 2), (m_y + m_h / 2)); +} + +//============================================================================= +// +// +// +//============================================================================= + +void DAutomap::activateNewScale () +{ + m_x += m_w/2; + m_y += m_h/2; + m_w = FTOM(f_w); + m_h = FTOM(f_h); + m_x -= m_w/2; + m_y -= m_h/2; + m_x2 = m_x + m_w; + m_y2 = m_y + m_h; +} + +//============================================================================= +// +// +// +//============================================================================= + +void DAutomap::saveScaleAndLoc () +{ + old_m_x = m_x; + old_m_y = m_y; + old_m_w = m_w; + old_m_h = m_h; +} + +//============================================================================= +// +// +// +//============================================================================= + +void DAutomap::restoreScaleAndLoc () +{ + m_w = old_m_w; + m_h = old_m_h; + if (!am_followplayer) + { + m_x = old_m_x; + m_y = old_m_y; + } + else + { + m_x = playerPos.X - m_w/2; + m_y = playerPos.Y - m_h/2; + } + m_x2 = m_x + m_w; + m_y2 = m_y + m_h; + + // Change the scaling multipliers + scale_mtof = f_w / m_w; + scale_ftom = 1. / scale_mtof; +} + +//============================================================================= +// +// adds a marker at the current location +// +//============================================================================= + +int DAutomap::addMark () +{ + // Add a mark when default font is selected and its textures (AMMNUM?) + // are loaded. Mark is always added when custom font is selected + if (stricmp(*am_markfont, DEFAULT_FONT_NAME) != 0 || marknums[0].isValid()) + { + auto m = markpointnum; + markpoints[markpointnum].x = m_x + m_w/2; + markpoints[markpointnum].y = m_y + m_h/2; + markpointnum = (markpointnum + 1) % AM_NUMMARKPOINTS; + return m; + } + return -1; +} + +//============================================================================= +// +// Determines bounding box of all vertices, +// sets global variables controlling zoom range. +// +//============================================================================= + +void DAutomap::findMinMaxBoundaries () +{ + min_x = min_y = FLT_MAX; + max_x = max_y = -FLT_MAX; + + for (auto &vert : wall) + {walltype a; + if (vert.pos.X < min_x) + min_x = vert.pos.X; + else if (vert.pos.X > max_x) + max_x = vert.pos.X; + + if (vert.pos.Y < min_y) + min_y = vert.pos.Y; + else if (vert.pos.Y > max_y) + max_y = vert.pos.Y; + } + + max_w = max_x - min_x; + max_h = max_y - min_y; + + min_w = 2*PLAYERRADIUS; // const? never changed? + min_h = 2*PLAYERRADIUS; + + calcMinMaxMtoF(); +} + +//============================================================================= +// +// +// +//============================================================================= + +void DAutomap::calcMinMaxMtoF() +{ + const double safe_frame = 1.0 - am_emptyspacemargin / 100.0; + double a = safe_frame * (twod->GetWidth() / max_w); + double b = safe_frame * (StatusBar->GetTopOfStatusbar() / max_h); + + min_scale_mtof = a < b ? a : b; + max_scale_mtof = twod->GetHeight() / (2*PLAYERRADIUS); +} + +//============================================================================= +// +// +// +//============================================================================= + +void DAutomap::ClipRotatedExtents (double pivotx, double pivoty) +{ + if (am_rotate == 0 || (am_rotate == 2 && !viewactive)) + { + if (m_x + m_w/2 > max_x) + m_x = max_x - m_w/2; + else if (m_x + m_w/2 < min_x) + m_x = min_x - m_w/2; + + if (m_y + m_h/2 > max_y) + m_y = max_y - m_h/2; + else if (m_y + m_h/2 < min_y) + m_y = min_y - m_h/2; + } + + m_x2 = m_x + m_w; + m_y2 = m_y + m_h; +} + +//============================================================================= +// +// +// +//============================================================================= + +void DAutomap::ScrollParchment (double dmapx, double dmapy) +{ + mapxstart = mapxstart - dmapx * scale_mtof; + mapystart = mapystart - dmapy * scale_mtof; + + mapxstart = clamp(mapxstart, -40000., 40000.); + mapystart = clamp(mapystart, -40000., 40000.); + + if (mapback.isValid()) + { + auto backtex = TexMan.GetGameTexture(mapback); + + if (backtex != nullptr) + { + int pwidth = int(backtex->GetDisplayWidth() * CleanXfac); + int pheight = int(backtex->GetDisplayHeight() * CleanYfac); + + while(mapxstart > 0) + mapxstart -= pwidth; + while(mapxstart <= -pwidth) + mapxstart += pwidth; + while(mapystart > 0) + mapystart -= pheight; + while(mapystart <= -pheight) + mapystart += pheight; + } + } +} + +//============================================================================= +// +// +// +//============================================================================= + +void DAutomap::changeWindowLoc () +{ + if (m_paninc.x || m_paninc.y) + { + am_followplayer = false; + f_oldloc.x = FLT_MAX; + } + + double oldmx = m_x, oldmy = m_y; + double incx, incy, oincx, oincy; + + incx = m_paninc.x; + incy = m_paninc.y; + + oincx = incx = m_paninc.x * twod->GetWidth() / 320; + oincy = incy = m_paninc.y * twod->GetHeight() / 200; + if (am_rotate == 1 || (am_rotate == 2 && viewactive)) + { + rotate(&incx, &incy, playerAngle - 90.); + } + + m_x += incx; + m_y += incy; + + ClipRotatedExtents (oldmx + m_w/2, oldmy + m_h/2); + ScrollParchment (m_x != oldmx ? oincx : 0, m_y != oldmy ? -oincy : 0); +} + + +//============================================================================= +// +// +// +//============================================================================= + +void DAutomap::startDisplay() +{ + int pnum; + + f_oldloc.x = FLT_MAX; + amclock = 0; + + m_paninc.x = m_paninc.y = 0; + mtof_zoommul = 1.; + + m_w = FTOM(twod->GetWidth()); + m_h = FTOM(twod->GetHeight()); + + m_x = playerPos.X - m_w/2; + m_y = playerPos.Y - m_h/2; + changeWindowLoc(); + + // for saving & restoring + old_m_x = m_x; + old_m_y = m_y; + old_m_w = m_w; + old_m_h = m_h; +} + +//============================================================================= +// +// +// +//============================================================================= + +bool DAutomap::clearMarks () +{ + for (int i = AM_NUMMARKPOINTS-1; i >= 0; i--) + markpoints[i].x = -1; // means empty + markpointnum = 0; + return marknums[0].isValid(); +} + +//============================================================================= +// +// called right after the level has been loaded +// +//============================================================================= + +void DAutomap::LevelInit () +{ + //if (Level->info->MapBackground.Len() == 0) + { + mapback = TexMan.CheckForTexture("AUTOPAGE", ETextureType::MiscPatch); + } + /* + else + { + mapback = TexMan.CheckForTexture(Level->info->MapBackground, ETextureType::MiscPatch); + } + */ + + clearMarks(); + + findMinMaxBoundaries(); + scale_mtof = min_scale_mtof / 0.7; + if (scale_mtof > max_scale_mtof) + scale_mtof = min_scale_mtof; + scale_ftom = 1 / scale_mtof; + + UpdateShowAllLines(); +} + +//============================================================================= +// +// set the window scale to the maximum size +// +//============================================================================= + +void DAutomap::minOutWindowScale () +{ + scale_mtof = min_scale_mtof; + scale_ftom = 1/ scale_mtof; +} + +//============================================================================= +// +// set the window scale to the minimum size +// +//============================================================================= + +void DAutomap::maxOutWindowScale () +{ + scale_mtof = max_scale_mtof; + scale_ftom = 1 / scale_mtof; +} + +//============================================================================= +// +// Called right after the resolution has changed +// +//============================================================================= + +void DAutomap::NewResolution() +{ + double oldmin = min_scale_mtof; + + if ( oldmin == 0 ) + { + return; // [SP] Not in a game, exit! + } + calcMinMaxMtoF(); + scale_mtof = scale_mtof * min_scale_mtof / oldmin; + scale_ftom = 1 / scale_mtof; + if (scale_mtof < min_scale_mtof) + minOutWindowScale(); + else if (scale_mtof > max_scale_mtof) + maxOutWindowScale(); + f_w = twod->GetWidth(); + f_h = StatusBar->GetTopOfStatusbar(); + activateNewScale(); +} + + +//============================================================================= +// +// Handle events (user inputs) in automap mode +// +//============================================================================= + +bool DAutomap::Responder (event_t *ev, bool last) +{ + if (automapactive && (ev->type == EV_KeyDown || ev->type == EV_KeyUp)) + { + if (am_followplayer) + { + // check for am_pan* and ignore in follow mode + const char *defbind = AutomapBindings.GetBind(ev->data1); + if (defbind && !strnicmp(defbind, "+am_pan", 7)) return false; + } + + bool res = C_DoKey(ev, &AutomapBindings, nullptr); + if (res && ev->type == EV_KeyUp && !last) + { + // If this is a release event we also need to check if it released a button in the main Bindings + // so that that button does not get stuck. + const char *defbind = Bindings.GetBind(ev->data1); + return (!defbind || defbind[0] != '+'); // Let G_Responder handle button releases + } + return res; + } + return false; +} + + +//============================================================================= +// +// Zooming +// +//============================================================================= + +void DAutomap::changeWindowScale (double delta) +{ + + double mtof_zoommul; + + if (am_zoomdir > 0) + { + mtof_zoommul = M_OLDZOOMIN * am_zoomdir; + } + else if (am_zoomdir < 0) + { + mtof_zoommul = M_OLDZOOMOUT / -am_zoomdir; + } + else if (buttonMap.ButtonDown(gamefunc_Enlarge_Screen)) + { + mtof_zoommul = (1 + (M_ZOOMIN - 1) * delta); + } + else if (buttonMap.ButtonDown(gamefunc_Shrink_Screen)) + { + mtof_zoommul = (1 + (M_ZOOMOUT - 1) * delta); + } + else + { + mtof_zoommul = 1; + } + am_zoomdir = 0; + + // Change the scaling multipliers + scale_mtof = scale_mtof * mtof_zoommul; + scale_ftom = 1 / scale_mtof; + + if (scale_mtof < min_scale_mtof) + minOutWindowScale(); + else if (scale_mtof > max_scale_mtof) + maxOutWindowScale(); +} + +//============================================================================= +// +// +// +//============================================================================= + +void DAutomap::doFollowPlayer () +{ + double sx, sy; + + DVector2 ampos = playerPos; + + if (f_oldloc.x != ampos.X || f_oldloc.y != ampos.Y) + { + m_x = ampos.X - m_w / 2; + m_y = ampos.Y - m_h / 2; + m_x2 = m_x + m_w; + m_y2 = m_y + m_h; + + // do the parallax parchment scrolling. + sx = (ampos.X - f_oldloc.x); + sy = (f_oldloc.y - ampos.Y); + if (am_rotate == 1 || (am_rotate == 2 && viewactive)) + { + rotate(&sx, &sy, playerAngle - 90); + } + ScrollParchment(sx, sy); + + f_oldloc.x = ampos.X; + f_oldloc.y = ampos.Y; + } +} + +//============================================================================= +// +// Updates on Game Tick +// +//============================================================================= + +void DAutomap::Ticker () +{ + if (!automapactive) + return; + + amclock++; +} + + +//============================================================================= +// +// Clear automap frame buffer. +// +//============================================================================= + +void DAutomap::clearFB (const AMColor &color) +{ + bool drawback = mapback.isValid() && am_drawmapback != 0; + if (am_drawmapback == 2) + { + // only draw background when using a mod defined custom color set or Raven colors, if am_drawmapback is 2. + if (!am_customcolors || !AMMod.defined) + { + drawback &= (am_colorset == 3); + } + } + + if (!drawback) + { + ClearRect(twod, 0, 0, f_w, f_h, -1, color.RGB); + } + else + { + auto backtex = TexMan.GetGameTexture(mapback); + if (backtex != nullptr) + { + int pwidth = int(backtex->GetDisplayWidth() * CleanXfac); + int pheight = int(backtex->GetDisplayHeight() * CleanYfac); + int x, y; + + //blit the automap background to the screen. + for (y = int(mapystart); y < f_h; y += pheight) + { + for (x = int(mapxstart); x < f_w; x += pwidth) + { + DrawTexture(twod, backtex, x, y, DTA_ClipBottom, f_h, DTA_TopOffset, 0, DTA_LeftOffset, 0, DTA_DestWidth, pwidth, DTA_DestHeight, pheight, TAG_DONE); + } + } + } + } +} + + +//============================================================================= +// +// Automap clipping of lines. +// +// Based on Cohen-Sutherland clipping algorithm but with a slightly +// faster reject and precalculated slopes. If the speed is needed, +// use a hash algorithm to handle the common cases. +// +//============================================================================= + +bool DAutomap::clipMline (mline_t *ml, fline_t *fl) +{ + enum { + LEFT =1, + RIGHT =2, + BOTTOM =4, + TOP =8 + }; + + int outcode1 = 0; + int outcode2 = 0; + int outside; + + fpoint_t tmp = { 0, 0 }; + int dx; + int dy; + + auto DOOUTCODE = [this](int &oc, double mx, double my) + { + oc = 0; + if (my < 0) oc |= TOP; + else if (my >= f_h) oc |= BOTTOM; + if (mx < 0) oc |= LEFT; + else if (mx >= f_w) oc |= RIGHT; + }; + + // do trivial rejects and outcodes + if (ml->a.y > m_y2) + outcode1 = TOP; + else if (ml->a.y < m_y) + outcode1 = BOTTOM; + + if (ml->b.y > m_y2) + outcode2 = TOP; + else if (ml->b.y < m_y) + outcode2 = BOTTOM; + + if (outcode1 & outcode2) + return false; // trivially outside + + if (ml->a.x < m_x) + outcode1 |= LEFT; + else if (ml->a.x > m_x2) + outcode1 |= RIGHT; + + if (ml->b.x < m_x) + outcode2 |= LEFT; + else if (ml->b.x > m_x2) + outcode2 |= RIGHT; + + if (outcode1 & outcode2) + return false; // trivially outside + + // transform to frame-buffer coordinates. + fl->a.x = CXMTOF(ml->a.x); + fl->a.y = CYMTOF(ml->a.y); + fl->b.x = CXMTOF(ml->b.x); + fl->b.y = CYMTOF(ml->b.y); + + DOOUTCODE(outcode1, fl->a.x, fl->a.y); + DOOUTCODE(outcode2, fl->b.x, fl->b.y); + + if (outcode1 & outcode2) + return false; + + while (outcode1 | outcode2) { + // may be partially inside box + // find an outside point + if (outcode1) + outside = outcode1; + else + outside = outcode2; + + // clip to each side + if (outside & TOP) + { + dy = fl->a.y - fl->b.y; + dx = fl->b.x - fl->a.x; + tmp.x = fl->a.x + Scale(dx, fl->a.y, dy); + tmp.y = 0; + } + else if (outside & BOTTOM) + { + dy = fl->a.y - fl->b.y; + dx = fl->b.x - fl->a.x; + tmp.x = fl->a.x + Scale(dx, fl->a.y - f_h, dy); + tmp.y = f_h-1; + } + else if (outside & RIGHT) + { + dy = fl->b.y - fl->a.y; + dx = fl->b.x - fl->a.x; + tmp.y = fl->a.y + Scale(dy, f_w-1 - fl->a.x, dx); + tmp.x = f_w-1; + } + else if (outside & LEFT) + { + dy = fl->b.y - fl->a.y; + dx = fl->b.x - fl->a.x; + tmp.y = fl->a.y + Scale(dy, -fl->a.x, dx); + tmp.x = 0; + } + + if (outside == outcode1) + { + fl->a = tmp; + DOOUTCODE(outcode1, fl->a.x, fl->a.y); + } + else + { + fl->b = tmp; + DOOUTCODE(outcode2, fl->b.x, fl->b.y); + } + + if (outcode1 & outcode2) + return false; // trivially outside + } + + return true; +} + +//============================================================================= +// +// Clip lines, draw visible parts of lines. +// +//============================================================================= + +void DAutomap::drawMline (mline_t *ml, const AMColor &color) +{ + fline_t fl; + + if (clipMline (ml, &fl)) + { + const int x1 = f_x + fl.a.x; + const int y1 = f_y + fl.a.y; + const int x2 = f_x + fl.b.x; + const int y2 = f_y + fl.b.y; + if (am_linethickness >= 2) { + twod->AddThickLine(x1, y1, x2, y2, am_linethickness, color.RGB, uint8_t(am_linealpha * 255)); + } else { + // Use more efficient thin line drawing routine. + twod->AddLine(x1, y1, x2, y2, -1, -1, INT_MAX, INT_MAX, color.RGB, uint8_t(am_linealpha * 255)); + } + } +} + +void DAutomap::drawMline (mline_t *ml, int colorindex) +{ + drawMline(ml, AMColors[colorindex]); +} + +//============================================================================= +// +// Draws flat (floor/ceiling tile) aligned grid lines. +// +//============================================================================= + +void DAutomap::drawGrid (int color) +{ + double x, y; + double start, end; + mline_t ml; + double minlen, extx, exty; + double minx, miny; + + // [RH] Calculate a minimum for how long the grid lines should be so that + // they cover the screen at any rotation. + minlen = sqrt (m_w*m_w + m_h*m_h); + extx = (minlen - m_w) / 2; + exty = (minlen - m_h) / 2; + + minx = m_x; + miny = m_y; + + // Figure out start of vertical gridlines + start = int(minx - extx) & ~127; + + end = minx + minlen - extx; + + // draw vertical gridlines + for (x = start; x < end; x += 128) + { + ml.a.x = x; + ml.b.x = x; + ml.a.y = miny - exty; + ml.b.y = ml.a.y + minlen; + if (am_rotate == 1 || (am_rotate == 2 && viewactive)) + { + rotatePoint (&ml.a.x, &ml.a.y); + rotatePoint (&ml.b.x, &ml.b.y); + } + drawMline(&ml, color); + } + + // Figure out start of horizontal gridlines + start = miny - exty; + end = int(miny + minlen - exty) & ~127; + + // draw horizontal gridlines + for (y=start; yGetWidth(); + int height = screen->GetHeight(); + TArray vertices; + TArray floorsprites; + TArray points; + + auto transformPt = [=](const FVector2& pos) -> FVector2 + { + mpoint_t pt = { pos.X, pos.Y }; + if (am_rotate == 1 || (am_rotate == 2 && viewactive)) + { + rotatePoint(&pt.x, &pt.y); + } + return { float(f_x + ((pt.x - m_x) * scale_mtof)), float(f_y + (f_h - (pt.y - m_y) * scale_mtof)) }; + }; + + + for (int i = (int)sector.Size() - 1; i >= 0; i--) + { + auto sect = §or[i]; + if (am_cheat == 0 && !show2dsector[i]) continue; + + //Collect floor sprites to draw + TSectIterator it(sect); + while (auto act = it.Next()) + { + if (act->spr.cstat & CSTAT_SPRITE_INVISIBLE) + continue; + + if (act->spr.cstat & CSTAT_SPRITE_ALIGNMENT_FLOOR) // floor and slope sprites + { + if ((act->spr.cstat & (CSTAT_SPRITE_ONE_SIDE | CSTAT_SPRITE_YFLIP)) == (CSTAT_SPRITE_ONE_SIDE | CSTAT_SPRITE_YFLIP)) + continue; // upside down + floorsprites.Push(act); + } + } + + if (sect->floorstat & CSTAT_SECTOR_SKY) continue; + + int picnum = sect->floorpicnum; + if ((unsigned)picnum >= (unsigned)MAXTILES) continue; + + int translation = TRANSLATION(Translation_Remap + curbasepal, sector[i].floorpal); + PalEntry light = shadeToLight(sector[i].floorshade); + gotpic.Set(picnum); + + for (auto section : sectionsPerSector[i]) + { + TArray* indices; + auto mesh = sectionGeometry.get(§ions[section], 0, { 0.f, 0.f }, &indices); + vertices.Resize(mesh->vertices.Size()); + for (unsigned j = 0; j < mesh->vertices.Size(); j++) + { + auto p = transformPt(mesh->vertices[j]); + vertices[j] = { p.X, p.Y, mesh->texcoords[j].X, mesh->texcoords[j].Y }; + } + + twod->AddPoly(tileGetTexture(picnum, true), vertices.Data(), vertices.Size(), (unsigned*)indices->Data(), indices->Size(), translation, light, + LegacyRenderStyles[STYLE_Translucent], windowxy1.X, windowxy1.Y, windowxy2.X + 1, windowxy2.Y + 1); + } + } + qsort(floorsprites.Data(), floorsprites.Size(), sizeof(DCoreActor*), [](const void* a, const void* b) + { + auto A = *(DCoreActor**)a; + auto B = *(DCoreActor**)b; + if (A->spr.pos.Z != B->spr.pos.Z) return B->spr.pos.Z - A->spr.pos.Z; + return A->time - B->time; // ensures stable sort. + }); + + vertices.Resize(4); + + for (auto actor : floorsprites) + { + if (am_cheat == 0 && !(actor->spr.cstat2 & CSTAT2_SPRITE_MAPPED)) continue; + vec2_t pp[4]; + GetFlatSpritePosition(actor, actor->spr.pos.vec2, pp, true); + + for (unsigned j = 0; j < 4; j++) + { + auto p = transformPt(FVector2(pp[j].X * maptoworld, pp[j].Y * maptoworld)); + vertices[j] = { p.X, p.Y, j == 1 || j == 2 ? 1.f : 0.f, j == 2 || j == 3 ? 1.f : 0.f }; + } + int shade; + if ((actor->sector()->ceilingstat & CSTAT_SECTOR_SKY)) shade = actor->sector()->ceilingshade; + else shade = actor->sector()->floorshade; + shade += actor->spr.shade; + PalEntry color = shadeToLight(shade); + FRenderStyle rs = LegacyRenderStyles[STYLE_Translucent]; + float alpha = 1; + if (actor->spr.cstat & CSTAT_SPRITE_TRANSLUCENT) + { + rs = GetRenderStyle(0, !!(actor->spr.cstat & CSTAT_SPRITE_TRANS_FLIP)); + alpha = GetAlphaFromBlend((actor->spr.cstat & CSTAT_SPRITE_TRANS_FLIP) ? DAMETH_TRANS2 : DAMETH_TRANS1, 0); + color.a = uint8_t(alpha * 255); + } + + int translation = TRANSLATION(Translation_Remap + curbasepal, actor->spr.pal); + int picnum = actor->spr.picnum; + gotpic.Set(picnum); + const static unsigned indices[] = { 0, 1, 2, 0, 2, 3 }; + twod->AddPoly(tileGetTexture(picnum, true), vertices.Data(), vertices.Size(), indices, 6, translation, color, rs, + windowxy1.X, windowxy1.Y, windowxy2.X + 1, windowxy2.Y + 1); + } +} + +//============================================================================= +// +// +// +//============================================================================= + +static int AM_CheckSecret(walltype *line) +{ + if (AMColors.isValid(AMColors.SecretSectorColor)) + { + // todo + } + return 0; +} + + +//============================================================================= +// +// Determines visible lines, draws them. +// This is LineDef based, not LineSeg based. +// +//============================================================================= + +void DAutomap::drawWalls (bool allmap) +{ + static mline_t l; + int lock, color; + + for(unsigned i = 0; i < wall.Size(); i++) + { + auto& line = wall[i]; + if ((unsigned)line.nextwall < i) continue; + + DVector2 offset = { 0, 0 }; + + l.a.x = (line.pos.X); + l.a.y = (line.pos.Y); + l.b.x = (line.point2Wall()->pos.X); + l.b.y = (line.point2Wall()-> pos.Y); + + if (am_rotate == 1 || (am_rotate == 2 && viewactive)) + { + rotatePoint(&l.a.x, &l.a.y); + rotatePoint(&l.b.x, &l.b.y); + } + + if (am_cheat != 0 || show2dwall[i]) + { + if (AM_CheckSecret(&line) == 1) + { + // map secret sectors like Boom + drawMline(&l, AMColors.SecretSectorColor); + } + else if (AM_CheckSecret(&line) == 2) + { + drawMline(&l, AMColors.UnexploredSecretColor); + } + else if (line.nextwall == -1) + { + drawMline(&l, AMColors.WallColor); // one-sided wall + } + else if (line.nextSector()->floorz != line.sectorp()->floorz) + { + drawMline(&l, AMColors.FDWallColor); // floor level change + } + else if (line.nextSector()->ceilingz != line.sectorp()->ceilingz) + { + drawMline(&l, AMColors.CDWallColor); // ceiling level change + } + else if (am_cheat > 0 && am_cheat < 4) + { + drawMline(&l, AMColors.TSWallColor); + } + } + else if (allmap || show2dwall[i]) + { + drawMline(&l, AMColors.NotSeenColor); + } + } +} + + +//============================================================================= +// +// Rotation in 2D. +// Used to rotate player arrow line character. +// +//============================================================================= + +void DAutomap::rotate(double *xp, double *yp, DAngle a) +{ + static DAngle angle_saved = 0.; + static double sinrot = 0; + static double cosrot = 1; + + if (angle_saved != a) + { + angle_saved = a; + sinrot = sin(a.Radians()); + cosrot = cos(a.Radians()); + } + + double x = *xp; + double y = *yp; + double tmpx = (x * cosrot) - (y * sinrot); + y = (x * sinrot) + (y * cosrot); + x = tmpx; + *xp = x; + *yp = y; +} + +//============================================================================= +// +// +// +//============================================================================= + +void DAutomap::rotatePoint (double *x, double *y) +{ + double pivotx = m_x + m_w/2; + double pivoty = m_y + m_h/2; + *x -= pivotx; + *y -= pivoty; + rotate (x, y, playerAngle); + *x += pivotx; + *y += pivoty; +} + +//============================================================================= +// +// +// +//============================================================================= + +void DAutomap::drawLineCharacter(const mline_t *lineguy, size_t lineguylines, double scale, DAngle angle, const AMColor &color, double x, double y) +{ + mline_t l; + + for (size_t i=0;i= 2 && am_cheat != 4 && am_showthingsprites > 0) + { + // Player sprites are drawn with the others + return; + } + + mpoint_t pt; + DAngle angle; + int i; + + if (!multiplayer) + { + mline_t *arrow; + int numarrowlines; + + double vh = players[consoleplayer].viewheight; + DVector2 pos = players[consoleplayer].camera->InterpolatedPosition(r_viewpoint.TicFrac); + pt.x = pos.X; + pt.y = pos.Y; + if (am_rotate == 1 || (am_rotate == 2 && viewactive)) + { + angle = 90.; + rotatePoint (&pt.x, &pt.y); + } + else + { + angle = players[consoleplayer].camera->InterpolatedAngles(r_viewpoint.TicFrac).Yaw; + } + + if (am_cheat != 0 && CheatMapArrow.Size() > 0) + { + arrow = &CheatMapArrow[0]; + numarrowlines = CheatMapArrow.Size(); + } + else + { + arrow = &MapArrow[0]; + numarrowlines = MapArrow.Size(); + } + drawLineCharacter(arrow, numarrowlines, 0, angle, AMColors[AMColors.YourColor], pt.x, pt.y); + return; + } + + for (i = 0; i < MAXPLAYERS; i++) + { + player_t *p = &players[i]; + AMColor color; + + if (!playeringame[i] || p->mo == nullptr) + { + continue; + } + + // We don't always want to show allies on the automap. + if (dmflags2 & DF2_NO_AUTOMAP_ALLIES && i != consoleplayer) + continue; + + if (deathmatch && !demoplayback && + !p->mo->IsTeammate (players[consoleplayer].mo) && + p != players[consoleplayer].camera->player) + { + continue; + } + + if (p->mo->Alpha < 1.) + { + color = AMColors[AMColors.AlmostBackgroundColor]; + } + else + { + float h, s, v, r, g, b; + + D_GetPlayerColor (i, &h, &s, &v, nullptr); + HSVtoRGB (&r, &g, &b, h, s, v); + + color.FromRGB(clamp (int(r*255.f),0,255), clamp (int(g*255.f),0,255), clamp (int(b*255.f),0,255)); + } + + if (p->mo != nullptr) + { + DVector3 pos = p->mo->PosRelative(MapPortalGroup); + pt.x = pos.X; + pt.y = pos.Y; + + angle = p->mo->InterpolatedAngles(r_viewpoint.TicFrac).Yaw; + + if (am_rotate == 1 || (am_rotate == 2 && viewactive)) + { + rotatePoint (&pt.x, &pt.y); + angle -= players[consoleplayer].camera->InterpolatedAngles(r_viewpoint.TicFrac).Yaw - 90.; + } + + drawLineCharacter(&MapArrow[0], MapArrow.Size(), 0, angle, color, pt.x, pt.y); + } + } +#endif +} + +//============================================================================= +// +// +// +//============================================================================= + +void DAutomap::drawKeys () +{ + AMColor color; + mpoint_t p; + DAngle angle; + + TSpriteIterator it; + DCoreActor *key; + + while ((key = it.Next()) != nullptr) + { + DVector3 pos = key->float_pos(); + p.x = pos.X; + p.y = pos.Y; + + angle = buildang(key->spr.ang).asdeg(); + + if (am_rotate == 1 || (am_rotate == 2 && viewactive)) + { + rotatePoint (&p.x, &p.y); + angle += playerAngle; + } + + // Find the key's own color. + // Only works correctly if single-key locks have lower numbers than any-key locks. + // That is the case for all default keys, however. + int c = 0;// P_GetMapColorForKey(key); + + if (c >= 0) + { + color.FromRGB(RPART(c), GPART(c), BPART(c)); + drawLineCharacter(&EasyKey[0], EasyKey.Size(), 0, 0., color, p.x, p.y); + } + } +} + +//============================================================================= +// +// +// +//============================================================================= +void DAutomap::drawThings () +{ +#if 0 + AMColor color; + AActor* t; + mpoint_t p; + DAngle angle; + + for (auto &sec : Level->sectors) + { + t = sec.thinglist; + while (t) + { + if (am_cheat > 0 || !(t->flags6 & MF6_NOTONAUTOMAP) + || (am_thingrenderstyles && !(t->renderflags & RF_INVISIBLE) && !(t->flags6 & MF6_NOTONAUTOMAP))) + { + DVector3 pos = t->InterpolatedPosition(r_viewpoint.TicFrac) + t->Level->Displacements.getOffset(sec.PortalGroup, MapPortalGroup); + p.x = pos.X; + p.y = pos.Y; + + if (am_showthingsprites > 0 && t->sprite > 0) + { + FGameTexture *texture = nullptr; + spriteframe_t *frame; + int rotation = 0; + + // try all modes backwards until a valid texture has been found. + for(int show = am_showthingsprites; show > 0 && texture == nullptr; show--) + { + const spritedef_t& sprite = sprites[t->sprite]; + const size_t spriteIndex = sprite.spriteframes + (show > 1 ? t->frame : 0); + + frame = &SpriteFrames[spriteIndex]; + DAngle angle = 270. + 22.5 - t->InterpolatedAngles(r_viewpoint.TicFrac).Yaw; + if (frame->Texture[0] != frame->Texture[1]) angle += 180. / 16; + if (am_rotate == 1 || (am_rotate == 2 && viewactive)) + { + angle += players[consoleplayer].camera->InterpolatedAngles(r_viewpoint.TicFrac).Yaw - 90.; + } + rotation = int((angle.Normalized360() * (16. / 360.)).Degrees); + + const FTextureID textureID = frame->Texture[show > 2 ? rotation : 0]; + texture = TexMan.GetGameTexture(textureID, true); + } + + if (texture == nullptr) goto drawTriangle; // fall back to standard display if no sprite can be found. + + const double spriteXScale = (t->Scale.X * (10. / 16.) * scale_mtof); + const double spriteYScale = (t->Scale.Y * (10. / 16.) * scale_mtof); + + if (am_thingrenderstyles) DrawMarker(texture, p.x, p.y, 0, !!(frame->Flip & (1 << rotation)), + spriteXScale, spriteYScale, t->Translation, t->Alpha, t->fillcolor, t->RenderStyle); + else DrawMarker(texture, p.x, p.y, 0, !!(frame->Flip & (1 << rotation)), + spriteXScale, spriteYScale, t->Translation, 1., 0, LegacyRenderStyles[STYLE_Normal]); + } + else + { + drawTriangle: + angle = t->InterpolatedAngles(r_viewpoint.TicFrac).Yaw; + + if (am_rotate == 1 || (am_rotate == 2 && viewactive)) + { + rotatePoint (&p.x, &p.y); + angle += -players[consoleplayer].camera->InterpolatedAngles(r_viewpoint.TicFrac).Yaw + 90.; + } + + color = AMColors[AMColors.ThingColor]; + + // use separate colors for special thing types + if (t->flags3&MF3_ISMONSTER && !(t->flags&MF_CORPSE)) + { + if (t->flags & MF_FRIENDLY) color = AMColors[AMColors.ThingColor_Friend]; + else if (!(t->flags & MF_COUNTKILL)) color = AMColors[AMColors.ThingColor_NocountMonster]; + else color = AMColors[AMColors.ThingColor_Monster]; + } + else if (t->flags&MF_SPECIAL) + { + // Find the key's own color. + // Only works correctly if single-key locks have lower numbers than any-key locks. + // That is the case for all default keys, however. + if (t->IsKindOf(NAME_Key)) + { + if (G_SkillProperty(SKILLP_EasyKey) || am_showkeys_always) + { + // Already drawn by AM_drawKeys(), so don't draw again + color.RGB = 0; + } + else if (am_showkeys) + { + int c = P_GetMapColorForKey(t); + + if (c >= 0) color.FromRGB(RPART(c), GPART(c), BPART(c)); + else color = AMColors[AMColors.ThingColor_CountItem]; + drawLineCharacter(&CheatKey[0], CheatKey.Size(), 0, 0., color, p.x, p.y); + color.RGB = 0; + } + else + { + color = AMColors[AMColors.ThingColor_Item]; + } + } + else if (t->flags&MF_COUNTITEM) + color = AMColors[AMColors.ThingColor_CountItem]; + else + color = AMColors[AMColors.ThingColor_Item]; + } + + if (color.isValid()) + { + drawLineCharacter(thintriangle_guy.data(), thintriangle_guy.size(), 16, angle, color, p.x, p.y); + } + + if (am_cheat == 3 || am_cheat == 6) + { + static const mline_t box[4] = + { + { { -1, -1 }, { 1, -1 } }, + { { 1, -1 }, { 1, 1 } }, + { { 1, 1 }, { -1, 1 } }, + { { -1, 1 }, { -1, -1 } }, + }; + + drawLineCharacter (box, 4, t->radius, angle - t->InterpolatedAngles(r_viewpoint.TicFrac).Yaw, color, p.x, p.y); + } + } + } + t = t->snext; + } + } +#endif +} + +//============================================================================= +// +// +// +//============================================================================= + +void DAutomap::DrawMarker (FGameTexture *tex, double x, double y, int yadjust, + INTBOOL flip, double xscale, double yscale, int translation, double alpha, uint32_t fillcolor, FRenderStyle renderstyle) +{ + if (tex == nullptr || !tex->isValid()) + { + return; + } + if (xscale < 0) + { + flip = !flip; + xscale = -xscale; + } + if (am_rotate == 1 || (am_rotate == 2 && viewactive)) + { + rotatePoint (&x, &y); + } + DrawTexture(twod, tex, CXMTOF(x) + f_x, CYMTOF(y) + yadjust + f_y, + DTA_DestWidthF, tex->GetDisplayWidth() * CleanXfac * xscale, + DTA_DestHeightF, tex->GetDisplayHeight() * CleanYfac * yscale, + DTA_ClipTop, f_y, + DTA_ClipBottom, f_y + f_h, + DTA_ClipLeft, f_x, + DTA_ClipRight, f_x + f_w, + DTA_FlipX, flip, + DTA_TranslationIndex, translation, + DTA_Alpha, alpha, + DTA_FillColor, fillcolor, + DTA_RenderStyle, renderstyle.AsDWORD, + TAG_DONE); +} + +//============================================================================= +// +// +// +//============================================================================= + +void DAutomap::drawMarks () +{ + FFont* font; + bool fontloaded = false; + + for (int i = 0; i < AM_NUMMARKPOINTS; i++) + { + if (markpoints[i].x != -1) + { + if (!fontloaded) + { + font = stricmp(*am_markfont, DEFAULT_FONT_NAME) == 0 ? nullptr : V_GetFont(am_markfont); + fontloaded = true; + } + + if (font == nullptr) + { + DrawMarker(TexMan.GetGameTexture(marknums[i], true), markpoints[i].x, markpoints[i].y, -3, 0, + 1, 1, 0, 1, 0, LegacyRenderStyles[STYLE_Normal]); + } + else + { + char numstr[2] = { char('0' + i), 0 }; + double x = markpoints[i].x; + double y = markpoints[i].y; + + if (am_rotate == 1 || (am_rotate == 2 && viewactive)) + { + rotatePoint (&x, &y); + } + + DrawText(twod, font, am_markcolor, CXMTOF(x), CYMTOF(y), numstr, TAG_DONE); + } + } + } +} + +//============================================================================= +// +// +// +//============================================================================= + +void DAutomap::drawAuthorMarkers () +{ +#if 0 + // [RH] Draw any actors derived from AMapMarker on the automap. + // If args[0] is 0, then the actor's sprite is drawn at its own location. + // Otherwise, its sprite is drawn at the location of any actors whose TIDs match args[0]. + auto it = Level->GetThinkerIterator(NAME_MapMarker, STAT_MAPMARKER); + AActor *mark; + + while ((mark = it.Next()) != nullptr) + { + if (mark->flags2 & MF2_DORMANT) + { + continue; + } + + FTextureID picnum; + FGameTexture *tex; + uint16_t flip = 0; + + if (mark->picnum.isValid()) + { + tex = TexMan.GetGameTexture(mark->picnum, true); + if (tex->GetRotations() != 0xFFFF) + { + spriteframe_t *sprframe = &SpriteFrames[tex->GetRotations()]; + picnum = sprframe->Texture[0]; + flip = sprframe->Flip & 1; + tex = TexMan.GetGameTexture(picnum); + } + } + else + { + spritedef_t *sprdef = &sprites[mark->sprite]; + if (mark->frame >= sprdef->numframes) + { + continue; + } + else + { + spriteframe_t *sprframe = &SpriteFrames[sprdef->spriteframes + mark->frame]; + picnum = sprframe->Texture[0]; + flip = sprframe->Flip & 1; + tex = TexMan.GetGameTexture(picnum); + } + } + auto it = Level->GetActorIterator(mark->args[0]); + AActor *marked = mark->args[0] == 0 ? mark : it.Next(); + + double xscale = mark->Scale.X; + double yscale = mark->Scale.Y; + // [MK] scale with automap zoom if args[2] is 1, otherwise keep a constant scale + if (mark->args[2] == 1) + { + xscale = MTOF(xscale); + yscale = MTOF(yscale); + } + + while (marked != nullptr) + { + if (mark->args[1] == 0 || (mark->args[1] == 1 && (marked->subsector->flags & SSECMF_DRAWN))) + { + DrawMarker (tex, marked->X(), marked->Y(), 0, flip, xscale, yscale, mark->Translation, + mark->Alpha, mark->fillcolor, mark->RenderStyle); + } + marked = mark->args[0] != 0 ? it.Next() : nullptr; + } + } +#endif +} + +//============================================================================= +// +// +// +//============================================================================= + +void DAutomap::drawCrosshair (const AMColor &color) +{ + twod->AddPixel(f_w/2, (f_h+1)/2, color.RGB); +} + +//============================================================================= +// +// +// +//============================================================================= + +void DAutomap::Drawer (int bottom) +{ + static uint64_t LastMS = 0; + // Use a delta to zoom/pan at a constant speed regardless of current FPS + uint64_t ms = screen->FrameTime; + double delta = (ms - LastMS) * 0.001; + + if (!automapactive) + return; + + if (am_followplayer) + { + doFollowPlayer(); + } + else + { + m_paninc.x = m_paninc.y = 0; + if (buttonMap.ButtonDown(gamefunc_AM_PanLeft)) + m_paninc.x -= FTOM(F_PANINC) * delta * GameTicRate; + if (buttonMap.ButtonDown(gamefunc_AM_PanRight)) + m_paninc.x += FTOM(F_PANINC) * delta * GameTicRate; + if (buttonMap.ButtonDown(gamefunc_AM_PanUp)) + m_paninc.y += FTOM(F_PANINC) * delta * GameTicRate; + if (buttonMap.ButtonDown(gamefunc_AM_PanDown)) + m_paninc.y -= FTOM(F_PANINC) * delta * GameTicRate; + } + + // Change the zoom if necessary + if (buttonMap.ButtonDown(gamefunc_Enlarge_Screen) || buttonMap.ButtonDown(gamefunc_Shrink_Screen) || am_zoomdir != 0) + changeWindowScale(delta); + + // Change x,y location + changeWindowLoc(); + + bool allmap = false; + bool allthings = false; + + AM_initColors (viewactive); + + if (!viewactive) + { + // [RH] Set f_? here now to handle automap overlaying + // and view size adjustments. + f_x = f_y = 0; + f_w = twod->GetWidth (); + f_h = bottom; + + clearFB(AMColors[AMColors.Background]); + } + else + { + f_x = windowxy1.X; + f_y = windowxy1.Y; + f_w = windowxy2.X - f_x; + f_h = windowxy2.Y - f_y; + } + activateNewScale(); + + if (am_textured && !viewactive) + drawSubsectors(); + + if (am_showgrid) + drawGrid(AMColors.GridColor); + + drawWalls(allmap); + drawPlayers(); + if (am_showkeys_always) + drawKeys(); + if ((am_cheat >= 2 && am_cheat != 4) || allthings) + drawThings(); + + drawAuthorMarkers(); + + if (!viewactive) + drawCrosshair(AMColors[AMColors.XHairColor]); + + drawMarks(); + + LastMS = ms; +} + +//============================================================================= +// +// +// +//============================================================================= + +void DAutomap::Serialize(FSerializer &arc) +{ + Super::Serialize(arc); + // This only stores those variables which do not get set each time the automap is either activated or drawn. + // Especially the screen coordinates can not be brought over because the display settings may have changed. + arc("markpointnum", markpointnum) + .Array("markpoints", &markpoints[0].x, AM_NUMMARKPOINTS * 2) // write as a double array. + ("scale_mtof", scale_mtof) + ("scale_ftom", scale_ftom) + ("bigstate", bigstate) + ("min_x", min_x) + ("min_y", min_y) + ("max_x", max_x) + ("max_y", max_y) + ("min_w", min_w) + ("min_h", min_h) + ("max_w", max_w) + ("max_h", max_h) + ("min_scale_mtof", min_scale_mtof) + ("max_scale_mtof", max_scale_mtof) + ("mapback", mapback); + + +} + + +//============================================================================= +// +// +// +//============================================================================= + +void DAutomap::UpdateShowAllLines() +{ +} + +void DAutomap::GoBig() +{ + bigstate = !bigstate; + if (bigstate) + { + saveScaleAndLoc(); + minOutWindowScale(); + } + else + restoreScaleAndLoc(); +} + +void DAutomap::ResetFollowLocation() +{ + f_oldloc.x = FLT_MAX; +} + + +//============================================================================= +// +// +// +//============================================================================= + +void AM_Stop() +{ + automapactive = false; + automap->viewactive = true; +} + +//============================================================================= +// +// +// +//============================================================================= + +void AM_ToggleMap() +{ + if (gamestate != GS_LEVEL) + return; + + if (!automapactive) + { + // Reset AM buttons + buttonMap.ClearButton(gamefunc_AM_PanLeft); + buttonMap.ClearButton(gamefunc_AM_PanRight); + buttonMap.ClearButton(gamefunc_AM_PanUp); + buttonMap.ClearButton(gamefunc_AM_PanDown); + buttonMap.ClearButton(gamefunc_Shrink_Screen); + buttonMap.ClearButton(gamefunc_Enlarge_Screen); + + automap->startDisplay(); + automapactive = true; + automap->viewactive = (am_overlay != 0.f); + } + else + { + if (am_overlay == 1 && automap->viewactive) + { + automap->viewactive = false; + } + else + { + AM_Stop(); + } + } +} + +//============================================================================= +// +// +// +//============================================================================= + +DAutomapBase *AM_Create(FLevelLocals *Level) +{ + auto am = Create(); + return am; +} diff --git a/source/core/am_map.h b/source/core/am_map.h new file mode 100755 index 000000000..941c85960 --- /dev/null +++ b/source/core/am_map.h @@ -0,0 +1,77 @@ +//----------------------------------------------------------------------------- +// +// Copyright 1993-1996 id Software +// Copyright 1999-2016 Randy Heit +// Copyright 2002-2016 Christoph Oelckers +// +// 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 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//----------------------------------------------------------------------------- +// +#ifndef __AMMAP_H__ +#define __AMMAP_H__ + +#include "dobject.h" +#include "maptypes.h" + +struct event_t; +class FSerializer; +struct FLevelLocals; + +class DAutomapBase : public DObject +{ + DECLARE_ABSTRACT_CLASS(DAutomapBase, DObject); +public: + DVector2 playerPos; + DAngle playerAngle; + bool viewactive; + + // Called by main loop. + virtual bool Responder(event_t* ev, bool last) = 0; + + // Called by main loop. + virtual void Ticker(void) = 0; + + // Called by main loop, + // called instead of view drawer if automap active. + virtual void Drawer(int bottom) = 0; + + virtual void NewResolution() = 0; + virtual void LevelInit() = 0; + virtual void UpdateShowAllLines() = 0; + virtual void GoBig() = 0; + virtual void ResetFollowLocation() = 0; + virtual int addMark() = 0; + virtual bool clearMarks() = 0; + virtual DVector2 GetPosition() = 0; + virtual void startDisplay() = 0; + + void SetPlayerPos(int x, int y) + { + playerPos.X = maptoworld * x; + playerPos.Y = maptoworld * y; + } + +}; + +extern BitArray show2dsector; +extern BitArray show2dwall; + +void AM_StaticInit(); +void AM_ClearColorsets(); // reset data for a restart. +DAutomapBase *AM_Create(FLevelLocals *Level); +void AM_Stop(); +void AM_ToggleMap(); + +#endif diff --git a/source/core/gamecontrol.h b/source/core/gamecontrol.h index 191f071e0..5fff60c81 100644 --- a/source/core/gamecontrol.h +++ b/source/core/gamecontrol.h @@ -261,6 +261,7 @@ enum gameaction_t : int ga_loadgamehidecon, ga_newgamenostopsound, // start a new game ga_endscreenjob, + ga_togglemap, ga_fullconsole, }; diff --git a/source/core/gi.h b/source/core/gi.h index a68ac8466..b772c5aae 100644 --- a/source/core/gi.h +++ b/source/core/gi.h @@ -47,6 +47,11 @@ struct gameinfo_t FName mSliderBackColor; FString mBackButton; FString TitlePage; + + FString mMapArrow; + FString mCheatMapArrow; + FString mEasyKey; + FString mCheatKey; };