diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 8c8460de6..35c1265ca 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -638,6 +638,9 @@ file( GLOB HEADER_FILES core/music/*.h core/menu/*.h core/input/*.h + core/rendering/*.h + core/rendering/scene/*.h + core/nodebuilder/*.h common/audio/sound/thirdparty/*.h common/audio/sound/*.h @@ -746,7 +749,6 @@ set( NOT_COMPILED_SOURCE_FILES games/blood/src/callback.cpp games/blood/src/choke.cpp games/blood/src/controls.cpp - games/blood/src/credits.cpp games/blood/src/db.cpp games/blood/src/dude.cpp games/blood/src/d_menu.cpp @@ -832,7 +834,6 @@ set( NOT_COMPILED_SOURCE_FILES games/duke/src/spawn_r.cpp # Shadow Warrior - games/sw/src/2d.cpp games/sw/src/actor.cpp games/sw/src/ai.cpp games/sw/src/break.cpp @@ -1019,19 +1020,15 @@ set (FASTMATH_SOURCES ${FASTMATH_SOURCES}) set (PCH_SOURCES glbackend/glbackend.cpp - glbackend/gl_palmanager.cpp glbackend/gl_texture.cpp - glbackend/gl_models.cpp thirdparty/src/md4.cpp # Todo: Split out the license-safe code from this. build/src/clip.cpp - build/src/defs.cpp build/src/engine.cpp build/src/mdsprite.cpp build/src/polymost.cpp - build/src/voxmodel.cpp core/movie/playmve.cpp core/movie/movieplayer.cpp @@ -1050,6 +1047,7 @@ set (PCH_SOURCES core/gamehud.cpp core/gamefuncs.cpp core/gameinput.cpp + core/g_mapinfo.cpp core/interpolate.cpp core/inputstate.cpp core/maphack.cpp @@ -1064,12 +1062,38 @@ set (PCH_SOURCES core/precache.cpp core/quotes.cpp core/screenshot.cpp + core/sectorgeometry.cpp core/raze_music.cpp core/raze_sound.cpp core/palette.cpp core/zcompile.cpp core/statusbar2.cpp core/gi.cpp + core/defparser.cpp + + core/nodebuilder/nodebuild.cpp + core/nodebuilder/nodebuild_classify_nosse2.cpp + core/nodebuilder/nodebuild_events.cpp + core/nodebuilder/nodebuild_extract.cpp + core/nodebuilder/nodebuild_gl.cpp + core/nodebuilder/nodebuild_utility.cpp + + core/rendering/hw_entrypoint.cpp + core/rendering/hw_models.cpp + core/rendering/hw_voxels.cpp + core/rendering/hw_palmanager.cpp + core/rendering/hw_sections.cpp + core/rendering/scene/hw_clipper.cpp + core/rendering/scene/hw_walls.cpp + core/rendering/scene/hw_flats.cpp + core/rendering/scene/hw_sprites.cpp + core/rendering/scene/hw_drawlistadd.cpp + core/rendering/scene/hw_drawlist.cpp + core/rendering/scene/hw_drawinfo.cpp + core/rendering/scene/hw_bunchdrawer.cpp + core/rendering/scene/hw_portal.cpp + core/rendering/scene/hw_skyportal.cpp + core/rendering/scene/hw_sky.cpp core/console/c_notifybuffer.cpp core/console/d_event.cpp @@ -1274,6 +1298,7 @@ set (PCH_SOURCES games/exhumed/all.cpp games/blood/all.cpp games/sw/all.cpp + ) if( ${HAVE_VM_JIT} ) @@ -1363,6 +1388,8 @@ include_directories( core/dobject core/menu core/input + core/rendering + core/rendering/scene platform common/audio/sound common/audio/music @@ -1513,6 +1540,8 @@ source_group("Core\\2D" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/core/2d source_group("Core\\Console" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/core/console/.+") source_group("Core\\DObject" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/core/dobject/.+") source_group("Core\\Menu" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/core/menu/.+") +source_group("Core\\Rendering" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/core/rendering/.+") +source_group("Core\\Rendering\\Scene" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/core/rendering/scene/.+") source_group("Rendering" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/glbackend/.+") source_group("Platform" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/platform/.+") source_group("Platform\\Win32" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/platform/win32/.+") diff --git a/source/build/include/build.h b/source/build/include/build.h index 027be46bb..74c7ec3d9 100644 --- a/source/build/include/build.h +++ b/source/build/include/build.h @@ -15,12 +15,10 @@ static_assert('\xff' == 255, "Char must be unsigned!"); -#if !defined __cplusplus || (__cplusplus < 201103L && !defined _MSC_VER) -# error C++11 or greater is required. -#endif - #include "compat.h" +#include "printf.h" #include "palette.h" +#include "binaryangle.h" //Make all variables in BUILD.H defined in the ENGINE, //and externed in GAME @@ -35,7 +33,6 @@ EXTERN int16_t sintable[2048]; #include "buildtiles.h" #include "c_cvars.h" #include "cmdlib.h" -#include "binaryangle.h" #include "mathutil.h" typedef int64_t coord_t; @@ -48,11 +45,6 @@ enum MAXVOXMIPS = 5, - MAXXDIM = 7680, - MAXYDIM = 3200, - MINXDIM = 640, - MINYDIM = 480, - MAXWALLSB = ((MAXWALLS >> 2) + (MAXWALLS >> 3)), MAXVOXELS = 1024, @@ -100,24 +92,11 @@ enum { }; -enum { - SPR_XFLIP = 4, - SPR_YFLIP = 8, - - SPR_WALL = 16, - SPR_FLOOR = 32, - SPR_ALIGN_MASK = 32+16, -}; - #include "buildtypes.h" -using usectortype = sectortype; -using uwalltype = walltype; -using uspritetype = spritetype; - -using uspriteptr_t = uspritetype const *; -using uwallptr_t = uwalltype const *; -using usectorptr_t = usectortype const *; +using uspriteptr_t = spritetype const *; +using uwallptr_t = walltype const *; +using usectorptr_t = sectortype const *; using tspriteptr_t = tspritetype *; @@ -150,21 +129,14 @@ typedef struct { #define SPREXT_TSPRACCESS 16 #define SPREXT_TEMPINVISIBLE 32 -#define NEG_ALPHA_TO_BLEND(alpha, blend, orientation) do { \ - if ((alpha) < 0) { (blend) = -(alpha); (alpha) = 0; (orientation) |= RS_TRANS1; } \ -} while (0) - // using the clipdist field enum { TSPR_FLAGS_MDHACK = 1u<<0u, TSPR_FLAGS_DRAW_LAST = 1u<<1u, - TSPR_FLAGS_NO_SHADOW = 1u<<2u, - TSPR_FLAGS_INVISIBLE_WITH_SHADOW = 1u<<3u, }; EXTERN int32_t guniqhudid; -EXTERN int32_t spritesortcnt; struct usermaphack_t { @@ -176,55 +148,35 @@ struct usermaphack_t EXTERN spriteext_t *spriteext; EXTERN spritesmooth_t *spritesmooth; -// Wrapper that makes an array of pointers look like an array of references. (Refactoring helper.) - -template -class ReferenceArray -{ - T* data[size]; -public: - T& operator[](size_t index) - { - assert(index < size); - return *data[index]; - } - - void set(int pos, T* spr) - { - data[pos] = spr; - } -}; - EXTERN sectortype *sector; EXTERN walltype *wall; EXTERN spritetype *sprite; -EXTERN tspriteptr_t tsprite; +EXTERN int leveltimer; extern sectortype sectorbackup[MAXSECTORS]; extern walltype wallbackup[MAXWALLS]; -static inline tspriteptr_t renderMakeTSpriteFromSprite(tspriteptr_t const tspr, uint16_t const spritenum) +inline tspriteptr_t renderAddTSpriteFromSprite(spritetype* tsprite, int& spritesortcnt, uint16_t const spritenum) { + auto tspr = &tsprite[spritesortcnt++]; auto const spr = &sprite[spritenum]; - *tspr = *spr; - tspr->clipdist = 0; tspr->owner = spritenum; - return tspr; } -static inline tspriteptr_t renderAddTSpriteFromSprite(uint16_t const spritenum) +// returns: 0=continue sprite collecting; +// 1=break out of sprite collecting; +inline int32_t renderAddTsprite(spritetype* tsprite, int& spritesortcnt, int16_t z, int16_t sectnum) { - return renderMakeTSpriteFromSprite(&tsprite[spritesortcnt++], spritenum); + if (spritesortcnt >= MAXSPRITESONSCREEN) return 1; + renderAddTSpriteFromSprite(tsprite, spritesortcnt, z); + return 0; } -EXTERN int16_t maskwall[MAXWALLSB], maskwallcnt; -EXTERN int16_t thewall[MAXWALLSB]; -EXTERN tspriteptr_t tspriteptr[MAXSPRITESONSCREEN + 1]; EXTERN int32_t xdim, ydim; EXTERN int32_t yxaspect, viewingrange; @@ -235,30 +187,15 @@ EXTERN int32_t display_mirror; EXTERN int32_t randomseed; -EXTERN int16_t numshades; EXTERN uint8_t paletteloaded; -// Return type is int because this gets passed to variadic functions where structs may produce undefined behavior. -inline int shadeToLight(int shade) -{ - shade = clamp(shade, 0, numshades-1); - int light = Scale(numshades-1-shade, 255, numshades-1); - return PalEntry(255,light,light,light); -} - -EXTERN int32_t maxspritesonscreen; - enum { PALETTE_MAIN = 1<<0, PALETTE_SHADE = 1<<1, PALETTE_TRANSLUC = 1<<2, }; -EXTERN int32_t g_visibility, parallaxvisibility; - -// blendtable[1] to blendtable[numalphatabs] are considered to be -// alpha-blending tables: -EXTERN uint8_t numalphatabs; +EXTERN int32_t g_visibility; EXTERN vec2_t windowxy1, windowxy2; @@ -271,13 +208,13 @@ typedef struct { // The proportion at which looking up/down affects the apparent 'horiz' of // a parallaxed sky, scaled by 65536 (so, a value of 65536 makes it align // with the drawn surrounding scene): - int32_t horizfrac; + int horizfrac; // The texel index offset in the y direction of a parallaxed sky: // XXX: currently always 0. - int32_t yoffs; + int yoffs; - int8_t lognumtiles; // 1<>3]; -EXTERN char gotsector[(MAXSECTORS+7)>>3]; +extern FixedBitArray gotsector; extern uint32_t drawlinepat; -extern int32_t novoxmips; - -extern int16_t tiletovox[MAXTILES]; -extern int32_t voxscale[MAXVOXELS]; -extern char g_haveVoxels; - extern uint8_t globalr, globalg, globalb; enum { @@ -332,8 +268,6 @@ enum { GLOBAL_NO_GL_FOGSHADE = 1<<2, }; -extern int32_t globalflags; - extern const char *engineerrstr; EXTERN int32_t editorzrange[2]; @@ -377,22 +311,8 @@ SPRITE VARIABLES: be in some sector, and must have some kind of status that you define. -TILE VARIABLES: - NUMTILES - the number of tiles found TILES.DAT. - -TIMING VARIABLES: - NUMFRAMES - The number of times the draw3dscreen function was called - since the engine was initialized. This helps to determine frame - rate. (Frame rate = numframes * 120 / I_GetBuildTime().) - OTHER VARIABLES: - STARTUMOST[320] is an array of the highest y-coordinates on each column - that my engine is allowed to write to. You need to set it only - once. - STARTDMOST[320] is an array of the lowest y-coordinates on each column - that my engine is allowed to write to. You need to set it only - once. SINTABLE[2048] is a sin table with 2048 angles rather than the normal 360 angles for higher precision. Also since SINTABLE is in all integers, the range is multiplied by 16383, so instead of the @@ -425,29 +345,14 @@ void engineLoadBoard(const char *filename, int flags, vec3_t *dapos, int16_t *da void loadMapBackup(const char* filename); void G_LoadMapHack(const char* filename, const unsigned char*); -int32_t qloadkvx(int32_t voxindex, const char *filename); -void vox_undefine(int32_t const); -void vox_deinit(); - void videoSetCorrectedAspect(); void videoSetViewableArea(int32_t x1, int32_t y1, int32_t x2, int32_t y2); void renderSetAspect(int32_t daxrange, int32_t daaspect); -void plotpixel(int32_t x, int32_t y, char col); FCanvasTexture *renderSetTarget(int16_t tilenume); void renderRestoreTarget(); -void renderPrepareMirror(int32_t dax, int32_t day, int32_t daz, fixed_t daang, fixed_t dahoriz, int16_t dawall, - int32_t *tposx, int32_t *tposy, fixed_t *tang); -void renderCompleteMirror(void); -int32_t renderDrawRoomsQ16(int32_t daposx, int32_t daposy, int32_t daposz, fixed_t daang, fixed_t dahoriz, int16_t dacursectnum); - -void renderDrawMasks(void); void setVideoMode(); -void videoInit(); -void videoClearViewableArea(int32_t dacol); -void videoClearScreen(int32_t dacol); -void renderDrawMapView(int32_t dax, int32_t day, int32_t zoome, int16_t ang); class F2DDrawer; @@ -498,13 +403,12 @@ void updatesectorneighbor(int32_t const x, int32_t const y, int16_t * const sect void updatesectorneighborz(int32_t const x, int32_t const y, int32_t const z, int16_t * const sectnum, int32_t initialMaxDistance = INITIALUPDATESECTORDIST, int32_t maxDistance = MAXUPDATESECTORDIST) ATTRIBUTE((nonnull(4))); int findwallbetweensectors(int sect1, int sect2); -static FORCE_INLINE int sectoradjacent(int sect1, int sect2) { return findwallbetweensectors(sect1, sect2) != -1; } +inline int sectoradjacent(int sect1, int sect2) { return findwallbetweensectors(sect1, sect2) != -1; } int32_t getsectordist(vec2_t const in, int const sectnum, vec2_t * const out = nullptr); extern const int16_t *chsecptr_onextwall; -int32_t checksectorpointer(int16_t i, int16_t sectnum); #if !KRANDDEBUG -static FORCE_INLINE int32_t krand(void) +inline int32_t krand(void) { randomseed = (randomseed * 1664525ul) + 221297ul; return ((uint32_t) randomseed)>>16; @@ -513,16 +417,19 @@ static FORCE_INLINE int32_t krand(void) int32_t krand(void); #endif -int32_t ksqrt(uint32_t num); -int32_t getangle(int32_t xvect, int32_t yvect); -fixed_t gethiq16angle(int32_t xvect, int32_t yvect); +inline int32_t ksqrt(uint32_t num) +{ + return int(sqrt((float)num)); +} -static FORCE_INLINE constexpr uint32_t uhypsq(int32_t const dx, int32_t const dy) +int32_t getangle(int32_t xvect, int32_t yvect); + +inline constexpr uint32_t uhypsq(int32_t const dx, int32_t const dy) { return (uint32_t)dx*dx + (uint32_t)dy*dy; } -static FORCE_INLINE int32_t logapproach(int32_t const val, int32_t const targetval) +inline int32_t logapproach(int32_t const val, int32_t const targetval) { int32_t const dif = targetval - val; return (dif>>1) ? val + (dif>>1) : targetval; @@ -551,36 +458,36 @@ void yax_getzsofslope(int sectNum, int playerX, int playerY, int32_t* pCeilZ, in int32_t yax_getceilzofslope(int const sectnum, vec2_t const vect); int32_t yax_getflorzofslope(int const sectnum, vec2_t const vect); -static FORCE_INLINE int32_t getceilzofslope(int16_t sectnum, int32_t dax, int32_t day) +inline int32_t getceilzofslope(int16_t sectnum, int32_t dax, int32_t day) { return getceilzofslopeptr((usectorptr_t)§or[sectnum], dax, day); } -static FORCE_INLINE int32_t getflorzofslope(int16_t sectnum, int32_t dax, int32_t day) +inline int32_t getflorzofslope(int16_t sectnum, int32_t dax, int32_t day) { return getflorzofslopeptr((usectorptr_t)§or[sectnum], dax, day); } -static FORCE_INLINE void getzsofslope(int16_t sectnum, int32_t dax, int32_t day, int32_t *ceilz, int32_t *florz) +inline void getzsofslope(int16_t sectnum, int32_t dax, int32_t day, int32_t *ceilz, int32_t *florz) { getzsofslopeptr((usectorptr_t)§or[sectnum], dax, day, ceilz, florz); } -static FORCE_INLINE void getcorrectzsofslope(int16_t sectnum, int32_t dax, int32_t day, int32_t *ceilz, int32_t *florz) +inline void getcorrectzsofslope(int16_t sectnum, int32_t dax, int32_t day, int32_t *ceilz, int32_t *florz) { vec2_t closest = { dax, day }; getsectordist(closest, sectnum, &closest); getzsofslopeptr((usectorptr_t)§or[sectnum], closest.x, closest.y, ceilz, florz); } -static FORCE_INLINE int32_t getcorrectceilzofslope(int16_t sectnum, int32_t dax, int32_t day) +inline int32_t getcorrectceilzofslope(int16_t sectnum, int32_t dax, int32_t day) { vec2_t closest = { dax, day }; getsectordist(closest, sectnum, &closest); return getceilzofslopeptr((usectorptr_t)§or[sectnum], closest.x, closest.y); } -static FORCE_INLINE int32_t getcorrectflorzofslope(int16_t sectnum, int32_t dax, int32_t day) +inline int32_t getcorrectflorzofslope(int16_t sectnum, int32_t dax, int32_t day) { vec2_t closest = { dax, day }; getsectordist(closest, sectnum, &closest); @@ -589,12 +496,12 @@ static FORCE_INLINE int32_t getcorrectflorzofslope(int16_t sectnum, int32_t dax, // Is a red wall in a safe fashion, i.e. only if consistency invariant // ".nextsector >= 0 iff .nextwall >= 0" holds. -static FORCE_INLINE int32_t redwallp(uwallptr_t wal) +inline int32_t redwallp(uwallptr_t wal) { return (wal->nextwall >= 0 && wal->nextsector >= 0); } -static FORCE_INLINE int32_t E_SpriteIsValid(const int32_t i) +inline int32_t E_SpriteIsValid(const int32_t i) { return ((unsigned)i < MAXSPRITES && sprite[i].statnum != MAXSTATUS); } @@ -602,7 +509,6 @@ static FORCE_INLINE int32_t E_SpriteIsValid(const int32_t i) void alignceilslope(int16_t dasect, int32_t x, int32_t y, int32_t z); void alignflorslope(int16_t dasect, int32_t x, int32_t y, int32_t z); -int32_t sectorofwall(int16_t wallNum); void setslope(int32_t sectnum, int32_t cf, int16_t slope); int32_t lintersect(int32_t originX, int32_t originY, int32_t originZ, @@ -612,13 +518,6 @@ int32_t lintersect(int32_t originX, int32_t originY, int32_t originZ, int32_t rayintersect(int32_t x1, int32_t y1, int32_t z1, int32_t vx, int32_t vy, int32_t vz, int32_t x3, int32_t y3, int32_t x4, int32_t y4, int32_t *intx, int32_t *inty, int32_t *intz); -#if !defined NETCODE_DISABLE -void do_insertsprite_at_headofstat(int16_t spritenum, int16_t statnum); -int32_t insertspritestat(int16_t statnum); -void do_deletespritestat(int16_t deleteme); -void do_insertsprite_at_headofsect(int16_t spritenum, int16_t sectnum); -void do_deletespritesect(int16_t deleteme); -#endif int32_t insertsprite(int16_t sectnum, int16_t statnum); int32_t deletesprite(int16_t spritenum); @@ -638,49 +537,15 @@ inline void setspritepos(int spnum, int x, int y, int z) int32_t setspritez(int16_t spritenum, const vec3_t *) ATTRIBUTE((nonnull(2))); int32_t spriteheightofsptr(uspriteptr_t spr, int32_t *height, int32_t alsotileyofs); -static FORCE_INLINE int32_t spriteheightofs(int16_t i, int32_t *height, int32_t alsotileyofs) +inline int32_t spriteheightofs(int16_t i, int32_t *height, int32_t alsotileyofs) { return spriteheightofsptr((uspriteptr_t)&sprite[i], height, alsotileyofs); } int videoCaptureScreen(); -struct OutputFileCounter { - uint16_t count = 0; - FileWriter *opennextfile(char *, char *); - FileWriter *opennextfile_withext(char *, const char *); -}; - -// PLAG: line utility functions -typedef struct s_equation -{ - float a, b, c; -} _equation; - -#define STATUS2DSIZ 144 -#define STATUS2DSIZ2 26 - -#ifdef USE_OPENGL -void renderSetRollAngle(float rolla); -#endif - void Polymost_Startup(); -typedef uint16_t polytintflags_t; - -enum cutsceneflags { - CUTSCENE_FORCEFILTER = 1, - CUTSCENE_FORCENOFILTER = 2, - CUTSCENE_TEXTUREFILTER = 4, -}; - -enum { - TEXFILTER_OFF = 0, // GL_NEAREST - TEXFILTER_ON = 5, // GL_LINEAR_MIPMAP_LINEAR -}; - -extern int32_t gltexmaxsize; - EXTERN_CVAR(Bool, hw_animsmoothing) EXTERN_CVAR(Bool, hw_hightile) EXTERN_CVAR(Bool, hw_models) @@ -692,7 +557,6 @@ EXTERN_CVAR(Bool, hw_useindexedcolortextures) EXTERN_CVAR(Bool, hw_parallaxskypanning) EXTERN_CVAR(Bool, r_voxels) -extern int32_t r_downsize; extern int32_t mdtims, omdtims; extern int32_t r_rortexture; @@ -705,11 +569,8 @@ int32_t md_loadmodel(const char *fn); int32_t md_setmisc(int32_t modelid, float scale, int32_t shadeoff, float zadd, float yoffset, int32_t flags); // int32_t md_tilehasmodel(int32_t tilenume, int32_t pal); -extern TArray g_clipMapFiles; - EXTERN int32_t nextvoxid; -EXTERN int8_t voxreserve[(MAXVOXELS+7)>>3]; -EXTERN int8_t voxrotate[(MAXVOXELS+7)>>3]; +EXTERN FixedBitArrayvoxreserve; #ifdef USE_OPENGL // TODO: dynamically allocate this @@ -723,7 +584,7 @@ typedef struct int16_t framenum; // calculate the number from the name when declaring int16_t nexttile; uint16_t smoothduration; - hudtyp *hudmem[2]; + hudtyp hudmem[2]; int8_t skinnum; char pal; } tile2model_t; @@ -733,19 +594,13 @@ typedef struct EXTERN int32_t mdinited; EXTERN tile2model_t tile2model[MAXTILES+EXTRATILES]; -static FORCE_INLINE int32_t md_tilehasmodel(int32_t const tilenume, int32_t const pal) +inline int32_t md_tilehasmodel(int32_t const tilenume, int32_t const pal) { return mdinited ? tile2model[Ptile2tile(tilenume,pal)].modelid : -1; } #endif // defined USE_OPENGL -static FORCE_INLINE int tilehasmodelorvoxel(int const tilenume, int pal) -{ - UNREFERENCED_PARAMETER(pal); - return - (mdinited && hw_models && tile2model[Ptile2tile(tilenume, pal)].modelid != -1) || - (r_voxels && tiletovox[tilenume] != -1); -} +int tilehasmodelorvoxel(int const tilenume, int pal); int32_t md_defineframe(int32_t modelid, const char *framename, int32_t tilenume, int32_t skinnum, float smoothduration, int32_t pal); @@ -758,12 +613,6 @@ int32_t md_definehud (int32_t modelid, int32_t tilex, vec3f_t add, int32_t md_undefinetile(int32_t tile); int32_t md_undefinemodel(int32_t modelid); -int32_t loaddefinitionsfile(const char *fn, bool loadadds = false, bool cumulative = false); - -// if loadboard() fails with -2 return, try loadoldboard(). if it fails with -// -2, board is dodgy -int32_t engineLoadBoardV5V6(const char *filename, char fromwhere, vec3_t *dapos, int16_t *daang, int16_t *dacursectnum); - #ifdef USE_OPENGL # include "polymost.h" #endif @@ -772,7 +621,7 @@ extern int skiptile; static vec2_t const zerovec = { 0, 0 }; -static FORCE_INLINE int inside_p(int32_t const x, int32_t const y, int const sectnum) { return (sectnum >= 0 && inside(x, y, sectnum) == 1); } +inline int inside_p(int32_t const x, int32_t const y, int const sectnum) { return (sectnum >= 0 && inside(x, y, sectnum) == 1); } #define SET_AND_RETURN(Lval, Rval) \ do \ @@ -842,15 +691,11 @@ extern int32_t rintersect(int32_t x1, int32_t y1, int32_t z1, int32_t *intx, int32_t *inty, int32_t *intz); extern int32_t(*animateoffs_replace)(int const tilenum, int fakevar); -extern int32_t(*getpalookup_replace)(int32_t davis, int32_t dashade); extern void(*initspritelists_replace)(void); extern int32_t(*insertsprite_replace)(int16_t sectnum, int16_t statnum); extern int32_t(*deletesprite_replace)(int16_t spritenum); extern int32_t(*changespritesect_replace)(int16_t spritenum, int16_t newsectnum); extern int32_t(*changespritestat_replace)(int16_t spritenum, int16_t newstatnum); -#ifdef USE_OPENGL -extern void(*PolymostProcessVoxels_Callback)(void); -#endif // Masking these into the object index to keep it in 16 bit was probably the single most dumbest and pointless thing Build ever did. // Gonna be fun to globally replace these to finally lift the limit this imposes on map size. @@ -867,6 +712,23 @@ enum EHitBits void updateModelInterpolation(); +inline void tileUpdatePicnum(int* const tileptr, int const obj, int stat) +{ + auto& tile = *tileptr; + + if (picanm[tile].sf & PICANM_ANIMTYPE_MASK) + tile += animateoffs(tile, obj); + + if (((obj & 16384) == 16384) && (stat & CSTAT_WALL_ROTATE_90) && RotTile(tile).newtile != -1) + tile = RotTile(tile).newtile; +} + +inline void setgotpic(int32_t tilenume) +{ + gotpic[tilenume >> 3] |= 1 << (tilenume & 7); +} + + #include "iterators.h" diff --git a/source/build/include/buildtypes.h b/source/build/include/buildtypes.h index 1432efd83..ba52cb80c 100644 --- a/source/build/include/buildtypes.h +++ b/source/build/include/buildtypes.h @@ -3,7 +3,7 @@ //ceilingstat/floorstat: // bit 0: 1 = parallaxing, 0 = not "P" -// bit 1: 1 = groudraw, 0 = not +// bit 1: 1 = sloped, 0 = not // bit 2: 1 = swap x&y, 0 = not "F" // bit 3: 1 = double smooshiness "E" // bit 4: 1 = x-flip "F" @@ -17,9 +17,38 @@ // bit 9: 1 = blocking ceiling/floor // bit 10: 1 = YAX'ed ceiling/floor // bit 11: 1 = hitscan-sensitive ceiling/floor -// bits 12-15: reserved +// bits 12-14: reserved +// bit 15: SW: block FAF hitscans //////////////////// Version 7 map format //////////////////// +enum +{ + CSTAT_SECTOR_SKY = 1, + CSTAT_SECTOR_SLOPE = 2, + CSTAT_SECTOR_SWAPXY = 4, + CSTAT_SECTOR_TEXHALF = 8, + CSTAT_SECTOR_XFLIP = 16, + CSTAT_SECTOR_YFLIP = 32, + CSTAT_SECTOR_ALIGN = 64, + CSTAT_SECTOR_TRANS = 128, + CSTAT_SECTOR_TRANS_INVERT = 256, + CSTAT_SECTOR_METHOD = 384, + + SECTOREX_CLOUDSCROLL = 1, +}; + +enum +{ + PORTAL_SECTOR_FLOOR = 1, + PORTAL_SECTOR_CEILING = 2, + PORTAL_SECTOR_FLOOR_REFLECT = 3, + PORTAL_SECTOR_CEILING_REFLECT = 4, + PORTAL_WALL_VIEW = 5, + PORTAL_WALL_MIRROR = 6, + PORTAL_WALL_TO_SPRITE = 7, + PORTAL_SECTOR_GEOMETRY = 8, +}; + //40 bytes struct sectortype @@ -40,7 +69,11 @@ struct sectortype int16_t hitag; int16_t extra; + uint8_t dirty; + uint8_t exflags; float ceilingxpan_, ceilingypan_, floorxpan_, floorypan_; + uint8_t portalflags; + int8_t portalnum; int ceilingxpan() const { return int(ceilingxpan_); } int ceilingypan() const { return int(ceilingypan_); } @@ -80,7 +113,7 @@ struct walltype }; vec2_t pos; }; - int16_t point2, nextwall, nextsector; + int16_t point2, nextwall, sector, nextsector; uint16_t cstat; int16_t picnum, overpicnum; int8_t shade; @@ -91,6 +124,9 @@ struct walltype int16_t hitag; int16_t extra; float xpan_, ypan_; + binangle clipangle; + uint8_t portalflags; + uint16_t portalnum; int xpan() const { return int(xpan_); } int ypan() const { return int(ypan_); } @@ -216,19 +252,13 @@ struct spritetype int8_t xoffset, yoffset; int16_t sectnum, statnum; int16_t oang, ang, owner; - union { - struct - { - union { - int16_t xvel, index; - }; - int16_t yvel; - union { - int16_t zvel, inittype; - }; + union { + int16_t xvel, index; + }; + int16_t yvel; + union { + int16_t zvel, inittype; }; - vec3_16_t vel; - }; union { int16_t lotag, type; }; @@ -237,6 +267,7 @@ struct spritetype }; int16_t extra; int16_t detail; + int time; #if 0 // make sure we do not accidentally copy this @@ -287,39 +318,41 @@ struct spritetype int32_t interpolatedx(double const smoothratio, int const scale = 16) { - return ox + MulScale(x - ox, smoothratio, scale); + return interpolatedvalue(ox, x, smoothratio, scale); } int32_t interpolatedy(double const smoothratio, int const scale = 16) { - return oy + MulScale(y - oy, smoothratio, scale); + return interpolatedvalue(oy, y, smoothratio, scale); } int32_t interpolatedz(double const smoothratio, int const scale = 16) { - return oz + MulScale(z - oz, smoothratio, scale); + return interpolatedvalue(oz, z, smoothratio, scale); } vec2_t interpolatedvec2(double const smoothratio, int const scale = 16) { - return vec2_t({ + return + { interpolatedx(smoothratio, scale), interpolatedy(smoothratio, scale) - }); + }; } vec3_t interpolatedvec3(double const smoothratio, int const scale = 16) { - return vec3_t({ + return + { interpolatedx(smoothratio, scale), interpolatedy(smoothratio, scale), interpolatedz(smoothratio, scale) - }); + }; } int16_t interpolatedang(double const smoothratio) { - return oang + MulScale(((ang + 1024 - oang) & 2047) - 1024, smoothratio, 16); + return interpolatedangle(oang, ang, smoothratio, 16); } }; diff --git a/source/build/include/compat.h b/source/build/include/compat.h index 562cdb10d..930315c4f 100644 --- a/source/build/include/compat.h +++ b/source/build/include/compat.h @@ -11,54 +11,9 @@ #include "m_alloc.h" #include "intvec.h" #include "m_swap.h" -#include "serializer.h" - -////////// Compiler detection ////////// - -#ifdef __GNUC__ -# define EDUKE32_GCC_PREREQ(major, minor) (major < __GNUC__ || (major == __GNUC__ && minor <= __GNUC_MINOR__)) -#else -# define EDUKE32_GCC_PREREQ(major, minor) 0 -#endif - - -////////// Language detection ////////// ////////// Language and compiler feature polyfills ////////// -# define EXTERNC - -#ifndef UNREFERENCED_PARAMETER -# define UNREFERENCED_PARAMETER(x) (x) = (x) -#endif - -#if defined __GNUC__ || defined __clang__ -# define ATTRIBUTE(attrlist) __attribute__(attrlist) -#else -# define ATTRIBUTE(attrlist) -#endif - - -#ifndef MAY_ALIAS -# ifdef _MSC_VER -# define MAY_ALIAS -# else -# define MAY_ALIAS __attribute__((may_alias)) -# endif -#endif - -#ifndef FORCE_INLINE -# ifdef _MSC_VER -# define FORCE_INLINE __forceinline -# else -# ifdef __GNUC__ -# define FORCE_INLINE inline __attribute__((always_inline)) -# else -# define FORCE_INLINE inline -# endif -# endif -#endif - # define fallthrough__ [[fallthrough]] ////////// Architecture detection ////////// @@ -103,45 +58,18 @@ #include "engineerrors.h" -////////// DEPRECATED: Standard library prefixing ////////// - -typedef intptr_t ssize_t; - -typedef ssize_t bssize_t; +typedef intptr_t bssize_t; #define BMAX_PATH 256 ////////// Metaprogramming structs ////////// -using std::enable_if_t; using native_t = intptr_t; -typedef struct MAY_ALIAS { - int32_t x, y; -} vec2_t; - typedef struct { float x, y; } vec2f_t; -typedef struct { - double x, y; -} vec2d_t; - -typedef struct MAY_ALIAS { - union { - struct { int32_t x, y, z; }; - vec2_t vec2; - }; -} vec3_t; - -typedef struct MAY_ALIAS { - union { - struct { int16_t x, y, z; }; - vec2_16_t vec2; - }; -} vec3_16_t; - typedef struct { union { struct { @@ -155,57 +83,14 @@ typedef struct { static_assert(sizeof(vec3f_t) == sizeof(float) * 3); -typedef struct { - union { double x; double d; }; - union { double y; double u; }; - union { double z; double v; }; -} vec3d_t; - -static_assert(sizeof(vec3d_t) == sizeof(double) * 3); - - ////////// Language tricks that depend on size_t ////////// #include "basics.h" -////////// Pointer management ////////// - -#define DO_FREE_AND_NULL(var) do { \ - Xfree(var); (var) = NULL; \ -} while (0) - - -////////// Data serialization ////////// - -inline int32_t B_LITTLE32(int32_t val) { return LittleLong(val); } -inline uint32_t B_LITTLE32(uint32_t val) { return LittleLong(val); } -inline int32_t B_LITTLE16(int16_t val) { return LittleShort(val); } -inline uint32_t B_LITTLE16(uint16_t val) { return LittleShort(val); } - -static FORCE_INLINE void B_BUF32(void * const buf, uint32_t const x) { *(uint32_t *) buf = x; } -static FORCE_INLINE uint32_t B_UNBUF32(void const * const buf) { return *(uint32_t const *) buf; } -static FORCE_INLINE uint16_t B_UNBUF16(void const * const buf) { return *(uint16_t const *) buf; } - - - -////////// Abstract data operations ////////// - -using std::min; -using std::max; - ////////// Bitfield manipulation ////////// -// This once was a static array, requiring a memory acces where a shift would suffice. -// Revert the above to a real bit shift through some C++ operator magic. That saves me from reverting all the code that uses this construct. -// Note: Only occurs 25 times in the code, should be removed for good. -static struct -{ - constexpr uint8_t operator[](int index) const { return 1 << index; }; -} pow2char; - - -static FORCE_INLINE void bitmap_set(uint8_t *const ptr, int const n) { ptr[n>>3] |= 1 << (n&7); } -static FORCE_INLINE char bitmap_test(uint8_t const *const ptr, int const n) { return ptr[n>>3] & (1 << (n&7)); } +inline void bitmap_set(uint8_t *const ptr, int const n) { ptr[n>>3] |= 1 << (n&7); } +inline char bitmap_test(uint8_t const *const ptr, int const n) { return ptr[n>>3] & (1 << (n&7)); } ////////// Utility functions ////////// @@ -230,42 +115,4 @@ void bfirst_search_try(T *const list, uint8_t *const bitmap, T *const eltnumptr, } } -////////// PANICKING ALLOCATION WRAPPERS ////////// - - -#define Xstrdup(s) (strdup(s)) -#define Xmalloc(size) (M_Malloc(size)) -#define Xcalloc(nmemb, size) (M_Calloc(nmemb, size)) -#define Xrealloc(ptr, size) (M_Realloc(ptr, size)) -#define Xfree(ptr) (M_Free(ptr)) - -////////// Inlined external libraries ////////// - -/* End dependence on compat.o object. */ - -inline FSerializer& Serialize(FSerializer& arc, const char* key, vec2_t& c, vec2_t* def) -{ - if (def && !memcmp(&c, def, sizeof(c))) return arc; - if (arc.BeginObject(key)) - { - arc("x", c.x, def? &def->x : nullptr) - ("y", c.y, def ? &def->y : nullptr) - .EndObject(); - } - return arc; -} - -inline FSerializer& Serialize(FSerializer& arc, const char* key, vec3_t& c, vec3_t* def) -{ - if (def && !memcmp(&c, def, sizeof(c))) return arc; - if (arc.BeginObject(key)) - { - arc("x", c.x, def ? &def->x : nullptr) - ("y", c.y, def ? &def->y : nullptr) - ("z", c.z, def ? &def->z : nullptr) - .EndObject(); - } - return arc; -} - #endif // compat_h_ diff --git a/source/build/include/cstat.h b/source/build/include/cstat.h deleted file mode 100644 index 51aa9b135..000000000 --- a/source/build/include/cstat.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once -// nobody uses these. What's so cool about naked numbers? :( - -// system defines for status bits -#define CEILING_STAT_PLAX BIT(0) -#define CEILING_STAT_SLOPE BIT(1) -#define CEILING_STAT_SWAPXY BIT(2) -#define CEILING_STAT_SMOOSH BIT(3) -#define CEILING_STAT_XFLIP BIT(4) -#define CEILING_STAT_YFLIP BIT(5) -#define CEILING_STAT_RELATIVE BIT(6) -#define CEILING_STAT_TYPE_MASK (BIT(7)|BIT(8)) -#define CEILING_STAT_MASKED BIT(7) -#define CEILING_STAT_TRANS BIT(8) -#define CEILING_STAT_TRANS_FLIP (BIT(7)|BIT(8)) -#define CEILING_STAT_FAF_BLOCK_HITSCAN BIT(15) - -#define FLOOR_STAT_PLAX BIT(0) -#define FLOOR_STAT_SLOPE BIT(1) -#define FLOOR_STAT_SWAPXY BIT(2) -#define FLOOR_STAT_SMOOSH BIT(3) -#define FLOOR_STAT_XFLIP BIT(4) -#define FLOOR_STAT_YFLIP BIT(5) -#define FLOOR_STAT_RELATIVE BIT(6) -#define FLOOR_STAT_TYPE_MASK (BIT(7)|BIT(8)) -#define FLOOR_STAT_MASKED BIT(7) -#define FLOOR_STAT_TRANS BIT(8) -#define FLOOR_STAT_TRANS_FLIP (BIT(7)|BIT(8)) -#define FLOOR_STAT_FAF_BLOCK_HITSCAN BIT(15) - -#define CSTAT_WALL_BLOCK BIT(0) -#define CSTAT_WALL_BOTTOM_SWAP BIT(1) -#define CSTAT_WALL_ALIGN_BOTTOM BIT(2) -#define CSTAT_WALL_XFLIP BIT(3) -#define CSTAT_WALL_MASKED BIT(4) -#define CSTAT_WALL_1WAY BIT(5) -#define CSTAT_WALL_BLOCK_HITSCAN BIT(6) -#define CSTAT_WALL_TRANSLUCENT BIT(7) -#define CSTAT_WALL_YFLIP BIT(8) -#define CSTAT_WALL_TRANS_FLIP BIT(9) -#define CSTAT_WALL_BLOCK_ACTOR (BIT(14)) // my def -#define CSTAT_WALL_WARP_HITSCAN (BIT(15)) // my def \ No newline at end of file diff --git a/source/build/include/mdsprite.h b/source/build/include/mdsprite.h index 05cf0b10e..f2cfa4415 100644 --- a/source/build/include/mdsprite.h +++ b/source/build/include/mdsprite.h @@ -177,25 +177,6 @@ struct md3model_t : public idmodel_t */ }; -#define VOXBORDWIDTH 1 //use 0 to save memory, but has texture artifacts; 1 looks better... -#define VOXUSECHAR 0 - -#if (VOXUSECHAR != 0) -typedef struct { uint8_t x, y, z, u, v; } vert_t; -#else -typedef struct { uint16_t x, y, z, u, v; } vert_t; -#endif - -typedef struct { vert_t v[4]; } voxrect_t; - -struct voxmodel_t : public mdmodel_t -{ - FVoxelModel* model = nullptr; - vec3_t siz; - vec3f_t piv; - int32_t is8bit; -}; - EXTERN mdmodel_t **models; FGameTexture* mdloadskin(idmodel_t* m, int32_t number, int32_t pal, int32_t surf, bool* exact); @@ -206,15 +187,6 @@ EXTERN void md3_vox_calcmat_common(tspriteptr_t tspr, const vec3f_t *a0, float f EXTERN int32_t mdpause; EXTERN int32_t nextmodelid; -EXTERN voxmodel_t *voxmodels[MAXVOXELS]; - -void voxfree(voxmodel_t *m); -voxmodel_t *voxload(int lumpnum); -int32_t polymost_voxdraw(voxmodel_t *m, tspriteptr_t const tspr, bool rotate); - -int md3postload_polymer(md3model_t* m); -//int32_t md_thinoutmodel(int32_t modelid, uint8_t *usedframebitmap); -EXTERN void md_freevbos(void); #endif // defined USE_OPENGL diff --git a/source/build/include/mmulti.h b/source/build/include/mmulti.h deleted file mode 100644 index 1c5d2c5ea..000000000 --- a/source/build/include/mmulti.h +++ /dev/null @@ -1,19 +0,0 @@ -// "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman -// Ken Silverman's official web site: "http://www.advsys.net/ken" -// See the included license file "BUILDLIC.TXT" for license info. -// -// This file has been modified from Ken Silverman's original release -// by Jonathon Fowler (jf@jonof.id.au) -// by the EDuke32 team (development@voidpoint.com) - -#ifndef mmulti_h_ -#define mmulti_h_ - -#define MAXMULTIPLAYERS 16 - -extern int myconnectindex, numplayers; -extern int connecthead, connectpoint2[MAXMULTIPLAYERS]; - - -#endif // mmulti_h_ - diff --git a/source/build/include/polymost.h b/source/build/include/polymost.h index ee9a14849..40bb31d11 100644 --- a/source/build/include/polymost.h +++ b/source/build/include/polymost.h @@ -4,18 +4,20 @@ #include "mdsprite.h" -typedef struct { uint8_t r, g, b, a; } coltype; -typedef struct { float r, g, b, a; } coltypef; +extern tspritetype pm_tsprite[MAXSPRITESONSCREEN]; +extern int pm_spritesortcnt; +extern int pm_smoothratio; + +namespace Polymost +{ extern float gtang; extern double gxyaspect; extern float grhalfxdown10x; extern float gcosang, gsinang, gcosang2, gsinang2; -extern int pm_smoothratio; extern void Polymost_prepare_loadboard(void); -void polymost_outputGLDebugMessage(uint8_t severity, const char* format, ...); //void phex(char v, char *s); void polymost_drawsprite(int32_t snum); @@ -25,57 +27,40 @@ void polymost_initosdfuncs(void); void polymost_drawrooms(void); void polymost_prepareMirror(int32_t dax, int32_t day, int32_t daz, fixed_t daang, fixed_t dahoriz, int16_t mirrorWall); void polymost_completeMirror(); +void polymost_deletesprite(int num); -int32_t polymost_maskWallHasTranslucency(uwalltype const * const wall); -int32_t polymost_spriteHasTranslucency(tspritetype const * const tspr); -int32_t polymost_spriteIsModelOrVoxel(tspritetype const * const tspr); +int32_t polymost_maskWallHasTranslucency(walltype const * const wall); +int32_t polymost_spriteHasTranslucency(spritetype const * const tspr); void polymost_glreset(void); - -enum { - INVALIDATE_ALL, - INVALIDATE_ART, - INVALIDATE_ALL_NON_INDEXED, - INVALIDATE_ART_NON_INDEXED -}; - +void polymost_scansector(int32_t sectnum); extern float curpolygonoffset; +} + +void renderPrepareMirror(int32_t dax, int32_t day, int32_t daz, fixed_t daang, fixed_t dahoriz, int16_t dawall, + int32_t* tposx, int32_t* tposy, fixed_t* tang); +void renderCompleteMirror(void); + +int32_t renderDrawRoomsQ16(int32_t daposx, int32_t daposy, int32_t daposz, fixed_t daang, fixed_t dahoriz, int16_t dacursectnum); + +void renderDrawMasks(void); + +// PLAG: line utility functions +typedef struct s_equation +{ + float a, b, c; +} _equation; + +void renderSetRollAngle(float rolla); + + +// these are defined in engine.cpp. extern int16_t globalpicnum; - -#define POLYMOST_CHOOSE_FOG_PAL(fogpal, pal) \ - ((fogpal) ? (fogpal) : (pal)) -static FORCE_INLINE int32_t get_floor_fogpal(usectorptr_t const sec) -{ - return POLYMOST_CHOOSE_FOG_PAL(sec->fogpal, sec->floorpal); -} -static FORCE_INLINE int32_t get_ceiling_fogpal(usectorptr_t const sec) -{ - return POLYMOST_CHOOSE_FOG_PAL(sec->fogpal, sec->ceilingpal); -} -static FORCE_INLINE int32_t fogshade(int32_t const shade, int32_t const pal) -{ - return (globalflags & GLOBAL_NO_GL_FOGSHADE) ? 0 : shade; -} - -static constexpr inline int check_nonpow2(int32_t const x) -{ - return (x > 1 && (x&(x-1))); -} - -static inline float polymost_invsqrt_approximation(float x) -{ -#if !B_BIG_ENDIAN - float const haf = x * .5f; - union { float f; uint32_t i; } n = { x }; - n.i = 0x5f375a86 - (n.i >> 1); - return n.f * (1.5f - haf * (n.f * n.f)); -#else - // this is the comment - return 1.f / sqrtf(x); -#endif -} +extern float fcosglobalang, fsinglobalang; +extern float fydimen, fviewingrange; +extern int32_t viewingrangerecip; // Flags of the argument of various functions enum { @@ -96,11 +81,4 @@ enum { DAMETH_BACKFACECULL = -1, }; -#define DAMETH_NARROW_MASKPROPS(dameth) (((dameth)&(~DAMETH_TRANS1))|(((dameth)&DAMETH_TRANS1)>>1)) -static_assert(DAMETH_NARROW_MASKPROPS(DAMETH_MASKPROPS) == DAMETH_MASK); - -extern float fcosglobalang, fsinglobalang; -extern float fydimen, fviewingrange; -extern int32_t viewingrangerecip; - #endif diff --git a/source/build/include/scriptfile.h b/source/build/include/scriptfile.h deleted file mode 100644 index c7aaf4fa8..000000000 --- a/source/build/include/scriptfile.h +++ /dev/null @@ -1,136 +0,0 @@ - -#ifndef BUILD_SCRIPTFILE_H_ -#define BUILD_SCRIPTFILE_H_ - -#include "sc_man.h" -#include "filesystem.h" - - - -using scriptfile = FScanner; - - -inline int32_t scriptfile_getnumber(scriptfile *sf, int32_t *num) -{ - bool res = sf->GetNumber(); - if (num) - { - if (res) *num = sf->Number; - else *num = 0; - } - return !res; -} - -inline int32_t scriptfile_getdouble(scriptfile *sf, double *num) -{ - bool res = sf->GetFloat(); - if (num) - { - if (res) *num = sf->Float; - else *num = 0; - } - return !res; -} - -inline int32_t scriptfile_getstring(scriptfile *sf, FString *st) -{ - bool res = sf->GetString(); - if (st) - { - if (res) *st = sf->String; - else *st = ""; - } - return !res; -} - -inline int32_t scriptfile_getsymbol(scriptfile *sf, int32_t *num) -{ - bool res = sf->GetNumber(true); - if (num) - { - if (res) *num = sf->Number; - else *num = 0; - } - return !res; -} - -inline int32_t scriptfile_getsymbol(scriptfile* sf, int64_t* num) -{ - bool res = sf->GetNumber(true); - if (num) - { - if (res) *num = sf->BigNumber; - else *num = 0; - } - return !res; -} - -inline FScriptPosition scriptfile_getposition(scriptfile *sf) -{ - return FScriptPosition(*sf); -} - -inline int32_t scriptfile_getbraces(scriptfile *sf, FScanner::SavedPos *braceend) -{ - if (sf->CheckString("{")) - { - auto here = sf->SavePos(); - sf->SkipToEndOfBlock(); - *braceend = sf->SavePos(); - sf->RestorePos(here); - return 0; - } - else - { - sf->ScriptError("'{' expected"); - return -1; - } -} -inline bool scriptfile_endofblock(scriptfile* sf, FScanner::SavedPos& braceend) -{ - auto here = sf->SavePos(); - return here.SavedScriptPtr >= braceend.SavedScriptPtr; -} - -inline void scriptfile_setposition(scriptfile* sf, const FScanner::SavedPos& pos) -{ - sf->RestorePos(pos); -} - -inline scriptfile *scriptfile_fromfile(const char *fn) -{ - int lump = fileSystem.FindFile(fn); - if (lump < 0) return nullptr; - auto sc = new FScanner; - sc->OpenLumpNum(lump); - sc->SetNoOctals(true); - sc->SetNoFatalErrors(true); - return sc; -} - -inline void scriptfile_close(scriptfile *sf) -{ - delete sf; -} - -inline int32_t scriptfile_addsymbolvalue(scriptfile *sf, char const *name, int32_t val) -{ - sf->AddSymbol(name, val); - return 1; -} - -typedef struct -{ - const char *text; - int32_t tokenid; -} -tokenlist; - - -enum -{ - T_EOF = -2, - T_ERROR = -1, -}; - -#endif diff --git a/source/build/src/clip.cpp b/source/build/src/clip.cpp index ece2c7b9d..109d34380 100644 --- a/source/build/src/clip.cpp +++ b/source/build/src/clip.cpp @@ -10,7 +10,9 @@ #include "clip.h" #include "engine_priv.h" #include "printf.h" +#include "gamefuncs.h" +enum { MAXCLIPDIST = 1024 }; static int16_t clipnum; static linetype clipit[MAXCLIPNUM]; @@ -21,46 +23,83 @@ static uint8_t clipsectormap[(MAXSECTORS+7)>>3]; static uint8_t origclipsectormap[(MAXSECTORS+7)>>3]; static int16_t clipobjectval[MAXCLIPNUM]; static uint8_t clipignore[(MAXCLIPNUM+7)>>3]; +static int32_t rxi[8], ryi[8]; + int32_t quickloadboard=0; -static int32_t numclipmaps; - -static int32_t numclipsects; // number in sectq[] -static int16_t *sectoidx; -static int16_t *sectq; // [numsectors] -static int16_t pictoidx[MAXTILES]; // maps tile num to clipinfo[] index -static int16_t *tempictoidx; - -static usectortype *loadsector; -static uwalltype *loadwall, *loadwallinv; -static uspritetype *loadsprite; - vec2_t hitscangoal = { (1<<29)-1, (1<<29)-1 }; int32_t hitallsprites = 0; -void engineInitClipMaps() +////////// CLIPMOVE ////////// + + +// x1, y1: in/out +// rest x/y: out +template +static inline void get_wallspr_points(T const * const spr, int32_t *x1, int32_t *x2, + int32_t *y1, int32_t *y2) { - numclipmaps = 0; - numclipsects = 0; + //These lines get the 2 points of the rotated sprite + //Given: (x1, y1) starts out as the center point - DO_FREE_AND_NULL(sectq); - DO_FREE_AND_NULL(sectoidx); - DO_FREE_AND_NULL(tempictoidx); - DO_FREE_AND_NULL(loadsector); - DO_FREE_AND_NULL(loadwall); - DO_FREE_AND_NULL(loadwallinv); - DO_FREE_AND_NULL(loadsprite); + const int32_t tilenum=spr->picnum, ang=spr->ang; + const int32_t xrepeat = spr->xrepeat; + int32_t xoff = tileLeftOffset(tilenum) + spr->xoffset; + int32_t k, l, dax, day; - // two's complement trick, -1 = 0xff - memset(&pictoidx, -1, sizeof(pictoidx)); + if (spr->cstat&4) + xoff = -xoff; - numsectors = 0; - numwalls = 0; + dax = bsin(ang) * xrepeat; + day = -bcos(ang) * xrepeat; + + l = tileWidth(tilenum); + k = (l>>1)+xoff; + + *x1 -= MulScale(dax,k, 16); + *x2 = *x1 + MulScale(dax,l, 16); + + *y1 -= MulScale(day,k, 16); + *y2 = *y1 + MulScale(day,l, 16); } -////////// CLIPMOVE ////////// +// x1, y1: in/out +// rest x/y: out +template +static inline void get_floorspr_points(T const * const spr, int32_t px, int32_t py, + int32_t *x1, int32_t *x2, int32_t *x3, int32_t *x4, + int32_t *y1, int32_t *y2, int32_t *y3, int32_t *y4) +{ + const int32_t tilenum = spr->picnum; + const int32_t cosang = bcos(spr->ang); + const int32_t sinang = bsin(spr->ang); + + vec2_t const span = { tileWidth(tilenum), tileHeight(tilenum)}; + vec2_t const repeat = { spr->xrepeat, spr->yrepeat }; + + vec2_t adjofs = { tileLeftOffset(tilenum) + spr->xoffset, tileTopOffset(tilenum) + spr->yoffset }; + + if (spr->cstat & 4) + adjofs.x = -adjofs.x; + + if (spr->cstat & 8) + adjofs.y = -adjofs.y; + + vec2_t const center = { ((span.x >> 1) + adjofs.x) * repeat.x, ((span.y >> 1) + adjofs.y) * repeat.y }; + vec2_t const rspan = { span.x * repeat.x, span.y * repeat.y }; + vec2_t const ofs = { -MulScale(cosang, rspan.y, 16), -MulScale(sinang, rspan.y, 16) }; + + *x1 += DMulScale(sinang, center.x, cosang, center.y, 16) - px; + *y1 += DMulScale(sinang, center.y, -cosang, center.x, 16) - py; + + *x2 = *x1 - MulScale(sinang, rspan.x, 16); + *y2 = *y1 + MulScale(cosang, rspan.x, 16); + + *x3 = *x2 + ofs.x, *x4 = *x1 + ofs.x; + *y3 = *y2 + ofs.y, *y4 = *y1 + ofs.y; +} int32_t clipmoveboxtracenum = 3; @@ -155,14 +194,14 @@ static void addclipline(int32_t dax1, int32_t day1, int32_t dax2, int32_t day2, clipit[clipnum].x2 = dax2; clipit[clipnum].y2 = day2; clipobjectval[clipnum] = daoval; - uint32_t const mask = pow2char[clipnum&7]; + uint32_t const mask = (1 << (clipnum&7)); uint8_t &value = clipignore[clipnum>>3]; value = (value & ~mask) | (-nofix & mask); clipnum++; } -static FORCE_INLINE void clipmove_tweak_pos(const vec3_t *pos, int32_t gx, int32_t gy, int32_t x1, int32_t y1, int32_t x2, +inline void clipmove_tweak_pos(const vec3_t *pos, int32_t gx, int32_t gy, int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t *daxptr, int32_t *dayptr) { int32_t daz; @@ -175,34 +214,6 @@ static FORCE_INLINE void clipmove_tweak_pos(const vec3_t *pos, int32_t gx, int32 } } -int32_t getceilzofslope_old(int32_t sectnum, int32_t dax, int32_t day) -{ - int32_t dx, dy, i, j; - - if (!(sector[sectnum].ceilingstat&2)) return sector[sectnum].ceilingz; - j = sector[sectnum].wallptr; - dx = wall[wall[j].point2].x-wall[j].x; - dy = wall[wall[j].point2].y-wall[j].y; - i = (ksqrtasm_old(dx*dx+dy*dy)); if (i == 0) return(sector[sectnum].ceilingz); - i = DivScale(sector[sectnum].ceilingheinum,i, 15); - dx *= i; dy *= i; - return sector[sectnum].ceilingz+DMulScale(dx,day-wall[j].y,-dy,dax-wall[j].x, 23); -} - -int32_t getflorzofslope_old(int32_t sectnum, int32_t dax, int32_t day) -{ - int32_t dx, dy, i, j; - - if (!(sector[sectnum].floorstat&2)) return sector[sectnum].floorz; - j = sector[sectnum].wallptr; - dx = wall[wall[j].point2].x-wall[j].x; - dy = wall[wall[j].point2].y-wall[j].y; - i = (ksqrtasm_old(dx*dx+dy*dy)); if (i == 0) return sector[sectnum].floorz; - i = DivScale(sector[sectnum].floorheinum,i, 15); - dx *= i; dy *= i; - return sector[sectnum].floorz+DMulScale(dx,day-wall[j].y,-dy,dax-wall[j].x, 23); -} - // Returns: should clip? static int cliptestsector(int const dasect, int const nextsect, int32_t const flordist, int32_t const ceildist, vec2_t const pos, int32_t const posz) { @@ -214,20 +225,6 @@ static int cliptestsector(int const dasect, int const nextsect, int32_t const fl { case ENGINECOMPATIBILITY_NONE: break; - case ENGINECOMPATIBILITY_19950829: - { - int32_t daz = getflorzofslope_old(dasect, pos.x, pos.y); - int32_t daz2 = getflorzofslope_old(nextsect, pos.x, pos.y); - - if (daz2 < daz && (sec2->floorstat&1) == 0) - if (posz >= daz2-(flordist-1)) return 1; - daz = getceilzofslope_old(dasect, pos.x, pos.y); - daz2 = getceilzofslope_old(nextsect, pos.x, pos.y); - if (daz2 > daz && (sec2->ceilingstat&1) == 0) - if (posz <= daz2+(ceildist-1)) return 1; - - return 0; - } default: { int32_t daz = getflorzofslope(dasect, pos.x, pos.y); @@ -491,7 +488,7 @@ int32_t clipmove(vec3_t * const pos, int16_t * const sectnum, int32_t xvect, int //Extra walldist for sprites on sector lines vec2_t const diff = { goal.x - (pos->x), goal.y - (pos->y) }; - int32_t const rad = clip_nsqrtasm(compat_maybe_truncate_to_int32(uhypsq(diff.x, diff.y))) + MAXCLIPDIST + walldist + 8; + int32_t const rad = ksqrt(compat_maybe_truncate_to_int32(uhypsq(diff.x, diff.y))) + MAXCLIPDIST + walldist + 8; vec2_t const clipMin = { cent.x - rad, cent.y - rad }; vec2_t const clipMax = { cent.x + rad, cent.y + rad }; @@ -550,7 +547,7 @@ int32_t clipmove(vec3_t * const pos, int16_t * const sectnum, int32_t xvect, int { clipyou = 1; } - else if (editstatus == 0) + else { clipmove_tweak_pos(pos, diff.x, diff.y, p1.x, p1.y, p2.x, p2.y, &v.x, &v.y); clipyou = cliptestsector(dasect, wal->nextsector, flordist, ceildist, v, pos->z); @@ -570,7 +567,7 @@ int32_t clipmove(vec3_t * const pos, int16_t * const sectnum, int32_t xvect, int if (clipyou) { - int16_t const objtype = curspr ? (int16_t)(curspr - (uspritetype *)sprite) + 49152 : (int16_t)j + 32768; + int16_t const objtype = curspr ? (int16_t)(curspr - sprite) + 49152 : (int16_t)j + 32768; //Add 2 boxes at endpoints int32_t bsz = walldist; if (diff.x < 0) bsz = -bsz; @@ -616,7 +613,7 @@ int32_t clipmove(vec3_t * const pos, int16_t * const sectnum, int32_t xvect, int if ((cstat&dasprclipmask) == 0) continue; - vec2_t p1 = *(vec2_t const *)spr; + auto p1 = spr->pos.vec2; switch (cstat & (CSTAT_SPRITE_ALIGNMENT_WALL | CSTAT_SPRITE_ALIGNMENT_FLOOR)) { @@ -989,11 +986,6 @@ void getzrange(const vec3_t *pos, int16_t sectnum, vec2_t closest = pos->vec2; if (enginecompatibility_mode == ENGINECOMPATIBILITY_NONE) getsectordist(closest, sectnum, &closest); - if (enginecompatibility_mode == ENGINECOMPATIBILITY_19950829) - { - *ceilz = getceilzofslope_old(sectnum,closest.x,closest.y); - *florz = getflorzofslope_old(sectnum,closest.x,closest.y); - } else getzsofslope(sectnum,closest.x,closest.y,ceilz,florz); *ceilhit = sectnum+16384; *florhit = sectnum+16384; @@ -1037,11 +1029,8 @@ void getzrange(const vec3_t *pos, int16_t sectnum, if (wall[j].cstat&dawalclipmask) continue; // XXX? auto const sec = (usectorptr_t)§or[k]; - if (editstatus == 0) - { - if (((sec->ceilingstat&1) == 0) && (pos->z <= sec->ceilingz+(3<<8))) continue; - if (((sec->floorstat&1) == 0) && (pos->z >= sec->floorz-(3<<8))) continue; - } + if (((sec->ceilingstat&1) == 0) && (pos->z <= sec->ceilingz+(3<<8))) continue; + if (((sec->floorstat&1) == 0) && (pos->z >= sec->floorz-(3<<8))) continue; if (bitmap_test(clipsectormap, k) == 0) addclipsect(k); @@ -1061,11 +1050,6 @@ void getzrange(const vec3_t *pos, int16_t sectnum, closest = pos->vec2; if (enginecompatibility_mode == ENGINECOMPATIBILITY_NONE) getsectordist(closest, k, &closest); - if (enginecompatibility_mode == ENGINECOMPATIBILITY_19950829) - { - daz = getceilzofslope_old(k, closest.x,closest.y); - daz2 = getflorzofslope_old(k, closest.x,closest.y); - } else getzsofslope(k, closest.x,closest.y, &daz,&daz2); @@ -1241,7 +1225,7 @@ static int32_t hitscan_trysector(const vec3_t *sv, usectorptr_t sec, hitdata_t * auto const wal2 = (uwallptr_t)&wall[wal->point2]; int32_t j, dax=wal2->x-wal->x, day=wal2->y-wal->y; - i = nsqrtasm(compat_maybe_truncate_to_int32(uhypsq(dax,day))); if (i == 0) return 1; //continue; + i = ksqrt(compat_maybe_truncate_to_int32(uhypsq(dax,day))); if (i == 0) return 1; //continue; i = DivScale(heinum,i, 15); dax *= i; day *= i; @@ -1273,22 +1257,22 @@ static int32_t hitscan_trysector(const vec3_t *sv, usectorptr_t sec, hitdata_t * { if (tmp==NULL) { - if (inside(x1,y1,sec-(usectortype *)sector) == 1) + if (inside(x1,y1,sec-sector) == 1) { - hit_set(hit, sec-(usectortype *)sector, -1, -1, x1, y1, z1); + hit_set(hit, sec-sector, -1, -1, x1, y1, z1); hitscan_hitsectcf = (how+1)>>1; } } else { const int32_t curidx=(int32_t)tmp[0]; - auto const curspr=(uspritetype *)tmp[1]; + auto const curspr=(spritetype *)tmp[1]; const int32_t thislastsec = tmp[2]; if (!thislastsec) { - if (inside(x1,y1,sec-(usectortype *)sector) == 1) - hit_set(hit, curspr->sectnum, -1, curspr-(uspritetype *)sprite, x1, y1, z1); + if (inside(x1,y1,sec-sector) == 1) + hit_set(hit, curspr->sectnum, -1, curspr-sprite, x1, y1, z1); } } } @@ -1476,7 +1460,7 @@ int32_t hitscan(const vec3_t *sv, int16_t sectnum, int32_t vx, int32_t vy, int32 { if (picanm[tilenum].sf&PICANM_TEXHITSCAN_BIT) { - tileUpdatePicnum(&tilenum, 0); + tileUpdatePicnum(&tilenum, 0, 0); if (tileLoad(tilenum)) { diff --git a/source/build/src/defs.cpp b/source/build/src/defs.cpp deleted file mode 100644 index d141f2219..000000000 --- a/source/build/src/defs.cpp +++ /dev/null @@ -1,3210 +0,0 @@ -/* - * Definitions file parser for Build - * by Jonathon Fowler (jf@jonof.id.au) - * Remixed substantially by Ken Silverman - * See the included license file "BUILDLIC.TXT" for license info. - */ - -#include "build.h" -#include "compat.h" -#include "engine_priv.h" -#include "scriptfile.h" - -#include "mdsprite.h" // md3model_t -#include "buildtiles.h" -#include "bitmap.h" -#include "m_argv.h" -#include "gamecontrol.h" -#include "palettecontainer.h" -#include "mapinfo.h" -#include "parsefuncs.h" - -int tileSetHightileReplacement(int picnum, int palnum, const char* filename, float alphacut, float xscale, float yscale, float specpower, float specfactor, uint8_t flags); -int tileSetSkybox(int picnum, int palnum, const char** facenames, int flags); -void tileRemoveReplacement(int num); - - -int32_t getatoken(scriptfile *sf, const tokenlist *tl, int32_t ntokens) -{ - int32_t i; - - if (!sf) return T_ERROR; - if (!sf->GetString()) return T_EOF; - - for (i=ntokens-1; i>=0; i--) - { - if (sf->Compare(tl[i].text)) - return tl[i].tokenid; - } - return T_ERROR; -} - -void AddUserMapHack(usermaphack_t&); -#if 0 -// For later -{ -if (sc.Compare("music")) -{ - FString id, mus; - sc.MustGetToken('{'); - while (!sc.CheckToken('}')) - { - sc.MustGetToken(TK_Identifier); - if (sc.Compare("id")) - { - sc.MustGetString(); - id = sc.String; - } - else if (sc.Compare("file")) - { - sc.MustGetString(); - mus = sc.String; - } - } - - if (!SetMusicForMap(id, mus, true)) - { - sc.ScriptError("Map %s not found in music definition", id.GetChars()); - } - - char* tokenPtr = pScript->ltextptr; - char* musicID = NULL; - char* fileName = NULL; - char* musicEnd; - - if (scriptfile_getbraces(pScript, &musicEnd)) - break; - - while (pScript->textptr < musicEnd) - { - switch (getatoken(pScript, soundTokens, countof(soundTokens))) - { - case T_ID: scriptfile_getstring(pScript, &musicID); break; - case T_FILE: scriptfile_getstring(pScript, &fileName); break; - } - } - - if (!firstPass) - { - if (musicID == NULL) - { - pos.Message(MSG_ERROR, "missing ID for music definition\n"); - break; - } - - if (fileName == NULL || fileSystem.FileExists(fileName)) - break; - - if (S_DefineMusic(musicID, fileName) == -1) - pos.Message(MSG_ERROR, "invalid music ID"); - } - -} -} -#endif - -enum scripttoken_t -{ - T_INCLUDE = 0, - T_DEFINE, - T_DEFINETEXTURE, - T_DEFINESKYBOX, - T_DEFINETINT, - T_DEFINEMODEL, - T_DEFINEMODELFRAME, - T_DEFINEMODELANIM, - T_DEFINEMODELSKIN, - T_SELECTMODELSKIN, - T_DEFINEVOXEL, - T_DEFINEVOXELTILES, - T_MODEL, - T_FILE, - T_SCALE, - T_SHADE, - T_FRAME, - T_SMOOTHDURATION, - T_ANIM, - T_SKIN, - T_SURF, - T_TILE, - T_TILE0, - T_TILE1, - T_FRAME0, - T_FRAME1, - T_FPS, - T_FLAGS, - T_PAL, - T_BASEPAL, - T_DETAIL, - T_GLOW, - T_SPECULAR, - T_NORMAL, - T_PARAM, - T_HUD, - T_XADD, - T_YADD, - T_ZADD, - T_ANGADD, - T_FOV, - T_FLIPPED, - T_HIDE, - T_NOBOB, - T_NODEPTH, - T_VOXEL, - T_SKYBOX, - T_FRONT,T_RIGHT,T_BACK,T_LEFT,T_TOP,T_BOTTOM, - T_HIGHPALOOKUP, - T_TINT, - T_MAKEPALOOKUP, T_REMAPPAL, T_REMAPSELF, - T_NOFLOORPAL, T_FLOORPAL, - T_RED,T_GREEN,T_BLUE, - T_TEXTURE,T_ALPHACUT,T_XSCALE,T_YSCALE,T_SPECPOWER,T_SPECFACTOR,T_NOCOMPRESS,T_NODOWNSIZE, - T_FORCEFILTER, - T_ARTQUALITY, - T_ORIGSIZEX,T_ORIGSIZEY, - T_UNDEFMODEL,T_UNDEFMODELRANGE,T_UNDEFMODELOF,T_UNDEFTEXTURE,T_UNDEFTEXTURERANGE, - T_ALPHAHACK,T_ALPHAHACKRANGE, - T_SPRITECOL,T_2DCOL,T_2DCOLIDXRANGE, - T_FOGPAL, - T_LOADGRP, - T_DUMMYTILE,T_DUMMYTILERANGE, - T_SETUPTILE,T_SETUPTILERANGE, - T_UNDEFINETILE,T_UNDEFINETILERANGE, - T_ANIMTILERANGE, - T_CACHESIZE, - T_IMPORTTILE, - T_MUSIC,T_ID,T_SOUND, - T_TILEFROMTEXTURE, T_XOFFSET, T_YOFFSET, T_TEXHITSCAN, T_NOFULLBRIGHT, - T_ARTFILE, - T_INCLUDEDEFAULT, - T_ANIMSOUNDS, - T_CUTSCENE, - T_NOFLOORPALRANGE, - T_TEXHITSCANRANGE, - T_NOFULLBRIGHTRANGE, - T_MAPINFO, T_MAPFILE, T_MAPTITLE, T_MAPMD4, T_MHKFILE, - T_ECHO, - T_GLOBALFLAGS, - T_COPYTILE, - T_GLOBALGAMEFLAGS, - T_MULTIPSKY, T_HORIZFRAC, T_LOGNUMTILES, - T_BASEPALETTE, T_PALOOKUP, T_BLENDTABLE, - T_RAW, T_OFFSET, T_SHIFTLEFT, T_NOSHADES, T_COPY, - T_NUMALPHATABS, - T_UNDEF, - T_UNDEFBASEPALETTERANGE, T_UNDEFPALOOKUPRANGE, T_UNDEFBLENDTABLERANGE, - T_GLBLEND, T_FORWARD, T_REVERSE, T_BOTH, T_SRC, T_DST, T_ALPHA, - T_ZERO, T_ONE, - T_SRC_COLOR, T_ONE_MINUS_SRC_COLOR, - T_SRC_ALPHA, T_ONE_MINUS_SRC_ALPHA, - T_DST_ALPHA, T_ONE_MINUS_DST_ALPHA, - T_DST_COLOR, T_ONE_MINUS_DST_COLOR, - T_SHADERED, T_SHADEGREEN, T_SHADEBLUE, - T_SHADEFACTOR, - T_IFCRC,T_IFMATCH,T_CRC32, - T_SIZE, - T_NEWGAMECHOICES, - T_RFFDEFINEID, - T_EXTRA, - T_ROTATE, - T_SURFACE, T_VIEW, -}; - -static int32_t lastmodelid = -1, lastvoxid = -1, modelskin = -1, lastmodelskin = -1, seenframe = 0; - -static const char *skyfaces[6] = -{ - "front face", "right face", "back face", - "left face", "top face", "bottom face" -}; - -static int32_t defsparser(scriptfile *script); - -static void defsparser_include(const char *fn, scriptfile *script, FScriptPosition *pos) -{ - scriptfile *included; - - included = scriptfile_fromfile(fn); - if (!included) - { - if (!pos) - Printf("Warning: Failed including %s as module\n", fn); - else - pos->Message(MSG_ERROR, "Failed including %s", fn); - } - else - { - if (script) included->symbols = std::move(script->symbols); - defsparser(included); - if (script) script->symbols = std::move(included->symbols); - scriptfile_close(included); - } -} - - -static int32_t check_tile_range(const char *defcmd, int32_t *tilebeg, int32_t *tileend, - const scriptfile *script, FScriptPosition pos) -{ - if (*tileend < *tilebeg) - { - pos.Message(MSG_WARNING, "%s: backwards tile range", defcmd); - std::swap(*tilebeg, *tileend); - } - - if ((unsigned)*tilebeg >= MAXUSERTILES || (unsigned)*tileend >= MAXUSERTILES) - { - pos.Message(MSG_ERROR, "%s: Invalid tile range", defcmd); - return 1; - } - - return 0; -} - -static int32_t check_tile(const char *defcmd, int32_t tile, const scriptfile *script, FScriptPosition pos) -{ - if ((unsigned)tile >= MAXUSERTILES) - { - pos.Message(MSG_ERROR, "%s: Invalid tile number", defcmd); - return 1; - } - - return 0; -} - -#undef USE_DEF_PROGRESS -#if defined _WIN32 || defined HAVE_GTK2 -# define USE_DEF_PROGRESS -#endif - -static int32_t defsparser(scriptfile *script) -{ - int32_t tokn; -#ifdef USE_DEF_PROGRESS - static uint32_t iter = 0; -#endif - - static const tokenlist basetokens[] = - { - { "include", T_INCLUDE }, - { "#include", T_INCLUDE }, - { "includedefault", T_INCLUDEDEFAULT }, - { "#includedefault", T_INCLUDEDEFAULT }, - { "define", T_DEFINE }, - { "#define", T_DEFINE }, - - // deprecated style - { "definetexture", T_DEFINETEXTURE }, - { "defineskybox", T_DEFINESKYBOX }, - { "definetint", T_DEFINETINT }, - { "definemodel", T_DEFINEMODEL }, - { "definemodelframe",T_DEFINEMODELFRAME }, - { "definemodelanim", T_DEFINEMODELANIM }, - { "definemodelskin", T_DEFINEMODELSKIN }, - { "selectmodelskin", T_SELECTMODELSKIN }, - { "definevoxel", T_DEFINEVOXEL }, - { "definevoxeltiles",T_DEFINEVOXELTILES }, - - // new style - { "model", T_MODEL }, - { "voxel", T_VOXEL }, - { "skybox", T_SKYBOX }, - { "highpalookup", T_HIGHPALOOKUP }, - { "tint", T_TINT }, - { "makepalookup", T_MAKEPALOOKUP }, - { "texture", T_TEXTURE }, - { "tile", T_TEXTURE }, - { "music", T_MUSIC }, - { "sound", T_SOUND }, - { "animsounds", T_ANIMSOUNDS }, // dummy - { "cutscene", T_CUTSCENE }, - { "nofloorpalrange", T_NOFLOORPALRANGE }, - { "texhitscanrange", T_TEXHITSCANRANGE }, - { "nofullbrightrange", T_NOFULLBRIGHTRANGE }, - // other stuff - { "undefmodel", T_UNDEFMODEL }, - { "undefmodelrange", T_UNDEFMODELRANGE }, - { "undefmodelof", T_UNDEFMODELOF }, - { "undeftexture", T_UNDEFTEXTURE }, - { "undeftexturerange", T_UNDEFTEXTURERANGE }, - { "alphahack", T_ALPHAHACK }, - { "alphahackrange", T_ALPHAHACKRANGE }, - { "spritecol", T_SPRITECOL }, - { "2dcol", T_2DCOL }, - { "2dcolidxrange", T_2DCOLIDXRANGE }, - { "fogpal", T_FOGPAL }, - { "loadgrp", T_LOADGRP }, - { "dummytile", T_DUMMYTILE }, - { "dummytilerange", T_DUMMYTILERANGE }, - { "setuptile", T_SETUPTILE }, - { "setuptilerange", T_SETUPTILERANGE }, - { "undefinetile", T_UNDEFINETILE }, - { "undefinetilerange", T_UNDEFINETILERANGE }, - { "animtilerange", T_ANIMTILERANGE }, - { "cachesize", T_CACHESIZE }, - { "dummytilefrompic",T_IMPORTTILE }, - { "tilefromtexture", T_TILEFROMTEXTURE }, - { "artfile", T_ARTFILE }, - { "mapinfo", T_MAPINFO }, - { "echo", T_ECHO }, - { "globalflags", T_GLOBALFLAGS }, - { "copytile", T_COPYTILE }, - { "globalgameflags", T_GLOBALGAMEFLAGS }, // dummy - { "multipsky", T_MULTIPSKY }, - { "basepalette", T_BASEPALETTE }, - { "palookup", T_PALOOKUP }, - { "blendtable", T_BLENDTABLE }, - { "numalphatables", T_NUMALPHATABS }, - { "undefbasepaletterange", T_UNDEFBASEPALETTERANGE }, - { "undefpalookuprange", T_UNDEFPALOOKUPRANGE }, - { "undefblendtablerange", T_UNDEFBLENDTABLERANGE }, - { "shadefactor", T_SHADEFACTOR }, - { "newgamechoices", T_NEWGAMECHOICES }, - { "rffdefineid", T_RFFDEFINEID }, // dummy - }; - - script->SetNoOctals(true); - while (1) - { -#ifdef USE_DEF_PROGRESS - if (++iter >= 50) - { - Printf("."); - iter = 0; - } -#endif - tokn = getatoken(script,basetokens,countof(basetokens)); - auto pos = scriptfile_getposition(script); - switch (tokn) - { - case T_ERROR: - pos.Message(MSG_ERROR, "Unknown error"); - break; - case T_EOF: - return 0; - case T_INCLUDE: - { - FString fn; - if (!scriptfile_getstring(script,&fn)) - defsparser_include(fn, script, &pos); - break; - } - case T_INCLUDEDEFAULT: - { - defsparser_include(G_DefaultDefFile(), script, &pos); - break; - } - case T_DEFINE: - { - FString name; - int32_t number; - - if (scriptfile_getstring(script,&name)) break; - if (scriptfile_getsymbol(script,&number)) break; - - if (scriptfile_addsymbolvalue(script, name,number) < 0) - pos.Message(MSG_WARNING, "Warning: Symbol %s was NOT redefined to %d", name.GetChars(),number); - break; - } - - // OLD (DEPRECATED) DEFINITION SYNTAX - case T_DEFINETEXTURE: - { - int32_t tile,pal,fnoo; - FString fn; - - if (scriptfile_getsymbol(script,&tile)) break; - if (scriptfile_getsymbol(script,&pal)) break; - if (scriptfile_getnumber(script,&fnoo)) break; //x-center - if (scriptfile_getnumber(script,&fnoo)) break; //y-center - if (scriptfile_getnumber(script,&fnoo)) break; //x-size - if (scriptfile_getnumber(script,&fnoo)) break; //y-size - if (scriptfile_getstring(script,&fn)) break; - - if (!fileSystem.FileExists(fn)) - break; - - tileSetHightileReplacement(tile,pal,fn,-1.0,1.0,1.0,1.0,1.0,0); - } - break; - case T_DEFINESKYBOX: - { - int32_t tile,pal,i; - FString fn[6]; - int happy = 1; - - if (scriptfile_getsymbol(script,&tile)) break; - if (scriptfile_getsymbol(script,&pal)) break; - if (scriptfile_getsymbol(script,&i)) break; //future expansion - for (i=0; i<6; i++) - { - if (scriptfile_getstring(script,&fn[i])) break; //grab the 6 faces - - if (!fileSystem.FileExists(fn[i])) - happy = 0; - } - if (i < 6 || !happy) break; - tileSetSkybox(tile, pal, (const char **)fn, 0); - } - break; - case T_DEFINETINT: - { - int32_t pal, r,g,b,f; - - if (scriptfile_getsymbol(script,&pal)) break; - if (scriptfile_getnumber(script,&r)) break; - if (scriptfile_getnumber(script,&g)) break; - if (scriptfile_getnumber(script,&b)) break; - if (scriptfile_getnumber(script,&f)) break; //effects - lookups.setPaletteTint(pal,r,g,b,0,0,0,f); - } - break; - case T_ALPHAHACK: - { - int32_t tile; - double alpha; - - if (scriptfile_getsymbol(script,&tile)) break; - if (scriptfile_getdouble(script,&alpha)) break; - if ((uint32_t)tile < MAXTILES) - TileFiles.tiledata[tile].texture->alphaThreshold = (float)alpha; - } - break; - case T_ALPHAHACKRANGE: - { - int32_t tilenume1,tilenume2; - double alpha; - - if (scriptfile_getsymbol(script,&tilenume1)) break; - if (scriptfile_getsymbol(script,&tilenume2)) break; - if (scriptfile_getdouble(script,&alpha)) break; - - if (check_tile_range("alphahackrange", &tilenume1, &tilenume2, script, pos)) - break; - - for (int i=tilenume1; i<=tilenume2; i++) - TileFiles.tiledata[i].texture->alphaThreshold = (float)alpha; - } - break; - case T_SPRITECOL: - { - int32_t tile,col,col2; - - if (scriptfile_getsymbol(script,&tile)) break; - if (scriptfile_getnumber(script,&col)) break; - if (scriptfile_getnumber(script,&col2)) break; - } - break; - case T_2DCOL: - { - int32_t col,b,g,r; - - if (scriptfile_getnumber(script,&col)) break; - if (scriptfile_getnumber(script,&r)) break; - if (scriptfile_getnumber(script,&g)) break; - if (scriptfile_getnumber(script,&b)) break; - - if ((unsigned)col < 256) - { - } - } - break; - case T_2DCOLIDXRANGE: // NOTE: takes precedence over 2dcol, see InitCustomColors() - { - int32_t col, idx, idxend; - - if (scriptfile_getnumber(script,&col)) break; - if (scriptfile_getnumber(script,&idx)) break; - if (scriptfile_getnumber(script,&idxend)) break; - - } - break; - case T_FOGPAL: - { - int32_t p,r,g,b; - - if (scriptfile_getsymbol(script,&p)) break; - if (scriptfile_getnumber(script,&r)) break; - if (scriptfile_getnumber(script,&g)) break; - if (scriptfile_getnumber(script,&b)) break; - - r = clamp(r, 0, 63); - g = clamp(g, 0, 63); - b = clamp(b, 0, 63); - - lookups.makeTable(p, NULL, r<<2, g<<2, b<<2, 1); - } - break; - case T_NOFLOORPALRANGE: - { - int32_t b,e,i; - - if (scriptfile_getsymbol(script,&b)) break; - if (scriptfile_getsymbol(script,&e)) break; - - b = max(b, 1); - e = min(e, MAXPALOOKUPS-1); - - for (i = b; i <= e; i++) - lookups.tables[i].noFloorPal = true; - } - break; - case T_LOADGRP: - { - scriptfile_getstring(script,nullptr); -#if 0 - if (!scriptfile_getstring(pScript, &fileName) && firstPass) - { - fileSystem.AddAdditionalFile(fileName); - } -#endif - } - break; - case T_CACHESIZE: - { - int32_t j; - - if (scriptfile_getnumber(script,&j)) break; - } - break; - case T_SHADEFACTOR: - //scriptfile_getnumber(script, &realmaxshade); - //frealmaxshade = (float)realmaxshade; - break; - case T_ARTFILE: - { - FScanner::SavedPos blockend; - FString fn; - int32_t tile = -1, havetile = 0; - - static const tokenlist artfiletokens[] = - { - { "file", T_FILE }, - { "tile", T_TILE }, - }; - - if (scriptfile_getbraces(script,&blockend)) break; - while (!scriptfile_endofblock(script, blockend)) - { - int32_t token = getatoken(script,artfiletokens,countof(artfiletokens)); - switch (token) - { - case T_FILE: - scriptfile_getstring(script,&fn); - break; - case T_TILE: - havetile = 1; - scriptfile_getsymbol(script,&tile); - break; - default: - break; - } - } - - if (fn.IsEmpty()) - { - pos.Message(MSG_ERROR, "missing 'file name' for artfile definition"); - break; - } - if (!check_tile("artfile", tile, script, pos)) - TileFiles.LoadArtFile(fn, nullptr, tile); - } - break; - case T_SETUPTILE: - { - int tile, tmp; - - if (scriptfile_getsymbol(script,&tile)) break; - if (check_tile("setuptile", tile, script, pos)) - break; - auto& tiled = TileFiles.tiledata[tile]; - if (scriptfile_getsymbol(script,&tmp)) break; // XXX - tiled.h_xsize = tmp; - if (scriptfile_getsymbol(script,&tmp)) break; - tiled.h_ysize = tmp; - if (scriptfile_getsymbol(script,&tmp)) break; - tiled.h_xoffs = tmp; - if (scriptfile_getsymbol(script,&tmp)) break; - tiled.h_yoffs = tmp; - break; - } - case T_SETUPTILERANGE: - { - int tile1,tile2,xsiz,ysiz,xoffs,yoffs,i; - - if (scriptfile_getsymbol(script,&tile1)) break; - if (scriptfile_getsymbol(script,&tile2)) break; - if (scriptfile_getnumber(script,&xsiz)) break; - if (scriptfile_getnumber(script,&ysiz)) break; - if (scriptfile_getsymbol(script,&xoffs)) break; - if (scriptfile_getsymbol(script,&yoffs)) break; - - if (check_tile_range("setuptilerange", &tile1, &tile2, script, pos)) - break; - - for (i=tile1; i<=tile2; i++) - { - auto& tiled = TileFiles.tiledata[i]; - - tiled.h_xsize = xsiz; - tiled.h_ysize = ysiz; - tiled.h_xoffs = xoffs; - tiled.h_yoffs = yoffs; - } - - break; - } - case T_ANIMTILERANGE: - parseAnimTileRange(*script, pos); - break; - - case T_TILEFROMTEXTURE: - { - auto texturepos = scriptfile_getposition(script); - FScanner::SavedPos textureend; - TileImport imp; - - static const tokenlist tilefromtexturetokens[] = - { - { "file", T_FILE }, - { "name", T_FILE }, - { "alphacut", T_ALPHACUT }, - { "xoffset", T_XOFFSET }, - { "xoff", T_XOFFSET }, - { "yoffset", T_YOFFSET }, - { "yoff", T_YOFFSET }, - { "texhitscan", T_TEXHITSCAN }, - { "nofullbright", T_NOFULLBRIGHT }, - { "texture", T_TEXTURE }, - { "ifcrc", T_IFCRC }, - { "ifmatch", T_IFMATCH }, - { "extra", T_EXTRA }, - // Blood also defines these. - { "surface", T_SURFACE }, - { "voxel", T_VOXEL }, - { "view", T_VIEW }, - { "shade", T_SHADE }, - - }; - - if (scriptfile_getsymbol(script,&imp.tile)) break; - if (scriptfile_getbraces(script,&textureend)) break; - while (!scriptfile_endofblock(script, textureend)) - { - int32_t token = getatoken(script,tilefromtexturetokens,countof(tilefromtexturetokens)); - switch (token) - { - case T_FILE: - scriptfile_getstring(script,&imp.fn); - break; - case T_ALPHACUT: - scriptfile_getsymbol(script,&imp.alphacut); - imp.alphacut = clamp(imp.alphacut, 0, 255); - break; - case T_XOFFSET: - scriptfile_getsymbol(script,&imp.xoffset); - imp.xoffset = clamp(imp.xoffset, -128, 127); - break; - case T_YOFFSET: - scriptfile_getsymbol(script,&imp.yoffset); - imp.yoffset = clamp(imp.yoffset, -128, 127); - break; - case T_IFCRC: - scriptfile_getsymbol(script, &imp.crc32); - break; - case T_IFMATCH: - { - FScanner::SavedPos ifmatchend; - - static const tokenlist ifmatchtokens[] = - { - { "crc32", T_CRC32 }, - { "size", T_SIZE }, - }; - - if (scriptfile_getbraces(script,&ifmatchend)) break; - while (!scriptfile_endofblock(script, ifmatchend)) - { - int32_t token = getatoken(script,ifmatchtokens,countof(ifmatchtokens)); - switch (token) - { - case T_CRC32: - scriptfile_getsymbol(script, &imp.crc32); - break; - case T_SIZE: - scriptfile_getsymbol(script, &imp.sizex); - scriptfile_getsymbol(script, &imp.sizey); - break; - default: - break; - } - } - break; - } - case T_TEXHITSCAN: - imp.flags |= PICANM_TEXHITSCAN_BIT; - break; - case T_NOFULLBRIGHT: - imp.flags |= PICANM_NOFULLBRIGHT_BIT; - break; - case T_TEXTURE: - imp.istexture = 1; - break; - case T_EXTRA: - scriptfile_getsymbol(script, &imp.extra); - break; - case T_SURFACE: - scriptfile_getsymbol(script, &imp.surface); - break; - case T_VOXEL: - scriptfile_getsymbol(script, &imp.vox); - break; - case T_VIEW: - scriptfile_getsymbol(script, &imp.extra); - imp.extra &= 7; - break; - case T_SHADE: - scriptfile_getsymbol(script, &imp.shade); - break; - - default: - break; - } - } - processTileImport("tileimporttexture", pos, imp); - } - break; - case T_COPYTILE: - { - FScanner::SavedPos blockend; - int32_t tile = -1, source; - int32_t havetile = 0, havexoffset = 0, haveyoffset = 0; - int32_t xoffset = -1024, yoffset = -1024; - int32_t flags = 0; - int32_t tsiz = 0; - int32_t temppal = -1; - int32_t tempsource = -1; - - static const tokenlist copytiletokens[] = - { - { "tile", T_TILE }, - { "pal", T_PAL }, - { "xoffset", T_XOFFSET }, - { "xoff", T_XOFFSET }, - { "yoffset", T_YOFFSET }, - { "yoff", T_YOFFSET }, - { "texhitscan", T_TEXHITSCAN }, - { "nofullbright", T_NOFULLBRIGHT }, - }; - - if (scriptfile_getsymbol(script,&tile)) break; - source = tile; // without a "tile" token, we still palettize self by default - if (scriptfile_getbraces(script,&blockend)) break; - while (!scriptfile_endofblock(script, blockend)) - { - int32_t token = getatoken(script,copytiletokens,countof(copytiletokens)); - switch (token) - { - case T_TILE: - { - scriptfile_getsymbol(script,&tempsource); - - if (check_tile("copytile", tempsource, script, pos)) - break; - source = tempsource; - - havetile = 1; - break; - } - case T_PAL: - { - scriptfile_getsymbol(script,&temppal); - - // palettize self case - if (!havetile) - { - if (check_tile("copytile", source, script, pos)) - break; - havetile = 1; - } - - if ((unsigned)temppal >= MAXPALOOKUPS-RESERVEDPALS) - { - pos.Message(MSG_ERROR, "copytile 'palette number' out of range (max=%d)\n", - MAXPALOOKUPS-RESERVEDPALS-1); - break; - } - break; - } - case T_XOFFSET: - havexoffset = 1; - scriptfile_getsymbol(script,&xoffset); break; - case T_YOFFSET: - haveyoffset = 1; - scriptfile_getsymbol(script,&yoffset); break; - case T_TEXHITSCAN: - flags |= PICANM_TEXHITSCAN_BIT; - break; - case T_NOFULLBRIGHT: - flags |= PICANM_NOFULLBRIGHT_BIT; - break; - default: - break; - } - } - - if (check_tile("copytile", tile, script, pos)) - break; - - if (!havetile) - { - // if !havetile, we have never confirmed a valid source - if (check_tile("copytile", source, script, pos)) - break; - } - - tileCopy(tile, tempsource, temppal, xoffset, yoffset, flags); - } - break; - case T_IMPORTTILE: - { - int32_t tile; - FString fn; - - if (scriptfile_getsymbol(script,&tile)) break; - if (scriptfile_getstring(script,&fn)) break; - - if (check_tile("importtile", tile, script, pos)) - break; - - int32_t const texstatus = tileImportFromTexture(fn, tile, 255, 0); - if (texstatus < 0) - break; - - picanm[tile] = {}; - - break; - } - case T_DUMMYTILE: - { - int32_t tile, xsiz, ysiz; - - if (scriptfile_getsymbol(script,&tile)) break; - if (scriptfile_getsymbol(script,&xsiz)) break; - if (scriptfile_getsymbol(script,&ysiz)) break; - - if (check_tile("dummytile", tile, script, pos)) - break; - - tileSetDummy(tile, xsiz, ysiz); - - break; - } - case T_DUMMYTILERANGE: - { - int32_t tile1,tile2,xsiz,ysiz,i; - - if (scriptfile_getsymbol(script,&tile1)) break; - if (scriptfile_getsymbol(script,&tile2)) break; - if (scriptfile_getnumber(script,&xsiz)) break; - if (scriptfile_getnumber(script,&ysiz)) break; - - if (check_tile_range("dummytilerange", &tile1, &tile2, script, pos)) - break; - - if (xsiz < 0 || ysiz < 0) - break; // TODO: message - - for (i=tile1; i<=tile2; i++) - { - tileSetDummy(i, xsiz, ysiz); - } - - break; - } - - case T_UNDEFINETILE: - { - int32_t tile; - - if (scriptfile_getsymbol(script,&tile)) break; - - if (check_tile("undefinetile", tile, script, pos)) - break; - - tileDelete(tile); - - break; - } - case T_UNDEFINETILERANGE: - { - int32_t tile1, tile2; - - if (scriptfile_getsymbol(script,&tile1)) break; - if (scriptfile_getsymbol(script,&tile2)) break; - - if (check_tile_range("undefinetilerange", &tile1, &tile2, script, pos)) - break; - - for (bssize_t i = tile1; i <= tile2; i++) - tileDelete(i); - - break; - } - - case T_DEFINEMODEL: - { - FString modelfn; - double scale; - int32_t shadeoffs; - - if (scriptfile_getstring(script,&modelfn)) break; - if (scriptfile_getdouble(script,&scale)) break; - if (scriptfile_getnumber(script,&shadeoffs)) break; - - lastmodelid = md_loadmodel(modelfn); - if (lastmodelid < 0) - { - Printf("Warning: Failed loading MD2/MD3 model \"%s\"\n", modelfn.GetChars()); - break; - } - md_setmisc(lastmodelid,(float)scale, shadeoffs,0.0,0.0,0); - - modelskin = lastmodelskin = 0; - seenframe = 0; - } - break; - case T_DEFINEMODELFRAME: - { - FString framename; - char happy=1; - int32_t tilex; - int32_t ftilenume, ltilenume; - - if (scriptfile_getstring(script,&framename)) break; - if (scriptfile_getsymbol(script,&ftilenume)) break; //first tile number - if (scriptfile_getsymbol(script,<ilenume)) break; //last tile number (inclusive) - - if (check_tile_range("definemodelframe", &ftilenume, <ilenume, script, pos)) - break; - - if (lastmodelid < 0) - { - pos.Message(MSG_WARNING, "Ignoring frame definition.\n"); - break; - } - for (tilex = ftilenume; tilex <= ltilenume && happy; tilex++) - { - switch (md_defineframe(lastmodelid, framename, tilex, max(0,modelskin), 0.0f,0)) - { - case -1: - happy = 0; break; // invalid model id!? - case -2: - Printf("Invalid tile number"); - happy = 0; - break; - case -3: - Printf("Invalid frame name"); - happy = 0; - break; - default: - break; - } - } - seenframe = 1; - } - break; - case T_DEFINEMODELANIM: - { - FString startframe, endframe; - int32_t flags; - double dfps; - - if (scriptfile_getstring(script,&startframe)) break; - if (scriptfile_getstring(script,&endframe)) break; - if (scriptfile_getdouble(script,&dfps)) break; //animation frame rate - if (scriptfile_getnumber(script,&flags)) break; - - if (lastmodelid < 0) - { - Printf("Warning: Ignoring animation definition.\n"); - break; - } - switch (md_defineanimation(lastmodelid, startframe, endframe, (int32_t)(dfps*(65536.0*.001)), flags)) - { - case 0: - break; - case -1: - break; // invalid model id!? - case -2: - Printf("Invalid starting frame name"); - break; - case -3: - Printf("Invalid ending frame name"); - break; - case -4: - Printf("Out of memory"); - break; - } - } - break; - case T_DEFINEMODELSKIN: - { - int32_t palnum; - FString skinfn; - - if (scriptfile_getsymbol(script,&palnum)) break; - if (scriptfile_getstring(script,&skinfn)) break; //skin filename - - // if we see a sequence of definemodelskin, then a sequence of definemodelframe, - // and then a definemodelskin, we need to increment the skin counter. - // - // definemodel "mymodel.md2" 1 1 - // definemodelskin 0 "normal.png" // skin 0 - // definemodelskin 21 "normal21.png" - // definemodelframe "foo" 1000 1002 // these use skin 0 - // definemodelskin 0 "wounded.png" // skin 1 - // definemodelskin 21 "wounded21.png" - // definemodelframe "foo2" 1003 1004 // these use skin 1 - // selectmodelskin 0 // resets to skin 0 - // definemodelframe "foo3" 1005 1006 // these use skin 0 - if (seenframe) { modelskin = ++lastmodelskin; } - seenframe = 0; - - if (!fileSystem.FileExists(skinfn)) - break; - - switch (md_defineskin(lastmodelid, skinfn, palnum, max(0,modelskin), 0, 0.0f, 1.0f, 1.0f, 0)) - { - case 0: - break; - case -1: - break; // invalid model id!? - case -2: - Printf("Invalid skin filename"); - break; - case -3: - Printf("Invalid palette number"); - break; - case -4: - Printf("Out of memory"); - break; - } - } - break; - case T_SELECTMODELSKIN: - { - if (scriptfile_getsymbol(script,&modelskin)) break; - } - break; - case T_DEFINEVOXEL: - { - FString fn; - - if (scriptfile_getstring(script,&fn)) - break; //voxel filename - - while (nextvoxid < MAXVOXELS && (voxreserve[nextvoxid>>3]&(1<<(nextvoxid&7)))) - nextvoxid++; - - if (nextvoxid == MAXVOXELS) - { - Printf("Maximum number of voxels (%d) already defined.\n", MAXVOXELS); - break; - } - - if (qloadkvx(nextvoxid, fn)) - { - Printf("Failure loading voxel file \"%s\"\n",fn.GetChars()); - break; - } - - lastvoxid = nextvoxid++; - } - break; - case T_DEFINEVOXELTILES: - { - int32_t ftilenume, ltilenume, tilex; - - if (scriptfile_getsymbol(script,&ftilenume)) break; //1st tile # - if (scriptfile_getsymbol(script,<ilenume)) break; //last tile # - - if (check_tile_range("definevoxeltiles", &ftilenume, <ilenume, script, pos)) - break; - - if (lastvoxid < 0) - { - Printf("Warning: Ignoring voxel tiles definition.\n"); - break; - } - - for (tilex = ftilenume; tilex <= ltilenume; tilex++) - tiletovox[tilex] = lastvoxid; - } - break; - - // NEW (ENCOURAGED) DEFINITION SYNTAX - case T_MODEL: - { - FScanner::SavedPos modelend; - FString modelfn; - double scale=1.0, mzadd=0.0, myoffset=0.0; - int32_t shadeoffs=0, pal=0, flags=0; - uint8_t usedframebitmap[(1024+7)>>3]; - - int32_t model_ok = 1; - - static const tokenlist modeltokens[] = - { - { "scale", T_SCALE }, - { "shade", T_SHADE }, - { "zadd", T_ZADD }, - { "yoffset", T_YOFFSET }, - { "frame", T_FRAME }, - { "anim", T_ANIM }, - { "skin", T_SKIN }, - { "detail", T_DETAIL }, - { "glow", T_GLOW }, - { "specular", T_SPECULAR }, - { "normal", T_NORMAL }, - { "hud", T_HUD }, - { "flags", T_FLAGS }, - }; - - memset(usedframebitmap, 0, sizeof(usedframebitmap)); - - modelskin = lastmodelskin = 0; - seenframe = 0; - - if (scriptfile_getstring(script,&modelfn)) break; - if (scriptfile_getbraces(script,&modelend)) break; - - lastmodelid = md_loadmodel(modelfn); - if (lastmodelid < 0) - { - Printf("Warning: Failed loading MD2/MD3 model \"%s\"\n", modelfn.GetChars()); - scriptfile_setposition(script, modelend); - break; - } - while (!scriptfile_endofblock(script, modelend)) - { - int32_t token = getatoken(script,modeltokens,countof(modeltokens)); - switch (token) - { - //case T_ERROR: Printf("Error on line %s:%d in model tokens\n", script->filename,script->linenum); break; - case T_SCALE: - scriptfile_getdouble(script,&scale); break; - case T_SHADE: - scriptfile_getnumber(script,&shadeoffs); break; - case T_ZADD: - scriptfile_getdouble(script,&mzadd); break; - case T_YOFFSET: - scriptfile_getdouble(script,&myoffset); break; - case T_FLAGS: - scriptfile_getnumber(script,&flags); break; - case T_FRAME: - { - auto framepos = scriptfile_getposition(script); - FScanner::SavedPos frameend; - FString framename; - char happy=1; - int32_t tilex = 0, framei; - int32_t ftilenume = -1, ltilenume = -1; - double smoothduration = 0.1f; - - static const tokenlist modelframetokens[] = - { - { "pal", T_PAL }, - { "frame", T_FRAME }, - { "name", T_FRAME }, - { "tile", T_TILE }, - { "tile0", T_TILE0 }, - { "tile1", T_TILE1 }, - { "smoothduration", T_SMOOTHDURATION }, - }; - - if (scriptfile_getbraces(script,&frameend)) break; - while (!scriptfile_endofblock(script, frameend)) - { - switch (getatoken(script,modelframetokens,countof(modelframetokens))) - { - case T_PAL: - scriptfile_getsymbol(script,&pal); break; - case T_FRAME: - scriptfile_getstring(script,&framename); break; - case T_TILE: - scriptfile_getsymbol(script,&ftilenume); ltilenume = ftilenume; break; - case T_TILE0: - scriptfile_getsymbol(script,&ftilenume); break; //first tile number - case T_TILE1: - scriptfile_getsymbol(script,<ilenume); break; //last tile number (inclusive) - case T_SMOOTHDURATION: - scriptfile_getdouble(script,&smoothduration); break; - } - } - - if (check_tile_range("model: frame", &ftilenume, <ilenume, script, pos)) - { - model_ok = 0; - break; - } - - if (lastmodelid < 0) - { - framepos.Message(MSG_WARNING, "ignoring frame definition"); - break; - } - - if (smoothduration > 1.0) - { - framepos.Message(MSG_WARNING, "smoothduration out of range"); - smoothduration = 1.0; - } - for (tilex = ftilenume; tilex <= ltilenume && happy; tilex++) - { - framei = md_defineframe(lastmodelid, framename, tilex, max(0,modelskin), smoothduration,pal); - switch (framei) - { - case -1: - happy = 0; break; // invalid model id!? - case -2: - framepos.Message(MSG_WARNING, "Invalid tile number"); - happy = 0; - break; - case -3: - framepos.Message(MSG_WARNING, "%s: Invalid frame name", framename.GetChars()); - happy = 0; - break; - default: - if (framei >= 0 && framei<1024) - usedframebitmap[framei>>3] |= pow2char[framei&7]; - } - model_ok &= happy; - } - seenframe = 1; - } - break; - case T_ANIM: - { - auto animpos = scriptfile_getposition(script); - FScanner::SavedPos animend; - FString startframe, endframe; - int happy=1; - int32_t flags = 0; - double dfps = 1.0; - - static const tokenlist modelanimtokens[] = - { - { "frame0", T_FRAME0 }, - { "frame1", T_FRAME1 }, - { "fps", T_FPS }, - { "flags", T_FLAGS }, - }; - - if (scriptfile_getbraces(script,&animend)) break; - while (!scriptfile_endofblock(script, animend)) - { - switch (getatoken(script,modelanimtokens,countof(modelanimtokens))) - { - case T_FRAME0: - scriptfile_getstring(script,&startframe); break; - case T_FRAME1: - scriptfile_getstring(script,&endframe); break; - case T_FPS: - scriptfile_getdouble(script,&dfps); break; //animation frame rate - case T_FLAGS: - scriptfile_getsymbol(script,&flags); break; - } - } - - if (startframe.IsEmpty()) animpos.Message(MSG_ERROR, "missing 'start frame' for anim definition"), happy = 0; - if (endframe.IsEmpty()) animpos.Message(MSG_ERROR, "missing 'end frame' for anim definition"), happy = 0; - model_ok &= happy; - if (!happy) break; - - if (lastmodelid < 0) - { - Printf("Warning: Ignoring animation definition.\n"); - break; - } - switch (md_defineanimation(lastmodelid, startframe, endframe, (int32_t)(dfps*(65536.0*.001)), flags)) - { - case 0: - break; - case -1: - break; // invalid model id!? - case -2: - pos.Message(MSG_ERROR, "Invalid starting frame name"); - model_ok = 0; - break; - case -3: - pos.Message(MSG_ERROR, "Invalid ending frame name"); - model_ok = 0; - break; - case -4: - pos.Message(MSG_ERROR, "Out of memory"); - model_ok = 0; - break; - } - } - break; - case T_SKIN: case T_DETAIL: case T_GLOW: case T_SPECULAR: case T_NORMAL: - { - auto skinpos = scriptfile_getposition(script); - FScanner::SavedPos skinend; - FString skinfn; - int32_t palnum = 0, surfnum = 0; - double param = 1.0, specpower = 1.0, specfactor = 1.0; - int32_t flags = 0; - - static const tokenlist modelskintokens[] = - { - { "pal", T_PAL }, - { "file", T_FILE }, - { "surf", T_SURF }, - { "surface", T_SURF }, - { "intensity", T_PARAM }, - { "scale", T_PARAM }, - { "detailscale", T_PARAM }, - { "specpower", T_SPECPOWER }, { "specularpower", T_SPECPOWER }, { "parallaxscale", T_SPECPOWER }, - { "specfactor", T_SPECFACTOR }, { "specularfactor", T_SPECFACTOR }, { "parallaxbias", T_SPECFACTOR }, - { "nocompress", T_NOCOMPRESS }, - { "nodownsize", T_NODOWNSIZE }, - { "forcefilter", T_FORCEFILTER }, - { "artquality", T_ARTQUALITY }, - }; - - if (scriptfile_getbraces(script,&skinend)) break; - while (!scriptfile_endofblock(script, skinend)) - { - switch (getatoken(script,modelskintokens,countof(modelskintokens))) - { - case T_PAL: - scriptfile_getsymbol(script,&palnum); break; - case T_PARAM: - scriptfile_getdouble(script,¶m); break; - case T_SPECPOWER: - scriptfile_getdouble(script,&specpower); break; - case T_SPECFACTOR: - scriptfile_getdouble(script,&specfactor); break; - case T_FILE: - scriptfile_getstring(script,&skinfn); break; //skin filename - case T_SURF: - scriptfile_getnumber(script,&surfnum); break; - } - } - - if (skinfn.IsEmpty()) - { - skinpos.Message(MSG_ERROR, "missing 'skin filename' for skin definition"); - model_ok = 0; - break; - } - - if (seenframe) { modelskin = ++lastmodelskin; } - seenframe = 0; - - switch (token) - { - case T_DETAIL: - palnum = DETAILPAL; - param = 1.0f / param; - break; - case T_GLOW: - palnum = GLOWPAL; - break; - case T_SPECULAR: - palnum = SPECULARPAL; - break; - case T_NORMAL: - palnum = NORMALPAL; - break; - } - - if (!fileSystem.FileExists(skinfn)) - break; - - switch (md_defineskin(lastmodelid, skinfn, palnum, max(0,modelskin), surfnum, param, specpower, specfactor, flags)) - { - case 0: - break; - case -1: - break; // invalid model id!? - case -2: - skinpos.Message(MSG_ERROR, "Invalid skin filename"); - model_ok = 0; - break; - case -3: - skinpos.Message(MSG_ERROR, "Invalid palette number"); - model_ok = 0; - break; - case -4: - skinpos.Message(MSG_ERROR, "Out of memory"); - model_ok = 0; - break; - } - } - break; - case T_HUD: - { - auto hudpos = scriptfile_getposition(script); - FScanner::SavedPos frameend; - char happy=1; - int32_t tilex = 0; - int32_t ftilenume = -1, ltilenume = -1, flags = 0, fov = -1, angadd = 0; - double xadd = 0.0, yadd = 0.0, zadd = 0.0; - - static const tokenlist modelhudtokens[] = - { - { "tile", T_TILE }, - { "tile0", T_TILE0 }, - { "tile1", T_TILE1 }, - { "xadd", T_XADD }, - { "yadd", T_YADD }, - { "zadd", T_ZADD }, - { "angadd", T_ANGADD }, - { "fov", T_FOV }, - { "hide", T_HIDE }, - { "nobob", T_NOBOB }, - { "flipped",T_FLIPPED}, - { "nodepth",T_NODEPTH}, - }; - - if (scriptfile_getbraces(script,&frameend)) break; - while (!scriptfile_endofblock(script, frameend)) - { - switch (getatoken(script,modelhudtokens,countof(modelhudtokens))) - { - case T_TILE: - scriptfile_getsymbol(script,&ftilenume); ltilenume = ftilenume; break; - case T_TILE0: - scriptfile_getsymbol(script,&ftilenume); break; //first tile number - case T_TILE1: - scriptfile_getsymbol(script,<ilenume); break; //last tile number (inclusive) - case T_XADD: - scriptfile_getdouble(script,&xadd); break; - case T_YADD: - scriptfile_getdouble(script,&yadd); break; - case T_ZADD: - scriptfile_getdouble(script,&zadd); break; - case T_ANGADD: - scriptfile_getsymbol(script,&angadd); break; - case T_FOV: - scriptfile_getsymbol(script,&fov); break; - case T_HIDE: - flags |= HUDFLAG_HIDE; break; - case T_NOBOB: - flags |= HUDFLAG_NOBOB; break; - case T_FLIPPED: - flags |= HUDFLAG_FLIPPED; break; - case T_NODEPTH: - flags |= HUDFLAG_NODEPTH; break; - } - } - - if (check_tile_range("hud", &ftilenume, <ilenume, script, hudpos)) - { - model_ok = 0; - break; - } - - if (lastmodelid < 0) - { - hudpos.Message(MSG_WARNING, "Ignoring frame definition."); - break; - } - for (tilex = ftilenume; tilex <= ltilenume && happy; tilex++) - { - vec3f_t const add = { (float)xadd, (float)yadd, (float)zadd }; - switch (md_definehud(lastmodelid, tilex, add, angadd, flags, fov)) - { - case 0: - break; - case -1: - happy = 0; break; // invalid model id!? - case -2: - hudpos.Message(MSG_ERROR, "Invalid tile number"); - happy = 0; - break; - case -3: - hudpos.Message(MSG_ERROR, "Invalid frame name"); - happy = 0; - break; - } - - model_ok &= happy; - } - } - break; - } - } - - if (!model_ok) - { - if (lastmodelid >= 0) - { - pos.Message(MSG_ERROR, "Removing model %d due to errors.", lastmodelid); - md_undefinemodel(lastmodelid); - nextmodelid--; - } - break; - } - - md_setmisc(lastmodelid,(float)scale,shadeoffs,(float)mzadd,(float)myoffset,flags); - modelskin = lastmodelskin = 0; - seenframe = 0; - - } - break; - case T_VOXEL: - { - auto voxelpos = scriptfile_getposition(script); - FScanner::SavedPos modelend; - FString fn; - int32_t tile0 = MAXTILES, tile1 = -1, tilex = -1; - - static const tokenlist voxeltokens[] = - { - { "tile", T_TILE }, - { "tile0", T_TILE0 }, - { "tile1", T_TILE1 }, - { "scale", T_SCALE }, - { "rotate", T_ROTATE }, - }; - - if (scriptfile_getstring(script,&fn)) - break; //voxel filename - - while (nextvoxid < MAXVOXELS && (voxreserve[nextvoxid>>3]&(1<<(nextvoxid&7)))) - nextvoxid++; - - if (nextvoxid == MAXVOXELS) - { - voxelpos.Message(MSG_ERROR, "Maximum number of voxels (%d) already defined.", MAXVOXELS); - break; - } - - if (qloadkvx(nextvoxid, fn)) - { - voxelpos.Message(MSG_ERROR, "Failure loading voxel file \"%s\"",fn.GetChars()); - break; - } - - lastvoxid = nextvoxid++; - - if (scriptfile_getbraces(script,&modelend)) break; - while (!scriptfile_endofblock(script, modelend)) - { - switch (getatoken(script, voxeltokens, countof(voxeltokens))) - { - //case T_ERROR: Printf("Error on line %s:%d in voxel tokens\n", script->filename,linenum); break; - case T_TILE: - scriptfile_getsymbol(script,&tilex); - - if (check_tile("voxel", tilex, script, voxelpos)) - break; - - tiletovox[tilex] = lastvoxid; - break; - - case T_TILE0: - scriptfile_getsymbol(script,&tile0); - break; //1st tile # - - case T_TILE1: - scriptfile_getsymbol(script,&tile1); - - if (check_tile_range("voxel", &tile0, &tile1, script, voxelpos)) - break; - - for (tilex=tile0; tilex<=tile1; tilex++) - tiletovox[tilex] = lastvoxid; - break; //last tile number (inclusive) - - case T_SCALE: - { - double scale=1.0; - scriptfile_getdouble(script,&scale); - voxscale[lastvoxid] = (int32_t)(65536*scale); - if (voxmodels[lastvoxid]) - voxmodels[lastvoxid]->scale = scale; - break; - } - - case T_ROTATE: - voxrotate[lastvoxid>>3] |= pow2char[lastvoxid&7]; - break; - } - } - lastvoxid = -1; - } - break; - case T_SKYBOX: - { - auto skyboxpos = scriptfile_getposition(script); - FString fn[6]; - FScanner::SavedPos modelend; - int32_t i, tile = -1, pal = 0, happy = 1; - int flags = 0; - - static const tokenlist skyboxtokens[] = - { - { "tile" ,T_TILE }, - { "pal" ,T_PAL }, - { "ft" ,T_FRONT },{ "front" ,T_FRONT },{ "forward",T_FRONT }, - { "rt" ,T_RIGHT },{ "right" ,T_RIGHT }, - { "bk" ,T_BACK },{ "back" ,T_BACK }, - { "lf" ,T_LEFT },{ "left" ,T_LEFT },{ "lt" ,T_LEFT }, - { "up" ,T_TOP },{ "top" ,T_TOP },{ "ceiling",T_TOP },{ "ceil" ,T_TOP }, - { "dn" ,T_BOTTOM },{ "bottom" ,T_BOTTOM },{ "floor" ,T_BOTTOM },{ "down" ,T_BOTTOM }, - { "nocompress", T_NOCOMPRESS }, - { "nodownsize", T_NODOWNSIZE }, - { "forcefilter", T_FORCEFILTER }, - { "artquality", T_ARTQUALITY }, - }; - - if (scriptfile_getbraces(script,&modelend)) break; - while (!scriptfile_endofblock(script, modelend)) - { - switch (getatoken(script,skyboxtokens,countof(skyboxtokens))) - { - //case T_ERROR: Printf("Error on line %s:%d in skybox tokens\n",script->filename,linenum); break; - case T_TILE: - scriptfile_getsymbol(script,&tile); break; - case T_PAL: - scriptfile_getsymbol(script,&pal); break; - case T_FRONT: - scriptfile_getstring(script,&fn[0]); break; - case T_RIGHT: - scriptfile_getstring(script,&fn[1]); break; - case T_BACK: - scriptfile_getstring(script,&fn[2]); break; - case T_LEFT: - scriptfile_getstring(script,&fn[3]); break; - case T_TOP: - scriptfile_getstring(script,&fn[4]); break; - case T_BOTTOM: - scriptfile_getstring(script,&fn[5]); break; - - } - } - - if (tile < 0) skyboxpos.Message(MSG_ERROR, "skybox: missing 'tile number'"), happy=0; - for (i=0; i<6; i++) - { - if (fn[i].IsEmpty()) skyboxpos.Message(MSG_ERROR, "skybox: missing '%s filename'", skyfaces[i]), happy = 0; - // FIXME? - if (!fileSystem.FileExists(fn[i])) - happy = 0; - } - if (!happy) break; - - const char* fns[] = { fn[0].GetChars(), fn[1].GetChars(), fn[2].GetChars(), fn[3].GetChars(), fn[4].GetChars(), fn[5].GetChars() }; - tileSetSkybox(tile, pal, fns, flags); - } - break; - case T_HIGHPALOOKUP: - { - int32_t basepal=-1, pal=-1; - FString fn; - FScanner::SavedPos highpalend; - static const tokenlist highpaltokens[] = - { - { "basepal", T_BASEPAL }, - { "pal", T_PAL }, - { "file", T_FILE } - }; - - if (scriptfile_getbraces(script,&highpalend)) break; - while (!scriptfile_endofblock(script, highpalend)) - { - switch (getatoken(script,highpaltokens,countof(highpaltokens))) - { - case T_BASEPAL: - scriptfile_getsymbol(script,&basepal); break; - case T_PAL: - scriptfile_getsymbol(script,&pal); break; - case T_FILE: - scriptfile_getstring(script,&fn); break; - } - } - if ((unsigned)basepal >= MAXBASEPALS) - { - pos.Message(MSG_ERROR, "missing or invalid 'base palette number' for highpalookup definition "); - break; - } - - if ((unsigned)pal >= MAXPALOOKUPS - RESERVEDPALS) - { - pos.Message(MSG_ERROR, "missing or invalid 'palette number' for highpalookup definition"); - break; - } - - if (fn.IsEmpty()) - { - pos.Message(MSG_ERROR, "missing 'file name' for highpalookup definition"); - break; - } - - if (!fileSystem.FileExists(fn)) - break; - - } - break; - case T_TINT: - { - auto tintpos = scriptfile_getposition(script); - int32_t red=255, green=255, blue=255, shadered=0, shadegreen=0, shadeblue=0, pal=-1, flags=0; - FScanner::SavedPos tintend; - - static const tokenlist tinttokens[] = - { - { "pal", T_PAL }, - { "red", T_RED },{ "r", T_RED }, - { "green", T_GREEN },{ "g", T_GREEN }, - { "blue", T_BLUE },{ "b", T_BLUE }, - { "shadered", T_SHADERED },{ "sr", T_SHADERED }, - { "shadegreen", T_SHADEGREEN },{ "sg", T_SHADEGREEN }, - { "shadeblue", T_SHADEBLUE },{ "sb", T_SHADEBLUE }, - { "flags", T_FLAGS } - }; - - if (scriptfile_getbraces(script,&tintend)) break; - while (!scriptfile_endofblock(script, tintend)) - { - switch (getatoken(script,tinttokens,countof(tinttokens))) - { - case T_PAL: - scriptfile_getsymbol(script,&pal); break; - case T_RED: - scriptfile_getnumber(script,&red); red = min(255,max(0,red)); break; - case T_GREEN: - scriptfile_getnumber(script,&green); green = min(255,max(0,green)); break; - case T_BLUE: - scriptfile_getnumber(script,&blue); blue = min(255,max(0,blue)); break; - case T_SHADERED: - scriptfile_getnumber(script,&shadered); shadered = min(255,max(0,shadered)); break; - case T_SHADEGREEN: - scriptfile_getnumber(script,&shadegreen); shadegreen = min(255,max(0,shadegreen)); break; - case T_SHADEBLUE: - scriptfile_getnumber(script,&shadeblue); shadeblue = min(255,max(0,shadeblue)); break; - case T_FLAGS: - scriptfile_getsymbol(script,&flags); break; - } - } - - if (pal < 0) - { - tintpos.Message(MSG_ERROR, "tint: missing 'palette number'"); - break; - } - - lookups.setPaletteTint(pal,red,green,blue,shadered,shadegreen,shadeblue,flags); - } - break; - case T_MAKEPALOOKUP: - { - int32_t red=0, green=0, blue=0, pal=-1; - int32_t havepal=0, remappal=0; - int32_t nofloorpal=-1; - FScanner::SavedPos endtextptr; - - static const tokenlist palookuptokens[] = - { - { "pal", T_PAL }, - { "red", T_RED }, { "r", T_RED }, - { "green", T_GREEN }, { "g", T_GREEN }, - { "blue", T_BLUE }, { "b", T_BLUE }, - { "remappal", T_REMAPPAL }, - { "remapself", T_REMAPSELF }, - { "nofloorpal", T_NOFLOORPAL }, - }; - - enum { - HAVE_PAL = 1, - HAVE_REMAPPAL = 2, - HAVE_REMAPSELF = 4, - - HAVEPAL_SPECIAL = HAVE_REMAPPAL | HAVE_REMAPSELF, - HAVEPAL_ERROR = 8, - }; - - if (scriptfile_getbraces(script,&endtextptr)) break; - while (!scriptfile_endofblock(script, endtextptr)) - { - switch (getatoken(script, palookuptokens, countof(palookuptokens))) - { - case T_PAL: - scriptfile_getsymbol(script, &pal); - havepal |= HAVE_PAL; - break; - case T_RED: - scriptfile_getnumber(script,&red); - red = clamp(red, 0, 63); - break; - case T_GREEN: - scriptfile_getnumber(script,&green); - green = clamp(green, 0, 63); - break; - case T_BLUE: - scriptfile_getnumber(script,&blue); - blue = clamp(blue, 0, 63); - break; - case T_REMAPPAL: - scriptfile_getsymbol(script,&remappal); - if (havepal & HAVEPAL_SPECIAL) - havepal |= HAVEPAL_ERROR; - havepal |= HAVE_REMAPPAL; - break; - case T_REMAPSELF: - if (havepal & HAVEPAL_SPECIAL) - havepal |= HAVEPAL_ERROR; - havepal |= HAVE_REMAPSELF; - break; - case T_NOFLOORPAL: - scriptfile_getsymbol(script, &nofloorpal); - nofloorpal = clamp(nofloorpal, 0, 1); - break; - } - } - - { - char msgend[BMAX_PATH+64]; - - sprintf(msgend, "for palookup definition"); - - if ((havepal & HAVE_PAL)==0) - { - pos.Message(MSG_ERROR, "missing 'palette number' %s\n", msgend); - break; - } - else if (pal==0 || (unsigned)pal >= MAXPALOOKUPS-RESERVEDPALS) - { - pos.Message(MSG_ERROR, "'palette number' out of range (1 .. %d) %s\n", - MAXPALOOKUPS-RESERVEDPALS-1, msgend); - break; - } - - if (havepal & HAVEPAL_ERROR) - { - // will also disallow multiple remappals or remapselfs - pos.Message(MSG_ERROR, "must have exactly one of either 'remappal' or 'remapself' %s\n", msgend); - break; - } - else if ((havepal & HAVE_REMAPPAL - && (unsigned)remappal >= MAXPALOOKUPS-RESERVEDPALS)) - { - pos.Message(MSG_ERROR, "'remap palette number' out of range (max=%d) %s\n", - MAXPALOOKUPS-RESERVEDPALS-1, msgend); - break; - } - - if (havepal & HAVE_REMAPSELF) - remappal = pal; - } - - // NOTE: all palookups are initialized, i.e. non-NULL! - // NOTE2: aliasing (pal==remappal) is OK - lookups.makeTable(pal, lookups.getTable(remappal), red<<2, green<<2, blue<<2, - remappal==0 ? 1 : (nofloorpal == -1 ? lookups.tables[remappal].noFloorPal : nofloorpal)); - } - break; - case T_TEXTURE: - { - FScanner::SavedPos textureend; - int32_t tile=-1, token; - - static const tokenlist texturetokens[] = - { - { "pal", T_PAL }, - { "detail", T_DETAIL }, - { "glow", T_GLOW }, - { "specular",T_SPECULAR }, - { "normal", T_NORMAL }, - }; - - if (scriptfile_getsymbol(script,&tile)) break; - if (scriptfile_getbraces(script,&textureend)) break; - while (!scriptfile_endofblock(script, textureend)) - { - token = getatoken(script,texturetokens,countof(texturetokens)); - switch (token) - { - case T_PAL: - { - auto palpos = scriptfile_getposition(script); - FScanner::SavedPos palend; - int32_t pal=-1, xsiz = 0, ysiz = 0; - FString fn; - double alphacut = -1.0, xscale = 1.0, yscale = 1.0, specpower = 1.0, specfactor = 1.0; - uint8_t flags = 0; - - static const tokenlist texturetokens_pal[] = - { - { "file", T_FILE },{ "name", T_FILE }, - { "alphacut", T_ALPHACUT }, - { "detailscale", T_XSCALE }, { "scale", T_XSCALE }, { "xscale", T_XSCALE }, { "intensity", T_XSCALE }, - { "yscale", T_YSCALE }, - { "specpower", T_SPECPOWER }, { "specularpower", T_SPECPOWER }, { "parallaxscale", T_SPECPOWER }, - { "specfactor", T_SPECFACTOR }, { "specularfactor", T_SPECFACTOR }, { "parallaxbias", T_SPECFACTOR }, - { "nocompress", T_NOCOMPRESS }, - { "nodownsize", T_NODOWNSIZE }, - { "forcefilter", T_FORCEFILTER }, - { "artquality", T_ARTQUALITY }, - { "orig_sizex", T_ORIGSIZEX }, { "orig_sizey", T_ORIGSIZEY } - }; - - if (scriptfile_getsymbol(script,&pal)) break; - if (scriptfile_getbraces(script,&palend)) break; - while (!scriptfile_endofblock(script, palend)) - { - switch (getatoken(script,texturetokens_pal,countof(texturetokens_pal))) - { - case T_FILE: - scriptfile_getstring(script,&fn); break; - case T_ALPHACUT: - scriptfile_getdouble(script,&alphacut); break; - case T_XSCALE: - scriptfile_getdouble(script,&xscale); break; - case T_YSCALE: - scriptfile_getdouble(script,&yscale); break; - case T_SPECPOWER: - scriptfile_getdouble(script,&specpower); break; - case T_SPECFACTOR: - scriptfile_getdouble(script,&specfactor); break; - case T_ORIGSIZEX: - scriptfile_getnumber(script, &xsiz); - break; - case T_ORIGSIZEY: - scriptfile_getnumber(script, &ysiz); - break; - default: - break; - } - } - - if ((unsigned)tile >= MAXUSERTILES) break; // message is printed later - if ((unsigned)pal >= MAXPALOOKUPS - RESERVEDPALS) - { - palpos.Message(MSG_ERROR, "missing or invalid 'palette number' for texture definition"); - break; - } - if (fn.IsEmpty()) - { - palpos.Message(MSG_ERROR, "missing 'file name' for texture definition"); - break; - } - - if (!fileSystem.FileExists(fn)) - { - palpos.Message(MSG_ERROR, "%s not found in replacement for tile %d", fn.GetChars(), tile); - break; - } - - if (xsiz > 0 && ysiz > 0) - { - tileSetDummy(tile, xsiz, ysiz); - } - xscale = 1.0f / xscale; - yscale = 1.0f / yscale; - - tileSetHightileReplacement(tile,pal,fn,alphacut,xscale,yscale, specpower, specfactor,flags); - } - break; - case T_DETAIL: case T_GLOW: case T_SPECULAR: case T_NORMAL: - { - auto detailpos = scriptfile_getposition(script); - FScanner::SavedPos detailend; - int32_t pal = 0; - char flags = 0; - FString fn; - double xscale = 1.0, yscale = 1.0, specpower = 1.0, specfactor = 1.0; - - static const tokenlist texturetokens_pal[] = - { - { "file", T_FILE },{ "name", T_FILE }, - { "alphacut", T_ALPHACUT }, - { "detailscale", T_XSCALE }, { "scale", T_XSCALE }, { "xscale", T_XSCALE }, { "intensity", T_XSCALE }, - { "yscale", T_YSCALE }, - { "specpower", T_SPECPOWER }, { "specularpower", T_SPECPOWER }, { "parallaxscale", T_SPECPOWER }, - { "specfactor", T_SPECFACTOR }, { "specularfactor", T_SPECFACTOR }, { "parallaxbias", T_SPECFACTOR }, - { "nocompress", T_NOCOMPRESS }, - { "nodownsize", T_NODOWNSIZE }, - { "forcefilter", T_FORCEFILTER }, - { "artquality", T_ARTQUALITY }, - }; - - if (scriptfile_getbraces(script,&detailend)) break; - while (!scriptfile_endofblock(script, detailend)) - { - switch (getatoken(script,texturetokens_pal,countof(texturetokens_pal))) - { - case T_FILE: - scriptfile_getstring(script,&fn); break; - case T_XSCALE: - scriptfile_getdouble(script,&xscale); break; - case T_YSCALE: - scriptfile_getdouble(script,&yscale); break; - case T_SPECPOWER: - scriptfile_getdouble(script,&specpower); break; - case T_SPECFACTOR: - scriptfile_getdouble(script,&specfactor); break; - default: - break; - } - } - - if ((unsigned)tile >= MAXUSERTILES) break; // message is printed later - if (fn.IsEmpty()) - { - detailpos.Message(MSG_ERROR, "missing 'file name' for texture definition"); - break; - } - - if (!fileSystem.FileExists(fn)) - break; - - switch (token) - { - case T_DETAIL: - pal = DETAILPAL; - xscale = 1.0f / xscale; - yscale = 1.0f / yscale; - break; - case T_GLOW: - pal = GLOWPAL; - break; - case T_SPECULAR: - pal = SPECULARPAL; - break; - case T_NORMAL: - pal = NORMALPAL; - break; - } - tileSetHightileReplacement(tile,pal,fn,-1.0f,xscale,yscale, specpower, specfactor,flags); - } - break; - default: - break; - } - } - if ((unsigned)tile >= MAXUSERTILES) - { - pos.Message(MSG_ERROR, "missing or invalid 'tile number' for texture definition"); - break; - } - } - break; - - case T_UNDEFMODEL: - case T_UNDEFMODELRANGE: - { - int32_t r0,r1; - - if (scriptfile_getsymbol(script,&r0)) break; - if (tokn == T_UNDEFMODELRANGE) - { - if (scriptfile_getsymbol(script,&r1)) break; - - if (check_tile_range("undefmodelrange", &r0, &r1, script, pos)) - break; - } - else - { - r1 = r0; - - if (check_tile("undefmodel", r0, script, pos)) - break; - } - for (; r0 <= r1; r0++) - md_undefinetile(r0); - } - break; - - case T_UNDEFMODELOF: - { - int32_t r0; - if (scriptfile_getsymbol(script,&r0)) break; - - if (check_tile("undefmodelof", r0, script, pos)) - break; - - // XXX: See comment of md_undefinemodel() - pos.Message(MSG_WARNING, "undefmodelof: currently non-functional."); - break; - -#if defined USE_OPENGL && 0 - mid = md_tilehasmodel(r0,0); - if (mid < 0) break; - - md_undefinemodel(mid); -#endif - } - break; - - case T_UNDEFTEXTURE: - case T_UNDEFTEXTURERANGE: - { - int32_t r0,r1; - if (scriptfile_getsymbol(script,&r0)) break; - if (tokn == T_UNDEFTEXTURERANGE) - { - if (scriptfile_getsymbol(script,&r1)) break; - - if (check_tile_range("undeftexturerange", &r0, &r1, script, pos)) - break; - } - else - { - r1 = r0; - - if (check_tile("undeftexture", r0, script, pos)) - break; - } - for (; r0 <= r1; r0++) tileRemoveReplacement(r0); - } - break; - - case T_CUTSCENE: - case T_ANIMSOUNDS: - { - FScanner::SavedPos dummy; - - static const tokenlist dummytokens[] = { { "id", T_ID }, }; - - if (scriptfile_getstring(script, nullptr)) break; - if (scriptfile_getbraces(script,&dummy)) break; - while (!scriptfile_endofblock(script, dummy)) - { - // XXX? - getatoken(script,dummytokens,sizeof(dummytokens)/sizeof(dummytokens)); - } - } - break; - - case T_TEXHITSCANRANGE: - case T_NOFULLBRIGHTRANGE: - { - int32_t b,e, i; - - if (scriptfile_getsymbol(script,&b)) break; - if (scriptfile_getsymbol(script,&e))break; - - b = max(b, 0); - e = min(e, MAXUSERTILES-1); - - for (i=b; i<=e; i++) - picanm[i].sf |= (tokn==T_TEXHITSCANRANGE) ? - PICANM_TEXHITSCAN_BIT : PICANM_NOFULLBRIGHT_BIT; - } - break; - - case T_SOUND: - case T_MUSIC: - { - FScanner::SavedPos p; - FString dummy, dummy2; - static const tokenlist sound_musictokens[] = - { - { "id", T_ID }, - { "file", T_FILE }, - }; - - if (scriptfile_getbraces(script,&p)) break; - while (!scriptfile_endofblock(script, p)) - { - switch (getatoken(script,sound_musictokens,countof(sound_musictokens))) - { - case T_ID: - scriptfile_getstring(script,&dummy2); - break; - case T_FILE: - scriptfile_getstring(script,&dummy); - break; - } - } - SetMusicForMap(dummy2, dummy, true); - } - break; - - case T_MAPINFO: - { - FString mapmd4string; - FScanner::SavedPos mapinfoend; - usermaphack_t mhk; - static const tokenlist mapinfotokens[] = - { - { "mapfile", T_MAPFILE }, - { "maptitle", T_MAPTITLE }, - { "mapmd4", T_MAPMD4 }, - { "mhkfile", T_MHKFILE }, - }; - - if (scriptfile_getbraces(script,&mapinfoend)) break; - while (!scriptfile_endofblock(script, mapinfoend)) - { - switch (getatoken(script,mapinfotokens,countof(mapinfotokens))) - { - case T_MAPFILE: - scriptfile_getstring(script,nullptr); - break; - case T_MAPTITLE: - scriptfile_getstring(script,&mhk.title); - break; - case T_MAPMD4: - { - scriptfile_getstring(script,&mapmd4string); - - for (int i = 0; i < 16; i++) - { - char smallbuf[3] = { mapmd4string[2 * i], mapmd4string[2 * i + 1], 0 }; - mhk.md4[i] = strtol(smallbuf, NULL, 16); - } - - break; - } - case T_MHKFILE: - scriptfile_getstring(script,&mhk.mhkfile); - break; - } - } - AddUserMapHack(mhk); - } - break; - - case T_ECHO: - { - FString string; - scriptfile_getstring(script,&string); - Printf("%s\n",string.GetChars()); - } - break; - - case T_GLOBALFLAGS: - { - if (scriptfile_getnumber(script,&globalflags)) break; - } - break; - - case T_GLOBALGAMEFLAGS: - { - int32_t dummy; - if (scriptfile_getnumber(script,&dummy)) break; - } - break; - - case T_MULTIPSKY: - { - FScanner::SavedPos blockend; - int32_t tile; - - static const tokenlist subtokens[] = - { - { "horizfrac", T_HORIZFRAC }, - { "yoffset", T_YOFFSET }, - { "lognumtiles", T_LOGNUMTILES }, - { "tile", T_TILE }, - { "panel", T_TILE }, - { "yscale", T_YSCALE }, - }; - - if (scriptfile_getsymbol(script,&tile)) - break; - if (scriptfile_getbraces(script,&blockend)) - break; - - if (tile != DEFAULTPSKY && (unsigned)tile >= MAXUSERTILES) - { - scriptfile_setposition(script, blockend); - break; - } - - psky_t * const newpsky = tileSetupSky(tile); - - while (!scriptfile_endofblock(script, blockend)) - { - int32_t token = getatoken(script,subtokens,countof(subtokens)); - switch (token) - { - case T_HORIZFRAC: - { - int32_t horizfrac; - scriptfile_getsymbol(script,&horizfrac); - - newpsky->horizfrac = horizfrac; - break; - } - case T_YOFFSET: - { - int32_t yoffset; - scriptfile_getsymbol(script,&yoffset); - - newpsky->yoffs = yoffset; - break; - } - case T_LOGNUMTILES: - { - int32_t lognumtiles; - scriptfile_getsymbol(script,&lognumtiles); - - if ((1< MAXPSKYTILES) - break; - - newpsky->lognumtiles = lognumtiles; - break; - } - case T_TILE: - { - int32_t panel, offset; - scriptfile_getsymbol(script,&panel); - scriptfile_getsymbol(script,&offset); - - if ((unsigned) panel >= MAXPSKYTILES) - break; - - if ((unsigned) offset > PSKYOFF_MAX) - break; - - newpsky->tileofs[panel] = offset; - break; - } - case T_YSCALE: - { - int32_t yscale; - scriptfile_getsymbol(script,&yscale); - - newpsky->yscale = yscale; - break; - } - default: - break; - } - } - } - break; - case T_BASEPALETTE: - { - FScanner::SavedPos blockend; - int32_t id; - - static const tokenlist subtokens[] = - { - { "raw", T_RAW }, - { "copy", T_COPY }, - { "undef", T_UNDEF }, - }; - - if (scriptfile_getsymbol(script,&id)) - break; - if (scriptfile_getbraces(script,&blockend)) - break; - - if ((unsigned)id >= MAXBASEPALS) - { - pos.Message(MSG_ERROR, "basepalette: Invalid basepal number"); - scriptfile_setposition(script, blockend); - break; - } - - int didLoadPal = 0; - - while (!scriptfile_endofblock(script, blockend)) - { - int32_t token = getatoken(script,subtokens,countof(subtokens)); - switch (token) - { - case T_RAW: - { - FScanner::SavedPos rawblockend; - - static const tokenlist rawsubtokens[] = - { - { "file", T_FILE }, - { "offset", T_OFFSET }, - { "shiftleft", T_SHIFTLEFT }, - }; - - if (scriptfile_getbraces(script,&rawblockend)) - break; - - FString fn; - int32_t offset = 0; - int32_t shiftleft = 0; - - while (!scriptfile_endofblock(script, rawblockend)) - { - int32_t token = getatoken(script,rawsubtokens,countof(rawsubtokens)); - switch (token) - { - case T_FILE: - { - scriptfile_getstring(script,&fn); - break; - } - case T_OFFSET: - { - scriptfile_getnumber(script,&offset); - break; - } - case T_SHIFTLEFT: - { - scriptfile_getnumber(script,&shiftleft); - break; - } - default: - break; - } - } - - if (fn.IsEmpty()) - { - pos.Message(MSG_ERROR, "basepalette: No filename provided"); - break; - } - - if (offset < 0) - { - pos.Message(MSG_ERROR, "basepalette: Invalid file offset"); - break; - } - - if ((unsigned)shiftleft >= 8) - { - pos.Message(MSG_ERROR, "basepalette: Invalid left shift provided"); - break; - } - - FileReader fil = fileSystem.OpenFileReader(fn); - if (!fil.isOpen()) - { - pos.Message(MSG_ERROR, "basepalette: Failed opening \"%s\"", fn.GetChars()); - break; - } - - if (fil.Seek(offset, FileReader::SeekSet) < 0) - { - pos.Message(MSG_ERROR, "basepalette: Seek failed"); - break; - } - - auto palbuf = fil.Read(); - if (palbuf.Size() < 768) - { - pos.Message(MSG_ERROR, "basepalette: Read failed"); - break; - } - - if (shiftleft != 0) - { - for (bssize_t k = 0; k < 768; k++) - palbuf[k] <<= shiftleft; - } - - paletteSetColorTable(id, palbuf.Data(), false, false); - didLoadPal = 1; - break; - } - case T_COPY: - { - int32_t source; - scriptfile_getsymbol(script,&source); - - if ((unsigned)source >= MAXBASEPALS || source == id) - { - pos.Message(MSG_ERROR, "basepalette: Invalid source basepal number"); - break; - } - - auto sourcepal = GPalette.GetTranslation(Translation_BasePalettes, source); - if (sourcepal == NULL) - { - pos.Message(MSG_ERROR, "basepalette: Source basepal does not exist"); - break; - } - - GPalette.CopyTranslation(TRANSLATION(Translation_BasePalettes, id), TRANSLATION(Translation_BasePalettes, source)); - didLoadPal = 1; - break; - } - case T_UNDEF: - { - GPalette.ClearTranslationSlot(TRANSLATION(Translation_BasePalettes, id)); - - didLoadPal = 0; - if (id == 0) - paletteloaded &= ~PALETTE_MAIN; - break; - } - default: - break; - } - } - - if (didLoadPal && id == 0) - { - paletteloaded |= PALETTE_MAIN; - } - } - break; - case T_PALOOKUP: - { - FScanner::SavedPos blockend; - int32_t id; - - static const tokenlist subtokens[] = - { - { "raw", T_RAW }, - { "copy", T_COPY }, - { "undef", T_UNDEF }, - - { "fogpal", T_FOGPAL }, - { "makepalookup", T_MAKEPALOOKUP }, - - { "floorpal", T_FLOORPAL }, - { "nofloorpal", T_NOFLOORPAL }, - }; - - if (scriptfile_getsymbol(script,&id)) - break; - if (scriptfile_getbraces(script,&blockend)) - break; - - if ((unsigned)id >= MAXPALOOKUPS) - { - pos.Message(MSG_ERROR, "palookup: Invalid pal number"); - scriptfile_setposition(script, blockend); - break; - } - - int didLoadShade = 0; - - while (!scriptfile_endofblock(script, blockend)) - { - int32_t token = getatoken(script,subtokens,countof(subtokens)); - switch (token) - { - case T_RAW: - { - FScanner::SavedPos subblockend; - - static const tokenlist rawsubtokens[] = - { - { "file", T_FILE }, - { "offset", T_OFFSET }, - { "noshades", T_NOSHADES }, - }; - - if (scriptfile_getbraces(script,&subblockend)) - break; - - FString fn; - int32_t offset = 0; - int32_t length = 256*32; // hardcoding 32 instead of numshades - - while (!scriptfile_endofblock(script, subblockend)) - { - int32_t token = getatoken(script,rawsubtokens,countof(rawsubtokens)); - switch (token) - { - case T_FILE: - { - scriptfile_getstring(script,&fn); - break; - } - case T_OFFSET: - { - scriptfile_getnumber(script,&offset); - break; - } - case T_NOSHADES: - { - length = 256; - break; - } - default: - break; - } - } - - if (fn.IsEmpty()) - { - pos.Message(MSG_ERROR, "palookup: No filename provided"); - break; - } - - if (offset < 0) - { - pos.Message(MSG_ERROR, "palookup: Invalid file offset"); - break; - } - - FileReader fil = fileSystem.OpenFileReader(fn); - if (!fil.isOpen()) - { - pos.Message(MSG_ERROR, "palookup: Failed opening \"%s\"", fn.GetChars()); - break; - } - - if (fil.Seek(offset, FileReader::SeekSet) < 0) - { - pos.Message(MSG_ERROR, "palookup: Seek failed"); - break; - } - - auto palookupbuf = fil.Read(); - if (palookupbuf.Size() < 256) - { - pos.Message(MSG_ERROR, "palookup: Read failed"); - break; - } - - if (palookupbuf.Size() >= 256*32) - { - didLoadShade = 1; - numshades = 32; - lookups.setTable(id, palookupbuf.Data()); - } - else - { - if (!(paletteloaded & PALETTE_SHADE)) - { - pos.Message(MSG_ERROR, "palookup: Shade tables not loaded"); - break; - } - - lookups.makeTable(id, palookupbuf.Data(), 0,0,0, lookups.tables[id].noFloorPal); - } - break; - } - case T_COPY: - { - int32_t source; - scriptfile_getsymbol(script,&source); - - if ((unsigned)source >= MAXPALOOKUPS || source == id) - { - pos.Message(MSG_ERROR, "palookup: Invalid source pal number"); - break; - } - - if (source == 0 && !(paletteloaded & PALETTE_SHADE)) - { - pos.Message(MSG_ERROR, "palookup: Shade tables not loaded"); - break; - } - - if (lookups.checkTable(source) || id > 0) // do not overwrite the base with an empty table. - lookups.copyTable(id, source); - didLoadShade = 1; - break; - } - case T_FOGPAL: - { - FScanner::SavedPos subblockend; - - static const tokenlist fogpaltokens[] = - { - { "red", T_RED }, { "r", T_RED }, - { "green", T_GREEN }, { "g", T_GREEN }, - { "blue", T_BLUE }, { "b", T_BLUE }, - }; - - int32_t red = 0, green = 0, blue = 0; - - if (scriptfile_getbraces(script,&subblockend)) - break; - - while (!scriptfile_endofblock(script, subblockend)) - { - switch (getatoken(script, fogpaltokens, countof(fogpaltokens))) - { - case T_RED: - scriptfile_getnumber(script,&red); - red = clamp(red, 0, 255); - break; - case T_GREEN: - scriptfile_getnumber(script,&green); - green = clamp(green, 0, 255); - break; - case T_BLUE: - scriptfile_getnumber(script,&blue); - blue = clamp(blue, 0, 255); - break; - } - } - - if (!(paletteloaded & PALETTE_SHADE)) - { - pos.Message(MSG_ERROR, "palookup: Shade tables not loaded"); - break; - } - - lookups.makeTable(id, NULL, red, green, blue, 1); - break; - } - case T_MAKEPALOOKUP: - { - FScanner::SavedPos subblockend; - - static const tokenlist makepalookuptokens[] = - { - { "red", T_RED }, { "r", T_RED }, - { "green", T_GREEN }, { "g", T_GREEN }, - { "blue", T_BLUE }, { "b", T_BLUE }, - { "remappal", T_REMAPPAL }, - { "remapself", T_REMAPSELF }, - }; - - int32_t red = 0, green = 0, blue = 0; - int32_t remappal = -1; - - if (scriptfile_getbraces(script,&subblockend)) - break; - - while (!scriptfile_endofblock(script, subblockend)) - { - switch (getatoken(script, makepalookuptokens, countof(makepalookuptokens))) - { - case T_RED: - scriptfile_getnumber(script,&red); - red = clamp(red, 0, 255); - break; - case T_GREEN: - scriptfile_getnumber(script,&green); - green = clamp(green, 0, 255); - break; - case T_BLUE: - scriptfile_getnumber(script,&blue); - blue = clamp(blue, 0, 255); - break; - case T_REMAPPAL: - scriptfile_getsymbol(script,&remappal); - break; - case T_REMAPSELF: - remappal = id; - break; - } - } - - if ((unsigned)remappal >= MAXPALOOKUPS) - { - pos.Message(MSG_ERROR, "palookup: Invalid remappal"); - break; - } - - if (!(paletteloaded & PALETTE_SHADE)) - { - pos.Message(MSG_ERROR, "palookup: Shade tables not loaded"); - break; - } - - lookups.makeTable(id, NULL, red, green, blue, lookups.tables[id].noFloorPal); - - break; - } - case T_NOFLOORPAL: - { - lookups.tables[id].noFloorPal = 1; - break; - } - case T_FLOORPAL: - { - lookups.tables[id].noFloorPal = 0; - break; - } - case T_UNDEF: - { - lookups.clearTable(id); - didLoadShade = 0; - if (id == 0) - paletteloaded &= ~PALETTE_SHADE; - break; - } - default: - break; - } - } - - if (didLoadShade && id == 0) - { - paletteloaded |= PALETTE_SHADE; - } - } - break; - case T_BLENDTABLE: - { - FScanner::SavedPos blockend; - int32_t id; - - static const tokenlist subtokens[] = - { - { "raw", T_RAW }, - { "glblend", T_GLBLEND }, - { "copy", T_COPY }, - { "undef", T_UNDEF }, - }; - - if (scriptfile_getsymbol(script,&id)) - break; - if (scriptfile_getbraces(script,&blockend)) - break; - - if ((unsigned)id >= MAXBLENDTABS) - { - pos.Message(MSG_ERROR, "blendtable: Invalid blendtable number"); - scriptfile_setposition(script, blockend); - break; - } - while (!scriptfile_endofblock(script, blockend)) - { - int32_t token = getatoken(script,subtokens,countof(subtokens)); - switch (token) - { - case T_RAW: - { - FScanner::SavedPos rawblockend; - - static const tokenlist rawsubtokens[] = - { - { "file", T_FILE }, - { "offset", T_OFFSET }, - }; - - if (scriptfile_getbraces(script,&rawblockend)) - break; - - FString fn; - int32_t offset = 0; - - while (!scriptfile_endofblock(script, rawblockend)) - { - int32_t token = getatoken(script,rawsubtokens,countof(rawsubtokens)); - switch (token) - { - case T_FILE: - { - scriptfile_getstring(script,&fn); - break; - } - case T_OFFSET: - { - scriptfile_getnumber(script,&offset); - break; - } - default: - break; - } - } - - if (fn.IsEmpty()) - { - pos.Message(MSG_ERROR, "blendtable: No filename provided"); - break; - } - - if (offset < 0) - { - pos.Message(MSG_ERROR, "blendtable: Invalid file offset"); - break; - } - - FileReader fil = fileSystem.OpenFileReader(fn); - if (!fil.isOpen()) - { - pos.Message(MSG_ERROR, "blendtable: Failed opening \"%s\"", fn.GetChars()); - break; - } - - if (fil.Seek(offset, FileReader::SeekSet) < 0) - { - pos.Message(MSG_ERROR, "blendtable: Seek failed"); - break; - } - - auto blendbuf = fil.Read(); - if (blendbuf.Size() < 256*256) - { - pos.Message(MSG_ERROR, "blendtable: Read failed"); - break; - } - break; - } - case T_COPY: - { - int32_t source; - scriptfile_getsymbol(script,&source); - - if ((unsigned)source >= MAXBLENDTABS || source == id) - { - pos.Message(MSG_ERROR, "blendtable: Invalid source blendtable number"); - break; - } - glblend[id] = glblend[source]; - break; - } - case T_UNDEF: - { - glblend[id] = defaultglblend; - break; - } - case T_GLBLEND: - { - FScanner::SavedPos glblendblockend; - - static const tokenlist glblendtokens[] = - { - { "forward", T_FORWARD }, - { "reverse", T_REVERSE }, - { "both", T_BOTH }, - }; - - if (scriptfile_getbraces(script,&glblendblockend)) - break; - - glblend_t * const glb = glblend + id; - *glb = nullglblend; - - while (!scriptfile_endofblock(script, glblendblockend)) - { - int32_t glblendtoken = getatoken(script,glblendtokens,countof(glblendtokens)); - switch (glblendtoken) - { - case T_FORWARD: - case T_REVERSE: - case T_BOTH: - { - FScanner::SavedPos glblenddefblockend; - - static const tokenlist glblenddeftokens[] = - { - { "src", T_SRC }, - { "sfactor", T_SRC }, - { "top", T_SRC }, - - { "dst", T_DST }, - { "dfactor", T_DST }, - { "bottom", T_DST }, - - { "alpha", T_ALPHA }, - }; - - if (scriptfile_getbraces(script,&glblenddefblockend)) - break; - - glblenddef_t * const glbdef = glb->def + (glblendtoken == T_REVERSE); - while (!scriptfile_endofblock(script, glblenddefblockend)) - { - int32_t glblenddeftoken = getatoken(script,glblenddeftokens,countof(glblenddeftokens)); - switch (glblenddeftoken) - { - case T_SRC: - case T_DST: - { - uint8_t * const factor = glblenddeftoken == T_SRC ? &glbdef->src : &glbdef->dst; - if (script->Compare("ZERO")) *factor = STYLEALPHA_Zero; - else if (script->Compare("ONE")) *factor = STYLEALPHA_One; - else if (script->Compare("SRC_COLOR")) *factor = STYLEALPHA_SrcCol; - else if (script->Compare("ONE_MINUS_SRC_COLOR")) *factor = STYLEALPHA_InvSrcCol; - else if (script->Compare("SRC_ALPHA")) *factor = STYLEALPHA_Src; - else if (script->Compare("ONE_MINUS_SRC_ALPHA")) *factor = STYLEALPHA_InvSrc; - else if (script->Compare("DST_ALPHA")) *factor = STYLEALPHA_Dst; - else if (script->Compare("ONE_MINUS_DST_ALPHA")) *factor = STYLEALPHA_InvDst; - else if (script->Compare("DST_COLOR")) *factor = STYLEALPHA_DstCol; - else if (script->Compare("ONE_MINUS_DST_COLOR")) *factor = STYLEALPHA_InvDstCol; - else script->ScriptMessage("Unknown blend operation %s", script->String); - break; - } - case T_ALPHA: - { - double tempalpha; - scriptfile_getdouble(script,&tempalpha); - glbdef->alpha = (float)tempalpha; - break; - } - } - } - if (glblendtoken == T_BOTH) - glb->def[1] = *glbdef; - break; - } - } - } - } - default: - break; - } - } - } - break; - case T_NUMALPHATABS: - { - int32_t value; - if (scriptfile_getnumber(script,&value)) break; - - switch (value) - { - case 1: case 3: case 7: case 15: case 31: case 63: case 127: - case 2: case 4: case 8: case 16: case 32: case 64: case 128: -#ifdef USE_OPENGL - for (int32_t a = 1, value2 = value*2 + (value&1); a <= value; ++a) - { - float finv2value = 1.f/(float)value2; - - glblend_t * const glb = glblend + a; - *glb = defaultglblend; - glb->def[0].alpha = (float)(value2-a) * finv2value; - glb->def[1].alpha = (float)a * finv2value; - } - fallthrough__; -#endif - case 0: - numalphatabs = value; - break; - default: - pos.Message(MSG_ERROR, "numalphatables: Invalid value"); - break; - } - } - break; - case T_UNDEFBASEPALETTERANGE: - { - int32_t id0, id1; - - if (scriptfile_getsymbol(script,&id0)) - break; - if (scriptfile_getsymbol(script,&id1)) - break; - - if (id0 > id1 || (unsigned)id0 >= MAXBASEPALS || (unsigned)id1 >= MAXBASEPALS) - { - pos.Message(MSG_ERROR, "undefbasepaletterange: Invalid range"); - break; - } - - for (bssize_t i = id0; i <= id1; i++) - GPalette.ClearTranslationSlot(TRANSLATION(Translation_BasePalettes, i)); - - if (id0 == 0) - paletteloaded &= ~PALETTE_MAIN; - } - break; - case T_UNDEFPALOOKUPRANGE: - { - int32_t id0, id1; - - if (scriptfile_getsymbol(script,&id0)) - break; - if (scriptfile_getsymbol(script,&id1)) - break; - - if (id0 > id1 || (unsigned)id0 >= MAXPALOOKUPS || (unsigned)id1 >= MAXPALOOKUPS) - { - pos.Message(MSG_ERROR, "undefpalookuprange: Invalid range"); - break; - } - - for (bssize_t i = id0; i <= id1; i++) - lookups.clearTable(i); - - if (id0 == 0) - paletteloaded &= ~PALETTE_SHADE; - } - break; - case T_UNDEFBLENDTABLERANGE: - { - int32_t id0, id1; - - if (scriptfile_getsymbol(script,&id0)) - break; - if (scriptfile_getsymbol(script,&id1)) - break; - - if (id0 > id1 || (unsigned)id0 >= MAXBLENDTABS || (unsigned)id1 >= MAXBLENDTABS) - { - pos.Message(MSG_ERROR, "undefblendtablerange: Invalid range"); - break; - } - } - break; - case T_NEWGAMECHOICES: // stub - { - FScanner::SavedPos blockend; - if (scriptfile_getbraces(script,&blockend)) - break; - scriptfile_setposition(script, blockend); - break; - } - - case T_RFFDEFINEID: - { - FString resName; - FString resType; - FString rffName; - int resID; - - if (scriptfile_getstring(script, &resName)) - break; - - if (scriptfile_getstring(script, &resType)) - break; - - if (scriptfile_getnumber(script, &resID)) - break; - - if (scriptfile_getstring(script, &rffName)) - break; - - FStringf name("%s.%s", resName.GetChars(), resType.GetChars()); - fileSystem.CreatePathlessCopy(name, resID, 0); - } - break; - - - default: - pos.Message(MSG_ERROR, "%s: Unknown token.", script->String); break; - } - } - - return 0; -} - -int32_t loaddefinitionsfile(const char *fn, bool loadadds, bool cumulative) -{ - bool done = false; - auto parseit = [&](int lump) - { - FScanner sc; - sc.OpenLumpNum(lump); - sc.SetNoOctals(true); - sc.SetNoFatalErrors(true); - defsparser(&sc); - done = true; - Printf(PRINT_NONOTIFY, "\n"); - }; - - if (!cumulative) - { - int lump = fileSystem.FindFile(fn); - if (lump >= 0) - { - Printf(PRINT_NONOTIFY, "Loading \"%s\"\n", fn); - parseit(lump); - } - } - else - { - int lump, lastlump = 0; - while ((lump = fileSystem.FindLumpFullName(fn, &lastlump)) >= 0) - { - Printf(PRINT_NONOTIFY, "Loading \"%s\"\n", fileSystem.GetFileFullPath(lump).GetChars()); - parseit(lump); - } - } - - if (userConfig.AddDefs && loadadds) for (auto& m : *userConfig.AddDefs) - { - Printf("Loading module \"%s\"\n",m.GetChars()); - defsparser_include(m, nullptr, nullptr); // Q: should we let the external script see our symbol table? - Printf(PRINT_NONOTIFY, "\n"); - } - return done ? 0 : -1; -} diff --git a/source/build/src/engine.cpp b/source/build/src/engine.cpp index f8b3f014d..d690af204 100644 --- a/source/build/src/engine.cpp +++ b/source/build/src/engine.cpp @@ -1,4 +1,5 @@ // "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman +// "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman // Ken Silverman's official web site: "http://www.advsys.net/ken" // See the included license file "BUILDLIC.TXT" for license info. // @@ -16,7 +17,6 @@ #include "compat.h" #include "engine_priv.h" #include "palette.h" -#include "scriptfile.h" #include "gamecvars.h" #include "c_console.h" #include "v_2ddrawer.h" @@ -29,6 +29,9 @@ #include "inputstate.h" #include "printf.h" #include "gamecontrol.h" +#include "render.h" +#include "gamefuncs.h" +#include "hw_voxels.h" #ifdef USE_OPENGL # include "mdsprite.h" @@ -38,7 +41,6 @@ #include "gl_renderer.h" #endif - int32_t r_rortexture = 0; int32_t r_rortexturerange = 0; int32_t r_rorphase = 0; @@ -51,36 +53,12 @@ uint8_t globalr = 255, globalg = 255, globalb = 255; int16_t pskybits_override = -1; -// This was on the cache but is permanently allocated, so put it into something static. This needs some rethinking anyway -static TArray> voxelmemory; - -int16_t tiletovox[MAXTILES]; -#ifdef USE_OPENGL -char *voxfilenames[MAXVOXELS]; -#endif -char g_haveVoxels; -//#define kloadvoxel loadvoxel - -int32_t novoxmips = 1; - -int32_t voxscale[MAXVOXELS]; - static int32_t beforedrawrooms = 1; -int32_t globalflags; - static int8_t tempbuf[MAXWALLS]; -// referenced from asm -int32_t reciptable[2048]; -intptr_t asm1, asm2; -int32_t globalx1, globaly2, globalx3, globaly3; - static int32_t no_radarang2 = 0; static int16_t radarang[1280]; -static int32_t qradarang[10240]; - -uint16_t ATTRIBUTE((used)) sqrtable[4096], ATTRIBUTE((used)) shlookup[4096+256], ATTRIBUTE((used)) sqrtable_old[2048]; const char *engineerrstr = "No error"; @@ -88,8 +66,7 @@ int32_t showfirstwall=0; int32_t showheightindicators=1; int32_t circlewall=-1; -int16_t editstatus = 0; -static fixed_t global100horiz; // (-100..300)-scale horiz (the one passed to drawrooms) +fixed_t global100horiz; // (-100..300)-scale horiz (the one passed to drawrooms) static FString printcoords(void) { @@ -119,8 +96,6 @@ ADD_STAT(printcoords) return printcoords(); } -int32_t(*getpalookup_replace)(int32_t davis, int32_t dashade) = NULL; - // adapted from build.c static void getclosestpointonwall_internal(vec2_t const p, int32_t const dawall, vec2_t *const closest) { @@ -149,22 +124,10 @@ static void getclosestpointonwall_internal(vec2_t const p, int32_t const dawall, *closest = { (int32_t)(w.x + ((d.x * i) >> 30)), (int32_t)(w.y + ((d.y * i) >> 30)) }; } -int32_t xb1[MAXWALLSB]; // Polymost uses this as a temp array -static int32_t xb2[MAXWALLSB]; -int32_t rx1[MAXWALLSB], ry1[MAXWALLSB]; -int16_t bunchp2[MAXWALLSB], thesector[MAXWALLSB]; - -int16_t bunchfirst[MAXWALLSB], bunchlast[MAXWALLSB]; - - -static vec3_t spritesxyz[MAXSPRITESONSCREEN+1]; - -int32_t xdimen = -1, xdimenrecip, halfxdimen, xdimenscale, xdimscale; +int32_t xdimen = -1, xdimenscale, xdimscale; float fxdimen = -1.f; int32_t ydimen; -int32_t rxi[8], ryi[8]; - int32_t globalposx, globalposy, globalposz; fixed_t qglobalhoriz; float fglobalposx, fglobalposy, fglobalposz; @@ -173,7 +136,6 @@ fixed_t qglobalang; int32_t globalpal, globalfloorpal, cosglobalang, singlobalang; int32_t cosviewingrangeglobalang, sinviewingrangeglobalang; -int32_t xyaspect; int32_t viewingrangerecip; static int32_t globalxpanning, globalypanning; @@ -183,85 +145,17 @@ int16_t globalpicnum; static int32_t globaly1, globalx2; -int16_t sectorborder[256]; int16_t pointhighlight=-1, linehighlight=-1, highlightcnt=0; -int32_t halfxdim16, midydim16; - -static_assert(MAXWALLSB < INT16_MAX); -int16_t numscans, numbunches; static int16_t numhits; -int16_t searchit; -int16_t searchsector, searchwall, searchstat; //search output - -// SEARCHBOTTOMWALL: -// When aiming at a the bottom part of a 2-sided wall whose bottom part -// is swapped (.cstat&2), searchbottomwall equals that wall's .nextwall. In all -// other cases (when aiming at a wall), searchbottomwall equals searchwall. -// -// SEARCHISBOTTOM: -// When aiming at a 2-sided wall, 1 if aiming at the bottom part, 0 else -int16_t searchbottomwall, searchisbottom; - char inpreparemirror = 0; -static int32_t mirrorsx1, mirrorsy1, mirrorsx2, mirrorsy2; - -#define MAXSETVIEW 4 // // Internal Engine Functions // -// returns: 0=continue sprite collecting; -// 1=break out of sprite collecting; -int32_t renderAddTsprite(int16_t z, int16_t sectnum) -{ - auto const spr = (uspriteptr_t)&sprite[z]; - if (spritesortcnt >= maxspritesonscreen) - return 1; - - renderAddTSpriteFromSprite(z); - - - return 0; -} - - -// -// wallfront (internal) -// -int32_t wallfront(int32_t l1, int32_t l2) -{ - vec2_t const l1vect = wall[thewall[l1]].pos; - vec2_t const l1p2vect = wall[wall[thewall[l1]].point2].pos; - vec2_t const l2vect = wall[thewall[l2]].pos; - vec2_t const l2p2vect = wall[wall[thewall[l2]].point2].pos; - vec2_t d = { l1p2vect.x - l1vect.x, l1p2vect.y - l1vect.y }; - int32_t t1 = DMulScale(l2vect.x-l1vect.x, d.y, -d.x, l2vect.y-l1vect.y, 2); //p1(l2) vs. l1 - int32_t t2 = DMulScale(l2p2vect.x-l1vect.x, d.y, -d.x, l2p2vect.y-l1vect.y, 2); //p2(l2) vs. l1 - - if (t1 == 0) { if (t2 == 0) return -1; t1 = t2; } - if (t2 == 0) t2 = t1; - - if ((t1^t2) >= 0) //pos vs. l1 - return (DMulScale(globalposx-l1vect.x, d.y, -d.x, globalposy-l1vect.y, 2) ^ t1) >= 0; - - d.x = l2p2vect.x-l2vect.x; - d.y = l2p2vect.y-l2vect.y; - - t1 = DMulScale(l1vect.x-l2vect.x, d.y, -d.x, l1vect.y-l2vect.y, 2); //p1(l1) vs. l2 - t2 = DMulScale(l1p2vect.x-l2vect.x, d.y, -d.x, l1p2vect.y-l2vect.y, 2); //p2(l1) vs. l2 - - if (t1 == 0) { if (t2 == 0) return -1; t1 = t2; } - if (t2 == 0) t2 = t1; - - if ((t1^t2) >= 0) //pos vs. l2 - return (DMulScale(globalposx-l2vect.x,d.y,-d.x,globalposy-l2vect.y, 2) ^ t1) < 0; - - return -2; -} // // animateoffs (internal) @@ -297,85 +191,6 @@ int32_t animateoffs(int const tilenum, int fakevar) return offs; } -static void renderDrawSprite(int32_t snum) -{ - polymost_drawsprite(snum); -} - - -// -// drawmaskwall (internal) -// -static void renderDrawMaskedWall(int16_t damaskwallcnt) -{ - polymost_drawmaskwall(damaskwallcnt); return; -} - - -static uint32_t msqrtasm(uint32_t c) -{ - uint32_t a = 0x40000000l, b = 0x20000000l; - - do - { - if (c >= a) - { - c -= a; - a += b*4; - } - a -= b; - a >>= 1; - b >>= 2; - } while (b); - - if (c >= a) - a++; - - return a >> 1; -} - -// -// initksqrt (internal) -// -static inline void initksqrt(void) -{ - int32_t i, j, k; - uint32_t root, num; - int32_t temp; - - j = 1; k = 0; - for (i=0; i<4096; i++) - { - if (i >= j) { j <<= 2; k++; } - sqrtable[i] = (uint16_t)(msqrtasm((i<<18)+131072)<<1); - shlookup[i] = (k<<1)+((10-k)<<8); - if (i < 256) shlookup[i+4096] = ((k+6)<<1)+((10-(k+6))<<8); - } - - for(i=0;i<2048;i++) - { - root = 128; - num = i<<20; - do - { - temp = root; - root = (root+num/root)>>1; - } while((temp-root+1) > 2); - temp = root*root-num; - while (abs(int32_t(temp-2*root+1)) < abs(temp)) - { - temp += 1-int(2*root); - root--; - } - while (abs(int32_t(temp+2*root+1)) < abs(temp)) - { - temp += 2*root+1; - root++; - } - sqrtable_old[i] = root; - } -} - static int32_t engineLoadTables(void) { static char tablesloaded = 0; @@ -384,11 +199,6 @@ static int32_t engineLoadTables(void) { int32_t i; - initksqrt(); - - for (i=0; i<2048; i++) - reciptable[i] = DivScale(2048, i+2048, 30); - for (i=0; i<=512; i++) sintable[i] = bsinf(i); for (i=513; i<1024; i++) @@ -401,11 +211,6 @@ static int32_t engineLoadTables(void) for (i=0; i<640; i++) radarang[1279-i] = -radarang[i]; - for (i=0; i<5120; i++) - qradarang[i] = FloatToFixed(atan((5119.5 - i) / 1280.) * (-64. / BAngRadian)); - for (i=0; i<5120; i++) - qradarang[10239-i] = -qradarang[i]; - tablesloaded = 1; } @@ -415,16 +220,10 @@ static int32_t engineLoadTables(void) ////////// SPRITE LIST MANIPULATION FUNCTIONS ////////// -#ifdef NETCODE_DISABLE -# define LISTFN_STATIC static -#else -# define LISTFN_STATIC -#endif - ///// sector lists of sprites ///// // insert sprite at the head of sector list, change .sectnum -LISTFN_STATIC void do_insertsprite_at_headofsect(int16_t spritenum, int16_t sectnum) +static void do_insertsprite_at_headofsect(int16_t spritenum, int16_t sectnum) { int16_t const ohead = headspritesect[sectnum]; @@ -438,7 +237,7 @@ LISTFN_STATIC void do_insertsprite_at_headofsect(int16_t spritenum, int16_t sect } // remove sprite 'deleteme' from its sector list -LISTFN_STATIC void do_deletespritesect(int16_t deleteme) +static void do_deletespritesect(int16_t deleteme) { int32_t const sectnum = sprite[deleteme].sectnum; int32_t const prev = prevspritesect[deleteme]; @@ -455,7 +254,7 @@ LISTFN_STATIC void do_deletespritesect(int16_t deleteme) ///// now, status lists ///// // insert sprite at head of status list, change .statnum -LISTFN_STATIC void do_insertsprite_at_headofstat(int16_t spritenum, int16_t statnum) +static void do_insertsprite_at_headofstat(int16_t spritenum, int16_t statnum) { int16_t const ohead = headspritestat[statnum]; @@ -469,7 +268,7 @@ LISTFN_STATIC void do_insertsprite_at_headofstat(int16_t spritenum, int16_t stat } // insertspritestat (internal) -LISTFN_STATIC int32_t insertspritestat(int16_t statnum) +static int32_t insertspritestat(int16_t statnum) { if ((statnum >= MAXSTATUS) || (headspritestat[MAXSTATUS] == -1)) return -1; //list full @@ -490,7 +289,7 @@ LISTFN_STATIC int32_t insertspritestat(int16_t statnum) } // remove sprite 'deleteme' from its status list -LISTFN_STATIC void do_deletespritestat(int16_t deleteme) +static void do_deletespritestat(int16_t deleteme) { int32_t const sectnum = sprite[deleteme].statnum; int32_t const prev = prevspritestat[deleteme]; @@ -524,6 +323,7 @@ int32_t insertsprite(int16_t sectnum, int16_t statnum) Numsprites++; } + sprite[newspritenum].time = leveltimer++; return newspritenum; } @@ -532,10 +332,9 @@ int32_t insertsprite(int16_t sectnum, int16_t statnum) // deletesprite // int32_t (*deletesprite_replace)(int16_t spritenum) = NULL; -void polymost_deletesprite(int num); int32_t deletesprite(int16_t spritenum) { - polymost_deletesprite(spritenum); + Polymost::polymost_deletesprite(spritenum); if (deletesprite_replace) return deletesprite_replace(spritenum); assert((sprite[spritenum].statnum == MAXSTATUS) @@ -834,14 +633,12 @@ static spritesmooth_t spritesmooth_s[MAXSPRITES+MAXUNIQHUDID]; static sectortype sector_s[MAXSECTORS]; static walltype wall_s[MAXWALLS]; spritetype sprite_s[MAXSPRITES]; -static tspritetype tsprite_s[MAXSPRITESONSCREEN]; int32_t enginePreInit(void) { sector = sector_s; wall = wall_s; sprite = sprite_s; - tsprite = tsprite_s; spriteext = spriteext_s; spritesmooth = spritesmooth_s; return 0; @@ -853,59 +650,19 @@ int32_t enginePreInit(void) // int32_t engineInit(void) { - if (engineLoadTables()) - return 1; - - xyaspect = -1; - - voxelmemory.Reset(); - - for (int i=0; iloadPalette(); - -#ifdef USE_OPENGL if (!mdinited) mdinit(); -#endif - return 0; } -// -// uninitengine -// - -void engineUnInit(void) -{ - polymost_glreset(); - freeallmodels(); -# ifdef POLYMER - polymer_uninit(); -# endif - - TileFiles.CloseAll(); -} - - // // initspritelists // void (*initspritelists_replace)(void) = NULL; void initspritelists(void) { + leveltimer = 0; if (initspritelists_replace) { initspritelists_replace(); @@ -954,927 +711,6 @@ void initspritelists(void) Numsprites = 0; } - -void set_globalang(fixed_t const ang) -{ - globalang = FixedToInt(ang)&2047; - qglobalang = ang & 0x7FFFFFF; - - float const f_ang = FixedToFloat(ang); - float const fcosang = bcosf(f_ang); - float const fsinang = bsinf(f_ang); - -#ifdef USE_OPENGL - fcosglobalang = fcosang; - fsinglobalang = fsinang; -#endif - - cosglobalang = (int)fcosang; - singlobalang = (int)fsinang; - - cosviewingrangeglobalang = MulScale(cosglobalang,viewingrange, 16); - sinviewingrangeglobalang = MulScale(singlobalang,viewingrange, 16); -} - -// -// drawrooms -// -EXTERN_CVAR(Int, gl_fogmode) -int32_t renderDrawRoomsQ16(int32_t daposx, int32_t daposy, int32_t daposz, - fixed_t daang, fixed_t dahoriz, int16_t dacursectnum) -{ - int32_t i; - - if (gl_fogmode == 1) gl_fogmode = 2; // only radial fog works with Build's screwed up coordinate system. - - set_globalpos(daposx, daposy, daposz); - set_globalang(daang); - - global100horiz = dahoriz; - - // xdimenscale is Scale(xdimen,yxaspect,320); - // normalization by viewingrange so that center-of-aim doesn't depend on it - qglobalhoriz = MulScale(dahoriz, DivScale(xdimenscale, viewingrange, 16), 16)+IntToFixed(ydimen>>1); - - globalcursectnum = dacursectnum; - - memset(gotsector, 0, sizeof(gotsector)); - - i = xdimen-1; - - for (int i = 0; i < numwalls; ++i) - { - if (wall[i].cstat & CSTAT_WALL_ROTATE_90) - { - auto &w = wall[i]; - auto &tile = RotTile(w.picnum+animateoffs(w.picnum,16384)); - - if (tile.newtile == -1 && tile.owner == -1) - { - auto owner = w.picnum + animateoffs(w.picnum, 16384); - - tile.newtile = TileFiles.tileCreateRotated(owner); - assert(tile.newtile != -1); - - RotTile(tile.newtile).owner = w.picnum+animateoffs(w.picnum,16384); - - } - } - } - - // Update starting sector number (common to classic and Polymost). - // ADJUST_GLOBALCURSECTNUM. - if (globalcursectnum >= MAXSECTORS) - globalcursectnum -= MAXSECTORS; - else - { - i = globalcursectnum; - updatesector(globalposx,globalposy,&globalcursectnum); - if (globalcursectnum < 0) globalcursectnum = i; - - // PK 20110123: I'm not sure what the line above is supposed to do, but 'i' - // *can* be negative, so let's just quit here in that case... - if (globalcursectnum<0) - return 0; - } - - polymost_drawrooms(); - - return inpreparemirror; -} - -// UTILITY TYPES AND FUNCTIONS FOR DRAWMASKS OCCLUSION TREE -// typedef struct s_maskleaf -// { -// int32_t index; -// _point2d p1, p2; -// _equation maskeq, p1eq, p2eq; -// struct s_maskleaf* branch[MAXWALLSB]; -// int32_t drawing; -// } _maskleaf; -// -// _maskleaf maskleaves[MAXWALLSB]; - -// returns equation of a line given two points -static inline _equation equation(float const x1, float const y1, float const x2, float const y2) -{ - const float f = x2-x1; - - // vertical - if (f == 0.f) - return { 1, 0, -x1 }; - else - { - float const ff = (y2 - y1) / f; - return { ff, -1, (y1 - (ff * x1)) }; - } -} - -static inline int32_t sameside(const _equation *eq, const vec2f_t *p1, const vec2f_t *p2) -{ - const float sign1 = (eq->a * p1->x) + (eq->b * p1->y) + eq->c; - const float sign2 = (eq->a * p2->x) + (eq->b * p2->y) + eq->c; - return (sign1 * sign2) > 0.f; -} - - -static inline int comparetsprites(int const k, int const l) -{ - if ((tspriteptr[k]->cstat & 48) != (tspriteptr[l]->cstat & 48)) - return (tspriteptr[k]->cstat & 48) - (tspriteptr[l]->cstat & 48); - - if ((tspriteptr[k]->cstat & 48) == 16 && tspriteptr[k]->ang != tspriteptr[l]->ang) - return tspriteptr[k]->ang - tspriteptr[l]->ang; - - if (tspriteptr[k]->statnum != tspriteptr[l]->statnum) - return tspriteptr[k]->statnum - tspriteptr[l]->statnum; - - if (tspriteptr[k]->x == tspriteptr[l]->x && - tspriteptr[k]->y == tspriteptr[l]->y && - tspriteptr[k]->z == tspriteptr[l]->z && - (tspriteptr[k]->cstat & 48) == (tspriteptr[l]->cstat & 48) && - tspriteptr[k]->owner != tspriteptr[l]->owner) - return tspriteptr[k]->owner - tspriteptr[l]->owner; - - if (abs(spritesxyz[k].z-globalposz) != abs(spritesxyz[l].z-globalposz)) - return abs(spritesxyz[k].z-globalposz)-abs(spritesxyz[l].z-globalposz); - - return 0; -} - -static void sortsprites(int const start, int const end) -{ - int32_t i, gap, y, ys; - - if (start >= end) - return; - - gap = 1; while (gap < end - start) gap = (gap<<1)+1; - for (gap>>=1; gap>0; gap>>=1) //Sort sprite list - for (i=start; i=start; l-=gap) - { - if (spritesxyz[l].y <= spritesxyz[l+gap].y) break; - std::swap(tspriteptr[l],tspriteptr[l+gap]); - std::swap(spritesxyz[l].x,spritesxyz[l+gap].x); - std::swap(spritesxyz[l].y,spritesxyz[l+gap].y); - } - - ys = spritesxyz[start].y; i = start; - for (bssize_t j=start+1; j<=end; j++) - { - if (j < end) - { - y = spritesxyz[j].y; - if (y == ys) - continue; - - ys = y; - } - - if (j > i+1) - { - for (bssize_t k=i; kz; - if ((s->cstat&48) != 32) - { - int32_t yoff = tileTopOffset(s->picnum) + s->yoffset; - int32_t yspan = (tileHeight(s->picnum) * s->yrepeat << 2); - - spritesxyz[k].z -= (yoff*s->yrepeat)<<2; - - if (!(s->cstat&128)) - spritesxyz[k].z -= (yspan>>1); - if (abs(spritesxyz[k].z-globalposz) < (yspan>>1)) - spritesxyz[k].z = globalposz; - } - } - - for (bssize_t k=i+1; k= 0; --i) - { - if (polymost_spriteHasTranslucency(&tsprite[i])) - { - tspriteptr[spritesortcnt] = &tsprite[i]; - ++spritesortcnt; - } else - { - tspriteptr[back] = &tsprite[i]; - --back; - } - } - - for (i=numSprites-1; i>=0; --i) - { - const int32_t xs = tspriteptr[i]->x-globalposx, ys = tspriteptr[i]->y-globalposy; - const int32_t yp = DMulScale(xs,cosviewingrangeglobalang,ys,sinviewingrangeglobalang, 6); - const int32_t modelp = polymost_spriteIsModelOrVoxel(tspriteptr[i]); - - if (yp > (4<<8)) - { - const int32_t xp = DMulScale(ys,cosglobalang,-xs,singlobalang, 6); - - if (MulScale(labs(xp+yp),xdimen, 24) >= yp) - goto killsprite; - - spritesxyz[i].x = Scale(xp+yp,xdimen<<7,yp); - } - else if ((tspriteptr[i]->cstat&48) == 0) - { -killsprite: - if (!modelp) - { - //Delete face sprite if on wrong side! - if (i >= spritesortcnt) - { - --numSprites; - if (i != numSprites) - { - tspriteptr[i] = tspriteptr[numSprites]; - spritesxyz[i].x = spritesxyz[numSprites].x; - spritesxyz[i].y = spritesxyz[numSprites].y; - } - } - else - { - --numSprites; - --spritesortcnt; - if (i != numSprites) - { - tspriteptr[i] = tspriteptr[spritesortcnt]; - spritesxyz[i].x = spritesxyz[spritesortcnt].x; - spritesxyz[i].y = spritesxyz[spritesortcnt].y; - tspriteptr[spritesortcnt] = tspriteptr[numSprites]; - spritesxyz[spritesortcnt].x = spritesxyz[numSprites].x; - spritesxyz[spritesortcnt].y = spritesxyz[numSprites].y; - } - } - continue; - } - } - spritesxyz[i].y = yp; - } - - sortsprites(0, spritesortcnt); - sortsprites(spritesortcnt, numSprites); - renderBeginScene(); - - GLInterface.EnableBlend(false); - GLInterface.EnableAlphaTest(true); - GLInterface.SetDepthBias(-2, -256); - - if (spritesortcnt < numSprites) - { - i = spritesortcnt; - for (bssize_t i = spritesortcnt; i < numSprites;) - { - int32_t py = spritesxyz[i].y; - int32_t pcstat = tspriteptr[i]->cstat & 48; - int32_t pangle = tspriteptr[i]->ang; - int j = i + 1; - if (!polymost_spriteIsModelOrVoxel(tspriteptr[i])) - { - while (j < numSprites && py == spritesxyz[j].y && pcstat == (tspriteptr[j]->cstat & 48) && (pcstat != 16 || pangle == tspriteptr[j]->ang) - && !polymost_spriteIsModelOrVoxel(tspriteptr[j])) - { - j++; - } - } - if (j - i == 1) - { - debugmask_add(i | 32768, tspriteptr[i]->owner); - renderDrawSprite(i); - tspriteptr[i] = NULL; - } - else - { - GLInterface.SetDepthMask(false); - - for (bssize_t k = j-1; k >= i; k--) - { - debugmask_add(k | 32768, tspriteptr[k]->owner); - renderDrawSprite(k); - } - - GLInterface.SetDepthMask(true); - - GLInterface.SetColorMask(false); - - for (bssize_t k = j-1; k >= i; k--) - { - renderDrawSprite(k); - tspriteptr[k] = NULL; - } - - GLInterface.SetColorMask(true); - - } - i = j; - } - } - - int32_t numMaskWalls = maskwallcnt; - maskwallcnt = 0; - for (i = 0; i < numMaskWalls; i++) - { - if (polymost_maskWallHasTranslucency((uwalltype *) &wall[thewall[maskwall[i]]])) - { - maskwall[maskwallcnt] = maskwall[i]; - maskwallcnt++; - } - else - renderDrawMaskedWall(i); - } - - GLInterface.EnableBlend(true); - GLInterface.EnableAlphaTest(true); - GLInterface.SetDepthMask(false); - - vec2f_t pos; - - pos.x = fglobalposx; - pos.y = fglobalposy; - - // CAUTION: maskwallcnt and spritesortcnt may be zero! - // Writing e.g. "while (maskwallcnt--)" is wrong! - while (maskwallcnt) - { - // PLAG: sorting stuff - const int32_t w = thewall[maskwall[maskwallcnt-1]]; - - maskwallcnt--; - - vec2f_t dot = { (float)wall[w].x, (float)wall[w].y }; - vec2f_t dot2 = { (float)wall[wall[w].point2].x, (float)wall[wall[w].point2].y }; - vec2f_t middle = { (dot.x + dot2.x) * .5f, (dot.y + dot2.y) * .5f }; - - _equation maskeq = equation(dot.x, dot.y, dot2.x, dot2.y); - _equation p1eq = equation(pos.x, pos.y, dot.x, dot.y); - _equation p2eq = equation(pos.x, pos.y, dot2.x, dot2.y); - - i = spritesortcnt; - while (i) - { - i--; - if (tspriteptr[i] != NULL) - { - vec2f_t spr; - auto const tspr = tspriteptr[i]; - - spr.x = (float)tspr->x; - spr.y = (float)tspr->y; - - if (!sameside(&maskeq, &spr, &pos)) - { - // Sprite and camera are on different sides of the - // masked wall. - - // Check if the sprite is inside the 'cone' given by - // the rays from the camera to the two wall-points. - const int32_t inleft = sameside(&p1eq, &middle, &spr); - const int32_t inright = sameside(&p2eq, &middle, &spr); - - int32_t ok = (inleft && inright); - - if (!ok) - { - // If not, check if any of the border points are... - int32_t xx[4] = { tspr->x }; - int32_t yy[4] = { tspr->y }; - int32_t numpts, jj; - - const _equation pineq = inleft ? p1eq : p2eq; - - if ((tspr->cstat & 48) == 32) - { - numpts = 4; - get_floorspr_points(tspr, 0, 0, - &xx[0], &xx[1], &xx[2], &xx[3], - &yy[0], &yy[1], &yy[2], &yy[3]); - } - else - { - const int32_t oang = tspr->ang; - numpts = 2; - - // Consider face sprites as wall sprites with camera ang. - // XXX: factor 4/5 needed? - if ((tspr->cstat & 48) != 16) - tspriteptr[i]->ang = globalang; - - get_wallspr_points(tspr, &xx[0], &xx[1], &yy[0], &yy[1]); - - if ((tspr->cstat & 48) != 16) - tspriteptr[i]->ang = oang; - } - - for (jj=0; jjowner); - renderDrawSprite(i); - - tspriteptr[i] = NULL; - } - } - } - } - - debugmask_add(maskwall[maskwallcnt], thewall[maskwall[maskwallcnt]]); - renderDrawMaskedWall(maskwallcnt); - } - - while (spritesortcnt) - { - --spritesortcnt; - if (tspriteptr[spritesortcnt] != NULL) - { - debugmask_add(i | 32768, tspriteptr[i]->owner); - renderDrawSprite(spritesortcnt); - tspriteptr[spritesortcnt] = NULL; - } - } - renderFinishScene(); - GLInterface.SetDepthMask(true); - GLInterface.SetDepthBias(0, 0); -} - - -//========================================================================== -// -// -// -//========================================================================== - -void FillPolygon(int* rx1, int* ry1, int* xb1, int32_t npoints, int picnum, int palette, int shade, int props, const FVector2& xtex, const FVector2& ytex, const FVector2& otex, - int clipx1, int clipy1, int clipx2, int clipy2) -{ - //Convert int32_t to float (in-place) - TArray points(npoints, true); - using Point = std::pair; - std::vector> polygon; - std::vector* curPoly; - - polygon.resize(1); - curPoly = &polygon.back(); - - for (bssize_t i = 0; i < npoints; ++i) - { - auto X = ((float)rx1[i]) * (1.0f / 4096.f); - auto Y = ((float)ry1[i]) * (1.0f / 4096.f); - curPoly->push_back(std::make_pair(X, Y)); - if (xb1[i] < i && i < npoints - 1) - { - polygon.resize(polygon.size() + 1); - curPoly = &polygon.back(); - } - } - // Now make sure that the outer boundary is the first polygon by picking a point that's as much to the outside as possible. - int outer = 0; - float minx = FLT_MAX; - float miny = FLT_MAX; - for (size_t a = 0; a < polygon.size(); a++) - { - for (auto& pt : polygon[a]) - { - if (pt.first < minx || (pt.first == minx && pt.second < miny)) - { - minx = pt.first; - miny = pt.second; - outer = a; - } - } - } - if (outer != 0) std::swap(polygon[0], polygon[outer]); - auto indices = mapbox::earcut(polygon); - - int p = 0; - for (size_t a = 0; a < polygon.size(); a++) - { - for (auto& pt : polygon[a]) - { - FVector4 point = { pt.first, pt.second, float(pt.first * xtex.X + pt.second * ytex.X + otex.X), float(pt.first * xtex.Y + pt.second * ytex.Y + otex.Y) }; - points[p++] = point; - } - } - - int maskprops = (props >> 7) & DAMETH_MASKPROPS; - FRenderStyle rs = LegacyRenderStyles[STYLE_Translucent]; - double alpha = 1.; - if (maskprops > DAMETH_MASK) - { - rs = GetRenderStyle(0, maskprops == DAMETH_TRANS2); - alpha = GetAlphaFromBlend(maskprops, 0); - } - int translation = TRANSLATION(Translation_Remap + curbasepal, palette); - int light = clamp(Scale((numshades - shade), 255, numshades), 0, 255); - PalEntry pe = PalEntry(uint8_t(alpha*255), light, light, light); - - twod->AddPoly(tileGetTexture(picnum), points.Data(), points.Size(), indices.data(), indices.size(), translation, pe, rs, clipx1, clipy1, clipx2, clipy2); -} - -//========================================================================== -// -// -// -//========================================================================== - - -#include "build.h" -#include "../src/engine_priv.h" - - -// -// fillpolygon (internal) -// -static void renderFillPolygon(int32_t npoints) -{ - int width = screen->GetWidth(); - int height = screen->GetHeight(); - - // fix for bad next-point (xb1) values... - for (int z = 0; z < npoints; z++) - if ((unsigned)xb1[z] >= (unsigned)npoints) - xb1[z] = 0; - - FVector2 xtex, ytex, otex; - int x1 = MulScale(globalx1, xyaspect, 16); - int y2 = MulScale(globaly2, xyaspect, 16); - xtex.X = ((float)asm1) * (1.f / 4294967296.f); - xtex.Y = ((float)asm2) * (1.f / 4294967296.f); - ytex.X = ((float)x1) * (1.f / 4294967296.f); - ytex.Y = ((float)y2) * (-1.f / 4294967296.f); - otex.X = (width * xtex.X + height * ytex.X) * -0.5f + fglobalposx * (1.f / 4294967296.f); - otex.Y = (width * xtex.Y + height * ytex.Y) * -0.5f - fglobalposy * (1.f / 4294967296.f); - FillPolygon(rx1, ry1, xb1, npoints, globalpicnum, globalpal, globalshade, globalorientation, xtex, ytex, otex, windowxy1.x, windowxy1.y, windowxy2.x, windowxy2.y); -} - -// -// drawmapview -// -void renderDrawMapView(int32_t dax, int32_t day, int32_t zoome, int16_t ang) -{ - int32_t i, j, k, l; - int32_t x, y; - int32_t s, ox, oy; - int width = screen->GetWidth(); - int height = screen->GetHeight(); - - int32_t const oyxaspect = yxaspect, oviewingrange = viewingrange; - - renderSetAspect(65536, DivScale((320*5)/8, 200, 16)); - - memset(gotsector, 0, sizeof(gotsector)); - - vec2_t const c1 = { (windowxy1.x<<12), (windowxy1.y<<12) }; - vec2_t const c2 = { ((windowxy2.x+1)<<12)-1, ((windowxy2.y+1)<<12)-1 }; - - zoome <<= 8; - - vec2_t const bakgvect = { DivScale(-bcos(ang), zoome, 28), DivScale(-bsin(ang), zoome, 28) }; - vec2_t const vect = { MulScale(-bsin(ang), zoome, 8), MulScale(-bcos(ang), zoome, 8) }; - vec2_t const vect2 = { MulScale(vect.x, yxaspect, 16), MulScale(vect.y, yxaspect, 16) }; - - int32_t sortnum = 0; - - usectorptr_t sec; - - for (s=0,sec=(usectorptr_t)§or[s]; swallptr; - j = startwall; l = 0; - uwallptr_t wal; - int32_t w; - for (w=sec->wallnum,wal=(uwallptr_t)&wall[startwall]; w>0; w--,wal++,j++) - { - k = lastwall(j); - if ((k > j) && (npoints > 0)) { xb1[npoints-1] = l; l = npoints; } //overwrite point2 - //wall[k].x wal->x wall[wal->point2].x - //wall[k].y wal->y wall[wal->point2].y - if (!DMulScale(wal->x-wall[k].x,wall[wal->point2].y-wal->y,-(wal->y-wall[k].y),wall[wal->point2].x-wal->x, 1)) continue; - ox = wal->x - dax; oy = wal->y - day; - x = DMulScale(ox,vect.x,-oy,vect.y, 16) + (width<<11); - y = DMulScale(oy,vect2.x,ox,vect2.y, 16) + (height<<11); - i |= getclipmask(x-c1.x,c2.x-x,y-c1.y,c2.y-y); - rx1[npoints] = x; - ry1[npoints] = y; - xb1[npoints] = npoints+1; - npoints++; - } - if (npoints > 0) xb1[npoints-1] = l; //overwrite point2 - - vec2_t bak = { rx1[0], MulScale(ry1[0]-(height<<11),xyaspect, 16)+(height<<11) }; - - - //Collect floor sprites to draw - SectIterator it(s); - while ((i = it.NextIndex()) >= 0) - { - if (sprite[i].cstat & 32768) - continue; - - if ((sprite[i].cstat & 48) == 32) - { - if ((sprite[i].cstat & (64 + 8)) == (64 + 8)) - continue; - tsprite[sortnum++].owner = i; - } - } - gotsector[s>>3] |= pow2char[s&7]; - - globalorientation = (int32_t)sec->floorstat; - if ((globalorientation&1) != 0) continue; - - globalfloorpal = globalpal = sec->floorpal; - - globalpicnum = sec->floorpicnum; - if ((unsigned)globalpicnum >= (unsigned)MAXTILES) globalpicnum = 0; - tileUpdatePicnum(&globalpicnum, s); - setgotpic(globalpicnum); - if ((tileWidth(globalpicnum) <= 0) || (tileHeight(globalpicnum) <= 0)) continue; - - globalshade = max(min(sec->floorshade, numshades - 1), 0); - if ((globalorientation&64) == 0) - { - set_globalpos(dax, day, globalposz); - globalx1 = bakgvect.x; globaly1 = bakgvect.y; - globalx2 = bakgvect.x; globaly2 = bakgvect.y; - } - else - { - ox = wall[wall[startwall].point2].x - wall[startwall].x; - oy = wall[wall[startwall].point2].y - wall[startwall].y; - i = nsqrtasm(uhypsq(ox,oy)); if (i == 0) continue; - i = 1048576/i; - globalx1 = MulScale(DMulScale(ox,bakgvect.x,oy,bakgvect.y, 10),i, 10); - globaly1 = MulScale(DMulScale(ox,bakgvect.y,-oy,bakgvect.x, 10),i, 10); - ox = (bak.x>>4)-(width<<7); oy = (bak.y>>4)-(height<<7); - globalposx = DMulScale(-oy, globalx1, -ox, globaly1, 28); - globalposy = DMulScale(-ox, globalx1, oy, globaly1, 28); - globalx2 = -globalx1; - globaly2 = -globaly1; - - int32_t const daslope = sector[s].floorheinum; - i = nsqrtasm(daslope*daslope+16777216); - set_globalpos(globalposx, MulScale(globalposy,i, 12), globalposz); - globalx2 = MulScale(globalx2,i, 12); - globaly2 = MulScale(globaly2,i, 12); - } - - int globalxshift = (8 - widthBits(globalpicnum)); - int globalyshift = (8 - heightBits(globalpicnum)); - if (globalorientation & 8) { globalxshift++; globalyshift++; } - // PK: the following can happen for large (>= 512) tile sizes. - if (globalxshift < 0) globalxshift = 0; - if (globalyshift < 0) globalyshift = 0; - - if ((globalorientation&0x4) > 0) - { - i = globalposx; globalposx = -globalposy; globalposy = -i; - i = globalx2; globalx2 = globaly1; globaly1 = i; - i = globalx1; globalx1 = -globaly2; globaly2 = -i; - } - if ((globalorientation&0x10) > 0) globalx1 = -globalx1, globaly1 = -globaly1, globalposx = -globalposx; - if ((globalorientation&0x20) > 0) globalx2 = -globalx2, globaly2 = -globaly2, globalposy = -globalposy; - asm1 = (globaly1<floorxpan())<<24), - ((int64_t) globalposy<<(20+globalyshift))-(((uint32_t) sec->floorypan())<<24), - globalposz); - renderFillPolygon(npoints); - } - - //Sort sprite list - int32_t gap = 1; - - while (gap < sortnum) gap = (gap << 1) + 1; - - for (gap>>=1; gap>0; gap>>=1) - for (i=0; i=0; j-=gap) - { - if (sprite[tsprite[j].owner].z <= sprite[tsprite[j+gap].owner].z) break; - std::swap(tsprite[j].owner, tsprite[j+gap].owner); - } - - for (s=sortnum-1; s>=0; s--) - { - auto const spr = (uspritetype * )&sprite[tsprite[s].owner]; - if ((spr->cstat&48) == 32) - { - const int32_t xspan = tileWidth(spr->picnum); - - int32_t npoints = 0; - vec2_t v1 = { spr->x, spr->y }, v2, v3, v4; - - get_floorspr_points(spr, 0, 0, &v1.x, &v2.x, &v3.x, &v4.x, - &v1.y, &v2.y, &v3.y, &v4.y); - - xb1[0] = 1; xb1[1] = 2; xb1[2] = 3; xb1[3] = 0; - npoints = 4; - - i = 0; - - ox = v1.x - dax; oy = v1.y - day; - x = DMulScale(ox,vect.x,-oy,vect.y, 16) + (width<<11); - y = DMulScale(oy,vect2.x,ox,vect2.y, 16) + (height<<11); - i |= getclipmask(x-c1.x,c2.x-x,y-c1.y,c2.y-y); - rx1[0] = x; ry1[0] = y; - - ox = v2.x - dax; oy = v2.y - day; - x = DMulScale(ox,vect.x,-oy,vect.y, 16) + (width<<11); - y = DMulScale(oy,vect2.x,ox,vect2.y, 16) + (height<<11); - i |= getclipmask(x-c1.x,c2.x-x,y-c1.y,c2.y-y); - rx1[1] = x; ry1[1] = y; - - ox = v3.x - dax; oy = v3.y - day; - x = DMulScale(ox,vect.x,-oy,vect.y, 16) + (width<<11); - y = DMulScale(oy,vect2.x,ox,vect2.y, 16) + (height<<11); - i |= getclipmask(x-c1.x,c2.x-x,y-c1.y,c2.y-y); - rx1[2] = x; ry1[2] = y; - - x = rx1[0]+rx1[2]-rx1[1]; - y = ry1[0]+ry1[2]-ry1[1]; - i |= getclipmask(x-c1.x,c2.x-x,y-c1.y,c2.y-y); - rx1[3] = x; ry1[3] = y; - - - vec2_t bak = { rx1[0], MulScale(ry1[0] - (height << 11), xyaspect, 16) + (height << 11) }; - - - globalpicnum = spr->picnum; - globalpal = spr->pal; // GL needs this, software doesn't - if ((unsigned)globalpicnum >= (unsigned)MAXTILES) globalpicnum = 0; - tileUpdatePicnum(&globalpicnum, s); - setgotpic(globalpicnum); - if ((tileWidth(globalpicnum) <= 0) || (tileHeight(globalpicnum) <= 0)) continue; - - if ((sector[spr->sectnum].ceilingstat&1) > 0) - globalshade = ((int32_t)sector[spr->sectnum].ceilingshade); - else - globalshade = ((int32_t)sector[spr->sectnum].floorshade); - globalshade = max(min(globalshade+spr->shade+6,numshades-1),0); - - //relative alignment stuff - ox = v2.x-v1.x; oy = v2.y-v1.y; - i = ox*ox+oy*oy; if (i == 0) continue; i = 65536*16384 / i; - globalx1 = MulScale(DMulScale(ox,bakgvect.x,oy,bakgvect.y, 10),i, 10); - globaly1 = MulScale(DMulScale(ox,bakgvect.y,-oy,bakgvect.x, 10),i, 10); - ox = v1.y-v4.y; oy = v4.x-v1.x; - i = ox*ox+oy*oy; if (i == 0) continue; i = 65536 * 16384 / i; - globalx2 = MulScale(DMulScale(ox,bakgvect.x,oy,bakgvect.y, 10),i, 10); - globaly2 = MulScale(DMulScale(ox,bakgvect.y,-oy,bakgvect.x, 10),i, 10); - - ox = widthBits(globalpicnum); - oy = heightBits(globalpicnum); - if ((1 << ox) != xspan) - { - ox++; - globalx1 = MulScale(globalx1,xspan,ox); - globaly1 = MulScale(globaly1,xspan,ox); - } - - bak.x = (bak.x>>4)-(width<<7); bak.y = (bak.y>>4)-(height<<7); - globalposx = DMulScale(-bak.y,globalx1,-bak.x,globaly1, 28); - globalposy = DMulScale(bak.x,globalx2,-bak.y,globaly2, 28); - - if ((spr->cstat&0x4) > 0) globalx1 = -globalx1, globaly1 = -globaly1, globalposx = -globalposx; - asm1 = (globaly1<<2); globalx1 <<= 2; globalposx <<= (20+2); - asm2 = (globalx2<<2); globaly2 <<= 2; globalposy <<= (20+2); - - set_globalpos(globalposx, globalposy, globalposz); - - // so polymost can get the translucency. ignored in software mode: - globalorientation = ((spr->cstat&2)<<7) | ((spr->cstat&512)>>2); - renderFillPolygon(npoints); - } - } -} - -// -// qloadkvx -// - - - -int32_t qloadkvx(int32_t voxindex, const char *filename) -{ - if ((unsigned)voxindex >= MAXVOXELS) - return -1; - - auto fil = fileSystem.OpenFileReader(filename); - if (!fil.isOpen()) - return -1; - - int32_t lengcnt = 0; - const int32_t lengtot = fil.GetLength(); - - for (bssize_t i=0; i= lengtot-768) - break; - } - - -#ifdef USE_OPENGL - if (voxmodels[voxindex]) - { - voxfree(voxmodels[voxindex]); - voxmodels[voxindex] = NULL; - } - - Xfree(voxfilenames[voxindex]); - voxfilenames[voxindex] = Xstrdup(filename); -#endif - - g_haveVoxels = 1; - - return 0; -} - -void vox_undefine(int32_t const tile) -{ - ssize_t voxindex = tiletovox[tile]; - if (voxindex < 0) - return; - -#ifdef USE_OPENGL - if (voxmodels[voxindex]) - { - voxfree(voxmodels[voxindex]); - voxmodels[voxindex] = NULL; - } - DO_FREE_AND_NULL(voxfilenames[voxindex]); -#endif - - voxscale[voxindex] = 65536; - voxrotate[voxindex>>3] &= ~pow2char[voxindex&7]; - tiletovox[tile] = -1; - - // TODO: nextvoxid -} - -void vox_deinit() -{ - for (auto &vox : voxmodels) - { - voxfree(vox); - vox = nullptr; - } -} - // // inside // @@ -2028,36 +864,6 @@ int32_t getangle(int32_t xvect, int32_t yvect) return rv; } -fixed_t gethiq16angle(int32_t xvect, int32_t yvect) -{ - fixed_t rv; - - if ((xvect | yvect) == 0) - rv = 0; - else if (xvect == 0) - rv = IntToFixed(512 + ((yvect < 0) << 10)); - else if (yvect == 0) - rv = IntToFixed(((xvect < 0) << 10)); - else if (xvect == yvect) - rv = IntToFixed(256 + ((xvect < 0) << 10)); - else if (xvect == -yvect) - rv = IntToFixed(768 + ((xvect > 0) << 10)); - else if (abs(xvect) > abs(yvect)) - rv = ((qradarang[5120 + Scale(1280, yvect, xvect)] >> 6) + IntToFixed(((xvect < 0) << 10))) & 0x7FFFFFF; - else rv = ((qradarang[5120 - Scale(1280, xvect, yvect)] >> 6) + IntToFixed(512 + ((yvect < 0) << 10))) & 0x7FFFFFF; - - return rv; -} - -// -// ksqrt -// -int32_t ksqrt(uint32_t num) -{ - if (enginecompatibility_mode == ENGINECOMPATIBILITY_19950829) - return ksqrtasm_old(num); - return nsqrtasm(num); -} // Gets the BUILD unit height and z offset of a sprite. // Returns the z offset, 'height' may be NULL. @@ -2214,7 +1020,7 @@ int32_t cansee(int32_t x1, int32_t y1, int32_t z1, int16_t sect1, int32_t x2, in if (x1 == x2 && y1 == y2) return (sect1 == sect2); - sectbitmap[sect1>>3] |= pow2char[sect1&7]; + sectbitmap[sect1>>3] |= (1 << (sect1&7)); clipsectorlist[0] = sect1; danum = 1; for (dacnt=0; dacnt= cfz[1]) return 0; - if (!(sectbitmap[nexts>>3] & pow2char[nexts&7])) + if (!(sectbitmap[nexts>>3] & (1 << (nexts&7)))) { - sectbitmap[nexts>>3] |= pow2char[nexts&7]; + sectbitmap[nexts>>3] |= (1 << (nexts&7)); clipsectorlist[danum++] = nexts; } } } - if (sectbitmap[sect2>>3] & pow2char[sect2&7]) + if (sectbitmap[sect2>>3] & (1<<(sect2&7))) return 1; return 0; @@ -2406,9 +1212,10 @@ void dragpoint(int16_t pointhighlight, int32_t dax, int32_t day, uint8_t flags) while (1) { + sector[wall[w].sector].dirty = 255; wall[w].x = dax; wall[w].y = day; - walbitmap[w>>3] |= pow2char[w&7]; + walbitmap[w>>3] |= (1<<(w&7)); if (!clockwise) //search points CCW { @@ -2438,7 +1245,7 @@ void dragpoint(int16_t pointhighlight, int32_t dax, int32_t day, uint8_t flags) break; } - if ((walbitmap[w>>3] & pow2char[w&7])) + if ((walbitmap[w>>3] & (1<<(w&7)))) { if (clockwise) break; @@ -2565,35 +1372,10 @@ int findwallbetweensectors(int sect1, int sect2) // void updatesector(int32_t const x, int32_t const y, int16_t * const sectnum) { -#if 0 - if (enginecompatibility_mode != ENGINECOMPATIBILITY_NONE) - { - if (inside_p(x, y, *sectnum)) - return; - - if ((unsigned)*sectnum < (unsigned)numsectors) - { - const uwalltype *wal = (uwalltype *)&wall[sector[*sectnum].wallptr]; - int wallsleft = sector[*sectnum].wallnum; - - do - { - int const next = wal->nextsector; - if (inside_p(x, y, next)) - SET_AND_RETURN(*sectnum, next); - wal++; - } - while (--wallsleft); - } - } - else -#endif - { - int16_t sect = *sectnum; - updatesectorneighbor(x, y, §, INITIALUPDATESECTORDIST, MAXUPDATESECTORDIST); - if (sect != -1) - SET_AND_RETURN(*sectnum, sect); - } + int16_t sect = *sectnum; + updatesectorneighbor(x, y, §, INITIALUPDATESECTORDIST, MAXUPDATESECTORDIST); + if (sect != -1) + SET_AND_RETURN(*sectnum, sect); // we need to support passing in a sectnum of -1, unfortunately @@ -2609,7 +1391,6 @@ void updatesector(int32_t const x, int32_t const y, int16_t * const sectnum) // as starting sector and the 'initial' z check is skipped // (not initial anymore because it follows the sector updating due to TROR) -// NOTE: This comes from Duke, therefore it's GPL! void updatesectorz(int32_t const x, int32_t const y, int32_t const z, int16_t * const sectnum) { if (enginecompatibility_mode != ENGINECOMPATIBILITY_NONE) @@ -2632,7 +1413,7 @@ void updatesectorz(int32_t const x, int32_t const y, int32_t const z, int16_t * if (inside_p(x, y, *sectnum)) return; - uwalltype const * wal = (uwalltype *)&wall[sector[*sectnum].wallptr]; + walltype const * wal = &wall[sector[*sectnum].wallptr]; int wallsleft = sector[*sectnum].wallnum; do { @@ -2758,22 +1539,6 @@ void rotatepoint(vec2_t const pivot, vec2_t p, int16_t const daang, vec2_t * con p2->y = DMulScale(p.y, dacos, p.x, dasin, 14) + pivot.y; } -void videoSetCorrectedAspect() -{ - // In DOS the game world is displayed with an aspect of 1.28 instead 1.333, - // meaning we have to stretch it by a factor of 1.25 instead of 1.2 - // to get perfect squares - int32_t yx = (65536 * 5) / 4; - int32_t vr, y, x; - - x = xdim; - y = ydim; - - vr = DivScale(x*3, y*4, 16); - - renderSetAspect(vr, yx); -} - // // setview // @@ -2784,60 +1549,20 @@ void videoSetViewableArea(int32_t x1, int32_t y1, int32_t x2, int32_t y2) windowxy2.x = x2; windowxy2.y = y2; - xdimen = (x2-x1)+1; halfxdimen = (xdimen>>1); - xdimenrecip = DivScale(1L,xdimen, 32); + xdimen = (x2-x1)+1; ydimen = (y2-y1)+1; fxdimen = (float) xdimen; -#ifdef USE_OPENGL fydimen = (float) ydimen; -#endif videoSetCorrectedAspect(); } -// -// setaspect -// -void renderSetAspect(int32_t daxrange, int32_t daaspect) -{ - if (daxrange == 65536) daxrange--; // This doesn't work correctly with 65536. All other values are fine. No idea where this is evaluated wrong. - viewingrange = daxrange; - viewingrangerecip = DivScale(1,daxrange, 32); -#ifdef USE_OPENGL - fviewingrange = (float) daxrange; -#endif - - yxaspect = daaspect; - xyaspect = DivScale(1,yxaspect, 32); - xdimenscale = Scale(xdimen,yxaspect,320); - xdimscale = Scale(320,xyaspect,xdimen); -} - - #include "v_2ddrawer.h" -// -// clearview -// -void videoClearViewableArea(int32_t dacol) -{ - GLInterface.ClearScreen(dacol, false); -} - - -// -// clearallviews -// -void videoClearScreen(int32_t dacol) -{ - GLInterface.ClearScreen(dacol | PalEntry(255,0,0,0)); -} - - //MUST USE RESTOREFORDRAWROOMS AFTER DRAWING static int32_t setviewcnt = 0; // interface layers use this now @@ -2888,71 +1613,6 @@ void renderRestoreTarget() } -// -// preparemirror -// -void renderPrepareMirror(int32_t dax, int32_t day, int32_t daz, fixed_t daang, fixed_t dahoriz, int16_t dawall, - int32_t *tposx, int32_t *tposy, fixed_t *tang) -{ - const int32_t x = wall[dawall].x, dx = wall[wall[dawall].point2].x-x; - const int32_t y = wall[dawall].y, dy = wall[wall[dawall].point2].y-y; - - const int32_t j = dx*dx + dy*dy; - if (j == 0) - return; - - int i = ((dax-x)*dx + (day-y)*dy)<<1; - - *tposx = (x<<1) + Scale(dx,i,j) - dax; - *tposy = (y<<1) + Scale(dy,i,j) - day; - *tang = ((gethiq16angle(dx, dy) << 1) - daang) & 0x7FFFFFF; - - inpreparemirror = 1; - - polymost_prepareMirror(dax, day, daz, daang, dahoriz, dawall); -} - - -// -// completemirror -// -void renderCompleteMirror(void) -{ - polymost_completeMirror(); - inpreparemirror = 0; -} - - -// -// sectorofwall -// -static int32_t sectorofwall_internal(int16_t wallNum) -{ - native_t gap = numsectors>>1, sectNum = gap; - - while (gap > 1) - { - gap >>= 1; - native_t const n = !!(sector[sectNum].wallptr < wallNum); - sectNum += (n ^ (n - 1)) * gap; - } - while (sector[sectNum].wallptr > wallNum) sectNum--; - while (sector[sectNum].wallptr + sector[sectNum].wallnum <= wallNum) sectNum++; - - return sectNum; -} - -int32_t sectorofwall(int16_t wallNum) -{ - if ((unsigned)wallNum < (unsigned)numwalls) - { - native_t const w = wall[wallNum].nextwall; - return ((unsigned)w < MAXWALLS) ? wall[w].nextsector : sectorofwall_internal(wallNum); - } - return -1; -} - - int32_t getceilzofslopeptr(usectorptr_t sec, int32_t dax, int32_t day) { if (!(sec->ceilingstat&2)) @@ -2964,7 +1624,7 @@ int32_t getceilzofslopeptr(usectorptr_t sec, int32_t dax, int32_t day) vec2_t const w = *(vec2_t const *)wal; vec2_t const d = { wal2->x - w.x, wal2->y - w.y }; - int const i = nsqrtasm(uhypsq(d.x,d.y))<<5; + int const i = ksqrt(uhypsq(d.x,d.y))<<5; if (i == 0) return sec->ceilingz; int const j = DMulScale(d.x, day-w.y, -d.y, dax-w.x, 3); @@ -2983,7 +1643,7 @@ int32_t getflorzofslopeptr(usectorptr_t sec, int32_t dax, int32_t day) vec2_t const w = *(vec2_t const *)wal; vec2_t const d = { wal2->x - w.x, wal2->y - w.y }; - int const i = nsqrtasm(uhypsq(d.x,d.y))<<5; + int const i = ksqrt(uhypsq(d.x,d.y))<<5; if (i == 0) return sec->floorz; int const j = DMulScale(d.x, day-w.y, -d.y, dax-w.x, 3); @@ -3003,7 +1663,7 @@ void getzsofslopeptr(usectorptr_t sec, int32_t dax, int32_t day, int32_t *ceilz, vec2_t const d = { wal2->x - wal->x, wal2->y - wal->y }; - int const i = nsqrtasm(uhypsq(d.x,d.y))<<5; + int const i = ksqrt(uhypsq(d.x,d.y))<<5; if (i == 0) return; int const j = DMulScale(d.x,day-wal->y, -d.y,dax-wal->x, 3); @@ -3028,7 +1688,7 @@ void alignceilslope(int16_t dasect, int32_t x, int32_t y, int32_t z) return; sector[dasect].ceilingheinum = Scale((z-sector[dasect].ceilingz)<<8, - nsqrtasm(uhypsq(dax,day)), i); + ksqrt(uhypsq(dax,day)), i); if (sector[dasect].ceilingheinum == 0) sector[dasect].ceilingstat &= ~2; else sector[dasect].ceilingstat |= 2; @@ -3049,21 +1709,16 @@ void alignflorslope(int16_t dasect, int32_t x, int32_t y, int32_t z) return; sector[dasect].floorheinum = Scale((z-sector[dasect].floorz)<<8, - nsqrtasm(uhypsq(dax,day)), i); + ksqrt(uhypsq(dax,day)), i); if (sector[dasect].floorheinum == 0) sector[dasect].floorstat &= ~2; else sector[dasect].floorstat |= 2; } - -// -// setrollangle -// -#ifdef USE_OPENGL -void renderSetRollAngle(float rolla) +int tilehasmodelorvoxel(int const tilenume, int pal) { - gtang = rolla * BAngRadian; + return + (mdinited && hw_models && tile2model[Ptile2tile(tilenume, pal)].modelid != -1) || + (r_voxels && tiletovox[tilenume] != -1); } -#endif - diff --git a/source/build/src/engine_priv.h b/source/build/src/engine_priv.h index 046845c03..6c094d2be 100644 --- a/source/build/src/engine_priv.h +++ b/source/build/src/engine_priv.h @@ -8,84 +8,13 @@ #pragma once +#include "cmdlib.h" + #ifndef ENGINE_PRIV_H #define ENGINE_PRIV_H -#define MAXARTFILES_BASE 200 -#define MAXARTFILES_TOTAL 220 -#define MAXCLIPDIST 1024 - -// Uncomment to clear the screen before each top-level draw (classic only). -// FIXME: doesn't work with mirrors. -//#define ENGINE_CLEAR_SCREEN - - extern intptr_t asm1, asm2; - extern int32_t globalx1, globaly2; - - -extern uint16_t sqrtable[4096], shlookup[4096+256],sqrtable_old[2048]; - - - static inline int32_t nsqrtasm(uint32_t a) - { - // JBF 20030901: This was a damn lot simpler to reverse engineer than - // msqrtasm was. Really, it was just like simplifying an algebra equation. - uint16_t c; - - if (a & 0xff000000) // test eax, 0xff000000 / jnz short over24 - { - c = shlookup[(a >> 24) + 4096]; // mov ebx, eax - // over24: shr ebx, 24 - // mov cx, word ptr shlookup[ebx*2+8192] - } - else - { - c = shlookup[a >> 12]; // mov ebx, eax - // shr ebx, 12 - // mov cx, word ptr shlookup[ebx*2] - // jmp short under24 - } - a >>= c&0xff; // under24: shr eax, cl - a = (a&0xffff0000)|(sqrtable[a]); // mov ax, word ptr sqrtable[eax*2] - a >>= ((c&0xff00) >> 8); // mov cl, ch - // shr eax, cl - return a; - } - - static inline int32_t getclipmask(int32_t a, int32_t b, int32_t c, int32_t d) - { - // Ken did this - d = ((a<0)<<3) + ((b<0)<<2) + ((c<0)<<1) + (d<0); - return (((d<<4)^0xf0)|d); - } - - - -inline int32_t ksqrtasm_old(int32_t n) -{ - uint32_t shift = 0; - n = abs((int32_t)n); - while (n >= 2048) - { - n >>= 2; - ++shift; - } - uint32_t const s = sqrtable_old[n]; - return (s << shift) >> 10; -} - -inline int32_t clip_nsqrtasm(int32_t n) -{ - if (enginecompatibility_mode == ENGINECOMPATIBILITY_19950829) - return ksqrtasm_old(n); - return nsqrtasm(n); -} - -extern int16_t thesector[MAXWALLSB], thewall[MAXWALLSB]; -extern int16_t bunchfirst[MAXWALLSB], bunchlast[MAXWALLSB]; -extern int16_t maskwall[MAXWALLSB], maskwallcnt; -extern tspriteptr_t tspriteptr[MAXSPRITESONSCREEN + 1]; -extern int32_t xdimen, xdimenrecip, halfxdimen, xdimenscale, xdimscale, ydimen; +extern int32_t globalpal, globalfloorpal; +extern int32_t xdimen, xdimenscale, xdimscale, ydimen; extern float fxdimen; extern int32_t globalposx, globalposy, globalposz; extern fixed_t qglobalhoriz, qglobalang; @@ -99,170 +28,35 @@ extern int16_t globalpicnum; extern int32_t globalorientation; -extern int16_t editstatus; - -extern int16_t searchit; -extern int16_t searchsector, searchwall, searchstat; -extern int16_t searchbottomwall, searchisbottom; - extern char inpreparemirror; -extern int16_t sectorborder[256]; extern int32_t hitallsprites; -extern int32_t xb1[MAXWALLSB]; -extern int32_t rx1[MAXWALLSB], ry1[MAXWALLSB]; -extern int16_t bunchp2[MAXWALLSB]; -extern int16_t numscans, numbunches; -extern int32_t rxi[8], ryi[8]; -extern int32_t reciptable[2048]; - - -// int32_t wallmost(int16_t *mostbuf, int32_t w, int32_t sectnum, char dastat); -int32_t wallfront(int32_t l1, int32_t l2); - -void set_globalang(fixed_t const ang); - int32_t animateoffs(int tilenum, int fakevar); -static FORCE_INLINE int32_t bad_tspr(tspriteptr_t tspr) +inline int32_t bad_tspr(tspriteptr_t tspr) { // NOTE: tspr->owner >= MAXSPRITES (could be model) has to be handled by // caller. return (tspr->owner < 0 || (unsigned)tspr->picnum >= MAXTILES); } -// -// getpalookup (internal) -// -static FORCE_INLINE int32_t getpalookup(int32_t davis, int32_t dashade) -{ - if (getpalookup_replace) - return getpalookup_replace(davis, dashade); - return min(max(dashade + (davis >> 8), 0), numshades - 1); -} - -static FORCE_INLINE int32_t getpalookupsh(int32_t davis) { return getpalookup(davis, globalshade) << 8; } - -////// yax'y stuff ////// -#ifdef USE_OPENGL -extern void polymost_scansector(int32_t sectnum); -#endif -int32_t renderAddTsprite(int16_t z, int16_t sectnum); - - -static FORCE_INLINE void setgotpic(int32_t tilenume) -{ - gotpic[tilenume>>3] |= pow2char[tilenume&7]; -} - - -// Get properties of parallaxed sky to draw. -// Returns: pointer to tile offset array. Sets-by-pointer the other three. -const int16_t* getpsky(int32_t picnum, int32_t* dapyscale, int32_t* dapskybits, int32_t* dapyoffs, int32_t* daptileyscale); - -static FORCE_INLINE void set_globalpos(int32_t const x, int32_t const y, int32_t const z) +inline void set_globalpos(int32_t const x, int32_t const y, int32_t const z) { globalposx = x, fglobalposx = (float)x; globalposy = y, fglobalposy = (float)y; globalposz = z, fglobalposz = (float)z; } -template static FORCE_INLINE void tileUpdatePicnum(T * const tileptr, int const obj) -{ - auto &tile = *tileptr; - - if (picanm[tile].sf & PICANM_ANIMTYPE_MASK) - tile += animateoffs(tile, obj); - - if (((obj & 16384) == 16384) && (globalorientation & CSTAT_WALL_ROTATE_90) && RotTile(tile).newtile != -1) - tile = RotTile(tile).newtile; -} - -// x1, y1: in/out -// rest x/y: out -template -static inline void get_wallspr_points(T const * const spr, int32_t *x1, int32_t *x2, - int32_t *y1, int32_t *y2) -{ - //These lines get the 2 points of the rotated sprite - //Given: (x1, y1) starts out as the center point - - const int32_t tilenum=spr->picnum, ang=spr->ang; - const int32_t xrepeat = spr->xrepeat; - int32_t xoff = tileLeftOffset(tilenum) + spr->xoffset; - int32_t k, l, dax, day; - - if (spr->cstat&4) - xoff = -xoff; - - dax = bsin(ang) * xrepeat; - day = -bcos(ang) * xrepeat; - - l = tileWidth(tilenum); - k = (l>>1)+xoff; - - *x1 -= MulScale(dax,k, 16); - *x2 = *x1 + MulScale(dax,l, 16); - - *y1 -= MulScale(day,k, 16); - *y2 = *y1 + MulScale(day,l, 16); -} - -// x1, y1: in/out -// rest x/y: out -template -static inline void get_floorspr_points(T const * const spr, int32_t px, int32_t py, - int32_t *x1, int32_t *x2, int32_t *x3, int32_t *x4, - int32_t *y1, int32_t *y2, int32_t *y3, int32_t *y4) -{ - const int32_t tilenum = spr->picnum; - const int32_t cosang = bcos(spr->ang); - const int32_t sinang = bsin(spr->ang); - - vec2_t const span = { tileWidth(tilenum), tileHeight(tilenum)}; - vec2_t const repeat = { spr->xrepeat, spr->yrepeat }; - - vec2_t adjofs = { tileLeftOffset(tilenum) + spr->xoffset, tileTopOffset(tilenum) + spr->yoffset }; - - if (spr->cstat & 4) - adjofs.x = -adjofs.x; - - if (spr->cstat & 8) - adjofs.y = -adjofs.y; - - vec2_t const center = { ((span.x >> 1) + adjofs.x) * repeat.x, ((span.y >> 1) + adjofs.y) * repeat.y }; - vec2_t const rspan = { span.x * repeat.x, span.y * repeat.y }; - vec2_t const ofs = { -MulScale(cosang, rspan.y, 16), -MulScale(sinang, rspan.y, 16) }; - - *x1 += DMulScale(sinang, center.x, cosang, center.y, 16) - px; - *y1 += DMulScale(sinang, center.y, -cosang, center.x, 16) - py; - - *x2 = *x1 - MulScale(sinang, rspan.x, 16); - *y2 = *y1 + MulScale(cosang, rspan.x, 16); - - *x3 = *x2 + ofs.x, *x4 = *x1 + ofs.x; - *y3 = *y2 + ofs.y, *y4 = *y1 + ofs.y; -} inline int widthBits(int num) { - int w = tileWidth(num); - int j = 15; - - while ((j > 1) && ((1 << j) > w)) - j--; - return j; + return sizeToBits(tileWidth(num)); } inline int heightBits(int num) { - int w = tileHeight(num); - int j = 15; - - while ((j > 1) && ((1 << j) > w)) - j--; - return j; + return sizeToBits(tileHeight(num)); } diff --git a/source/build/src/mdsprite.cpp b/source/build/src/mdsprite.cpp index dcd16b5c6..bac6b65cf 100644 --- a/source/build/src/mdsprite.cpp +++ b/source/build/src/mdsprite.cpp @@ -16,18 +16,20 @@ #include "texturemanager.h" #include "hw_renderstate.h" #include "printf.h" +#include "hw_voxels.h" #include "../../glbackend/glbackend.h" static int32_t curextra=MAXTILES; #define MIN_CACHETIME_PRINT 10 +using namespace Polymost; +int32_t polymost_voxdraw(voxmodel_t* m, tspriteptr_t const tspr, bool rotate); -static int32_t addtileP(int32_t model,int32_t tile,int32_t pallet) +static int32_t addtileP(int32_t ,int32_t tile,int32_t pallet) { // tile >= 0 && tile < MAXTILES - UNREFERENCED_PARAMETER(model); if (curextra==MAXTILES+EXTRATILES-1) { Printf("warning: max EXTRATILES reached\n"); @@ -97,27 +99,23 @@ void freeallmodels() if (models) { for (i=0; i= nummodelsalloced) { - ml = (mdmodel_t **)Xrealloc(models,(nummodelsalloced+MODELALLOCGROUP)*sizeof(void *)); + ml = (mdmodel_t **)M_Realloc(models,(nummodelsalloced+MODELALLOCGROUP)*sizeof(void *)); models = ml; nummodelsalloced += MODELALLOCGROUP; } @@ -246,7 +244,7 @@ int32_t md_defineanimation(int32_t modelid, const char *framestart, const char * ma.fpssc = fpssc; ma.flags = flags; - map = (mdanim_t *)Xmalloc(sizeof(mdanim_t)); + map = (mdanim_t *)M_Malloc(sizeof(mdanim_t)); memcpy(map, &ma, sizeof(ma)); @@ -256,110 +254,6 @@ int32_t md_defineanimation(int32_t modelid, const char *framestart, const char * return 0; } -#if 0 -// FIXME: CURRENTLY DISABLED: interpolation may access frames we consider 'unused'? -int32_t md_thinoutmodel(int32_t modelid, uint8_t *usedframebitmap) -{ - md3model_t *m; - md3surf_t *s; - mdanim_t *anm; - int32_t i, surfi, sub, usedframes; - static int16_t otonframe[1024]; - - if ((uint32_t)modelid >= (uint32_t)nextmodelid) return -1; - m = (md3model_t *)models[modelid]; - if (m->mdnum != 3) return -2; - - for (anm=m->animations; anm; anm=anm->next) - { - if (anm->endframe <= anm->startframe) - { -// Printf("backward anim %d-%d\n", anm->startframe, anm->endframe); - return -3; - } - - for (i=anm->startframe; iendframe; i++) - usedframebitmap[i>>3] |= pow2char[i&7]; - } - - sub = 0; - for (i=0; inumframes; i++) - { - if (!(usedframebitmap[i>>3]&pow2char[i&7])) - { - sub++; - otonframe[i] = -1; - continue; - } - - otonframe[i] = i-sub; - } - - usedframes = m->numframes - sub; - if (usedframes==0 || usedframes==m->numframes) - return usedframes; - - //// THIN OUT! //// - - for (i=0; inumframes; i++) - { - if (otonframe[i]>=0 && otonframe[i] != i) - { - if (m->muladdframes) - memcpy(&m->muladdframes[2*otonframe[i]], &m->muladdframes[2*i], 2*sizeof(vec3f_t)); - memcpy(&m->head.frames[otonframe[i]], &m->head.frames[i], sizeof(md3frame_t)); - } - } - - for (surfi=0; surfi < m->head.numsurfs; surfi++) - { - s = &m->head.surfs[surfi]; - - for (i=0; inumframes; i++) - if (otonframe[i]>=0 && otonframe[i] != i) - memcpy(&s->xyzn[otonframe[i]*s->numverts], &s->xyzn[i*s->numverts], s->numverts*sizeof(md3xyzn_t)); - } - - ////// tweak frame indices in various places - - for (anm=m->animations; anm; anm=anm->next) - { - if (otonframe[anm->startframe]==-1 || otonframe[anm->endframe-1]==-1) - Printf("md %d WTF: anm %d %d\n", modelid, anm->startframe, anm->endframe); - - anm->startframe = otonframe[anm->startframe]; - anm->endframe = otonframe[anm->endframe-1]; - } - - for (i=0; imuladdframes) - m->muladdframes = Xrealloc(m->muladdframes, 2*sizeof(vec3f_t)*usedframes); - m->head.frames = Xrealloc(m->head.frames, sizeof(md3frame_t)*usedframes); - - for (surfi=0; surfi < m->head.numsurfs; surfi++) - { - m->head.surfs[surfi].numframes = usedframes; - // CAN'T do that because xyzn is offset from a larger block when loaded from md3: -// m->head.surfs[surfi].xyzn = Xrealloc(m->head.surfs[surfi].xyzn, s->numverts*usedframes*sizeof(md3xyzn_t)); - } - - m->head.numframes = usedframes; - m->numframes = usedframes; - - //////////// - return usedframes; -} -#endif - int32_t md_defineskin(int32_t modelid, const char *skinfn, int32_t palnum, int32_t skinnum, int32_t surfnum, float param, float specpower, float specfactor, int32_t flags) { mdskinmap_t *sk, *skl; @@ -381,7 +275,7 @@ int32_t md_defineskin(int32_t modelid, const char *skinfn, int32_t palnum, int32 break; if (!sk) { - sk = (mdskinmap_t *)Xcalloc(1,sizeof(mdskinmap_t)); + sk = (mdskinmap_t *)M_Calloc(1,sizeof(mdskinmap_t)); if (!skl) m->skinmap = sk; else skl->next = sk; @@ -410,9 +304,7 @@ int32_t md_definehud(int32_t modelid, int32_t tilex, vec3f_t add, int32_t angadd if ((uint32_t)modelid >= (uint32_t)nextmodelid) return -1; if ((uint32_t)tilex >= (uint32_t)MAXTILES) return -2; - tile2model[tilex].hudmem[(flags>>2)&1] = (hudtyp *)Xmalloc(sizeof(hudtyp)); - - hudtyp * const hud = tile2model[tilex].hudmem[(flags>>2)&1]; + hudtyp * const hud = &tile2model[tilex].hudmem[(flags>>2)&1]; hud->add = add; hud->angadd = ((int16_t)angadd)|2048; @@ -429,8 +321,6 @@ int32_t md_undefinetile(int32_t tile) tile2model[tile].modelid = -1; tile2model[tile].nexttile = -1; - DO_FREE_AND_NULL(tile2model[tile].hudmem[0]); - DO_FREE_AND_NULL(tile2model[tile].hudmem[1]); return 0; } @@ -446,8 +336,6 @@ int32_t md_undefinemodel(int32_t modelid) if (tile2model[i].modelid == modelid) { tile2model[i].modelid = -1; - DO_FREE_AND_NULL(tile2model[i].hudmem[0]); - DO_FREE_AND_NULL(tile2model[i].hudmem[1]); } if (models) @@ -669,23 +557,23 @@ static md2model_t *md2load(FileReader & fil, const char *filnam) int32_t ournumskins, ournumglcmds; - m = (md2model_t *)Xcalloc(1,sizeof(md2model_t)); + m = (md2model_t *)M_Calloc(1,sizeof(md2model_t)); m->mdnum = 2; m->scale = .01f; fil.Read((char *)&head,sizeof(md2head_t)); #if B_BIG_ENDIAN != 0 - head.id = B_LITTLE32(head.id); head.vers = B_LITTLE32(head.vers); - head.skinxsiz = B_LITTLE32(head.skinxsiz); head.skinysiz = B_LITTLE32(head.skinysiz); - head.framebytes = B_LITTLE32(head.framebytes); head.numskins = B_LITTLE32(head.numskins); - head.numverts = B_LITTLE32(head.numverts); head.numuv = B_LITTLE32(head.numuv); - head.numtris = B_LITTLE32(head.numtris); head.numglcmds = B_LITTLE32(head.numglcmds); - head.numframes = B_LITTLE32(head.numframes); head.ofsskins = B_LITTLE32(head.ofsskins); - head.ofsuv = B_LITTLE32(head.ofsuv); head.ofstris = B_LITTLE32(head.ofstris); - head.ofsframes = B_LITTLE32(head.ofsframes); head.ofsglcmds = B_LITTLE32(head.ofsglcmds); - head.ofseof = B_LITTLE32(head.ofseof); + head.id = LittleLong(head.id); head.vers = LittleLong(head.vers); + head.skinxsiz = LittleLong(head.skinxsiz); head.skinysiz = LittleLong(head.skinysiz); + head.framebytes = LittleLong(head.framebytes); head.numskins = LittleLong(head.numskins); + head.numverts = LittleLong(head.numverts); head.numuv = LittleLong(head.numuv); + head.numtris = LittleLong(head.numtris); head.numglcmds = LittleLong(head.numglcmds); + head.numframes = LittleLong(head.numframes); head.ofsskins = LittleLong(head.ofsskins); + head.ofsuv = LittleLong(head.ofsuv); head.ofstris = LittleLong(head.ofstris); + head.ofsframes = LittleLong(head.ofsframes); head.ofsglcmds = LittleLong(head.ofsglcmds); + head.ofseof = LittleLong(head.ofseof); #endif - if ((head.id != IDP2_MAGIC) || (head.vers != 8)) { Xfree(m); return 0; } //"IDP2" + if ((head.id != IDP2_MAGIC) || (head.vers != 8)) { M_Free(m); return 0; } //"IDP2" ournumskins = head.numskins ? head.numskins : 1; ournumglcmds = head.numglcmds ? head.numglcmds : 1; @@ -696,29 +584,29 @@ static md2model_t *md2load(FileReader & fil, const char *filnam) m->numglcmds = head.numglcmds; m->framebytes = head.framebytes; - m->frames = (char *)Xmalloc(m->numframes*m->framebytes); - m->glcmds = (int32_t *)Xmalloc(ournumglcmds*sizeof(int32_t)); - m->tris = (md2tri_t *)Xmalloc(head.numtris*sizeof(md2tri_t)); - m->uv = (md2uv_t *)Xmalloc(head.numuv*sizeof(md2uv_t)); + m->frames = (char *)M_Malloc(m->numframes*m->framebytes); + m->glcmds = (int32_t *)M_Malloc(ournumglcmds*sizeof(int32_t)); + m->tris = (md2tri_t *)M_Malloc(head.numtris*sizeof(md2tri_t)); + m->uv = (md2uv_t *)M_Malloc(head.numuv*sizeof(md2uv_t)); fil.Seek(head.ofsframes,FileReader::SeekSet); if (fil.Read((char *)m->frames,m->numframes*m->framebytes) != m->numframes*m->framebytes) - { Xfree(m->uv); Xfree(m->tris); Xfree(m->glcmds); Xfree(m->frames); Xfree(m); return 0; } + { M_Free(m->uv); M_Free(m->tris); M_Free(m->glcmds); M_Free(m->frames); M_Free(m); return 0; } if (m->numglcmds > 0) { fil.Seek(head.ofsglcmds,FileReader::SeekSet); if (fil.Read((char *)m->glcmds,m->numglcmds*sizeof(int32_t)) != (int32_t)(m->numglcmds*sizeof(int32_t))) - { Xfree(m->uv); Xfree(m->tris); Xfree(m->glcmds); Xfree(m->frames); Xfree(m); return 0; } + { M_Free(m->uv); M_Free(m->tris); M_Free(m->glcmds); M_Free(m->frames); M_Free(m); return 0; } } fil.Seek(head.ofstris,FileReader::SeekSet); if (fil.Read((char *)m->tris,head.numtris*sizeof(md2tri_t)) != (int32_t)(head.numtris*sizeof(md2tri_t))) - { Xfree(m->uv); Xfree(m->tris); Xfree(m->glcmds); Xfree(m->frames); Xfree(m); return 0; } + { M_Free(m->uv); M_Free(m->tris); M_Free(m->glcmds); M_Free(m->frames); M_Free(m); return 0; } fil.Seek(head.ofsuv,FileReader::SeekSet); if (fil.Read((char *)m->uv,head.numuv*sizeof(md2uv_t)) != (int32_t)(head.numuv*sizeof(md2uv_t))) - { Xfree(m->uv); Xfree(m->tris); Xfree(m->glcmds); Xfree(m->frames); Xfree(m); return 0; } + { M_Free(m->uv); M_Free(m->tris); M_Free(m->glcmds); M_Free(m->frames); M_Free(m); return 0; } #if B_BIG_ENDIAN != 0 { @@ -730,27 +618,27 @@ static md2model_t *md2load(FileReader & fil, const char *filnam) { fr = (md2frame_t *)f; l = (int32_t *)&fr->mul; - for (j=5; j>=0; j--) l[j] = B_LITTLE32(l[j]); + for (j=5; j>=0; j--) l[j] = LittleLong(l[j]); f += m->framebytes; } for (i = m->numglcmds-1; i>=0; i--) { - m->glcmds[i] = B_LITTLE32(m->glcmds[i]); + m->glcmds[i] = LittleLong(m->glcmds[i]); } for (i = head.numtris-1; i>=0; i--) { - m->tris[i].v[0] = B_LITTLE16(m->tris[i].v[0]); - m->tris[i].v[1] = B_LITTLE16(m->tris[i].v[1]); - m->tris[i].v[2] = B_LITTLE16(m->tris[i].v[2]); - m->tris[i].u[0] = B_LITTLE16(m->tris[i].u[0]); - m->tris[i].u[1] = B_LITTLE16(m->tris[i].u[1]); - m->tris[i].u[2] = B_LITTLE16(m->tris[i].u[2]); + m->tris[i].v[0] = LittleShort(m->tris[i].v[0]); + m->tris[i].v[1] = LittleShort(m->tris[i].v[1]); + m->tris[i].v[2] = LittleShort(m->tris[i].v[2]); + m->tris[i].u[0] = LittleShort(m->tris[i].u[0]); + m->tris[i].u[1] = LittleShort(m->tris[i].u[1]); + m->tris[i].u[2] = LittleShort(m->tris[i].u[2]); } for (i = head.numuv-1; i>=0; i--) { - m->uv[i].u = B_LITTLE16(m->uv[i].u); - m->uv[i].v = B_LITTLE16(m->uv[i].v); + m->uv[i].u = LittleShort(m->uv[i].u); + m->uv[i].v = LittleShort(m->uv[i].v); } } #endif @@ -760,15 +648,15 @@ static md2model_t *md2load(FileReader & fil, const char *filnam) if ((st[i] == '/') || (st[i] == '\\')) { i++; break; } if (i<0) i=0; st[i] = 0; - m->basepath = (char *)Xmalloc(i+1); + m->basepath = (char *)M_Malloc(i+1); strcpy(m->basepath, st); - m->skinfn = (char *)Xmalloc(ournumskins*64); + m->skinfn = (char *)M_Malloc(ournumskins*64); if (m->numskins > 0) { fil.Seek(head.ofsskins,FileReader::SeekSet); if (fil.Read(m->skinfn,64*m->numskins) != 64*m->numskins) - { Xfree(m->glcmds); Xfree(m->frames); Xfree(m); return 0; } + { M_Free(m->glcmds); M_Free(m->frames); M_Free(m); return 0; } } maxmodelverts = max(maxmodelverts, m->numverts); @@ -778,7 +666,7 @@ static md2model_t *md2load(FileReader & fil, const char *filnam) // the MD2 is now loaded internally - let's begin the MD3 conversion process //Printf("Beginning md3 conversion.\n"); - m3 = (md3model_t *)Xcalloc(1, sizeof(md3model_t)); + m3 = (md3model_t *)M_Calloc(1, sizeof(md3model_t)); m3->mdnum = 3; m3->texture = nullptr; m3->scale = m->scale; m3->head.id = IDP3_MAGIC; m3->head.vers = 15; @@ -791,8 +679,8 @@ static md2model_t *md2load(FileReader & fil, const char *filnam) m3->numskins = m3->head.numskins; m3->numframes = m3->head.numframes; - m3->head.frames = (md3frame_t *)Xcalloc(m3->head.numframes, sizeof(md3frame_t)); - m3->muladdframes = (vec3f_t *)Xcalloc(m->numframes * 2, sizeof(vec3f_t)); + m3->head.frames = (md3frame_t *)M_Calloc(m3->head.numframes, sizeof(md3frame_t)); + m3->muladdframes = (vec3f_t *)M_Calloc(m->numframes * 2, sizeof(vec3f_t)); f = (md2frame_t *)(m->frames); @@ -810,7 +698,7 @@ static md2model_t *md2load(FileReader & fil, const char *filnam) m3->head.tags = NULL; - m3->head.surfs = (md3surf_t *)Xcalloc(1, sizeof(md3surf_t)); + m3->head.surfs = (md3surf_t *)M_Calloc(1, sizeof(md3surf_t)); s = m3->head.surfs; // model converting @@ -827,9 +715,9 @@ static md2model_t *md2load(FileReader & fil, const char *filnam) s->shaders = NULL; - s->tris = (md3tri_t *)Xcalloc(head.numtris, sizeof(md3tri_t)); - s->uv = (md3uv_t *)Xcalloc(s->numverts, sizeof(md3uv_t)); - s->xyzn = (md3xyzn_t *)Xcalloc(s->numverts * m->numframes, sizeof(md3xyzn_t)); + s->tris = (md3tri_t *)M_Calloc(head.numtris, sizeof(md3tri_t)); + s->uv = (md3uv_t *)M_Calloc(s->numverts, sizeof(md3uv_t)); + s->xyzn = (md3xyzn_t *)M_Calloc(s->numverts * m->numframes, sizeof(md3xyzn_t)); //memoryusage += (s->numverts * m->numframes * sizeof(md3xyzn_t)); //Printf("Current model geometry memory usage : %i.\n", memoryusage); @@ -874,7 +762,7 @@ static md2model_t *md2load(FileReader & fil, const char *filnam) { mdskinmap_t *sk; - sk = (mdskinmap_t *)Xcalloc(1,sizeof(mdskinmap_t)); + sk = (mdskinmap_t *)M_Calloc(1,sizeof(mdskinmap_t)); sk->palette = 0; sk->skinnum = 0; sk->surfnum = 0; @@ -891,12 +779,12 @@ static md2model_t *md2load(FileReader & fil, const char *filnam) m3->skinmap = sk; } - m3->indexes = (uint16_t *)Xmalloc(sizeof(uint16_t) * s->numtris); - m3->vindexes = (uint16_t *)Xmalloc(sizeof(uint16_t) * s->numtris * 3); - m3->maxdepths = (float *)Xmalloc(sizeof(float) * s->numtris); + m3->indexes = (uint16_t *)M_Malloc(sizeof(uint16_t) * s->numtris); + m3->vindexes = (uint16_t *)M_Malloc(sizeof(uint16_t) * s->numtris * 3); + m3->maxdepths = (float *)M_Malloc(sizeof(float) * s->numtris); // die MD2 ! DIE ! - Xfree(m->skinfn); Xfree(m->basepath); Xfree(m->uv); Xfree(m->tris); Xfree(m->glcmds); Xfree(m->frames); Xfree(m); + M_Free(m->skinfn); M_Free(m->basepath); M_Free(m->uv); M_Free(m->tris); M_Free(m->glcmds); M_Free(m->frames); M_Free(m); return ((md2model_t *)m3); } @@ -948,7 +836,7 @@ static md3model_t *md3load(FileReader & fil) md3model_t *m; md3surf_t *s; - m = (md3model_t *)Xcalloc(1,sizeof(md3model_t)); + m = (md3model_t *)M_Calloc(1,sizeof(md3model_t)); m->mdnum = 3; m->texture = nullptr; m->scale = .01f; m->muladdframes = NULL; @@ -956,15 +844,15 @@ static md3model_t *md3load(FileReader & fil) fil.Read(&m->head,SIZEOF_MD3HEAD_T); #if B_BIG_ENDIAN != 0 - m->head.id = B_LITTLE32(m->head.id); m->head.vers = B_LITTLE32(m->head.vers); - m->head.flags = B_LITTLE32(m->head.flags); m->head.numframes = B_LITTLE32(m->head.numframes); - m->head.numtags = B_LITTLE32(m->head.numtags); m->head.numsurfs = B_LITTLE32(m->head.numsurfs); - m->head.numskins = B_LITTLE32(m->head.numskins); m->head.ofsframes = B_LITTLE32(m->head.ofsframes); - m->head.ofstags = B_LITTLE32(m->head.ofstags); m->head.ofssurfs = B_LITTLE32(m->head.ofssurfs); - m->head.eof = B_LITTLE32(m->head.eof); + m->head.id = LittleLong(m->head.id); m->head.vers = LittleLong(m->head.vers); + m->head.flags = LittleLong(m->head.flags); m->head.numframes = LittleLong(m->head.numframes); + m->head.numtags = LittleLong(m->head.numtags); m->head.numsurfs = LittleLong(m->head.numsurfs); + m->head.numskins = LittleLong(m->head.numskins); m->head.ofsframes = LittleLong(m->head.ofsframes); + m->head.ofstags = LittleLong(m->head.ofstags); m->head.ofssurfs = LittleLong(m->head.ofssurfs); + m->head.eof = LittleLong(m->head.eof); #endif - if ((m->head.id != IDP3_MAGIC) && (m->head.vers != 15)) { Xfree(m); return 0; } //"IDP3" + if ((m->head.id != IDP3_MAGIC) && (m->head.vers != 15)) { M_Free(m); return 0; } //"IDP3" m->numskins = m->head.numskins; //<- dead code? m->numframes = m->head.numframes; @@ -972,19 +860,19 @@ static md3model_t *md3load(FileReader & fil) ofsurf = m->head.ofssurfs; fil.Seek(m->head.ofsframes,FileReader::SeekSet); i = m->head.numframes*sizeof(md3frame_t); - m->head.frames = (md3frame_t *)Xmalloc(i); + m->head.frames = (md3frame_t *)M_Malloc(i); fil.Read(m->head.frames,i); if (m->head.numtags == 0) m->head.tags = NULL; else { fil.Seek(m->head.ofstags,FileReader::SeekSet); i = m->head.numtags*sizeof(md3tag_t); - m->head.tags = (md3tag_t *)Xmalloc(i); + m->head.tags = (md3tag_t *)M_Malloc(i); fil.Read(m->head.tags,i); } fil.Seek(m->head.ofssurfs,FileReader::SeekSet); - m->head.surfs = (md3surf_t *)Xcalloc(m->head.numsurfs, sizeof(md3surf_t)); + m->head.surfs = (md3surf_t *)M_Calloc(m->head.numsurfs, sizeof(md3surf_t)); // NOTE: We assume that NULL is represented by all-zeros. // surfs[0].geometry is for POLYMER_MD_PROCESS_CHECK (else: crashes). // surfs[i].geometry is for FREE_SURFS_GEOMETRY. @@ -997,13 +885,13 @@ static md3model_t *md3load(FileReader & fil) for (i = m->head.numframes-1; i>=0; i--) { l = (int32_t *)&m->head.frames[i].min; - for (j=3+3+3+1-1; j>=0; j--) l[j] = B_LITTLE32(l[j]); + for (j=3+3+3+1-1; j>=0; j--) l[j] = LittleLong(l[j]); } for (i = m->head.numtags-1; i>=0; i--) { l = (int32_t *)&m->head.tags[i].p; - for (j=3+3+3+3-1; j>=0; j--) l[j] = B_LITTLE32(l[j]); + for (j=3+3+3+3-1; j>=0; j--) l[j] = LittleLong(l[j]); } } #endif @@ -1018,9 +906,9 @@ static md3model_t *md3load(FileReader & fil) #if B_BIG_ENDIAN != 0 { int32_t j, *l; - s->id = B_LITTLE32(s->id); + s->id = LittleLong(s->id); l = (int32_t *)&s->flags; - for (j=1+1+1+1+1+1+1+1+1+1-1; j>=0; j--) l[j] = B_LITTLE32(l[j]); + for (j=1+1+1+1+1+1+1+1+1+1-1; j>=0; j--) l[j] = LittleLong(l[j]); } #endif @@ -1037,7 +925,7 @@ static md3model_t *md3load(FileReader & fil) //memoryusage += (s->numverts * s->numframes * sizeof(md3xyzn_t)); //Printf("Current model geometry memory usage : %i.\n", memoryusage); - s->tris = (md3tri_t *)Xmalloc((leng[0] + leng[1]) + (leng[2] + leng[3])); + s->tris = (md3tri_t *)M_Malloc((leng[0] + leng[1]) + (leng[2] + leng[3])); s->shaders = (md3shader_t *)(((intptr_t)s->tris)+leng[0]); s->uv = (md3uv_t *)(((intptr_t)s->shaders)+leng[1]); @@ -1054,23 +942,23 @@ static md3model_t *md3load(FileReader & fil) for (i=s->numtris-1; i>=0; i--) { - for (j=2; j>=0; j--) s->tris[i].i[j] = B_LITTLE32(s->tris[i].i[j]); + for (j=2; j>=0; j--) s->tris[i].i[j] = LittleLong(s->tris[i].i[j]); } for (i=s->numshaders-1; i>=0; i--) { - s->shaders[i].i = B_LITTLE32(s->shaders[i].i); + s->shaders[i].i = LittleLong(s->shaders[i].i); } for (i=s->numverts-1; i>=0; i--) { l = (int32_t *)&s->uv[i].u; - l[0] = B_LITTLE32(l[0]); - l[1] = B_LITTLE32(l[1]); + l[0] = LittleLong(l[0]); + l[1] = LittleLong(l[1]); } for (i=s->numframes*s->numverts-1; i>=0; i--) { - s->xyzn[i].x = (int16_t)B_LITTLE16((uint16_t)s->xyzn[i].x); - s->xyzn[i].y = (int16_t)B_LITTLE16((uint16_t)s->xyzn[i].y); - s->xyzn[i].z = (int16_t)B_LITTLE16((uint16_t)s->xyzn[i].z); + s->xyzn[i].x = (int16_t)LittleShort((uint16_t)s->xyzn[i].x); + s->xyzn[i].y = (int16_t)LittleShort((uint16_t)s->xyzn[i].y); + s->xyzn[i].z = (int16_t)LittleShort((uint16_t)s->xyzn[i].z); } } #endif @@ -1080,9 +968,9 @@ static md3model_t *md3load(FileReader & fil) ofsurf += s->ofsend; } - m->indexes = (uint16_t *)Xmalloc(sizeof(uint16_t) * maxtrispersurf); - m->vindexes = (uint16_t *)Xmalloc(sizeof(uint16_t) * maxtrispersurf * 3); - m->maxdepths = (float *)Xmalloc(sizeof(float) * maxtrispersurf); + m->indexes = (uint16_t *)M_Malloc(sizeof(uint16_t) * maxtrispersurf); + m->vindexes = (uint16_t *)M_Malloc(sizeof(uint16_t) * maxtrispersurf * 3); + m->maxdepths = (float *)M_Malloc(sizeof(float) * maxtrispersurf); return m; } @@ -1219,7 +1107,7 @@ void md3_vox_calcmat_common(tspriteptr_t tspr, const vec3f_t *a0, float f, float } static void md3draw_handle_triangles(const md3surf_t *s, uint16_t *indexhandle, - int32_t texunits, const md3model_t *M) + int32_t , const md3model_t *M) { int32_t i; @@ -1241,10 +1129,6 @@ static void md3draw_handle_triangles(const md3surf_t *s, uint16_t *indexhandle, } } GLInterface.Draw(DT_Triangles, data.second, s->numtris *3); - -#ifndef USE_GLEXT - UNREFERENCED_PARAMETER(texunits); -#endif } static int32_t polymost_md3draw(md3model_t *m, tspriteptr_t tspr) @@ -1261,9 +1145,6 @@ static int32_t polymost_md3draw(md3model_t *m, tspriteptr_t tspr) const uint8_t lpal = ((unsigned)owner < MAXSPRITES) ? sprite[tspr->owner].pal : tspr->pal; const int32_t sizyrep = tileHeight(tspr->picnum) * tspr->yrepeat; - polymost_outputGLDebugMessage(3, "polymost_md3draw(m:%p, tspr:%p)", m, tspr); - // if ((tspr->cstat&48) == 32) return 0; - updateanimation((md2model_t *)m, tspr, lpal); //create current&next frame's vertex list from whole list @@ -1558,12 +1439,12 @@ static void md3free(md3model_t *m) for (anim=m->animations; anim; anim=nanim) { nanim = anim->next; - Xfree(anim); + M_Free(anim); } for (sk=m->skinmap; sk; sk=nsk) { nsk = sk->next; - Xfree(sk); + M_Free(sk); } if (m->head.surfs) @@ -1571,21 +1452,21 @@ static void md3free(md3model_t *m) for (bssize_t surfi=m->head.numsurfs-1; surfi>=0; surfi--) { md3surf_t *s = &m->head.surfs[surfi]; - Xfree(s->tris); - Xfree(s->geometry); // FREE_SURFS_GEOMETRY + M_Free(s->tris); + M_Free(s->geometry); // FREE_SURFS_GEOMETRY } - Xfree(m->head.surfs); + M_Free(m->head.surfs); } - Xfree(m->head.tags); - Xfree(m->head.frames); + M_Free(m->head.tags); + M_Free(m->head.frames); - Xfree(m->muladdframes); + M_Free(m->muladdframes); - Xfree(m->indexes); - Xfree(m->vindexes); - Xfree(m->maxdepths); + M_Free(m->indexes); + M_Free(m->vindexes); + M_Free(m->maxdepths); - Xfree(m); + M_Free(m); } //---------------------------------------- MD3 LIBRARY ENDS ---------------------------------------- @@ -1607,7 +1488,7 @@ static mdmodel_t *mdload(const char *filnam) fil.Read(&i,4); fil.Seek(0,FileReader::SeekSet); - switch (B_LITTLE32(i)) + switch (LittleLong(i)) { case IDP2_MAGIC: // Printf("Warning: model \"%s\" is version IDP2; wanted version IDP3\n",filnam); @@ -1641,7 +1522,7 @@ int32_t polymost_mddraw(tspriteptr_t tspr) { if (maxmodelverts > allocmodelverts) { - vertlist = (vec3f_t *) Xrealloc(vertlist, sizeof(vec3f_t)*maxmodelverts); + vertlist = (vec3f_t *) M_Realloc(vertlist, sizeof(vec3f_t)*maxmodelverts); allocmodelverts = maxmodelverts; } @@ -1654,6 +1535,11 @@ int32_t polymost_mddraw(tspriteptr_t tspr) return 0; } +void voxfree(voxmodel_t* m) +{ + if (m) delete m; +} + static void mdfree(mdmodel_t *vm) { if (vm->mdnum == 1) { voxfree((voxmodel_t *)vm); return; } diff --git a/source/build/src/polymost.cpp b/source/build/src/polymost.cpp index 4901b8155..4e52720b6 100644 --- a/source/build/src/polymost.cpp +++ b/source/build/src/polymost.cpp @@ -22,20 +22,34 @@ Ken Silverman's official web site: http://www.advsys.net/ken #include "texturemanager.h" #include "hw_renderstate.h" #include "printf.h" +#include "gamefuncs.h" +#include "hw_drawinfo.h" #include "gamestruct.h" +#include "gamestruct.h" +#include "hw_voxels.h" + + +typedef struct { + union { double x; double d; }; + union { double y; double u; }; + union { double z; double v; }; +} vec3d_t; + +static_assert(sizeof(vec3d_t) == sizeof(double) * 3); +int32_t xyaspect = -1; + + + +int skiptile = -1; +FGameTexture* GetSkyTexture(int basetile, int lognumtiles, const int16_t* tilemap, int remap = 0); +int32_t polymost_voxdraw(voxmodel_t* m, tspriteptr_t const tspr, bool rotate); int checkTranslucentReplacement(FTextureID picnum, int pal); CVARD(Bool, hw_animsmoothing, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG, "enable/disable model animation smoothing") -CVARD(Bool, hw_hightile, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG, "enable/disable hightile texture rendering") CVARD(Bool, hw_models, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG, "enable/disable model rendering") CVARD(Bool, hw_parallaxskypanning, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG, "enable/disable parallaxed floor/ceiling panning when drawing a parallaxing sky") CVARD(Float, hw_shadescale, 1.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG, "multiplier for shading") -bool hw_int_useindexedcolortextures; -CUSTOM_CVARD(Bool, hw_useindexedcolortextures, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG, "enable/disable indexed color texture rendering") -{ - if (screen) screen->SetTextureFilterMode(); -} int pm_smoothratio; @@ -43,6 +57,25 @@ int pm_smoothratio; // For testing - will be removed later. CVAR(Int, skytile, 0, 0) +CVAR(Bool, testnewrenderer, true, 0) + +extern fixed_t global100horiz; // (-100..300)-scale horiz (the one passed to drawrooms) +static vec3_t spritesxyz[MAXSPRITESONSCREEN + 1]; +static int16_t thewall[MAXWALLSB]; +static int16_t bunchp2[MAXWALLSB], thesector[MAXWALLSB]; +static int16_t bunchfirst[MAXWALLSB], bunchlast[MAXWALLSB]; +static int16_t numscans, numbunches; +static int16_t maskwall[MAXWALLSB], maskwallcnt; +static int16_t sectorborder[256]; +static tspriteptr_t tspriteptr[MAXSPRITESONSCREEN + 1]; +tspritetype pm_tsprite[MAXSPRITESONSCREEN]; +int pm_spritesortcnt; + + + +namespace Polymost +{ + typedef struct { float x, cy[2], fy[2]; int32_t tag; int16_t n, p, ctag, ftag; } vsptyp; #define VSPMAX 2048 //<- careful! @@ -103,19 +136,56 @@ static int32_t drawingskybox = 0; static hitdata_t polymost_hitdata; FGameTexture* globalskytex = nullptr; -FGameTexture* GetSkyTexture(int basetile, int lognumtiles, const int16_t* tilemap); void polymost_outputGLDebugMessage(uint8_t severity, const char* format, ...) { } -float sectorVisibility(int sectnum) +static void set_globalang(fixed_t const ang) +{ + globalang = FixedToInt(ang) & 2047; + qglobalang = ang & 0x7FFFFFF; + + float const f_ang = FixedToFloat(ang); + float const fcosang = bcosf(f_ang); + float const fsinang = bsinf(f_ang); + +#ifdef USE_OPENGL + fcosglobalang = fcosang; + fsinglobalang = fsinang; +#endif + + cosglobalang = (int)fcosang; + singlobalang = (int)fsinang; + + cosviewingrangeglobalang = MulScale(cosglobalang, viewingrange, 16); + sinviewingrangeglobalang = MulScale(singlobalang, viewingrange, 16); +} + +static inline float polymost_invsqrt_approximation(float x) +{ + // this is the comment + return 1.f / sqrtf(x); +} + +static float sectorVisibility(int sectnum) { // Beware of wraparound madness... int v = sector[sectnum].visibility; return v? ((uint8_t)(v + 16)) / 16.f : 1.f; } +static void tileUpdatePicnum(short* const tileptr, int const obj) +{ + auto& tile = *tileptr; + + if (picanm[tile].sf & PICANM_ANIMTYPE_MASK) + tile += animateoffs(tile, obj); + + if (((obj & 16384) == 16384) && (globalorientation & CSTAT_WALL_ROTATE_90) && RotTile(tile).newtile != -1) + tile = RotTile(tile).newtile; +} + //-------------------------------------------------------------------------------------------------- //Use this for both initialization and uninitialization of OpenGL. @@ -200,7 +270,7 @@ static int32_t pow2xsplit = 0, skyzbufferhack = 0, flatskyrender = 0; static float drawpoly_alpha = 0.f; static uint8_t drawpoly_blend = 0; -int32_t polymost_maskWallHasTranslucency(uwalltype const * const wall) +int32_t polymost_maskWallHasTranslucency(walltype const * const wall) { if (wall->cstat & CSTAT_WALL_TRANSLUCENT) return true; @@ -208,7 +278,7 @@ int32_t polymost_maskWallHasTranslucency(uwalltype const * const wall) return checkTranslucentReplacement(tileGetTexture(wall->picnum)->GetID(), wall->pal); } -int32_t polymost_spriteHasTranslucency(tspritetype const * const tspr) +int32_t polymost_spriteHasTranslucency(spritetype const * const tspr) { if ((tspr->cstat & CSTAT_SPRITE_TRANSLUCENT) || (tspr->clipdist & TSPR_FLAGS_DRAW_LAST) || ((unsigned)tspr->owner < MAXSPRITES && spriteext[tspr->owner].alpha)) @@ -217,25 +287,6 @@ int32_t polymost_spriteHasTranslucency(tspritetype const * const tspr) return checkTranslucentReplacement(tileGetTexture(tspr->picnum)->GetID(), tspr->pal); } -int32_t polymost_spriteIsModelOrVoxel(tspritetype const * const tspr) -{ - if ((unsigned)tspr->owner < MAXSPRITES && spriteext[tspr->owner].flags&SPREXT_NOTMD) - return false; - - - if (hw_models && tile2model[Ptile2tile(tspr->picnum, tspr->pal)].modelid >= 0 && - tile2model[Ptile2tile(tspr->picnum, tspr->pal)].framenum >= 0) - return true; - - if (r_voxels && (tspr->cstat & CSTAT_SPRITE_ALIGNMENT) != CSTAT_SPRITE_ALIGNMENT_SLAB && tiletovox[tspr->picnum] >= 0 && voxmodels[tiletovox[tspr->picnum]]) - return true; - - if ((tspr->cstat & CSTAT_SPRITE_ALIGNMENT) == CSTAT_SPRITE_ALIGNMENT_SLAB && voxmodels[tspr->picnum]) - return true; - - return false; -} - static void polymost_updaterotmat(void) { //Up/down rotation @@ -254,7 +305,8 @@ static void polymost_updaterotmat(void) }; multiplyMatrix4f(matrix, tiltmatrix); renderSetViewMatrix(matrix); - renderSetVisibility(MulScale(g_visibility, MulScale(xdimenscale, viewingrangerecip, 16), 16) * fviewingrange * (1.f / (65536.f * 65536.f)) / r_ambientlight); + float fxdimen = FixedToFloat(xdimenscale); + renderSetVisibility(g_visibility * fxdimen * (1.f / (65536.f)) / r_ambientlight); } const vec2_16_t tileSize(size_t index) @@ -266,7 +318,6 @@ const vec2_16_t tileSize(size_t index) static void polymost_flatskyrender(vec2f_t const* const dpxy, int32_t const n, int32_t method, const vec2_16_t& tilesize); // Hack for Duke's camera until I can find out why this behaves erratically. -int skiptile = -1; static void polymost_drawpoly(vec2f_t const * const dpxy, int32_t const n, int32_t method, const vec2_16_t &tilesize) { @@ -334,8 +385,6 @@ static void polymost_drawpoly(vec2f_t const * const dpxy, int32_t const n, int32 float usub = 0; float vsub = 0; - polymost_outputGLDebugMessage(3, "polymost_drawpoly(dpxy:%p, n:%d, method_:%X), method: %X", dpxy, n, method_, method); - // This only takes effect for textures with their default set to SamplerClampXY. int sampleroverride = CLAMP_NONE; if (method & DAMETH_CLAMPED) @@ -347,7 +396,7 @@ static void polymost_drawpoly(vec2f_t const * const dpxy, int32_t const n, int32 int palid = TRANSLATION(Translation_Remap + curbasepal, globalpal); GLInterface.SetFade(globalfloorpal); - bool success = GLInterface.SetTexture(globalskytex? globalskytex : tileGetTexture(globalpicnum), palid, sampleroverride); + bool success = GLInterface.SetTexture(globalskytex? globalskytex : tileGetTexture(globalpicnum), globalskytex? 0 : palid, sampleroverride); if (!success) { tsiz.x = tsiz.y = 1; @@ -920,16 +969,6 @@ skip: ; #endif } -// Moved in from pragmas.h -static inline int32_t krecipasm(int32_t i) -{ - // Ken did this - union { int32_t i; float f; } x; - x.f = (float)i; - i = x.i; - return ((reciptable[(i >> 12) & 2047] >> (((i - 0x3f800000) >> 23) & 31)) ^ (i >> 31)); -} - // variables that are set to ceiling- or floor-members, depending // on which one is processed right now static int32_t global_cf_z; @@ -961,14 +1000,14 @@ static void polymost_internal_nonparallaxed(vec2f_t n0, vec2f_t n1, float ryp0, wall[wall[sec->wallptr].point2].y - wall[sec->wallptr].y }; float r; + int length = ksqrt(uhypsq(xy.x, xy.y)); if (globalorientation & 2) { - int i = krecipasm(nsqrtasm(uhypsq(xy.x,xy.y))); - r = i * (1.f/1073741824.f); + r = 1.f / length; } else { - int i = nsqrtasm(uhypsq(xy.x,xy.y)); if (i == 0) i = 1024; else i = 1048576 / i; + int i; if (length == 0) i = 1024; else i = 1048576 / length; // consider integer truncation r = i * (1.f/1048576.f); } @@ -1211,7 +1250,7 @@ static float fgetceilzofslope(usectorptr_t sec, float dax, float day) vec2_t const w = *(vec2_t const *)wal; vec2_t const d = { wal2->x - w.x, wal2->y - w.y }; - int const i = nsqrtasm(uhypsq(d.x,d.y))<<5; + int const i = ksqrt(uhypsq(d.x,d.y))<<5; if (i == 0) return sec->ceilingz; float const j = (d.x*(day-w.y)-d.y*(dax-w.x))*(1.f/8.f); @@ -1229,7 +1268,7 @@ static float fgetflorzofslope(usectorptr_t sec, float dax, float day) vec2_t const w = *(vec2_t const *)wal; vec2_t const d = { wal2->x - w.x, wal2->y - w.y }; - int const i = nsqrtasm(uhypsq(d.x,d.y))<<5; + int const i = ksqrt(uhypsq(d.x,d.y))<<5; if (i == 0) return sec->floorz; float const j = (d.x*(day-w.y)-d.y*(dax-w.x))*(1.f/8.f); @@ -1248,7 +1287,7 @@ static void fgetzsofslope(usectorptr_t sec, float dax, float day, float* ceilz, vec2_t const d = { wal2->x - wal->x, wal2->y - wal->y }; - int const i = nsqrtasm(uhypsq(d.x,d.y))<<5; + int const i = ksqrt(uhypsq(d.x,d.y))<<5; if (i == 0) return; float const j = (d.x*(day-wal->y)-d.y*(dax-wal->x))*(1.f/8.f); @@ -1280,9 +1319,13 @@ static void polymost_flatskyrender(vec2f_t const* const dpxy, int32_t const n, i float const fglobalang = FixedToFloat(qglobalang); int32_t dapyscale, dapskybits, dapyoffs, daptileyscale; int16_t const * dapskyoff = getpsky(globalpicnum, &dapyscale, &dapskybits, &dapyoffs, &daptileyscale); - globalskytex = skytile? nullptr : GetSkyTexture(globalpicnum, dapskybits, dapskyoff); + int remap = TRANSLATION(Translation_Remap + curbasepal, globalpal); + globalskytex = skytile? nullptr : GetSkyTexture(globalpicnum, dapskybits, dapskyoff, remap); int realskybits = dapskybits; - if (globalskytex) dapskybits = 0; + if (globalskytex) + { + dapskybits = 0; + } ghoriz = (qglobalhoriz*(1.f/65536.f)-float(ydimen>>1))*dapyscale*(1.f/65536.f)+float(ydimen>>1)+ghorizcorrect; @@ -1591,184 +1634,6 @@ static void polymost_drawalls(int32_t const bunch) flatskyrender = 0; ghoriz = ghorizbak; } -#if 0 - else //NOTE: code copied from ceiling code... lots of duplicated stuff :/ - { - //Skybox code for parallax floor! - float sky_t0, sky_t1; // _nx0, _ny0, _nx1, _ny1; - float sky_ryp0, sky_ryp1, sky_x0, sky_x1, sky_cy0, sky_fy0, sky_cy1, sky_fy1, sky_ox0, sky_ox1; - static vec2f_t const skywal[4] = { { -512, -512 }, { 512, -512 }, { 512, 512 }, { -512, 512 } }; - - pow2xsplit = 0; - - for (bssize_t i=0; i<4; i++) - { - walpos = skywal[i&3]; - vec2f_t skyp0 = { walpos.y * gcosang - walpos.x * gsinang, - walpos.x * gcosang2 + walpos.y * gsinang2 }; - - walpos = skywal[(i + 1) & 3]; - vec2f_t skyp1 = { walpos.y * gcosang - walpos.x * gsinang, - walpos.x * gcosang2 + walpos.y * gsinang2 }; - - vec2f_t const oskyp0 = skyp0; - - //Clip to close parallel-screen plane - if (skyp0.y < SCISDIST) - { - if (skyp1.y < SCISDIST) continue; - sky_t0 = (SCISDIST - skyp0.y) / (skyp1.y - skyp0.y); - skyp0 = { (skyp1.x - skyp0.x) * sky_t0 + skyp0.x, SCISDIST }; - } - else { sky_t0 = 0.f; } - - if (skyp1.y < SCISDIST) - { - sky_t1 = (SCISDIST - oskyp0.y) / (skyp1.y - oskyp0.y); - skyp1 = { (skyp1.x - oskyp0.x) * sky_t1 + oskyp0.x, SCISDIST }; - } - else { sky_t1 = 1.f; } - - sky_ryp0 = 1.f/skyp0.y; sky_ryp1 = 1.f/skyp1.y; - - //Generate screen coordinates for front side of wall - sky_x0 = ghalfx*skyp0.x*sky_ryp0 + ghalfx; - sky_x1 = ghalfx*skyp1.x*sky_ryp1 + ghalfx; - if ((sky_x1 <= sky_x0) || (sky_x0 >= x1) || (x0 >= sky_x1)) continue; - - sky_ryp0 *= gyxscale; sky_ryp1 *= gyxscale; - - sky_cy0 = -8192.f*sky_ryp0 + ghoriz; - sky_fy0 = 8192.f*sky_ryp0 + ghoriz; - sky_cy1 = -8192.f*sky_ryp1 + ghoriz; - sky_fy1 = 8192.f*sky_ryp1 + ghoriz; - - sky_ox0 = sky_x0; sky_ox1 = sky_x1; - - //Make sure: x0<=_x0<_x1<=x1 - float nfy[2] = { fy0, fy1 }; - - if (sky_x0 < x0) - { - float const t = (x0-sky_x0)/(sky_x1-sky_x0); - sky_cy0 += (sky_cy1-sky_cy0)*t; - sky_fy0 += (sky_fy1-sky_fy0)*t; - sky_x0 = x0; - } - else if (sky_x0 > x0) nfy[0] += (sky_x0-x0)*(fy1-fy0)/(x1-x0); - - if (sky_x1 > x1) - { - float const t = (x1-sky_x1)/(sky_x1-sky_x0); - sky_cy1 += (sky_cy1-sky_cy0)*t; - sky_fy1 += (sky_fy1-sky_fy0)*t; - sky_x1 = x1; - } - else if (sky_x1 < x1) nfy[1] += (sky_x1-x1)*(fy1-fy0)/(x1-x0); - - // (skybox floor) - //(_x0,_fy0)-(_x1,_fy1) - // (skybox wall) - //(_x0,_cy0)-(_x1,_cy1) - // (skybox ceiling) - //(_x0,nfy0)-(_x1,nfy1) - - //floor of skybox - drawingskybox = 6; //floor/6th texture/index 5 of skybox - float const ft[4] = { 512 / 16, 512 / -16, fcosglobalang * (1.f / 2147483648.f), - fsinglobalang * (1.f / 2147483648.f) }; - - xtex.d = 0; - ytex.d = gxyaspect*(1.0/4194304.0); - otex.d = -ghoriz*ytex.d; - xtex.u = ft[3]*fviewingrange*(-1.0/65536.0); - xtex.v = ft[2]*fviewingrange*(-1.0/65536.0); - ytex.u = ft[0]*ytex.d; ytex.v = ft[1]*ytex.d; - otex.u = ft[0]*otex.d; otex.v = ft[1]*otex.d; - otex.u += (ft[2]-xtex.u)*ghalfx; - otex.v -= (ft[3]+xtex.v)*ghalfx; - xtex.v = -xtex.v; ytex.v = -ytex.v; otex.v = -otex.v; //y-flip skybox floor - - if ((sky_fy0 > nfy[0]) && (sky_fy1 > nfy[1])) - polymost_domost(sky_x0,sky_fy0,sky_x1,sky_fy1); - else if ((sky_fy0 > nfy[0]) != (sky_fy1 > nfy[1])) - { - //(ox,oy) is intersection of: (_x0,_fy0)-(_x1,_fy1) - // (_x0,nfy0)-(_x1,nfy1) - float const t = (sky_fy0-nfy[0])/(nfy[1]-nfy[0]-sky_fy1+sky_fy0); - vec2f_t const o = { sky_x0 + (sky_x1-sky_x0)*t, sky_fy0 + (sky_fy1-sky_fy0)*t }; - if (nfy[0] > sky_fy0) - { - polymost_domost(sky_x0,nfy[0],o.x,o.y); - polymost_domost(o.x,o.y,sky_x1,sky_fy1); - } - else - { - polymost_domost(sky_x0,sky_fy0,o.x,o.y); - polymost_domost(o.x,o.y,sky_x1,nfy[1]); - } - } - else - polymost_domost(sky_x0,nfy[0],sky_x1,nfy[1]); - - //wall of skybox - drawingskybox = i+1; //i+1th texture/index i of skybox - xtex.d = (sky_ryp0-sky_ryp1)*gxyaspect*(1.0/512.0) / (sky_ox0-sky_ox1); - ytex.d = 0; - otex.d = sky_ryp0*gxyaspect*(1.0/512.0) - xtex.d*sky_ox0; - xtex.u = (sky_t0*sky_ryp0 - sky_t1*sky_ryp1)*gxyaspect*(64.0/512.0) / (sky_ox0-sky_ox1); - otex.u = sky_t0*sky_ryp0*gxyaspect*(64.0/512.0) - xtex.u*sky_ox0; - ytex.u = 0; - sky_t0 = -8192.f*sky_ryp0 + ghoriz; - sky_t1 = -8192.f*sky_ryp1 + ghoriz; - float const t = ((xtex.d*sky_ox0 + otex.d)*8.f) / ((sky_ox1-sky_ox0) * sky_ryp0 * 2048.f); - xtex.v = (sky_t0-sky_t1)*t; - ytex.v = (sky_ox1-sky_ox0)*t; - otex.v = -xtex.v*sky_ox0 - ytex.v*sky_t0; - - if ((sky_cy0 > nfy[0]) && (sky_cy1 > nfy[1])) - polymost_domost(sky_x0,sky_cy0,sky_x1,sky_cy1); - else if ((sky_cy0 > nfy[0]) != (sky_cy1 > nfy[1])) - { - //(ox,oy) is intersection of: (_x0,_fy0)-(_x1,_fy1) - // (_x0,nfy0)-(_x1,nfy1) - float const t = (sky_cy0-nfy[0])/(nfy[1]-nfy[0]-sky_cy1+sky_cy0); - vec2f_t const o = { sky_x0 + (sky_x1 - sky_x0) * t, sky_cy0 + (sky_cy1 - sky_cy0) * t }; - if (nfy[0] > sky_cy0) - { - polymost_domost(sky_x0,nfy[0],o.x,o.y); - polymost_domost(o.x,o.y,sky_x1,sky_cy1); - } - else - { - polymost_domost(sky_x0,sky_cy0,o.x,o.y); - polymost_domost(o.x,o.y,sky_x1,nfy[1]); - } - } - else - polymost_domost(sky_x0,nfy[0],sky_x1,nfy[1]); - } - - //Ceiling of skybox - drawingskybox = 5; //ceiling/5th texture/index 4 of skybox - float const ft[4] = { 512 / 16, -512 / -16, fcosglobalang * (1.f / 2147483648.f), - fsinglobalang * (1.f / 2147483648.f) }; - - xtex.d = 0; - ytex.d = gxyaspect*(-1.0/4194304.0); - otex.d = -ghoriz*ytex.d; - xtex.u = ft[3]*fviewingrange*(-1.0/65536.0); - xtex.v = ft[2]*fviewingrange*(-1.0/65536.0); - ytex.u = ft[0]*ytex.d; ytex.v = ft[1]*ytex.d; - otex.u = ft[0]*otex.d; otex.v = ft[1]*otex.d; - otex.u += (ft[2]-xtex.u)*ghalfx; - otex.v -= (ft[3]+xtex.v)*ghalfx; - - polymost_domost(x0,fy0,x1,fy1); - - drawingskybox = 0; - } -#endif } @@ -1822,184 +1687,6 @@ static void polymost_drawalls(int32_t const bunch) flatskyrender = 0; ghoriz = ghorizbak; } -#if 0 - else - { - //Skybox code for parallax ceiling! - float sky_t0, sky_t1; // _nx0, _ny0, _nx1, _ny1; - float sky_ryp0, sky_ryp1, sky_x0, sky_x1, sky_cy0, sky_fy0, sky_cy1, sky_fy1, sky_ox0, sky_ox1; - static vec2f_t const skywal[4] = { { -512, -512 }, { 512, -512 }, { 512, 512 }, { -512, 512 } }; - - pow2xsplit = 0; - - for (bssize_t i=0; i<4; i++) - { - walpos = skywal[i&3]; - vec2f_t skyp0 = { walpos.y * gcosang - walpos.x * gsinang, - walpos.x * gcosang2 + walpos.y * gsinang2 }; - - walpos = skywal[(i + 1) & 3]; - vec2f_t skyp1 = { walpos.y * gcosang - walpos.x * gsinang, - walpos.x * gcosang2 + walpos.y * gsinang2 }; - - vec2f_t const oskyp0 = skyp0; - - //Clip to close parallel-screen plane - if (skyp0.y < SCISDIST) - { - if (skyp1.y < SCISDIST) continue; - sky_t0 = (SCISDIST - skyp0.y) / (skyp1.y - skyp0.y); - skyp0 = { (skyp1.x - skyp0.x) * sky_t0 + skyp0.x, SCISDIST }; - } - else { sky_t0 = 0.f; } - - if (skyp1.y < SCISDIST) - { - sky_t1 = (SCISDIST - oskyp0.y) / (skyp1.y - oskyp0.y); - skyp1 = { (skyp1.x - oskyp0.x) * sky_t1 + oskyp0.x, SCISDIST }; - } - else { sky_t1 = 1.f; } - - sky_ryp0 = 1.f/skyp0.y; sky_ryp1 = 1.f/skyp1.y; - - //Generate screen coordinates for front side of wall - sky_x0 = ghalfx*skyp0.x*sky_ryp0 + ghalfx; - sky_x1 = ghalfx*skyp1.x*sky_ryp1 + ghalfx; - if ((sky_x1 <= sky_x0) || (sky_x0 >= x1) || (x0 >= sky_x1)) continue; - - sky_ryp0 *= gyxscale; sky_ryp1 *= gyxscale; - - sky_cy0 = -8192.f*sky_ryp0 + ghoriz; - sky_fy0 = 8192.f*sky_ryp0 + ghoriz; - sky_cy1 = -8192.f*sky_ryp1 + ghoriz; - sky_fy1 = 8192.f*sky_ryp1 + ghoriz; - - sky_ox0 = sky_x0; sky_ox1 = sky_x1; - - //Make sure: x0<=_x0<_x1<=x1 - float ncy[2] = { cy0, cy1 }; - - if (sky_x0 < x0) - { - float const t = (x0-sky_x0)/(sky_x1-sky_x0); - sky_cy0 += (sky_cy1-sky_cy0)*t; - sky_fy0 += (sky_fy1-sky_fy0)*t; - sky_x0 = x0; - } - else if (sky_x0 > x0) ncy[0] += (sky_x0-x0)*(cy1-cy0)/(x1-x0); - - if (sky_x1 > x1) - { - float const t = (x1-sky_x1)/(sky_x1-sky_x0); - sky_cy1 += (sky_cy1-sky_cy0)*t; - sky_fy1 += (sky_fy1-sky_fy0)*t; - sky_x1 = x1; - } - else if (sky_x1 < x1) ncy[1] += (sky_x1-x1)*(cy1-cy0)/(x1-x0); - - // (skybox ceiling) - //(_x0,_cy0)-(_x1,_cy1) - // (skybox wall) - //(_x0,_fy0)-(_x1,_fy1) - // (skybox floor) - //(_x0,ncy0)-(_x1,ncy1) - - //ceiling of skybox - drawingskybox = 5; //ceiling/5th texture/index 4 of skybox - float const ft[4] = { 512 / 16, -512 / -16, fcosglobalang * (1.f / 2147483648.f), - fsinglobalang * (1.f / 2147483648.f) }; - - xtex.d = 0; - ytex.d = gxyaspect*(-1.0/4194304.0); - otex.d = -ghoriz*ytex.d; - xtex.u = ft[3]*fviewingrange*(-1.0/65536.0); - xtex.v = ft[2]*fviewingrange*(-1.0/65536.0); - ytex.u = ft[0]*ytex.d; ytex.v = ft[1]*ytex.d; - otex.u = ft[0]*otex.d; otex.v = ft[1]*otex.d; - otex.u += (ft[2]-xtex.u)*ghalfx; - otex.v -= (ft[3]+xtex.v)*ghalfx; - - - if ((sky_cy0 < ncy[0]) && (sky_cy1 < ncy[1])) - polymost_domost(sky_x1,sky_cy1,sky_x0,sky_cy0); - else if ((sky_cy0 < ncy[0]) != (sky_cy1 < ncy[1])) - { - //(ox,oy) is intersection of: (_x0,_cy0)-(_x1,_cy1) - // (_x0,ncy0)-(_x1,ncy1) - float const t = (sky_cy0-ncy[0])/(ncy[1]-ncy[0]-sky_cy1+sky_cy0); - vec2f_t const o = { sky_x0 + (sky_x1-sky_x0)*t, sky_cy0 + (sky_cy1-sky_cy0)*t }; - if (ncy[0] < sky_cy0) - { - polymost_domost(o.x,o.y,sky_x0,ncy[0]); - polymost_domost(sky_x1,sky_cy1,o.x,o.y); - } - else - { - polymost_domost(o.x,o.y,sky_x0,sky_cy0); - polymost_domost(sky_x1,ncy[1],o.x,o.y); - } - } - else - polymost_domost(sky_x1,ncy[1],sky_x0,ncy[0]); - - //wall of skybox - drawingskybox = i+1; //i+1th texture/index i of skybox - xtex.d = (sky_ryp0-sky_ryp1)*gxyaspect*(1.0/512.0) / (sky_ox0-sky_ox1); - ytex.d = 0; - otex.d = sky_ryp0*gxyaspect*(1.0/512.0) - xtex.d*sky_ox0; - xtex.u = (sky_t0*sky_ryp0 - sky_t1*sky_ryp1)*gxyaspect*(64.0/512.0) / (sky_ox0-sky_ox1); - otex.u = sky_t0*sky_ryp0*gxyaspect*(64.0/512.0) - xtex.u*sky_ox0; - ytex.u = 0; - sky_t0 = -8192.f*sky_ryp0 + ghoriz; - sky_t1 = -8192.f*sky_ryp1 + ghoriz; - float const t = ((xtex.d*sky_ox0 + otex.d)*8.f) / ((sky_ox1-sky_ox0) * sky_ryp0 * 2048.f); - xtex.v = (sky_t0-sky_t1)*t; - ytex.v = (sky_ox1-sky_ox0)*t; - otex.v = -xtex.v*sky_ox0 - ytex.v*sky_t0; - - if ((sky_fy0 < ncy[0]) && (sky_fy1 < ncy[1])) - polymost_domost(sky_x1,sky_fy1,sky_x0,sky_fy0); - else if ((sky_fy0 < ncy[0]) != (sky_fy1 < ncy[1])) - { - //(ox,oy) is intersection of: (_x0,_fy0)-(_x1,_fy1) - // (_x0,ncy0)-(_x1,ncy1) - float const t = (sky_fy0-ncy[0])/(ncy[1]-ncy[0]-sky_fy1+sky_fy0); - vec2f_t const o = { sky_x0 + (sky_x1 - sky_x0) * t, sky_fy0 + (sky_fy1 - sky_fy0) * t }; - if (ncy[0] < sky_fy0) - { - polymost_domost(o.x,o.y,sky_x0,ncy[0]); - polymost_domost(sky_x1,sky_fy1,o.x,o.y); - } - else - { - polymost_domost(o.x,o.y,sky_x0,sky_fy0); - polymost_domost(sky_x1,ncy[1],o.x,o.y); - } - } - else - polymost_domost(sky_x1,ncy[1],sky_x0,ncy[0]); - } - - //Floor of skybox - drawingskybox = 6; //floor/6th texture/index 5 of skybox - float const ft[4] = { 512 / 16, 512 / -16, fcosglobalang * (1.f / 2147483648.f), - fsinglobalang * (1.f / 2147483648.f) }; - - xtex.d = 0; - ytex.d = gxyaspect*(1.0/4194304.0); - otex.d = -ghoriz*ytex.d; - xtex.u = ft[3]*fviewingrange*(-1.0/65536.0); - xtex.v = ft[2]*fviewingrange*(-1.0/65536.0); - ytex.u = ft[0]*ytex.d; ytex.v = ft[1]*ytex.d; - otex.u = ft[0]*otex.d; otex.v = ft[1]*otex.d; - otex.u += (ft[2]-xtex.u)*ghalfx; - otex.v -= (ft[3]+xtex.v)*ghalfx; - xtex.v = -xtex.v; ytex.v = -ytex.v; otex.v = -otex.v; //y-flip skybox floor - polymost_domost(x1,cy1,x0,cy0); - - drawingskybox = 0; - } -#endif skyzbufferhack = 0; } @@ -2142,11 +1829,46 @@ static void polymost_drawalls(int32_t const bunch) domostpolymethod = DAMETH_NOMASK; if (nextsectnum >= 0) - if ((!(gotsector[nextsectnum>>3]&pow2char[nextsectnum&7])) && testvisiblemost(x0,x1)) + if (!gotsector[nextsectnum] && testvisiblemost(x0,x1)) polymost_scansector(nextsectnum); } } +// +// wallfront (internal) +// +int32_t wallfront(int32_t l1, int32_t l2) +{ + vec2_t const l1vect = wall[thewall[l1]].pos; + vec2_t const l1p2vect = wall[wall[thewall[l1]].point2].pos; + vec2_t const l2vect = wall[thewall[l2]].pos; + vec2_t const l2p2vect = wall[wall[thewall[l2]].point2].pos; + vec2_t d = { l1p2vect.x - l1vect.x, l1p2vect.y - l1vect.y }; + int32_t t1 = DMulScale(l2vect.x - l1vect.x, d.y, -d.x, l2vect.y - l1vect.y, 2); //p1(l2) vs. l1 + int32_t t2 = DMulScale(l2p2vect.x - l1vect.x, d.y, -d.x, l2p2vect.y - l1vect.y, 2); //p2(l2) vs. l1 + + if (t1 == 0) { if (t2 == 0) return -1; t1 = t2; } + if (t2 == 0) t2 = t1; + + if ((t1 ^ t2) >= 0) //pos vs. l1 + return (DMulScale(globalposx - l1vect.x, d.y, -d.x, globalposy - l1vect.y, 2) ^ t1) >= 0; + + d.x = l2p2vect.x - l2vect.x; + d.y = l2p2vect.y - l2vect.y; + + t1 = DMulScale(l1vect.x - l2vect.x, d.y, -d.x, l1vect.y - l2vect.y, 2); //p1(l1) vs. l2 + t2 = DMulScale(l1p2vect.x - l2vect.x, d.y, -d.x, l1p2vect.y - l2vect.y, 2); //p2(l1) vs. l2 + + if (t1 == 0) { if (t2 == 0) return -1; t1 = t2; } + if (t2 == 0) t2 = t1; + + if ((t1 ^ t2) >= 0) //pos vs. l2 + return (DMulScale(globalposx - l2vect.x, d.y, -d.x, globalposy - l2vect.y, 2) ^ t1) < 0; + + return -2; +} + + static int32_t polymost_bunchfront(const int32_t b1, const int32_t b2) { int b1f = bunchfirst[b1]; @@ -2204,12 +1926,12 @@ void polymost_scansector(int32_t sectnum) (r_voxels && tiletovox[spr->picnum] >= 0 && voxmodels[tiletovox[spr->picnum]]) || (r_voxels && gi->Voxelize(spr->picnum) > -1) || DMulScale(bcos(spr->ang), -s.x, bsin(spr->ang), -s.y, 6) > 0) - if (renderAddTsprite(z, sectnum)) + if (renderAddTsprite(pm_tsprite, pm_spritesortcnt, z, sectnum)) break; } } - gotsector[sectnum>>3] |= pow2char[sectnum&7]; + gotsector.Set(sectnum); int const bunchfrst = numbunches; int const onumscans = numscans; @@ -2218,7 +1940,7 @@ void polymost_scansector(int32_t sectnum) int scanfirst = numscans; - vec2d_t p2 = { 0, 0 }; + DVector2 p2 = { 0, 0 }; uwallptr_t wal; @@ -2226,37 +1948,37 @@ void polymost_scansector(int32_t sectnum) { auto const wal2 = (uwallptr_t)&wall[wal->point2]; - vec2d_t const fp1 = { double(wal->x - globalposx), double(wal->y - globalposy) }; - vec2d_t const fp2 = { double(wal2->x - globalposx), double(wal2->y - globalposy) }; + DVector2 const fp1 = { double(wal->x - globalposx), double(wal->y - globalposy) }; + DVector2 const fp2 = { double(wal2->x - globalposx), double(wal2->y - globalposy) }; int const nextsectnum = wal->nextsector; //Scan close sectors if (nextsectnum >= 0 && !(wal->cstat&32) && sectorbordercnt < countof(sectorborder)) - if ((gotsector[nextsectnum>>3]&pow2char[nextsectnum&7]) == 0) + if (gotsector[nextsectnum] == 0) { - double const d = fp1.x*fp2.y - fp2.x*fp1.y; - vec2d_t const p1 = { fp2.x-fp1.x, fp2.y-fp1.y }; + double const d = fp1.X* fp2.Y - fp2.X * fp1.Y; + DVector2 const p1 = fp2 - fp1; // this said (SCISDIST*SCISDIST*260.f), but SCISDIST is 1 and the significance of 260 isn't obvious to me // is 260 fudged to solve a problem, and does the problem still apply to our version of the renderer? - if (d*d < (p1.x*p1.x + p1.y*p1.y) * 256.f) + if (d*d < (p1.LengthSquared()) * 256.f) { sectorborder[sectorbordercnt++] = nextsectnum; - gotsector[nextsectnum>>3] |= pow2char[nextsectnum&7]; + gotsector.Set(nextsectnum); } } - vec2d_t p1; + DVector2 p1; if ((z == startwall) || (wall[z-1].point2 != z)) { - p1 = { (((fp1.y * fcosglobalang) - (fp1.x * fsinglobalang)) * (1.0/64.0)), - (((fp1.x * cosviewingrangeglobalang) + (fp1.y * sinviewingrangeglobalang)) * (1.0/64.0)) }; + p1 = { (((fp1.Y * fcosglobalang) - (fp1.X * fsinglobalang)) * (1.0/64.0)), + (((fp1.X * cosviewingrangeglobalang) + (fp1.Y * sinviewingrangeglobalang)) * (1.0/64.0)) }; } else { p1 = p2; } - p2 = { (((fp2.y * fcosglobalang) - (fp2.x * fsinglobalang)) * (1.0/64.0)), - (((fp2.x * cosviewingrangeglobalang) + (fp2.y * sinviewingrangeglobalang)) * (1.0/64.0)) }; + p2 = { (((fp2.Y * fcosglobalang) - (fp2.X * fsinglobalang)) * (1.0/64.0)), + (((fp2.X * cosviewingrangeglobalang) + (fp2.Y * sinviewingrangeglobalang)) * (1.0/64.0)) }; if (numscans >= MAXWALLSB-1) { @@ -2265,10 +1987,10 @@ void polymost_scansector(int32_t sectnum) } //if wall is facing you... - if ((p1.y >= SCISDIST || p2.y >= SCISDIST) && (nexttoward(p1.x*p2.y, p2.x*p1.y) < p2.x*p1.y)) + if ((p1.Y >= SCISDIST || p2.Y >= SCISDIST) && (nexttoward(p1.X*p2.Y, p2.X*p1.Y) < p2.X*p1.Y)) { - dxb1[numscans] = (p1.y >= SCISDIST) ? float(p1.x*ghalfx/p1.y + ghalfx) : -1e32f; - dxb2[numscans] = (p2.y >= SCISDIST) ? float(p2.x*ghalfx/p2.y + ghalfx) : 1e32f; + dxb1[numscans] = (p1.Y >= SCISDIST) ? float(p1.X*ghalfx/p1.Y + ghalfx) : -1e32f; + dxb2[numscans] = (p2.Y >= SCISDIST) ? float(p2.X*ghalfx/p2.Y + ghalfx) : 1e32f; if (dxb1[numscans] < xbl) dxb1[numscans] = xbl; @@ -2412,12 +2134,13 @@ void polymost_drawrooms() { polymost_outputGLDebugMessage(3, "polymost_drawrooms()"); - GLInterface.ClearDepth(); + GLInterface.ClearDepth(); GLInterface.EnableBlend(false); GLInterface.EnableAlphaTest(false); GLInterface.EnableDepthTest(true); GLInterface.SetDepthFunc(DF_LEqual); GLInterface.SetRenderStyle(LegacyRenderStyles[STYLE_Translucent]); + renderSetViewpoint(0, 0, 0); gvrcorrection = viewingrange*(1.f/65536.f); //if (glprojectionhacks == 2) @@ -2813,7 +2536,7 @@ void polymost_prepareMirror(int32_t dax, int32_t day, int32_t daz, fixed_t daang gvrcorrection = viewingrange*(1.f/65536.f); //if (glprojectionhacks == 2) { - // calculates the extend of the zenith glitch + // calculates the extent of the zenith glitch float verticalfovtan = (fviewingrange * (windowxy2.y-windowxy1.y) * 5.f) / ((float)yxaspect * (windowxy2.x-windowxy1.x) * 4.f); float verticalfov = atanf(verticalfovtan) * (2.f / pi::pi()); static constexpr float const maxhorizangle = 0.6361136f; // horiz of 199 in degrees @@ -2906,7 +2629,7 @@ void polymost_deletesprite(int num) static inline int32_t polymost_findwall(tspritetype const * const tspr, vec2_t const * const tsiz, int32_t * rd) { int32_t dist = 4, closest = -1; - auto const sect = (usectortype * )§or[tspr->sectnum]; + auto const sect = §or[tspr->sectnum]; vec2_t n; for (bssize_t i=sect->wallptr; iwallptr + sect->wallnum; i++) @@ -2987,8 +2710,6 @@ void polymost_drawsprite(int32_t snum) int32_t spritenum = tspr->owner; - polymost_outputGLDebugMessage(3, "polymost_drawsprite(snum:%d)", snum); - if ((tspr->cstat&48) != 48) tileUpdatePicnum(&tspr->picnum, spritenum + 32768); @@ -3004,9 +2725,9 @@ void polymost_drawsprite(int32_t snum) if ((globalorientation & 48) != 48) // only non-voxel sprites should do this { - int const flag = hw_hightile && TileFiles.tiledata[globalpicnum].h_xsize; - off = { (int32_t)tspr->xoffset + (flag ? TileFiles.tiledata[globalpicnum].h_xoffs : tileLeftOffset(globalpicnum)), - (int32_t)tspr->yoffset + (flag ? TileFiles.tiledata[globalpicnum].h_yoffs : tileTopOffset(globalpicnum)) }; + int const flag = hw_hightile && TileFiles.tiledata[globalpicnum].hiofs.xsize; + off = { (int32_t)tspr->xoffset + (flag ? TileFiles.tiledata[globalpicnum].hiofs.xoffs : tileLeftOffset(globalpicnum)), + (int32_t)tspr->yoffset + (flag ? TileFiles.tiledata[globalpicnum].hiofs.yoffs : tileTopOffset(globalpicnum)) }; } int32_t method = DAMETH_MASK | DAMETH_CLAMPED; @@ -3035,14 +2756,14 @@ void polymost_drawsprite(int32_t snum) if ((tspr->cstat & 48) != 48 && tiletovox[tspr->picnum] >= 0 && voxmodels[tiletovox[tspr->picnum]]) { int num = tiletovox[tspr->picnum]; - if (polymost_voxdraw(voxmodels[num], tspr, voxrotate[num>>3] & (1<<(num&7)))) return; + if (polymost_voxdraw(voxmodels[num], tspr, voxrotate[num])) return; break; // else, render as flat sprite } if ((tspr->cstat & 48) == 48 && tspr->picnum < MAXVOXELS && voxmodels[tspr->picnum]) { int num = tspr->picnum; - polymost_voxdraw(voxmodels[tspr->picnum], tspr, voxrotate[num >> 3] & (1 << (num & 7))); + polymost_voxdraw(voxmodels[tspr->picnum], tspr, voxrotate[num]); return; } } @@ -3066,8 +2787,8 @@ void polymost_drawsprite(int32_t snum) vec2_t tsiz; - if (hw_hightile && TileFiles.tiledata[globalpicnum].h_xsize) - tsiz = { TileFiles.tiledata[globalpicnum].h_xsize, TileFiles.tiledata[globalpicnum].h_ysize }; + if (hw_hightile && TileFiles.tiledata[globalpicnum].hiofs.xsize) + tsiz = { TileFiles.tiledata[globalpicnum].hiofs.xsize, TileFiles.tiledata[globalpicnum].hiofs.ysize }; else tsiz = { tileWidth(globalpicnum), tileHeight(globalpicnum) }; @@ -3240,10 +2961,10 @@ void polymost_drawsprite(int32_t snum) { int32_t const ang = getangle(wall[w].x - POINT2(w).x, wall[w].y - POINT2(w).y); float const foffs = TSPR_OFFSET(tspr); - vec2d_t const offs = { -bsinf(ang, -6) * foffs, bcosf(ang, -6) * foffs }; + DVector2 const offs = { -bsinf(ang, -6) * foffs, bcosf(ang, -6) * foffs }; - vec0.x -= offs.x; - vec0.y -= offs.y; + vec0.x -= offs.X; + vec0.y -= offs.Y; } } @@ -3540,34 +3261,688 @@ _drawsprite_return: ; } +////////////////////////////////// + static_assert((int)RS_YFLIP == (int)HUDFLAG_FLIPPED); -extern char* voxfilenames[MAXVOXELS]; -void (*PolymostProcessVoxels_Callback)(void) = NULL; -void PolymostProcessVoxels(void) -{ - if (PolymostProcessVoxels_Callback) - PolymostProcessVoxels_Callback(); +} - if (g_haveVoxels != 1) +// +// preparemirror +// +void renderPrepareMirror(int32_t dax, int32_t day, int32_t daz, fixed_t daang, fixed_t dahoriz, int16_t dawall, + int32_t* tposx, int32_t* tposy, fixed_t* tang) +{ + const int32_t x = wall[dawall].x, dx = wall[wall[dawall].point2].x - x; + const int32_t y = wall[dawall].y, dy = wall[wall[dawall].point2].y - y; + + const int32_t j = dx * dx + dy * dy; + if (j == 0) return; - g_haveVoxels = 2; + int i = ((dax - x) * dx + (day - y) * dy) << 1; - Printf(PRINT_NONOTIFY, "Generating voxel models for Polymost. This may take a while...\n"); + *tposx = (x << 1) + Scale(dx, i, j) - dax; + *tposy = (y << 1) + Scale(dy, i, j) - day; + *tang = ((bvectangbam(dx, dy).asq16() << 1) - daang) & 0x7FFFFFF; - for (bssize_t i = 0; i < MAXVOXELS; i++) + inpreparemirror = 1; + + Polymost::polymost_prepareMirror(dax, day, daz, daang, dahoriz, dawall); +} + + +// +// completemirror +// +void renderCompleteMirror(void) +{ + Polymost::polymost_completeMirror(); + inpreparemirror = 0; +} + +// +// drawrooms +// +EXTERN_CVAR(Int, gl_fogmode) + +int32_t renderDrawRoomsQ16(int32_t daposx, int32_t daposy, int32_t daposz, + fixed_t daang, fixed_t dahoriz, int16_t dacursectnum) +{ + pm_spritesortcnt = 0; + checkRotatedWalls(); + + if (gl_fogmode == 1) gl_fogmode = 2; // only radial fog works with Build's screwed up coordinate system. + + // Update starting sector number (common to classic and Polymost). + // ADJUST_GLOBALCURSECTNUM. + if (dacursectnum >= MAXSECTORS) + dacursectnum -= MAXSECTORS; + else { - if (voxfilenames[i]) - { - int lumpnum = fileSystem.FindFile(voxfilenames[i]); - if (lumpnum >= 0) - { - voxmodels[i] = voxload(lumpnum); - voxmodels[i]->scale = voxscale[i] * (1.f / 65536.f); - } - DO_FREE_AND_NULL(voxfilenames[i]); - } + int i = dacursectnum; + updatesector(daposx, daposy, &dacursectnum); + if (dacursectnum < 0) dacursectnum = i; + + // PK 20110123: I'm not sure what the line above is supposed to do, but 'i' + // *can* be negative, so let's just quit here in that case... + if (dacursectnum < 0) + return 0; + } + + set_globalpos(daposx, daposy, daposz); + Polymost::set_globalang(daang); + + global100horiz = dahoriz; + + gotsector.Zero(); + qglobalhoriz = MulScale(dahoriz, DivScale(xdimenscale, viewingrange, 16), 16) + IntToFixed(ydimen >> 1); + globalcursectnum = dacursectnum; + Polymost::polymost_drawrooms(); + return inpreparemirror; +} + +// UTILITY TYPES AND FUNCTIONS FOR DRAWMASKS OCCLUSION TREE +// typedef struct s_maskleaf +// { +// int32_t index; +// _point2d p1, p2; +// _equation maskeq, p1eq, p2eq; +// struct s_maskleaf* branch[MAXWALLSB]; +// int32_t drawing; +// } _maskleaf; +// +// _maskleaf maskleaves[MAXWALLSB]; + +// returns equation of a line given two points +static inline _equation equation(float const x1, float const y1, float const x2, float const y2) +{ + const float f = x2 - x1; + + // vertical + if (f == 0.f) + return { 1, 0, -x1 }; + else + { + float const ff = (y2 - y1) / f; + return { ff, -1, (y1 - (ff * x1)) }; } } + +static inline int32_t sameside(const _equation* eq, const vec2f_t* p1, const vec2f_t* p2) +{ + const float sign1 = (eq->a * p1->x) + (eq->b * p1->y) + eq->c; + const float sign2 = (eq->a * p2->x) + (eq->b * p2->y) + eq->c; + return (sign1 * sign2) > 0.f; +} + + +static inline int comparetsprites(int const k, int const l) +{ + if ((tspriteptr[k]->cstat & 48) != (tspriteptr[l]->cstat & 48)) + return (tspriteptr[k]->cstat & 48) - (tspriteptr[l]->cstat & 48); + + if ((tspriteptr[k]->cstat & 48) == 16 && tspriteptr[k]->ang != tspriteptr[l]->ang) + return tspriteptr[k]->ang - tspriteptr[l]->ang; + + if (tspriteptr[k]->statnum != tspriteptr[l]->statnum) + return tspriteptr[k]->statnum - tspriteptr[l]->statnum; + + if (tspriteptr[k]->x == tspriteptr[l]->x && + tspriteptr[k]->y == tspriteptr[l]->y && + tspriteptr[k]->z == tspriteptr[l]->z && + (tspriteptr[k]->cstat & 48) == (tspriteptr[l]->cstat & 48) && + tspriteptr[k]->owner != tspriteptr[l]->owner) + return tspriteptr[k]->owner - tspriteptr[l]->owner; + + if (abs(spritesxyz[k].z - globalposz) != abs(spritesxyz[l].z - globalposz)) + return abs(spritesxyz[k].z - globalposz) - abs(spritesxyz[l].z - globalposz); + + return 0; +} + +static void sortsprites(int const start, int const end) +{ + int32_t i, gap, y, ys; + + if (start >= end) + return; + + gap = 1; while (gap < end - start) gap = (gap << 1) + 1; + for (gap >>= 1; gap > 0; gap >>= 1) //Sort sprite list + for (i = start; i < end - gap; i++) + for (bssize_t l = i; l >= start; l -= gap) + { + if (spritesxyz[l].y <= spritesxyz[l + gap].y) break; + std::swap(tspriteptr[l], tspriteptr[l + gap]); + std::swap(spritesxyz[l].x, spritesxyz[l + gap].x); + std::swap(spritesxyz[l].y, spritesxyz[l + gap].y); + } + + ys = spritesxyz[start].y; i = start; + for (bssize_t j = start + 1; j <= end; j++) + { + if (j < end) + { + y = spritesxyz[j].y; + if (y == ys) + continue; + + ys = y; + } + + if (j > i + 1) + { + for (bssize_t k = i; k < j; k++) + { + auto const s = tspriteptr[k]; + + spritesxyz[k].z = s->z; + if ((s->cstat & 48) != 32) + { + int32_t yoff = tileTopOffset(s->picnum) + s->yoffset; + int32_t yspan = (tileHeight(s->picnum) * s->yrepeat << 2); + + spritesxyz[k].z -= (yoff * s->yrepeat) << 2; + + if (!(s->cstat & 128)) + spritesxyz[k].z -= (yspan >> 1); + if (abs(spritesxyz[k].z - globalposz) < (yspan >> 1)) + spritesxyz[k].z = globalposz; + } + } + + for (bssize_t k = i + 1; k < j; k++) + for (bssize_t l = i; l < k; l++) + if (comparetsprites(k, l) < 0) + { + std::swap(tspriteptr[k], tspriteptr[l]); + vec3_t tv3 = spritesxyz[k]; + spritesxyz[k] = spritesxyz[l]; + spritesxyz[l] = tv3; + } + } + i = j; + } +} + +static bool spriteIsModelOrVoxel(const spritetype* tspr) +{ + if ((unsigned)tspr->owner < MAXSPRITES && spriteext[tspr->owner].flags & SPREXT_NOTMD) + return false; + + if (hw_models) + { + auto& mdinfo = tile2model[Ptile2tile(tspr->picnum, tspr->pal)]; + if (mdinfo.modelid >= 0 && mdinfo.framenum >= 0) return true; + } + + auto slabalign = (tspr->cstat & CSTAT_SPRITE_ALIGNMENT) == CSTAT_SPRITE_ALIGNMENT_SLAB; + if (r_voxels && !slabalign && tiletovox[tspr->picnum] >= 0 && voxmodels[tiletovox[tspr->picnum]]) return true; + return (slabalign && voxmodels[tspr->picnum]); +} + + +// +// drawmasks +// +void renderDrawMasks(void) +{ +# define debugmask_add(dispidx, idx) do {} while (0) + int32_t i = pm_spritesortcnt - 1; + int32_t numSprites = pm_spritesortcnt; + + pm_spritesortcnt = 0; + int32_t back = i; + for (; i >= 0; --i) + { + if (Polymost::polymost_spriteHasTranslucency(&pm_tsprite[i])) + { + tspriteptr[pm_spritesortcnt] = &pm_tsprite[i]; + ++pm_spritesortcnt; + } + else + { + tspriteptr[back] = &pm_tsprite[i]; + --back; + } + } + + for (i = numSprites - 1; i >= 0; --i) + { + const int32_t xs = tspriteptr[i]->x - globalposx, ys = tspriteptr[i]->y - globalposy; + const int32_t yp = DMulScale(xs, cosviewingrangeglobalang, ys, sinviewingrangeglobalang, 6); + const int32_t modelp = spriteIsModelOrVoxel(tspriteptr[i]); + + if (yp > (4 << 8)) + { + const int32_t xp = DMulScale(ys, cosglobalang, -xs, singlobalang, 6); + + if (MulScale(labs(xp + yp), xdimen, 24) >= yp) + goto killsprite; + + spritesxyz[i].x = Scale(xp + yp, xdimen << 7, yp); + } + else if ((tspriteptr[i]->cstat & 48) == 0) + { + killsprite: + if (!modelp) + { + //Delete face sprite if on wrong side! + if (i >= pm_spritesortcnt) + { + --numSprites; + if (i != numSprites) + { + tspriteptr[i] = tspriteptr[numSprites]; + spritesxyz[i].x = spritesxyz[numSprites].x; + spritesxyz[i].y = spritesxyz[numSprites].y; + } + } + else + { + --numSprites; + --pm_spritesortcnt; + if (i != numSprites) + { + tspriteptr[i] = tspriteptr[pm_spritesortcnt]; + spritesxyz[i].x = spritesxyz[pm_spritesortcnt].x; + spritesxyz[i].y = spritesxyz[pm_spritesortcnt].y; + tspriteptr[pm_spritesortcnt] = tspriteptr[numSprites]; + spritesxyz[pm_spritesortcnt].x = spritesxyz[numSprites].x; + spritesxyz[pm_spritesortcnt].y = spritesxyz[numSprites].y; + } + } + continue; + } + } + spritesxyz[i].y = yp; + } + + sortsprites(0, pm_spritesortcnt); + sortsprites(pm_spritesortcnt, numSprites); + renderBeginScene(); + + GLInterface.EnableBlend(false); + GLInterface.EnableAlphaTest(true); + GLInterface.SetDepthBias(-2, -256); + + if (pm_spritesortcnt < numSprites) + { + i = pm_spritesortcnt; + for (bssize_t i = pm_spritesortcnt; i < numSprites;) + { + int32_t py = spritesxyz[i].y; + int32_t pcstat = tspriteptr[i]->cstat & 48; + int32_t pangle = tspriteptr[i]->ang; + int j = i + 1; + if (!spriteIsModelOrVoxel(tspriteptr[i])) + { + while (j < numSprites && py == spritesxyz[j].y && pcstat == (tspriteptr[j]->cstat & 48) && (pcstat != 16 || pangle == tspriteptr[j]->ang) + && !spriteIsModelOrVoxel(tspriteptr[j])) + { + j++; + } + } + if (j - i == 1) + { + debugmask_add(i | 32768, tspriteptr[i]->owner); + Polymost::polymost_drawsprite(i); + tspriteptr[i] = NULL; + } + else + { + GLInterface.SetDepthMask(false); + + for (bssize_t k = j - 1; k >= i; k--) + { + debugmask_add(k | 32768, tspriteptr[k]->owner); + Polymost::polymost_drawsprite(k); + } + + GLInterface.SetDepthMask(true); + + GLInterface.SetColorMask(false); + + for (bssize_t k = j - 1; k >= i; k--) + { + Polymost::polymost_drawsprite(k); + tspriteptr[k] = NULL; + } + + GLInterface.SetColorMask(true); + + } + i = j; + } + } + + int32_t numMaskWalls = maskwallcnt; + maskwallcnt = 0; + for (i = 0; i < numMaskWalls; i++) + { + if (Polymost::polymost_maskWallHasTranslucency(&wall[thewall[maskwall[i]]])) + { + maskwall[maskwallcnt] = maskwall[i]; + maskwallcnt++; + } + else + Polymost::polymost_drawmaskwall(i); + } + + GLInterface.EnableBlend(true); + GLInterface.EnableAlphaTest(true); + GLInterface.SetDepthMask(false); + + vec2f_t pos; + + pos.x = fglobalposx; + pos.y = fglobalposy; + + // CAUTION: maskwallcnt and spritesortcnt may be zero! + // Writing e.g. "while (maskwallcnt--)" is wrong! + while (maskwallcnt) + { + // PLAG: sorting stuff + const int32_t w = thewall[maskwall[maskwallcnt - 1]]; + + maskwallcnt--; + + vec2f_t dot = { (float)wall[w].x, (float)wall[w].y }; + vec2f_t dot2 = { (float)wall[wall[w].point2].x, (float)wall[wall[w].point2].y }; + vec2f_t middle = { (dot.x + dot2.x) * .5f, (dot.y + dot2.y) * .5f }; + + _equation maskeq = equation(dot.x, dot.y, dot2.x, dot2.y); + _equation p1eq = equation(pos.x, pos.y, dot.x, dot.y); + _equation p2eq = equation(pos.x, pos.y, dot2.x, dot2.y); + + i = pm_spritesortcnt; + while (i) + { + i--; + if (tspriteptr[i] != NULL) + { + vec2f_t spr; + auto const tspr = tspriteptr[i]; + + spr.x = (float)tspr->x; + spr.y = (float)tspr->y; + + if (!sameside(&maskeq, &spr, &pos)) + { + // Sprite and camera are on different sides of the + // masked wall. + + // Check if the sprite is inside the 'cone' given by + // the rays from the camera to the two wall-points. + const int32_t inleft = sameside(&p1eq, &middle, &spr); + const int32_t inright = sameside(&p2eq, &middle, &spr); + + int32_t ok = (inleft && inright); + + if (!ok) + { + // If not, check if any of the border points are... + vec2_t pp[4]; + int32_t numpts, jj; + + const _equation pineq = inleft ? p1eq : p2eq; + + if ((tspr->cstat & 48) == 32) + { + numpts = 4; + GetFlatSpritePosition(tspr, tspr->pos.vec2, pp); + } + else + { + const int32_t oang = tspr->ang; + numpts = 2; + + // Consider face sprites as wall sprites with camera ang. + // XXX: factor 4/5 needed? + if ((tspr->cstat & 48) != 16) + tspriteptr[i]->ang = globalang; + + GetWallSpritePosition(tspr, tspr->pos.vec2, pp); + + if ((tspr->cstat & 48) != 16) + tspriteptr[i]->ang = oang; + } + + for (jj = 0; jj < numpts; jj++) + { + spr.x = (float)pp[jj].x; + spr.y = (float)pp[jj].y; + + if (!sameside(&maskeq, &spr, &pos)) // behind the maskwall, + if ((sameside(&p1eq, &middle, &spr) && // inside the 'cone', + sameside(&p2eq, &middle, &spr)) + || !sameside(&pineq, &middle, &spr)) // or on the other outside. + { + ok = 1; + break; + } + } + } + + if (ok) + { + debugmask_add(i | 32768, tspr->owner); + Polymost::polymost_drawsprite(i); + + tspriteptr[i] = NULL; + } + } + } + } + + debugmask_add(maskwall[maskwallcnt], thewall[maskwall[maskwallcnt]]); + Polymost::polymost_drawmaskwall(maskwallcnt); + } + + while (pm_spritesortcnt) + { + --pm_spritesortcnt; + if (tspriteptr[pm_spritesortcnt] != NULL) + { + debugmask_add(i | 32768, tspriteptr[i]->owner); + Polymost::polymost_drawsprite(pm_spritesortcnt); + tspriteptr[pm_spritesortcnt] = NULL; + } + } + renderFinishScene(); + GLInterface.SetDepthMask(true); + GLInterface.SetDepthBias(0, 0); +} + + +// +// setrollangle +// +void renderSetRollAngle(float rolla) +{ + Polymost::gtang = rolla * BAngRadian; +} + +void videoSetCorrectedAspect() +{ + // In DOS the game world is displayed with an aspect of 1.28 instead 1.333, + // meaning we have to stretch it by a factor of 1.25 instead of 1.2 + // to get perfect squares + int32_t yx = (65536 * 5) / 4; + int32_t vr, y, x; + + x = xdim; + y = ydim; + + vr = DivScale(x * 3, y * 4, 16); + + renderSetAspect(vr, yx); +} + + +// +// setaspect +// +void renderSetAspect(int32_t daxrange, int32_t daaspect) +{ + if (daxrange == 65536) daxrange--; // This doesn't work correctly with 65536. All other values are fine. No idea where this is evaluated wrong. + viewingrange = daxrange; + viewingrangerecip = DivScale(1, daxrange, 32); + fviewingrange = (float)daxrange; + + yxaspect = daaspect; + xyaspect = DivScale(1, yxaspect, 32); + xdimenscale = Scale(xdimen, yxaspect, 320); + xdimscale = Scale(320, xyaspect, xdimen); +} + +//Draw voxel model as perfect cubes +int32_t polymost_voxdraw(voxmodel_t* m, tspriteptr_t const tspr, bool rotate) +{ + float f, g, k0, zoff; + + if ((intptr_t)m == (intptr_t)(-1)) // hackhackhack + return 0; + + if ((tspr->cstat & 48) == 32) + return 0; + + if ((tspr->cstat & CSTAT_SPRITE_MDLROTATE) || rotate) + { + int myclock = (PlayClock << 3) + MulScale(4 << 3, pm_smoothratio, 16); + tspr->ang = (tspr->ang + myclock) & 2047; // will be applied in md3_vox_calcmat_common. + } + + + vec3f_t m0 = { m->scale, m->scale, m->scale }; + vec3f_t a0 = { 0, 0, m->zadd * m->scale }; + + k0 = m->bscale / 64.f; + f = (float)tspr->xrepeat * (256.f / 320.f) * k0; + if ((sprite[tspr->owner].cstat & 48) == 16) + { + f *= 1.25f; + a0.y -= tspr->xoffset * bcosf(spriteext[tspr->owner].angoff, -20); + a0.x += tspr->xoffset * bsinf(spriteext[tspr->owner].angoff, -20); + } + + if (globalorientation & 8) { m0.z = -m0.z; a0.z = -a0.z; } //y-flipping + if (globalorientation & 4) { m0.x = -m0.x; a0.x = -a0.x; a0.y = -a0.y; } //x-flipping + + m0.x *= f; a0.x *= f; f = -f; + m0.y *= f; a0.y *= f; + f = (float)tspr->yrepeat * k0; + m0.z *= f; a0.z *= f; + + k0 = (float)(tspr->z + spriteext[tspr->owner].position_offset.z); + f = ((globalorientation & 8) && (sprite[tspr->owner].cstat & 48) != 0) ? -4.f : 4.f; + k0 -= (tspr->yoffset * tspr->yrepeat) * f * m->bscale; + zoff = m->siz.z * .5f; + if (!(tspr->cstat & 128)) + zoff += m->piv.z; + else if ((tspr->cstat & 48) != 48) + { + zoff += m->piv.z; + zoff -= m->siz.z * .5f; + } + if (globalorientation & 8) zoff = m->siz.z - zoff; + + f = (65536.f * 512.f) / ((float)xdimen * viewingrange); + g = 32.f / ((float)xdimen * Polymost::gxyaspect); + + int const shadowHack = !!(tspr->clipdist & TSPR_FLAGS_MDHACK); + + m0.y *= f; a0.y = (((float)(tspr->x + spriteext[tspr->owner].position_offset.x - globalposx)) * (1.f / 1024.f) + a0.y) * f; + m0.x *= -f; a0.x = (((float)(tspr->y + spriteext[tspr->owner].position_offset.y - globalposy)) * -(1.f / 1024.f) + a0.x) * -f; + m0.z *= g; a0.z = (((float)(k0 - globalposz - shadowHack)) * -(1.f / 16384.f) + a0.z) * g; + + float mat[16]; + md3_vox_calcmat_common(tspr, &a0, f, mat); + + //Mirrors + if (Polymost::grhalfxdown10x < 0) + { + mat[0] = -mat[0]; + mat[4] = -mat[4]; + mat[8] = -mat[8]; + mat[12] = -mat[12]; + } + + if (shadowHack) + { + GLInterface.SetDepthFunc(DF_LEqual); + } + + + int winding = ((Polymost::grhalfxdown10x >= 0) ^ ((globalorientation & 8) != 0) ^ ((globalorientation & 4) != 0)) ? Winding_CW : Winding_CCW; + GLInterface.SetCull(Cull_Back, winding); + + float pc[4]; + + pc[0] = pc[1] = pc[2] = 1.f; + + + if (!shadowHack) + { + pc[3] = (tspr->cstat & 2) ? glblend[tspr->blend].def[!!(tspr->cstat & 512)].alpha : 1.0f; + pc[3] *= 1.0f - spriteext[tspr->owner].alpha; + + SetRenderStyleFromBlend(!!(tspr->cstat & 2), tspr->blend, !!(tspr->cstat & 512)); + + if (!(tspr->cstat & 2) || spriteext[tspr->owner].alpha > 0.f || pc[3] < 1.0f) + GLInterface.EnableBlend(true); // else GLInterface.EnableBlend(false); + } + else pc[3] = 1.f; + GLInterface.SetShade(std::max(0, globalshade), numshades); + //------------ + + //transform to Build coords + float omat[16]; + memcpy(omat, mat, sizeof(omat)); + + f = 1.f / 64.f; + g = m0.x * f; mat[0] *= g; mat[1] *= g; mat[2] *= g; + g = m0.y * f; mat[4] = omat[8] * g; mat[5] = omat[9] * g; mat[6] = omat[10] * g; + g = -m0.z * f; mat[8] = omat[4] * g; mat[9] = omat[5] * g; mat[10] = omat[6] * g; + // + mat[12] -= (m->piv.x * mat[0] + m->piv.y * mat[4] + zoff * mat[8]); + mat[13] -= (m->piv.x * mat[1] + m->piv.y * mat[5] + zoff * mat[9]); + mat[14] -= (m->piv.x * mat[2] + m->piv.y * mat[6] + zoff * mat[10]); + // + //Let OpenGL (and perhaps hardware :) handle the matrix rotation + mat[3] = mat[7] = mat[11] = 0.f; mat[15] = 1.f; + + for (int i = 0; i < 15; i++) mat[i] *= 1024.f; + + // Adjust to backend coordinate system being used by the vertex buffer. + for (int i = 4; i < 8; i++) + { + float f = mat[i]; + mat[i] = -mat[i + 4]; + mat[i + 4] = -f; + } + + GLInterface.SetMatrix(Matrix_Model, mat); + + int palId = TRANSLATION(Translation_Remap + curbasepal, globalpal); + GLInterface.SetPalswap(globalpal); + GLInterface.SetFade(sector[tspr->sectnum].floorpal); + + auto tex = TexMan.GetGameTexture(m->model->GetPaletteTexture()); + GLInterface.SetTexture(tex, TRANSLATION(Translation_Remap + curbasepal, globalpal), CLAMP_NOFILTER_XY, true); + GLInterface.SetModel(m->model, 0, 0, 0); + GLInterface.Draw(DT_Triangles, 0, 0); + GLInterface.SetModel(nullptr, 0, 0, 0); + GLInterface.SetCull(Cull_None); + + if (shadowHack) + { + GLInterface.SetDepthFunc(DF_Less); + } + GLInterface.SetIdentityMatrix(Matrix_Model); + return 1; +} + + diff --git a/source/build/src/voxmodel.cpp b/source/build/src/voxmodel.cpp deleted file mode 100644 index 64bf05abb..000000000 --- a/source/build/src/voxmodel.cpp +++ /dev/null @@ -1,209 +0,0 @@ -//--------------------------------------- VOX LIBRARY BEGINS --------------------------------------- - -#ifdef USE_OPENGL - -#include "compat.h" -#include "build.h" -#include "engine_priv.h" -#include "polymost.h" -#include "mdsprite.h" -#include "v_video.h" -#include "flatvertices.h" -#include "hw_renderstate.h" -#include "texturemanager.h" -#include "voxels.h" -#include "gamecontrol.h" -#include "glbackend/gl_models.h" - -#include "palette.h" -#include "../../glbackend/glbackend.h" - -void voxfree(voxmodel_t *m) -{ - if (!m) - return; - - Xfree(m); -} - -voxmodel_t *voxload(int lumpnum) -{ - FVoxel* voxel = R_LoadKVX(lumpnum); - if (voxel != nullptr) - { - voxmodel_t* vm = (voxmodel_t*)Xmalloc(sizeof(voxmodel_t)); - memset(vm, 0, sizeof(voxmodel_t)); - auto pivot = voxel->Mips[0].Pivot; - vm->mdnum = 1; //VOXel model id - vm->scale = vm->bscale = 1.f; - vm->piv.x = float(pivot.X); - vm->piv.y = float(pivot.Y); - vm->piv.z = float(pivot.Z); - vm->siz.x = voxel->Mips[0].SizeX; - vm->siz.y = voxel->Mips[0].SizeY; - vm->siz.z = voxel->Mips[0].SizeZ; - vm->is8bit = true; - voxel->Mips[0].Pivot.Zero(); // Needs to be taken out of the voxel data because it gets baked into the vertex buffer which we cannot use here. - vm->model = new FVoxelModel(voxel, true); - return vm; - } - return nullptr; -} - -//Draw voxel model as perfect cubes -int32_t polymost_voxdraw(voxmodel_t* m, tspriteptr_t const tspr, bool rotate) -{ - float f, g, k0, zoff; - - if ((intptr_t)m == (intptr_t)(-1)) // hackhackhack - return 0; - - if ((tspr->cstat & 48) == 32) - return 0; - - polymost_outputGLDebugMessage(3, "polymost_voxdraw(m:%p, tspr:%p)", m, tspr); - - //updateanimation((md2model *)m,tspr); - - if ((tspr->cstat & CSTAT_SPRITE_MDLROTATE) || rotate) - { - int myclock = (PlayClock << 3) + MulScale(4 << 3, pm_smoothratio, 16); - tspr->ang = (tspr->ang + myclock) & 2047; // will be applied in md3_vox_calcmat_common. - } - - - vec3f_t m0 = { m->scale, m->scale, m->scale }; - vec3f_t a0 = { 0, 0, m->zadd*m->scale }; - - k0 = m->bscale / 64.f; - f = (float) tspr->xrepeat * (256.f/320.f) * k0; - if ((sprite[tspr->owner].cstat&48)==16) - { - f *= 1.25f; - a0.y -= tspr->xoffset * bcosf(spriteext[tspr->owner].angoff, -20); - a0.x += tspr->xoffset * bsinf(spriteext[tspr->owner].angoff, -20); - } - - if (globalorientation&8) { m0.z = -m0.z; a0.z = -a0.z; } //y-flipping - if (globalorientation&4) { m0.x = -m0.x; a0.x = -a0.x; a0.y = -a0.y; } //x-flipping - - m0.x *= f; a0.x *= f; f = -f; - m0.y *= f; a0.y *= f; - f = (float) tspr->yrepeat * k0; - m0.z *= f; a0.z *= f; - - k0 = (float) (tspr->z+spriteext[tspr->owner].position_offset.z); - f = ((globalorientation&8) && (sprite[tspr->owner].cstat&48)!=0) ? -4.f : 4.f; - k0 -= (tspr->yoffset*tspr->yrepeat)*f*m->bscale; - zoff = m->siz.z*.5f; - if (!(tspr->cstat&128)) - zoff += m->piv.z; - else if ((tspr->cstat&48) != 48) - { - zoff += m->piv.z; - zoff -= m->siz.z*.5f; - } - if (globalorientation&8) zoff = m->siz.z-zoff; - - f = (65536.f*512.f) / ((float)xdimen*viewingrange); - g = 32.f / ((float)xdimen*gxyaspect); - - int const shadowHack = !!(tspr->clipdist & TSPR_FLAGS_MDHACK); - - m0.y *= f; a0.y = (((float)(tspr->x+spriteext[tspr->owner].position_offset.x-globalposx)) * (1.f/1024.f) + a0.y) * f; - m0.x *=-f; a0.x = (((float)(tspr->y+spriteext[tspr->owner].position_offset.y-globalposy)) * -(1.f/1024.f) + a0.x) * -f; - m0.z *= g; a0.z = (((float)(k0 -globalposz - shadowHack)) * -(1.f/16384.f) + a0.z) * g; - - float mat[16]; - md3_vox_calcmat_common(tspr, &a0, f, mat); - - //Mirrors - if (grhalfxdown10x < 0) - { - mat[0] = -mat[0]; - mat[4] = -mat[4]; - mat[8] = -mat[8]; - mat[12] = -mat[12]; - } - - if (shadowHack) - { - GLInterface.SetDepthFunc(DF_LEqual); - } - - - int winding = ((grhalfxdown10x >= 0) ^ ((globalorientation & 8) != 0) ^ ((globalorientation & 4) != 0)) ? Winding_CW : Winding_CCW; - GLInterface.SetCull(Cull_Back, winding); - - float pc[4]; - - if (!shadowHack) - { - pc[3] = (tspr->cstat & 2) ? glblend[tspr->blend].def[!!(tspr->cstat & 512)].alpha : 1.0f; - pc[3] *= 1.0f - spriteext[tspr->owner].alpha; - - SetRenderStyleFromBlend(!!(tspr->cstat & 2), tspr->blend, !!(tspr->cstat & 512)); - - if (!(tspr->cstat & 2) || spriteext[tspr->owner].alpha > 0.f || pc[3] < 1.0f) - GLInterface.EnableBlend(true); // else GLInterface.EnableBlend(false); - } - else pc[3] = 1.f; - GLInterface.SetShade(std::max(0, globalshade), numshades); - - pc[0] = (float)globalr * (1.f / 255.f); - pc[1] = (float)globalg * (1.f / 255.f); - pc[2] = (float)globalb * (1.f / 255.f); - GLInterface.SetColor(pc[0], pc[1], pc[2], pc[3]); - - //------------ - - //transform to Build coords - float omat[16]; - memcpy(omat, mat, sizeof(omat)); - - f = 1.f/64.f; - g = m0.x*f; mat[0] *= g; mat[1] *= g; mat[2] *= g; - g = m0.y*f; mat[4] = omat[8]*g; mat[5] = omat[9]*g; mat[6] = omat[10]*g; - g =-m0.z*f; mat[8] = omat[4]*g; mat[9] = omat[5]*g; mat[10] = omat[6]*g; - // - mat[12] -= (m->piv.x*mat[0] + m->piv.y*mat[4] + zoff*mat[8]); - mat[13] -= (m->piv.x*mat[1] + m->piv.y*mat[5] + zoff*mat[9]); - mat[14] -= (m->piv.x*mat[2] + m->piv.y*mat[6] + zoff*mat[10]); - // - //Let OpenGL (and perhaps hardware :) handle the matrix rotation - mat[3] = mat[7] = mat[11] = 0.f; mat[15] = 1.f; - - for (int i = 0; i < 15; i++) mat[i] *= 1024.f; - - // Adjust to backend coordinate system being used by the vertex buffer. - for (int i = 4; i < 8; i++) - { - float f = mat[i]; - mat[i] = -mat[i + 4]; - mat[i + 4] = -f; - } - - GLInterface.SetMatrix(Matrix_Model, mat); - - int palId = TRANSLATION(Translation_Remap + curbasepal, globalpal); - GLInterface.SetPalswap(globalpal); - GLInterface.SetFade(sector[tspr->sectnum].floorpal); - - auto tex = TexMan.GetGameTexture(m->model->GetPaletteTexture()); - GLInterface.SetTexture(tex, TRANSLATION(Translation_Remap + curbasepal, globalpal), CLAMP_NOFILTER_XY, true); - GLInterface.SetModel(m->model, 0, 0, 0); - GLInterface.Draw(DT_Triangles, 0, 0); - GLInterface.SetModel(nullptr, 0, 0, 0); - GLInterface.SetCull(Cull_None); - - if (shadowHack) - { - GLInterface.SetDepthFunc(DF_Less); - } - GLInterface.SetIdentityMatrix(Matrix_Model); - GLInterface.SetFadeDisable(false); - return 1; -} -#endif - -//---------------------------------------- VOX LIBRARY ENDS ---------------------------------------- diff --git a/source/common/2d/v_2ddrawer.cpp b/source/common/2d/v_2ddrawer.cpp index 1a14683e7..2d1387f02 100644 --- a/source/common/2d/v_2ddrawer.cpp +++ b/source/common/2d/v_2ddrawer.cpp @@ -739,12 +739,12 @@ void F2DDrawer::AddPoly(FGameTexture *texture, FVector2 *points, int npoints, // //========================================================================== -void F2DDrawer::AddPoly(FGameTexture* img, FVector4* vt, size_t vtcount, unsigned int* ind, size_t idxcount, int translation, PalEntry color, FRenderStyle style, int clipx1, int clipy1, int clipx2, int clipy2) +void F2DDrawer::AddPoly(FGameTexture* img, FVector4* vt, size_t vtcount, const unsigned int* ind, size_t idxcount, int translation, PalEntry color, FRenderStyle style, int clipx1, int clipy1, int clipx2, int clipy2) { RenderCommand dg; int method = 0; - if (!img->isValid()) return; + if (!img || !img->isValid()) return; dg.mType = DrawTypeTriangles; if (clipx1 > 0 || clipy1 > 0 || clipx2 < GetWidth() - 1 || clipy2 < GetHeight() - 1) @@ -770,14 +770,28 @@ void F2DDrawer::AddPoly(FGameTexture* img, FVector4* vt, size_t vtcount, unsigne Set(ptr, vt[i].X, vt[i].Y, 0.f, vt[i].Z, vt[i].W, color); ptr++; } - dg.mIndexIndex = mIndices.Size(); - mIndices.Reserve(idxcount); - for (size_t i = 0; i < idxcount; i++) + + if (idxcount > 0) { - mIndices[dg.mIndexIndex + i] = ind[i] + dg.mVertIndex; + mIndices.Reserve(idxcount); + for (size_t i = 0; i < idxcount; i++) + { + mIndices[dg.mIndexIndex + i] = ind[i] + dg.mVertIndex; + } + dg.mIndexCount = (int)idxcount; + } + else + { + // If we have no index buffer, treat this as an unindexed list of triangles. + mIndices.Reserve(vtcount); + for (size_t i = 0; i < vtcount; i++) + { + mIndices[dg.mIndexIndex + i] = int(i + dg.mVertIndex); + } + dg.mIndexCount = (int)vtcount; + } - dg.mIndexCount = (int)idxcount; AddCommand(&dg); } diff --git a/source/common/2d/v_2ddrawer.h b/source/common/2d/v_2ddrawer.h index b3e7d2aa5..feac2e4d7 100644 --- a/source/common/2d/v_2ddrawer.h +++ b/source/common/2d/v_2ddrawer.h @@ -189,7 +189,7 @@ public: void AddPoly(FGameTexture *texture, FVector2 *points, int npoints, double originx, double originy, double scalex, double scaley, DAngle rotation, const FColormap &colormap, PalEntry flatcolor, double lightlevel, uint32_t *indices, size_t indexcount); - void AddPoly(FGameTexture* img, FVector4 *vt, size_t vtcount, unsigned int *ind, size_t idxcount, int translation, PalEntry color, FRenderStyle style, int clipx1, int clipy1, int clipx2, int clipy2); + void AddPoly(FGameTexture* img, FVector4 *vt, size_t vtcount, const unsigned int *ind, size_t idxcount, int translation, PalEntry color, FRenderStyle style, int clipx1, int clipy1, int clipx2, int clipy2); void FillPolygon(int* rx1, int* ry1, int* xb1, int32_t npoints, int picnum, int palette, int shade, int props, const FVector2& xtex, const FVector2& ytex, const FVector2& otex, int clipx1, int clipy1, int clipx2, int clipy2); void AddFlatFill(int left, int top, int right, int bottom, FGameTexture *src, int local_origin = false, double flatscale = 1.0, PalEntry color = 0xffffffff, ERenderStyle rs = STYLE_Normal); diff --git a/source/common/2d/v_draw.cpp b/source/common/2d/v_draw.cpp index 70bf53042..9c53e754b 100644 --- a/source/common/2d/v_draw.cpp +++ b/source/common/2d/v_draw.cpp @@ -50,7 +50,7 @@ CVAR(Bool, ui_screenborder_classic_scaling, true, CVAR_ARCHIVE) // nonsense that graphics should not be able to actually use that extra screen space. extern bool setsizeneeded; -CUSTOM_CVAR(Bool, vid_allowtrueultrawide, true, CVAR_GLOBALCONFIG|CVAR_ARCHIVE|CVAR_NOINITCALL) +CUSTOM_CVAR(Int, vid_allowtrueultrawide, 1, CVAR_ARCHIVE|CVAR_NOINITCALL) { setsizeneeded = true; } @@ -324,6 +324,22 @@ DEFINE_ACTION_FUNCTION(_Screen, ClearClipRect) return 0; } +DEFINE_ACTION_FUNCTION(_Screen, ClearScreen) +{ + PARAM_PROLOGUE; + twod->ClearScreen(); + return 0; +} + +DEFINE_ACTION_FUNCTION(_Screen, SetScreenFade) +{ + PARAM_PROLOGUE; + PARAM_FLOAT(x); + twod->SetScreenFade(float(x)); + return 0; +} + + void F2DDrawer::GetClipRect(int *x, int *y, int *w, int *h) { if (x) *x = clipleft; @@ -1263,11 +1279,20 @@ static void VirtualToRealCoords(F2DDrawer *drawer, double Width, double Height, { float myratio = float(handleaspect ? ActiveRatio (Width, Height) : (4.0 / 3.0)); - // if 21:9 AR, map to 16:9 for all callers. - // this allows for black bars and stops the stretching of fullscreen images - if ((myratio > 1.7f) && !vid_allowtrueultrawide) { - myratio = 16.0f / 9.0f; - } + // if 21:9 AR, map to 16:9 for all callers. + // this allows for black bars and stops the stretching of fullscreen images + + switch (vid_allowtrueultrawide) + { + case 1: + default: + myratio = MIN(64.0f / 27.0f, myratio); + break; + case 0: + myratio = MIN(16.0f / 9.0f, myratio); + case -1: + break; + } double right = x + w; double bottom = y + h; diff --git a/source/common/audio/music/music.cpp b/source/common/audio/music/music.cpp index c1c8290f6..f81b2396c 100644 --- a/source/common/audio/music/music.cpp +++ b/source/common/audio/music/music.cpp @@ -138,6 +138,10 @@ void S_StopCustomStream(SoundStream *stream) void S_PauseAllCustomStreams(bool on) { + static bool paused = false; + + if (paused == on) return; + paused = on; for (auto s : customStreams) { s->SetPaused(on); diff --git a/source/common/console/c_enginecmds.cpp b/source/common/console/c_enginecmds.cpp index 0af6f3b56..542abe91d 100644 --- a/source/common/console/c_enginecmds.cpp +++ b/source/common/console/c_enginecmds.cpp @@ -293,8 +293,8 @@ CCMD (md5sum) } for (int i = 1; i < argv.argc(); ++i) { - FileReader fr; - if (!fr.OpenFile(argv[i])) + FileReader fr = fileSystem.OpenFileReader(argv[i]); + if (!fr.isOpen()) { Printf("%s: %s\n", argv[i], strerror(errno)); } diff --git a/source/common/engine/namedef.h b/source/common/engine/namedef.h index cf8b8da62..8ec5d4081 100644 --- a/source/common/engine/namedef.h +++ b/source/common/engine/namedef.h @@ -1107,4 +1107,4 @@ xy(menu_change, "menu/change") xy(menu_advance, "menu/advance") xx(zoomsize) - +xx(ScreenJobRunner) diff --git a/source/common/engine/sc_man.cpp b/source/common/engine/sc_man.cpp index fa9a2f6c4..f24f25d92 100644 --- a/source/common/engine/sc_man.cpp +++ b/source/common/engine/sc_man.cpp @@ -1287,6 +1287,42 @@ void FScanner::AddSymbol(const char* name, double value) symbols.Insert(name, sym); } +//========================================================================== +// +// +// +//========================================================================== + +int FScanner::StartBraces(FScanner::SavedPos* braceend) +{ + if (CheckString("{")) + { + auto here = SavePos(); + SkipToEndOfBlock(); + *braceend = SavePos(); + RestorePos(here); + return 0; + } + else + { + ScriptError("'{' expected"); + return -1; + } +} + +//========================================================================== +// +// +// +//========================================================================== + +bool FScanner::FoundEndBrace(FScanner::SavedPos& braceend) +{ + auto here = SavePos(); + return here.SavedScriptPtr >= braceend.SavedScriptPtr; +} + + //========================================================================== // // a class that remembers a parser position diff --git a/source/common/engine/sc_man.h b/source/common/engine/sc_man.h index 3a894fcb3..56a4a8d82 100644 --- a/source/common/engine/sc_man.h +++ b/source/common/engine/sc_man.h @@ -94,6 +94,8 @@ public: inline void AddSymbol(const char* name, uint32_t value) { return AddSymbol(name, uint64_t(value)); } void AddSymbol(const char* name, double value); void SkipToEndOfBlock(); + int StartBraces(FScanner::SavedPos* braceend); + bool FoundEndBrace(FScanner::SavedPos& braceend); static FString TokenName(int token, const char *string=NULL); @@ -120,7 +122,30 @@ public: return true; } + bool GetNumber(int64_t& var, bool evaluate = false) + { + if (!GetNumber(evaluate)) return false; + var = BigNumber; + return true; + } + + bool GetString(FString& var) + { + if (!GetString()) return false; + var = String; + return true; + } + bool GetFloat(bool evaluate = false); + + bool GetFloat(double& var, bool evaluate = false) + { + if (!GetFloat(evaluate)) return false; + var = Float; + return true; + } + + void MustGetFloat(bool evaluate = false); bool CheckFloat(bool evaluate = false); diff --git a/source/common/platform/posix/sdl/sdlglvideo.cpp b/source/common/platform/posix/sdl/sdlglvideo.cpp index 4a419b10d..9487854f9 100644 --- a/source/common/platform/posix/sdl/sdlglvideo.cpp +++ b/source/common/platform/posix/sdl/sdlglvideo.cpp @@ -61,12 +61,9 @@ // MACROS ------------------------------------------------------------------ -// Requires SDL 2.0.6 or newer -//#define SDL2_STATIC_LIBRARY - -#if defined SDL2_STATIC_LIBRARY && defined HAVE_VULKAN +#if defined HAVE_VULKAN #include -#endif // SDL2_STATIC_LIBRARY && HAVE_VULKAN +#endif // HAVE_VULKAN // TYPES ------------------------------------------------------------------- @@ -118,30 +115,7 @@ CCMD(vid_list_sdl_render_drivers) namespace Priv { -#ifdef SDL2_STATIC_LIBRARY - -#define SDL2_OPTIONAL_FUNCTION(RESULT, NAME, ...) \ - RESULT(*NAME)(__VA_ARGS__) = SDL_ ## NAME - -#else // !SDL2_STATIC_LIBRARY - - FModule library("SDL2"); - -#define SDL2_OPTIONAL_FUNCTION(RESULT, NAME, ...) \ - static TOptProc NAME("SDL_" #NAME) - -#endif // SDL2_STATIC_LIBRARY - - SDL2_OPTIONAL_FUNCTION(int, GetWindowBordersSize, SDL_Window *window, int *top, int *left, int *bottom, int *right); -#ifdef HAVE_VULKAN - SDL2_OPTIONAL_FUNCTION(void, Vulkan_GetDrawableSize, SDL_Window *window, int *width, int *height); - SDL2_OPTIONAL_FUNCTION(SDL_bool, Vulkan_GetInstanceExtensions, SDL_Window *window, unsigned int *count, const char **names); - SDL2_OPTIONAL_FUNCTION(SDL_bool, Vulkan_CreateSurface, SDL_Window *window, VkInstance instance, VkSurfaceKHR *surface); -#endif - -#undef SDL2_OPTIONAL_FUNCTION - - static const uint32_t VulkanWindowFlag = 0x1000'0000; + static const uint32_t VulkanWindowFlag = SDL_WINDOW_VULKAN; SDL_Window *window; bool vulkanEnabled; @@ -240,22 +214,21 @@ void I_GetVulkanDrawableSize(int *width, int *height) { assert(Priv::vulkanEnabled); assert(Priv::window != nullptr); - assert(Priv::Vulkan_GetDrawableSize); - Priv::Vulkan_GetDrawableSize(Priv::window, width, height); + SDL_Vulkan_GetDrawableSize(Priv::window, width, height); } bool I_GetVulkanPlatformExtensions(unsigned int *count, const char **names) { assert(Priv::vulkanEnabled); assert(Priv::window != nullptr); - return Priv::Vulkan_GetInstanceExtensions(Priv::window, count, names) == SDL_TRUE; + return SDL_Vulkan_GetInstanceExtensions(Priv::window, count, names) == SDL_TRUE; } bool I_CreateVulkanSurface(VkInstance instance, VkSurfaceKHR *surface) { assert(Priv::vulkanEnabled); assert(Priv::window != nullptr); - return Priv::Vulkan_CreateSurface(Priv::window, instance, surface) == SDL_TRUE; + return SDL_Vulkan_CreateSurface(Priv::window, instance, surface) == SDL_TRUE; } #endif @@ -419,20 +392,19 @@ SDLVideo::SDLVideo () return; } -#ifndef SDL2_STATIC_LIBRARY - // Load optional SDL functions - if (!Priv::library.IsLoaded()) + // Fail gracefully if we somehow reach here after linking against a SDL2 library older than 2.0.6. + SDL_version sdlver; + SDL_GetVersion(&sdlver); + if (!(sdlver.patch >= 6)) { - Priv::library.Load({ "libSDL2-2.0.so.0", "libSDL2-2.0.so", "libSDL2.so" }); + I_FatalError("Only SDL 2.0.6 or later is supported."); } -#endif // !SDL2_STATIC_LIBRARY #ifdef HAVE_SOFTPOLY Priv::softpolyEnabled = vid_preferbackend == 2; #endif #ifdef HAVE_VULKAN - Priv::vulkanEnabled = vid_preferbackend == 1 - && Priv::Vulkan_GetDrawableSize && Priv::Vulkan_GetInstanceExtensions && Priv::Vulkan_CreateSurface; + Priv::vulkanEnabled = vid_preferbackend == 1; if (Priv::vulkanEnabled) { @@ -539,7 +511,7 @@ int SystemBaseFrameBuffer::GetClientWidth() #ifdef HAVE_VULKAN assert(Priv::vulkanEnabled); - Priv::Vulkan_GetDrawableSize(Priv::window, &width, nullptr); + SDL_Vulkan_GetDrawableSize(Priv::window, &width, nullptr); #endif return width; @@ -562,7 +534,7 @@ int SystemBaseFrameBuffer::GetClientHeight() #ifdef HAVE_VULKAN assert(Priv::vulkanEnabled); - Priv::Vulkan_GetDrawableSize(Priv::window, nullptr, &height); + SDL_Vulkan_GetDrawableSize(Priv::window, nullptr, &height); #endif return height; @@ -745,10 +717,10 @@ void ProcessSDLWindowEvent(const SDL_WindowEvent &event) break; case SDL_WINDOWEVENT_MOVED: - if (!vid_fullscreen && Priv::GetWindowBordersSize) + if (!vid_fullscreen) { int top = 0, left = 0; - Priv::GetWindowBordersSize(Priv::window, &top, &left, nullptr, nullptr); + SDL_GetWindowBordersSize(Priv::window, &top, &left, nullptr, nullptr); win_x = event.data1-left; win_y = event.data2-top; } diff --git a/source/common/platform/win32/base_sysfb.cpp b/source/common/platform/win32/base_sysfb.cpp index 9455dbae6..3cb8858a3 100644 --- a/source/common/platform/win32/base_sysfb.cpp +++ b/source/common/platform/win32/base_sysfb.cpp @@ -371,7 +371,6 @@ SystemBaseFrameBuffer::~SystemBaseFrameBuffer() SetWindowLong(Window, GWL_STYLE, WS_VISIBLE | WS_CLIPSIBLINGS | WS_OVERLAPPEDWINDOW); SetWindowLong(Window, GWL_EXSTYLE, WS_EX_WINDOWEDGE); SetWindowPos(Window, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); - I_GetEvent(); static_cast(Video)->Shutdown(); } diff --git a/source/common/rendering/hwrenderer/data/flatvertices.h b/source/common/rendering/hwrenderer/data/flatvertices.h index 04feb8388..d28339ee7 100644 --- a/source/common/rendering/hwrenderer/data/flatvertices.h +++ b/source/common/rendering/hwrenderer/data/flatvertices.h @@ -9,7 +9,6 @@ class FRenderState; struct secplane_t; -struct subsector_t; struct FFlatVertex { diff --git a/source/common/rendering/hwrenderer/data/hw_aabbtree.h b/source/common/rendering/hwrenderer/data/hw_aabbtree.h index 389a991ab..2bd0e1506 100644 --- a/source/common/rendering/hwrenderer/data/hw_aabbtree.h +++ b/source/common/rendering/hwrenderer/data/hw_aabbtree.h @@ -4,8 +4,6 @@ #include "tarray.h" #include "vectors.h" -struct FLevelLocals; - namespace hwrenderer { diff --git a/source/common/rendering/hwrenderer/data/hw_skydome.cpp b/source/common/rendering/hwrenderer/data/hw_skydome.cpp index 52bdc4d36..5d5ee8000 100644 --- a/source/common/rendering/hwrenderer/data/hw_skydome.cpp +++ b/source/common/rendering/hwrenderer/data/hw_skydome.cpp @@ -62,13 +62,7 @@ #include "hw_renderstate.h" #include "v_video.h" #include "hwrenderer/data/buffers.h" - -// 57 world units roughly represent one sky texel for the glTranslate call. -enum -{ - skyoffsetfactor = 57 -}; - +#include "version.h" //----------------------------------------------------------------------------- // @@ -150,7 +144,7 @@ FSkyVertexBuffer::~FSkyVertexBuffer() // //----------------------------------------------------------------------------- -void FSkyVertexBuffer::SkyVertex(int r, int c, bool zflip) +void FSkyVertexBuffer::SkyVertexDoom(int r, int c, bool zflip) { static const FAngle maxSideAngle = 60.f; static const float scale = 10000.; @@ -187,6 +181,37 @@ void FSkyVertexBuffer::SkyVertex(int r, int c, bool zflip) mVertices.Push(vert); } +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +void FSkyVertexBuffer::SkyVertexBuild(int r, int c, bool zflip) +{ + static const FAngle maxSideAngle = 60.f; + static const float scale = 10000.; + + FAngle topAngle = (c / (float)mColumns * 360.f); + FVector2 pos = topAngle.ToVector(scale); + float z = (!zflip) ? (mRows - r) * 4000.f : -(mRows - r) * 4000.f; + + FSkyVertex vert; + + vert.color = r == 0 ? 0xffffff : 0xffffffff; + + // And the texture coordinates. + if (zflip) r = mRows * 2 - r; + vert.u = 0.5f + (-c / (float)mColumns); + vert.v = (r / (float)(2*mRows)); + + // And finally the vertex. + vert.x = pos.X; + vert.y = z - 1.f; + vert.z = pos.Y; + + mVertices.Push(vert); +} //----------------------------------------------------------------------------- // @@ -194,27 +219,58 @@ void FSkyVertexBuffer::SkyVertex(int r, int c, bool zflip) // //----------------------------------------------------------------------------- -void FSkyVertexBuffer::CreateSkyHemisphere(int hemi) +void FSkyVertexBuffer::CreateSkyHemisphereDoom(int hemi) { int r, c; bool zflip = !!(hemi & SKYHEMI_LOWER); - mPrimStart.Push(mVertices.Size()); + mPrimStartDoom.Push(mVertices.Size()); for (c = 0; c < mColumns; c++) { - SkyVertex(1, c, zflip); + SkyVertexDoom(1, c, zflip); } // The total number of triangles per hemisphere can be calculated // as follows: rows * columns * 2 + 2 (for the top cap). for (r = 0; r < mRows; r++) { - mPrimStart.Push(mVertices.Size()); + mPrimStartDoom.Push(mVertices.Size()); for (c = 0; c <= mColumns; c++) { - SkyVertex(r + zflip, c, zflip); - SkyVertex(r + 1 - zflip, c, zflip); + SkyVertexDoom(r + zflip, c, zflip); + SkyVertexDoom(r + 1 - zflip, c, zflip); + } + } +} + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +void FSkyVertexBuffer::CreateSkyHemisphereBuild(int hemi) +{ + int r, c; + bool zflip = !!(hemi & SKYHEMI_LOWER); + + mPrimStartBuild.Push(mVertices.Size()); + + for (c = 0; c < mColumns; c++) + { + SkyVertexBuild(1, c, zflip); + } + + // The total number of triangles per hemisphere can be calculated + // as follows: rows * columns * 2 + 2 (for the top cap). + for (r = 0; r < mRows; r++) + { + mPrimStartBuild.Push(mVertices.Size()); + for (c = 0; c <= mColumns; c++) + { + SkyVertexBuild(r + zflip, c, zflip); + SkyVertexBuild(r + 1 - zflip, c, zflip); } } } @@ -248,9 +304,13 @@ void FSkyVertexBuffer::CreateDome() mColumns = 128; mRows = 4; - CreateSkyHemisphere(SKYHEMI_UPPER); - CreateSkyHemisphere(SKYHEMI_LOWER); - mPrimStart.Push(mVertices.Size()); + CreateSkyHemisphereDoom(SKYHEMI_UPPER); + CreateSkyHemisphereDoom(SKYHEMI_LOWER); + mPrimStartDoom.Push(mVertices.Size()); + + CreateSkyHemisphereBuild(SKYHEMI_UPPER); + CreateSkyHemisphereBuild(SKYHEMI_LOWER); + mPrimStartBuild.Push(mVertices.Size()); mSideStart = mVertices.Size(); mFaceStart[0] = mSideStart + 10; @@ -324,7 +384,7 @@ void FSkyVertexBuffer::CreateDome() // //----------------------------------------------------------------------------- -void FSkyVertexBuffer::SetupMatrices(FGameTexture *tex, float x_offset, float y_offset, bool mirror, int mode, VSMatrix &modelMatrix, VSMatrix &textureMatrix, bool tiled) +void FSkyVertexBuffer::SetupMatrices(FGameTexture *tex, float x_offset, float y_offset, bool mirror, int mode, VSMatrix &modelMatrix, VSMatrix &textureMatrix, bool tiled, float xscale, float yscale) { float texw = tex->GetDisplayWidth(); float texh = tex->GetDisplayHeight(); @@ -332,37 +392,46 @@ void FSkyVertexBuffer::SetupMatrices(FGameTexture *tex, float x_offset, float y_ modelMatrix.loadIdentity(); modelMatrix.rotate(-180.0f + x_offset, 0.f, 1.f, 0.f); - float xscale = texw < 1024.f ? floorf(1024.f / float(texw)) : 1.f; - float yscale = 1.f; + if (xscale == 0) xscale = texw < 1024.f ? floorf(1024.f / float(texw)) : 1.f; auto texskyoffset = tex->GetSkyOffset() + skyoffset; - if (texh <= 128 && tiled) + if (yscale == 0) { - modelMatrix.translate(0.f, (-40 + texskyoffset)*skyoffsetfactor, 0.f); - modelMatrix.scale(1.f, 1.2f * 1.17f, 1.f); - yscale = 240.f / texh; - } - else if (texh < 128) - { - // smaller sky textures must be tiled. We restrict it to 128 sky pixels, though - modelMatrix.translate(0.f, -1250.f, 0.f); - modelMatrix.scale(1.f, 128 / 230.f, 1.f); - yscale = float(128 / texh); // intentionally left as integer. - } - else if (texh < 200) - { - modelMatrix.translate(0.f, -1250.f, 0.f); - modelMatrix.scale(1.f, texh / 230.f, 1.f); - } - else if (texh <= 240) - { - modelMatrix.translate(0.f, (200 - texh + texskyoffset)*skyoffsetfactor, 0.f); - modelMatrix.scale(1.f, 1.f + ((texh - 200.f) / 200.f) * 1.17f, 1.f); + if (texh <= 128 && tiled) + { + modelMatrix.translate(0.f, (-40 + texskyoffset) * skyoffsetfactor, 0.f); + modelMatrix.scale(1.f, 1.2f * 1.17f, 1.f); + yscale = 240.f / texh; + } + else if (texh < 128) + { + // smaller sky textures must be tiled. We restrict it to 128 sky pixels, though + modelMatrix.translate(0.f, -1250.f, 0.f); + modelMatrix.scale(1.f, 128 / 230.f, 1.f); + yscale = float(128 / texh); // intentionally left as integer. + } + else if (texh < 200) + { + modelMatrix.translate(0.f, -1250.f, 0.f); + modelMatrix.scale(1.f, texh / 230.f, 1.f); + yscale = 1.f; + } + else if (texh <= 240) + { + modelMatrix.translate(0.f, (200 - texh + texskyoffset) * skyoffsetfactor, 0.f); + modelMatrix.scale(1.f, 1.f + ((texh - 200.f) / 200.f) * 1.17f, 1.f); + yscale = 1.f; + } + else + { + modelMatrix.translate(0.f, (-40 + texskyoffset) * skyoffsetfactor, 0.f); + modelMatrix.scale(1.f, 1.2f * 1.17f, 1.f); + yscale = 240.f / texh; + } } else { - modelMatrix.translate(0.f, (-40 + texskyoffset)*skyoffsetfactor, 0.f); - modelMatrix.scale(1.f, 1.2f * 1.17f, 1.f); - yscale = 240.f / texh; + modelMatrix.translate(0.f, (-40 + texskyoffset) * skyoffsetfactor, 0.f); + modelMatrix.scale(1.f, 0.8f * 1.17f, 1.f); } textureMatrix.loadIdentity(); textureMatrix.scale(mirror ? -xscale : xscale, yscale, 1.f); @@ -375,7 +444,7 @@ void FSkyVertexBuffer::SetupMatrices(FGameTexture *tex, float x_offset, float y_ // //----------------------------------------------------------------------------- -void FSkyVertexBuffer::RenderRow(FRenderState& state, EDrawType prim, int row, bool apply) +void FSkyVertexBuffer::RenderRow(FRenderState& state, EDrawType prim, int row, TArray& mPrimStart, bool apply) { state.Draw(prim, mPrimStart[row], mPrimStart[row + 1] - mPrimStart[row]); } @@ -386,36 +455,35 @@ void FSkyVertexBuffer::RenderRow(FRenderState& state, EDrawType prim, int row, b // //----------------------------------------------------------------------------- -void FSkyVertexBuffer::RenderDome(FRenderState& state, FGameTexture* tex, float x_offset, float y_offset, bool mirror, int mode, bool tiled) +void FSkyVertexBuffer::RenderDome(FRenderState& state, FGameTexture* tex, int mode, bool which) { - if (tex) + auto& primStart = which ? mPrimStartBuild : mPrimStartDoom; + if (tex && tex->isValid()) { state.SetMaterial(tex, UF_Texture, 0, CLAMP_NONE, 0, -1); state.EnableModelMatrix(true); state.EnableTextureMatrix(true); - - SetupMatrices(tex, x_offset, y_offset, mirror, mode, state.mModelMatrix, state.mTextureMatrix, tiled); } int rc = mRows + 1; // The caps only get drawn for the main layer but not for the overlay. - if (mode == FSkyVertexBuffer::SKYMODE_MAINLAYER && tex != NULL) + if (mode == FSkyVertexBuffer::SKYMODE_MAINLAYER && tex != nullptr) { auto& col = R_GetSkyCapColor(tex); state.SetObjectColor(col.first); state.EnableTexture(false); - RenderRow(state, DT_TriangleFan, 0); + RenderRow(state, DT_TriangleFan, 0, primStart); state.SetObjectColor(col.second); - RenderRow(state, DT_TriangleFan, rc); + RenderRow(state, DT_TriangleFan, rc, primStart); state.EnableTexture(true); } state.SetObjectColor(0xffffffff); for (int i = 1; i <= mRows; i++) { - RenderRow(state, DT_TriangleStrip, i, i == 1); - RenderRow(state, DT_TriangleStrip, rc + i, false); + RenderRow(state, DT_TriangleStrip, i, primStart, i == 1); + RenderRow(state, DT_TriangleStrip, rc + i, primStart, false); } state.EnableTextureMatrix(false); @@ -423,13 +491,29 @@ void FSkyVertexBuffer::RenderDome(FRenderState& state, FGameTexture* tex, float } +//----------------------------------------------------------------------------- +// +// This is only for Doom-style skies. +// +//----------------------------------------------------------------------------- + +void FSkyVertexBuffer::RenderDome(FRenderState& state, FGameTexture* tex, float x_offset, float y_offset, bool mirror, int mode, bool tiled, float xscale, float yscale) +{ + if (tex) + { + SetupMatrices(tex, x_offset, y_offset, mirror, mode, state.mModelMatrix, state.mTextureMatrix, tiled, xscale, yscale); + } + RenderDome(state, tex, mode, false); +} + + //----------------------------------------------------------------------------- // // // //----------------------------------------------------------------------------- -void FSkyVertexBuffer::RenderBox(FRenderState& state, FTextureID texno, FSkyBox* tex, float x_offset, bool sky2, float stretch, const FVector3& skyrotatevector, const FVector3& skyrotatevector2) +void FSkyVertexBuffer::RenderBox(FRenderState& state, FSkyBox* tex, float x_offset, bool sky2, float stretch, const FVector3& skyrotatevector, const FVector3& skyrotatevector2) { int faces; diff --git a/source/common/rendering/hwrenderer/data/hw_skydome.h b/source/common/rendering/hwrenderer/data/hw_skydome.h index 7cbd37948..2e10869bb 100644 --- a/source/common/rendering/hwrenderer/data/hw_skydome.h +++ b/source/common/rendering/hwrenderer/data/hw_skydome.h @@ -11,6 +11,12 @@ class IVertexBuffer; struct HWSkyPortal; struct HWDrawInfo; +// 57 world units roughly represent one sky texel for the glTranslate call. +enum +{ + skyoffsetfactor = 57 +}; + struct FSkyVertex { float x, y, z, u, v; @@ -55,7 +61,8 @@ public: IVertexBuffer *mVertexBuffer; TArray mVertices; - TArray mPrimStart; + TArray mPrimStartDoom; + TArray mPrimStartBuild; int mRows, mColumns; @@ -63,15 +70,17 @@ public: int mFaceStart[7]; int mSideStart; - void SkyVertex(int r, int c, bool yflip); - void CreateSkyHemisphere(int hemi); + void SkyVertexDoom(int r, int c, bool yflip); + void SkyVertexBuild(int r, int c, bool yflip); + void CreateSkyHemisphereDoom(int hemi); + void CreateSkyHemisphereBuild(int hemi); void CreateDome(); public: FSkyVertexBuffer(); ~FSkyVertexBuffer(); - void SetupMatrices(FGameTexture *tex, float x_offset, float y_offset, bool mirror, int mode, VSMatrix &modelmatrix, VSMatrix &textureMatrix, bool tiled); + void SetupMatrices(FGameTexture *tex, float x_offset, float y_offset, bool mirror, int mode, VSMatrix &modelmatrix, VSMatrix &textureMatrix, bool tiled, float xscale = 0, float vertscale = 0); std::pair GetBufferObjects() const { return std::make_pair(mVertexBuffer, nullptr); @@ -83,8 +92,9 @@ public: else return mSideStart; } - void RenderRow(FRenderState& state, EDrawType prim, int row, bool apply = true); - void RenderDome(FRenderState& state, FGameTexture* tex, float x_offset, float y_offset, bool mirror, int mode, bool tiled); - void RenderBox(FRenderState& state, FTextureID texno, FSkyBox* tex, float x_offset, bool sky2, float stretch, const FVector3& skyrotatevector, const FVector3& skyrotatevector2); + void RenderRow(FRenderState& state, EDrawType prim, int row, TArray& mPrimStart, bool apply = true); + void RenderDome(FRenderState& state, FGameTexture* tex, int mode, bool which); + void RenderDome(FRenderState& state, FGameTexture* tex, float x_offset, float y_offset, bool mirror, int mode, bool tiled, float xscale = 0, float yscale = 0); + void RenderBox(FRenderState& state, FSkyBox* tex, float x_offset, bool sky2, float stretch, const FVector3& skyrotatevector, const FVector3& skyrotatevector2); }; diff --git a/source/common/rendering/hwrenderer/data/hw_vrmodes.cpp b/source/common/rendering/hwrenderer/data/hw_vrmodes.cpp index caf11f947..59f215e02 100644 --- a/source/common/rendering/hwrenderer/data/hw_vrmodes.cpp +++ b/source/common/rendering/hwrenderer/data/hw_vrmodes.cpp @@ -60,7 +60,6 @@ static VRMode vrmi_checker = { 2, isqrt2, isqrt2, 1.f,{ { -.5f, 1.f },{ .5f, 1.f const VRMode *VRMode::GetVRMode(bool toscreen) { -#ifdef VR3D_ENABLED int mode = !toscreen || (sysCallbacks.DisableTextureFilter && sysCallbacks.DisableTextureFilter()) ? 0 : vr_mode; switch (mode) @@ -96,9 +95,6 @@ const VRMode *VRMode::GetVRMode(bool toscreen) case VR_CHECKERINTERLEAVED: return &vrmi_checker; } -#else - return &vrmi_mono; -#endif } void VRMode::AdjustViewport(DFrameBuffer *screen) const diff --git a/source/common/rendering/v_video.h b/source/common/rendering/v_video.h index 69ccdc3bc..c9124e05f 100644 --- a/source/common/rendering/v_video.h +++ b/source/common/rendering/v_video.h @@ -45,7 +45,6 @@ #include "hw_shadowmap.h" -struct sector_t; struct FPortalSceneState; class FSkyVertexBuffer; class IIndexBuffer; diff --git a/source/common/scripting/backend/codegen.cpp b/source/common/scripting/backend/codegen.cpp index f6f52f0bc..eaafaba98 100644 --- a/source/common/scripting/backend/codegen.cpp +++ b/source/common/scripting/backend/codegen.cpp @@ -8706,7 +8706,7 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx) bool writable; ArgList[i] = ArgList[i]->Resolve(ctx); // must be resolved before the address is requested. - if (ArgList[i]->ValueType->isRealPointer()) + if (ArgList[i] && ArgList[i]->ValueType->isRealPointer()) { auto pointedType = ArgList[i]->ValueType->toPointer()->PointedType; if (pointedType && pointedType->isDynArray()) diff --git a/source/common/scripting/core/dynarrays.cpp b/source/common/scripting/core/dynarrays.cpp index 3d8e87e85..6be216c12 100644 --- a/source/common/scripting/core/dynarrays.cpp +++ b/source/common/scripting/core/dynarrays.cpp @@ -37,6 +37,7 @@ #include "dobject.h" #include "vm.h" #include "types.h" +#include "v_draw.h" // We need one specific type for each of the 8 integral VM types and instantiate the needed functions for each of them. // Dynamic arrays cannot hold structs because for every type there'd need to be an internal implementation which is impossible. @@ -412,6 +413,28 @@ DEFINE_ACTION_FUNCTION_NATIVE(FDynArray_I32, Push, ArrayPushPush(val)); } +DEFINE_ACTION_FUNCTION(FDynArray_I32, PushV) +{ + PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I32); + PARAM_VA_POINTER(va_reginfo); // Get the hidden type information array + VMVa_List args = { param + 1, 0, numparam - 2, va_reginfo + 1 }; + while (args.curindex < args.numargs) + { + if (args.reginfo[args.curindex] == REGT_INT) + { + self->Push(args.args[args.curindex++].i); + } + else if (args.reginfo[args.curindex] == REGT_FLOAT) + { + self->Push(int(args.args[args.curindex++].f)); + } + else ThrowAbortException(X_OTHER, "Invalid parameter in pushv, int expected"); + } + + + ACTION_RETURN_INT(self->Size()-1); +} + DEFINE_ACTION_FUNCTION_NATIVE(FDynArray_I32, Pop, ArrayPop) { PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I32); diff --git a/source/common/scripting/interface/stringformat.cpp b/source/common/scripting/interface/stringformat.cpp index 8e0bf0c75..da667cbbf 100644 --- a/source/common/scripting/interface/stringformat.cpp +++ b/source/common/scripting/interface/stringformat.cpp @@ -550,7 +550,21 @@ DEFINE_ACTION_FUNCTION_NATIVE(FStringStruct, ToDouble, StringToDbl) ACTION_RETURN_FLOAT(self->ToDouble()); } -static void StringSplit(FString *self, TArray *tokens, const FString &delimiter, int keepEmpty) +static void StringSubst(FString *self, const FString &substr, const FString& replc) +{ + self->Substitute(substr, replc); +} + +DEFINE_ACTION_FUNCTION_NATIVE(FStringStruct, Substitute, StringSubst) +{ + PARAM_SELF_STRUCT_PROLOGUE(FString); + PARAM_STRING(substr); + PARAM_STRING(replc); + StringSubst(self, substr, replc); + return 0; +} + +static void StringSplit(FString* self, TArray* tokens, const FString& delimiter, int keepEmpty) { self->Split(*tokens, delimiter, static_cast(keepEmpty)); } diff --git a/source/common/scripting/interface/vmnatives.cpp b/source/common/scripting/interface/vmnatives.cpp index 502d78de3..23705b93a 100644 --- a/source/common/scripting/interface/vmnatives.cpp +++ b/source/common/scripting/interface/vmnatives.cpp @@ -49,6 +49,7 @@ #include "s_music.h" #include "i_interface.h" #include "base_sbar.h" +#include "image.h" //========================================================================== // @@ -80,10 +81,10 @@ DEFINE_ACTION_FUNCTION_NATIVE(DStatusBarCore, StatusbarToRealCoords, StatusbarTo return MIN(4, numret); } -void SBar_DrawTexture(DStatusBarCore* self, int texid, double x, double y, int flags, double alpha, double w, double h, double scaleX, double scaleY) +void SBar_DrawTexture(DStatusBarCore* self, int texid, double x, double y, int flags, double alpha, double w, double h, double scaleX, double scaleY, int style) { if (!twod->HasBegun2D()) ThrowAbortException(X_OTHER, "Attempt to draw to screen outside a draw function"); - self->DrawGraphic(FSetTextureID(texid), x, y, flags, alpha, w, h, scaleX, scaleY); + self->DrawGraphic(FSetTextureID(texid), x, y, flags, alpha, w, h, scaleX, scaleY, ERenderStyle(style)); } DEFINE_ACTION_FUNCTION_NATIVE(DStatusBarCore, DrawTexture, SBar_DrawTexture) @@ -98,14 +99,15 @@ DEFINE_ACTION_FUNCTION_NATIVE(DStatusBarCore, DrawTexture, SBar_DrawTexture) PARAM_FLOAT(h); PARAM_FLOAT(scaleX); PARAM_FLOAT(scaleY); - SBar_DrawTexture(self, texid, x, y, flags, alpha, w, h, scaleX, scaleY); + PARAM_INT(style); + SBar_DrawTexture(self, texid, x, y, flags, alpha, w, h, scaleX, scaleY, style); return 0; } -void SBar_DrawImage(DStatusBarCore* self, const FString& texid, double x, double y, int flags, double alpha, double w, double h, double scaleX, double scaleY) +void SBar_DrawImage(DStatusBarCore* self, const FString& texid, double x, double y, int flags, double alpha, double w, double h, double scaleX, double scaleY, int style) { if (!twod->HasBegun2D()) ThrowAbortException(X_OTHER, "Attempt to draw to screen outside a draw function"); - self->DrawGraphic(TexMan.CheckForTexture(texid, ETextureType::Any), x, y, flags, alpha, w, h, scaleX, scaleY); + self->DrawGraphic(TexMan.CheckForTexture(texid, ETextureType::Any), x, y, flags, alpha, w, h, scaleX, scaleY, ERenderStyle(style)); } DEFINE_ACTION_FUNCTION_NATIVE(DStatusBarCore, DrawImage, SBar_DrawImage) @@ -120,11 +122,12 @@ DEFINE_ACTION_FUNCTION_NATIVE(DStatusBarCore, DrawImage, SBar_DrawImage) PARAM_FLOAT(h); PARAM_FLOAT(scaleX); PARAM_FLOAT(scaleY); - SBar_DrawImage(self, texid, x, y, flags, alpha, w, h, scaleX, scaleY); + PARAM_INT(style); + SBar_DrawImage(self, texid, x, y, flags, alpha, w, h, scaleX, scaleY, style); return 0; } -void SBar_DrawString(DStatusBarCore* self, DHUDFont* font, const FString& string, double x, double y, int flags, int trans, double alpha, int wrapwidth, int linespacing, double scaleX, double scaleY, int translation); +void SBar_DrawString(DStatusBarCore* self, DHUDFont* font, const FString& string, double x, double y, int flags, int trans, double alpha, int wrapwidth, int linespacing, double scaleX, double scaleY, int translation, int style); DEFINE_ACTION_FUNCTION_NATIVE(DStatusBarCore, DrawString, SBar_DrawString) { @@ -141,7 +144,8 @@ DEFINE_ACTION_FUNCTION_NATIVE(DStatusBarCore, DrawString, SBar_DrawString) PARAM_FLOAT(scaleX); PARAM_FLOAT(scaleY); PARAM_INT(pt); - SBar_DrawString(self, font, string, x, y, flags, trans, alpha, wrapwidth, linespacing, scaleX, scaleY, pt); + PARAM_INT(style); + SBar_DrawString(self, font, string, x, y, flags, trans, alpha, wrapwidth, linespacing, scaleX, scaleY, pt, style); return 0; } @@ -336,8 +340,7 @@ DEFINE_ACTION_FUNCTION(_TexMan, GetName) static int CheckForTexture(const FString& name, int type, int flags) { - // ForceLookup is intentionally blocked here, this flag is for internal use only. - return TexMan.CheckForTexture(name, static_cast(type), (flags & ~FTextureManager::TEXMAN_ForceLookup)).GetIndex(); + return TexMan.CheckForTexture(name, static_cast(type), flags).GetIndex(); } DEFINE_ACTION_FUNCTION_NATIVE(_TexMan, CheckForTexture, CheckForTexture) @@ -477,6 +480,20 @@ DEFINE_ACTION_FUNCTION_NATIVE(_TexMan, OkForLocalization, OkForLocalization_) ACTION_RETURN_INT(OkForLocalization_(name, subst)); } +static int UseGamePalette(int index) +{ + auto tex = TexMan.GameByIndex(index, false); + if (!tex) return false; + auto image = tex->GetTexture()->GetImage(); + return image ? image->UseGamePalette() : false; +} + +DEFINE_ACTION_FUNCTION_NATIVE(_TexMan, UseGamePalette, UseGamePalette) +{ + PARAM_PROLOGUE; + PARAM_INT(texid); + ACTION_RETURN_INT(UseGamePalette(texid)); +} //===================================================================================== // @@ -867,6 +884,13 @@ DEFINE_ACTION_FUNCTION(FKeyBindings, GetAllKeysForCommand) return 0; } +DEFINE_ACTION_FUNCTION(FKeyBindings, GetBinding) +{ + PARAM_SELF_STRUCT_PROLOGUE(FKeyBindings); + PARAM_INT(key); + ACTION_RETURN_STRING(self->GetBinding(key)); +} + DEFINE_ACTION_FUNCTION(FKeyBindings, UnbindACommand) { PARAM_SELF_STRUCT_PROLOGUE(FKeyBindings); @@ -914,6 +938,7 @@ DEFINE_GLOBAL_NAMED(mus_playing, musplaying); DEFINE_FIELD_X(MusPlayingInfo, MusPlayingInfo, name); DEFINE_FIELD_X(MusPlayingInfo, MusPlayingInfo, baseorder); DEFINE_FIELD_X(MusPlayingInfo, MusPlayingInfo, loop); +DEFINE_FIELD_X(MusPlayingInfo, MusPlayingInfo, handle); DEFINE_GLOBAL_NAMED(PClass::AllClasses, AllClasses) DEFINE_GLOBAL(Bindings) diff --git a/source/common/statusbar/base_sbar.cpp b/source/common/statusbar/base_sbar.cpp index ae1a375c3..948b2504b 100644 --- a/source/common/statusbar/base_sbar.cpp +++ b/source/common/statusbar/base_sbar.cpp @@ -452,16 +452,16 @@ void DStatusBarCore::StatusbarToRealCoords(double& x, double& y, double& w, doub // //============================================================================ -void DStatusBarCore::DrawGraphic(FTextureID texture, double x, double y, int flags, double Alpha, double boxwidth, double boxheight, double scaleX, double scaleY, PalEntry color, int translation, ERenderStyle style, double clipwidth) +void DStatusBarCore::DrawGraphic(FTextureID texture, double x, double y, int flags, double Alpha, double boxwidth, double boxheight, double scaleX, double scaleY, ERenderStyle style, PalEntry color, int translation, double clipwidth) { if (!texture.isValid()) return; FGameTexture* tex = TexMan.GetGameTexture(texture, !(flags & DI_DONTANIMATE)); - DrawGraphic(tex, x, y, flags, Alpha, boxwidth, boxheight, scaleX, scaleY, color, translation, style); + DrawGraphic(tex, x, y, flags, Alpha, boxwidth, boxheight, scaleX, scaleY, style, color, translation); } -void DStatusBarCore::DrawGraphic(FGameTexture* tex, double x, double y, int flags, double Alpha, double boxwidth, double boxheight, double scaleX, double scaleY, PalEntry color, int translation, ERenderStyle style, double clipwidth) +void DStatusBarCore::DrawGraphic(FGameTexture* tex, double x, double y, int flags, double Alpha, double boxwidth, double boxheight, double scaleX, double scaleY, ERenderStyle style, PalEntry color, int translation, double clipwidth) { double texwidth = tex->GetDisplayWidth() * scaleX; double texheight = tex->GetDisplayHeight() * scaleY; @@ -695,7 +695,7 @@ void DStatusBarCore::DrawRotated(FGameTexture* tex, double x, double y, int flag // //============================================================================ -void DStatusBarCore::DrawString(FFont* font, const FString& cstring, double x, double y, int flags, double Alpha, int translation, int spacing, EMonospacing monospacing, int shadowX, int shadowY, double scaleX, double scaleY, int pt) +void DStatusBarCore::DrawString(FFont* font, const FString& cstring, double x, double y, int flags, double Alpha, int translation, int spacing, EMonospacing monospacing, int shadowX, int shadowY, double scaleX, double scaleY, int pt, int style) { bool monospaced = monospacing != EMonospacing::Off; double dx = 0; @@ -822,6 +822,7 @@ void DStatusBarCore::DrawString(FFont* font, const FString& cstring, double x, d DTA_DestHeightF, rh, DTA_Alpha, Alpha, DTA_TranslationIndex, pt, + DTA_LegacyRenderStyle, ERenderStyle(style), TAG_DONE); dx = monospaced @@ -833,7 +834,7 @@ void DStatusBarCore::DrawString(FFont* font, const FString& cstring, double x, d } } -void SBar_DrawString(DStatusBarCore* self, DHUDFont* font, const FString& string, double x, double y, int flags, int trans, double alpha, int wrapwidth, int linespacing, double scaleX, double scaleY, int pt) +void SBar_DrawString(DStatusBarCore* self, DHUDFont* font, const FString& string, double x, double y, int flags, int trans, double alpha, int wrapwidth, int linespacing, double scaleX, double scaleY, int pt, int style) { if (font == nullptr) ThrowAbortException(X_READ_NIL, nullptr); if (!twod->HasBegun2D()) ThrowAbortException(X_OTHER, "Attempt to draw to screen outside a draw function"); @@ -852,13 +853,13 @@ void SBar_DrawString(DStatusBarCore* self, DHUDFont* font, const FString& string auto brk = V_BreakLines(font->mFont, int(wrapwidth * scaleX), string, true); for (auto& line : brk) { - self->DrawString(font->mFont, line.Text, x, y, flags, alpha, trans, font->mSpacing, font->mMonospacing, font->mShadowX, font->mShadowY, scaleX, scaleY, pt); + self->DrawString(font->mFont, line.Text, x, y, flags, alpha, trans, font->mSpacing, font->mMonospacing, font->mShadowX, font->mShadowY, scaleX, scaleY, pt, style); y += (font->mFont->GetHeight() + linespacing) * scaleY; } } else { - self->DrawString(font->mFont, string, x, y, flags, alpha, trans, font->mSpacing, font->mMonospacing, font->mShadowX, font->mShadowY, scaleX, scaleY, pt); + self->DrawString(font->mFont, string, x, y, flags, alpha, trans, font->mSpacing, font->mMonospacing, font->mShadowX, font->mShadowY, scaleX, scaleY, pt, style); } } diff --git a/source/common/statusbar/base_sbar.h b/source/common/statusbar/base_sbar.h index 7f892b4e4..9b1204412 100644 --- a/source/common/statusbar/base_sbar.h +++ b/source/common/statusbar/base_sbar.h @@ -184,11 +184,11 @@ public: virtual void SetScale(); void ValidateResolution(int& hres, int& vres) const; void StatusbarToRealCoords(double& x, double& y, double& w, double& h) const; - void DrawGraphic(FGameTexture* texture, double x, double y, int flags, double Alpha, double boxwidth, double boxheight, double scaleX, double scaleY, PalEntry color = 0xffffffff, int translation = 0, ERenderStyle style = STYLE_Translucent, double clipwidth = -1.0); - void DrawGraphic(FTextureID texture, double x, double y, int flags, double Alpha, double boxwidth, double boxheight, double scaleX, double scaleY, PalEntry color = 0xffffffff, int translation = 0, ERenderStyle style = STYLE_Translucent, double clipwidth = -1.0); + void DrawGraphic(FGameTexture* texture, double x, double y, int flags, double Alpha, double boxwidth, double boxheight, double scaleX, double scaleY, ERenderStyle style = STYLE_Translucent, PalEntry color = 0xffffffff, int translation = 0, double clipwidth = -1.0); + void DrawGraphic(FTextureID texture, double x, double y, int flags, double Alpha, double boxwidth, double boxheight, double scaleX, double scaleY, ERenderStyle style = STYLE_Translucent, PalEntry color = 0xffffffff, int translation = 0, double clipwidth = -1.0); void DrawRotated(FTextureID texture, double x, double y, int flags, double angle, double Alpha, double scaleX, double scaleY, PalEntry color = 0xffffffff, int translation = 0, ERenderStyle style = STYLE_Translucent); void DrawRotated(FGameTexture* tex, double x, double y, int flags, double angle, double Alpha, double scaleX, double scaleY, PalEntry color = 0xffffffff, int translation = 0, ERenderStyle style = STYLE_Translucent); - void DrawString(FFont* font, const FString& cstring, double x, double y, int flags, double Alpha, int translation, int spacing, EMonospacing monospacing, int shadowX, int shadowY, double scaleX, double scaleY, int pt); + void DrawString(FFont* font, const FString& cstring, double x, double y, int flags, double Alpha, int translation, int spacing, EMonospacing monospacing, int shadowX, int shadowY, double scaleX, double scaleY, int pt, int style); void TransformRect(double& x, double& y, double& w, double& h, int flags = 0); 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); diff --git a/source/common/textures/gametexture.h b/source/common/textures/gametexture.h index c7a753372..e9851fbe3 100644 --- a/source/common/textures/gametexture.h +++ b/source/common/textures/gametexture.h @@ -231,6 +231,7 @@ public: void SetFullbright() { flags |= GTexf_RenderFullbright; } void SetDisableFullbright(bool on) { if (on) flags |= GTexf_DisableFullbrightSprites; else flags &= ~GTexf_DisableFullbrightSprites; } void SetGlowing(PalEntry color) { flags = (flags & ~GTexf_AutoGlowing) | GTexf_Glowing; GlowColor = color; } + void SetDisableBrightmap() { flags |= GTexf_BrightmapChecked; Brightmap = nullptr; } bool isUserContent() const; int CheckRealHeight() { return xs_RoundToInt(Base->CheckRealHeight() / ScaleY); } diff --git a/source/common/textures/hw_material.cpp b/source/common/textures/hw_material.cpp index f8d379cd4..a157cbc07 100644 --- a/source/common/textures/hw_material.cpp +++ b/source/common/textures/hw_material.cpp @@ -30,6 +30,7 @@ #include "v_video.h" static IHardwareTexture* (*layercallback)(int layer, int translation); +TArray usershaders; void FMaterial::SetLayerCallback(IHardwareTexture* (*cb)(int layer, int translation)) { diff --git a/source/common/utility/basics.h b/source/common/utility/basics.h index 34450469a..703a84c30 100644 --- a/source/common/utility/basics.h +++ b/source/common/utility/basics.h @@ -2,6 +2,7 @@ #include #include +#include #define MAXWIDTH 12000 #define MAXHEIGHT 5000 @@ -101,3 +102,6 @@ enum EStateUseFlags SUF_WEAPON = 4, SUF_ITEM = 8, }; + +using std::min; +using std::max; diff --git a/source/common/utility/cmdlib.h b/source/common/utility/cmdlib.h index 3792030ae..b998cf99d 100644 --- a/source/common/utility/cmdlib.h +++ b/source/common/utility/cmdlib.h @@ -103,4 +103,14 @@ inline void fillshort(void* buff, size_t count, uint16_t clear) template inline constexpr T Sgn(const T& val) { return (val > 0) - (val < 0); } +inline int sizeToBits(int w) +{ + int j = 15; + + while ((j > 1) && ((1 << j) > w)) + j--; + return j; +} + + #endif diff --git a/source/common/utility/matrix.h b/source/common/utility/matrix.h index 1c46c2397..81d7be6f2 100644 --- a/source/common/utility/matrix.h +++ b/source/common/utility/matrix.h @@ -33,9 +33,7 @@ class VSMatrix { public: - VSMatrix() - { - } + VSMatrix() = default; VSMatrix(int) { diff --git a/source/core/automap.cpp b/source/core/automap.cpp index 370e48f77..1b7ba5519 100644 --- a/source/core/automap.cpp +++ b/source/core/automap.cpp @@ -27,7 +27,6 @@ Modifications for JonoF's port by Jonathon Fowler (jf@jonof.id.au) //------------------------------------------------------------------------- #include "automap.h" -#include "cstat.h" #include "c_dispatch.h" #include "c_cvars.h" #include "gstrings.h" @@ -44,6 +43,9 @@ Modifications for JonoF's port by Jonathon Fowler (jf@jonof.id.au) #include "v_video.h" #include "gamestruct.h" #include "v_draw.h" +#include "sectorgeometry.h" +#include "gamefuncs.h" +#include "hw_sections.h" CVAR(Bool, am_followplayer, true, CVAR_ARCHIVE) CVAR(Bool, am_rotate, true, CVAR_ARCHIVE) @@ -408,8 +410,6 @@ void drawredlines(int cposx, int cposy, int czoom, int cang) { int xvect = -bsin(cang) * czoom; int yvect = -bcos(cang) * czoom; - int xvect2 = MulScale(xvect, yxaspect, 16); - int yvect2 = MulScale(yvect, yxaspect, 16); int width = screen->GetWidth(); int height = screen->GetHeight(); @@ -441,13 +441,13 @@ void drawredlines(int cposx, int cposy, int czoom, int cang) int ox = wal->x - cposx; int oy = wal->y - cposy; int x1 = DMulScale(ox, xvect, -oy, yvect, 16) + (width << 11); - int y1 = DMulScale(oy, xvect2, ox, yvect2, 16) + (height << 11); + int y1 = DMulScale(oy, xvect, ox, yvect, 16) + (height << 11); auto wal2 = &wall[wal->point2]; ox = wal2->x - cposx; oy = wal2->y - cposy; int x2 = DMulScale(ox, xvect, -oy, yvect, 16) + (width << 11); - int y2 = DMulScale(oy, xvect2, ox, yvect2, 16) + (height << 11); + int y2 = DMulScale(oy, xvect, ox, yvect, 16) + (height << 11); drawlinergb(x1, y1, x2, y2, RedLineColor()); } @@ -465,8 +465,6 @@ static void drawwhitelines(int cposx, int cposy, int czoom, int cang) { int xvect = -bsin(cang) * czoom; int yvect = -bcos(cang) * czoom; - int xvect2 = MulScale(xvect, yxaspect, 16); - int yvect2 = MulScale(yvect, yxaspect, 16); int width = screen->GetWidth(); int height = screen->GetHeight(); @@ -483,7 +481,7 @@ static void drawwhitelines(int cposx, int cposy, int czoom, int cang) for (j = startwall, wal = &wall[startwall]; j < endwall; j++, wal++) { if (wal->nextwall >= 0) continue; - if (!tileGetTexture(wal->picnum)->isValid()) continue; + if (!gFullMap && !tileGetTexture(wal->picnum)->isValid()) continue; if ((g_gameType & GAMEFLAG_SW) && !gFullMap && !show2dwall[j]) continue; @@ -491,20 +489,25 @@ static void drawwhitelines(int cposx, int cposy, int czoom, int cang) int ox = wal->x - cposx; int oy = wal->y - cposy; int x1 = DMulScale(ox, xvect, -oy, yvect, 16) + (width << 11); - int y1 = DMulScale(oy, xvect2, ox, yvect2, 16) + (height << 11); + int y1 = DMulScale(oy, xvect, ox, yvect, 16) + (height << 11); int k = wal->point2; auto wal2 = &wall[k]; ox = wal2->x - cposx; oy = wal2->y - cposy; int x2 = DMulScale(ox, xvect, -oy, yvect, 16) + (width << 11); - int y2 = DMulScale(oy, xvect2, ox, yvect2, 16) + (height << 11); + int y2 = DMulScale(oy, xvect, ox, yvect, 16) + (height << 11); drawlinergb(x1, y1, x2, y2, WhiteLineColor()); } } } +//--------------------------------------------------------------------------- +// +// player sprite fallback +// +//--------------------------------------------------------------------------- void DrawPlayerArrow(int cposx, int cposy, int cang, int pl_x, int pl_y, int zoom, int pl_angle) { @@ -517,8 +520,6 @@ void DrawPlayerArrow(int cposx, int cposy, int cang, int pl_x, int pl_y, int zoo int xvect = -bsin(cang) * zoom; int yvect = -bcos(cang) * zoom; - int xvect2 = MulScale(xvect, yxaspect, 16); - int yvect2 = MulScale(yvect, yxaspect, 16); int pxvect = -bsin(pl_angle); int pyvect = -bcos(pl_angle); @@ -540,14 +541,124 @@ void DrawPlayerArrow(int cposx, int cposy, int cang, int pl_x, int pl_y, int zoo int oy2 = py2 - cposx; int sx1 = DMulScale(ox1, xvect, -oy1, yvect, 16) + (width << 11); - int sy1 = DMulScale(oy1, xvect2, ox1, yvect2, 16) + (height << 11); + int sy1 = DMulScale(oy1, xvect, ox1, yvect, 16) + (height << 11); int sx2 = DMulScale(ox2, xvect, -oy2, yvect, 16) + (width << 11); - int sy2 = DMulScale(oy2, xvect2, ox2, yvect2, 16) + (height << 11); + int sy2 = DMulScale(oy2, xvect, ox2, yvect, 16) + (height << 11); drawlinergb(sx1, sy1, sx2, sy2, WhiteLineColor()); } } + +//--------------------------------------------------------------------------- +// +// floor textures +// +//--------------------------------------------------------------------------- + +void renderDrawMapView(int cposx, int cposy, int czoom, int cang) +{ + int xvect = -bsin(cang) * czoom; + int yvect = -bcos(cang) * czoom; + int width = screen->GetWidth(); + int height = screen->GetHeight(); + TArray vertices; + TArray floorsprites; + + + for (int i = numsectors - 1; i >= 0; i--) + { + if (!gFullMap && !show2dsector[i]) continue; + + //Collect floor sprites to draw + SectIterator it(i); + int s; + while ((s = it.NextIndex()) >= 0) + { + if (sprite[s].cstat & CSTAT_SPRITE_INVISIBLE) + continue; + + if ((sprite[s].cstat & CSTAT_SPRITE_ALIGNMENT_MASK) == CSTAT_SPRITE_ALIGNMENT_FLOOR) + { + if ((sprite[s].cstat & (CSTAT_SPRITE_ONE_SIDED | CSTAT_SPRITE_YFLIP)) == (CSTAT_SPRITE_ONE_SIDED | CSTAT_SPRITE_YFLIP)) + continue; // upside down + floorsprites.Push(s); + } + } + + if (sector[i].floorstat & CSTAT_SECTOR_SKY) continue; + + int picnum = sector[i].floorpicnum; + if ((unsigned)picnum >= (unsigned)MAXTILES) continue; + + for (auto ii : sectionspersector[i]) + { + auto mesh = sectorGeometry.get(ii, 0, { 0.f,0.f }); + vertices.Resize(mesh->vertices.Size()); + for (unsigned j = 0; j < mesh->vertices.Size(); j++) + { + int ox = int(mesh->vertices[j].X * 16.f) - cposx; + int oy = int(mesh->vertices[j].Y * -16.f) - cposy; + int x1 = DMulScale(ox, xvect, -oy, yvect, 16) + (width << 11); + int y1 = DMulScale(oy, xvect, ox, yvect, 16) + (height << 11); + vertices[j] = { x1 / 4096.f, y1 / 4096.f, mesh->texcoords[j].X, mesh->texcoords[j].Y }; + } + } + + int translation = TRANSLATION(Translation_Remap + curbasepal, sector[i].floorpal); + setgotpic(picnum); + twod->AddPoly(tileGetTexture(picnum, true), vertices.Data(), vertices.Size(), nullptr, 0, translation, shadeToLight(sector[i].floorshade), + LegacyRenderStyles[STYLE_Translucent], windowxy1.x, windowxy1.y, windowxy2.x + 1, windowxy2.y + 1); + + + } + qsort(floorsprites.Data(), floorsprites.Size(), sizeof(int), [](const void* a, const void* b) + { + int A = *(int*)a; + int B = *(int*)b; + if (sprite[A].z != sprite[B].z) return sprite[B].z - sprite[A].z; + return A - B; // ensures stable sort. + }); + + vertices.Resize(4); + for (auto sn : floorsprites) + { + if (!gFullMap && !show2dsprite[sn]) continue; + auto spr = &sprite[sn]; + vec2_t pp[4]; + GetFlatSpritePosition(spr, spr->pos.vec2, pp, true); + + for (unsigned j = 0; j < 4; j++) + { + int ox = pp[j].x - cposx; + int oy = pp[j].y - cposy; + int x1 = DMulScale(ox, xvect, -oy, yvect, 16) + (width << 11); + int y1 = DMulScale(oy, xvect, ox, yvect, 16) + (height << 11); + vertices[j] = { x1 / 4096.f, y1 / 4096.f, j == 1 || j == 2 ? 1.f : 0.f, j == 2 || j == 3 ? 1.f : 0.f }; + } + int shade; + if ((sector[spr->sectnum].ceilingstat & CSTAT_SECTOR_SKY)) shade = sector[spr->sectnum].ceilingshade; + else shade = sector[spr->sectnum].floorshade; + shade += spr->shade; + PalEntry color = shadeToLight(shade); + FRenderStyle rs = LegacyRenderStyles[STYLE_Translucent]; + float alpha = 1; + if (spr->cstat & CSTAT_SPRITE_TRANSLUCENT) + { + rs = GetRenderStyle(0, !!(spr->cstat & CSTAT_SPRITE_TRANSLUCENT_INVERT)); + alpha = GetAlphaFromBlend((spr->cstat & CSTAT_SPRITE_TRANSLUCENT_INVERT) ? DAMETH_TRANS2 : DAMETH_TRANS1, 0); + color.a = uint8_t(alpha * 255); + } + + int translation = TRANSLATION(Translation_Remap + curbasepal, spr->pal); + int picnum = spr->picnum; + setgotpic(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); + } +} + //--------------------------------------------------------------------------- // // @@ -573,7 +684,6 @@ void DrawOverheadMap(int pl_x, int pl_y, int pl_angle, double const smoothratio) renderDrawMapView(x, y, gZoom, follow_a); } int32_t tmpydim = (width * 5) / 8; - renderSetAspect(65536, DivScale(tmpydim * 320, width * 200, 16)); drawredlines(x, y, gZoom, follow_a); drawwhitelines(x, y, gZoom, follow_a); diff --git a/source/core/binaryangle.h b/source/core/binaryangle.h index 3cdc8dc49..b843b1688 100644 --- a/source/core/binaryangle.h +++ b/source/core/binaryangle.h @@ -40,8 +40,8 @@ #include "m_fixed.h" #include "xs_Float.h" // needed for reliably overflowing float->int conversions. #include "serializer.h" -#include "build.h" #include "math/cmath.h" +#include "templates.h" class FSerializer; @@ -59,9 +59,9 @@ enum //--------------------------------------------------------------------------- constexpr double BAngRadian = pi::pi() * (1. / 1024.); -constexpr double BRadAngScale = 1. / BAngRadian; constexpr double BAngToDegree = 360. / 2048.; +extern int16_t sintable[2048]; //--------------------------------------------------------------------------- // @@ -69,11 +69,11 @@ constexpr double BAngToDegree = 360. / 2048.; // //--------------------------------------------------------------------------- -inline int32_t bsin(const int ang, const int8_t shift = 0) +inline int bsin(const int ang, const int shift = 0) { return shift < 0 ? sintable[ang & 2047] >> abs(shift) : sintable[ang & 2047] << shift; } -inline double bsinf(const double ang, const int8_t shift = 0) +inline double bsinf(const double ang, const int shift = 0) { return g_sin(ang * BAngRadian) * (shift >= -SINSHIFT ? uint64_t(1) << (SINSHIFT + shift) : 1. / (uint64_t(1) << abs(SINSHIFT + shift))); } @@ -85,156 +85,16 @@ inline double bsinf(const double ang, const int8_t shift = 0) // //--------------------------------------------------------------------------- -inline int32_t bcos(const int ang, const int8_t shift = 0) +inline int bcos(const int ang, const int shift = 0) { return shift < 0 ? sintable[(ang + 512) & 2047] >> abs(shift) : sintable[(ang + 512) & 2047] << shift; } -inline double bcosf(const double ang, const int8_t shift = 0) +inline double bcosf(const double ang, const int shift = 0) { return g_cos(ang * BAngRadian) * (shift >= -SINSHIFT ? uint64_t(1) << (SINSHIFT + shift) : 1. / (uint64_t(1) << abs(SINSHIFT + shift))); } -//--------------------------------------------------------------------------- -// -// Shift a Build angle left by 21 bits. -// -//--------------------------------------------------------------------------- - -inline constexpr int64_t BAngToBAM(int ang) -{ - return ang << BAMBITS; -} - - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -class lookangle -{ - int32_t value; - - constexpr lookangle(int32_t v) : value(v) {} - - friend constexpr lookangle bamlook(int32_t v); - friend constexpr lookangle q16look(int32_t v); - friend constexpr lookangle buildlook(int32_t v); - friend lookangle buildflook(double v); - friend lookangle radlook(double v); - friend lookangle deglook(double v); - - friend FSerializer &Serialize(FSerializer &arc, const char *key, lookangle &obj, lookangle *defval); - - friend class binangle; - -public: - lookangle() = default; - lookangle(const lookangle &other) = default; - // This class intentionally makes no allowances for implicit type conversions because those would render it ineffective. - constexpr short asbuild() const { return value >> 21; } - constexpr double asbuildf() const { return value * (1. / BAMUNIT); } - constexpr fixed_t asq16() const { return value >> 5; } - constexpr double asrad() const { return value * (pi::pi() / 0x80000000u); } - constexpr double asdeg() const { return AngleToFloat(value); } - constexpr int32_t asbam() const { return value; } - - double fsin() const { return g_sin(asrad()); } - double fcos() const { return g_cos(asrad()); } - double ftan() const { return g_tan(asrad()); } - int bsin(const int8_t& shift = 0) const { return ::bsin(asbuild(), shift); } - int bcos(const int8_t& shift = 0) const { return ::bcos(asbuild(), shift); } - - bool operator< (lookangle other) const - { - return value < other.value; - } - - bool operator> (lookangle other) const - { - return value > other.value; - } - - bool operator<= (lookangle other) const - { - return value <= other.value; - } - - bool operator>= (lookangle other) const - { - return value >= other.value; - } - constexpr bool operator== (lookangle other) const - { - return value == other.value; - } - - constexpr bool operator!= (lookangle other) const - { - return value != other.value; - } - - constexpr lookangle &operator+= (lookangle other) - { - value += other.value; - return *this; - } - - constexpr lookangle &operator-= (lookangle other) - { - value -= other.value; - return *this; - } - - constexpr lookangle operator+ (lookangle other) const - { - return lookangle(value + other.value); - } - - constexpr lookangle operator- (lookangle other) const - { - return lookangle(value - other.value); - } - - constexpr lookangle &operator<<= (const uint8_t shift) - { - value <<= shift; - return *this; - } - - constexpr lookangle &operator>>= (const uint8_t shift) - { - value >>= shift; - return *this; - } - - constexpr lookangle operator<< (const uint8_t shift) const - { - return lookangle(value << shift); - } - - constexpr lookangle operator>> (const uint8_t shift) const - { - return lookangle(value >> shift); - } - -}; - -inline constexpr lookangle bamlook(int32_t v) { return lookangle(v); } -inline constexpr lookangle q16look(int32_t v) { return lookangle(v << 5); } -inline constexpr lookangle buildlook(int32_t v) { return lookangle(v << BAMBITS); } -inline lookangle buildflook(double v) { return lookangle(xs_CRoundToInt(v * BAMUNIT)); } -inline lookangle radlook(double v) { return lookangle(xs_CRoundToInt(v * (0x80000000u / pi::pi()))); } -inline lookangle deglook(double v) { return lookangle(FloatToAngle(v)); } - -inline FSerializer &Serialize(FSerializer &arc, const char *key, lookangle &obj, lookangle *defval) -{ - return Serialize(arc, key, obj.value, defval ? &defval->value : nullptr); -} - - //--------------------------------------------------------------------------- // // @@ -260,12 +120,19 @@ public: binangle() = default; binangle(const binangle &other) = default; // This class intentionally makes no allowances for implicit type conversions because those would render it ineffective. - constexpr short asbuild() const { return value >> 21; } + constexpr int32_t tosigned() const { return value > INT32_MAX ? int64_t(value) - UINT32_MAX : value; } + constexpr short asbuild() const { return value >> BAMBITS; } constexpr double asbuildf() const { return value * (1. / BAMUNIT); } constexpr fixed_t asq16() const { return value >> 5; } + constexpr uint32_t asbam() const { return value; } constexpr double asrad() const { return value * (pi::pi() / 0x80000000u); } constexpr double asdeg() const { return AngleToFloat(value); } - constexpr uint32_t asbam() const { return value; } + constexpr short signedbuild() const { return tosigned() >> BAMBITS; } + constexpr double signedbuildf() const { return tosigned() * (1. / BAMUNIT); } + constexpr fixed_t signedq16() const { return tosigned() >> 5; } + constexpr int32_t signedbam() const { return tosigned(); } + constexpr double signedrad() const { return tosigned() * (pi::pi() / 0x80000000u); } + constexpr double signeddeg() const { return AngleToFloat(tosigned()); } double fsin() const { return g_sin(asrad()); } double fcos() const { return g_cos(asrad()); } @@ -305,28 +172,6 @@ public: return binangle(value - other.value); } - constexpr binangle &operator+= (lookangle other) - { - value += other.value; - return *this; - } - - constexpr binangle &operator-= (lookangle other) - { - value -= other.value; - return *this; - } - - constexpr binangle operator+ (lookangle other) const - { - return binangle(value + other.value); - } - - constexpr binangle operator- (lookangle other) const - { - return binangle(value - other.value); - } - constexpr binangle &operator<<= (const uint8_t shift) { value <<= shift; @@ -348,7 +193,6 @@ public: { return binangle(value >> shift); } - }; inline constexpr binangle bamang(uint32_t v) { return binangle(v); } @@ -366,24 +210,14 @@ inline FSerializer &Serialize(FSerializer &arc, const char *key, binangle &obj, //--------------------------------------------------------------------------- // -// Constants and functions for use with fixedhoriz and friendly functions. +// Functions for use with fixedhoriz and friendly functions. // //--------------------------------------------------------------------------- -// 280039127 is the maximum horizon in Q16.16 the engine will handle before wrapping around. -constexpr double horizDiff = 280039127 * 3. / 100.; - -// Degrees needed to convert horizAngle into pitch degrees. -constexpr double horizDegrees = 183.503609961216825; - -// Ratio to convert inverse tangent to -90/90 degrees of pitch. -constexpr double horizRatio = horizDegrees / pi::pi(); - -// Horizon conversion functions. -inline double HorizToPitch(double horiz) { return atan2(horiz, horizDiff / 65536.) * horizRatio; } -inline double HorizToPitch(fixed_t q16horiz) { return atan2(q16horiz, horizDiff) * horizRatio; } -inline fixed_t PitchToHoriz(double horizAngle) { return xs_CRoundToInt(horizDiff * tan(horizAngle * (pi::pi() / horizDegrees))); } -inline int32_t PitchToBAM(double horizAngle) { return xs_CRoundToInt(clamp(horizAngle * (1073741823.5 / 45.), -INT32_MAX, INT32_MAX)); } +inline double HorizToPitch(double horiz) { return atan2(horiz, 128) * (180. / pi::pi()); } +inline double HorizToPitch(fixed_t q16horiz) { return atan2(q16horiz, IntToFixed(128)) * (180. / pi::pi()); } +inline fixed_t PitchToHoriz(double pitch) { return xs_CRoundToInt(IntToFixed(128) * tan(pitch * (pi::pi() / 180.))); } +inline int32_t PitchToBAM(double pitch) { return xs_CRoundToInt(clamp(pitch * (1073741823.5 / 45.), -INT32_MAX, INT32_MAX)); } inline constexpr double BAMToPitch(int32_t bam) { return bam * (45. / 1073741823.5); } @@ -512,54 +346,38 @@ inline FSerializer &Serialize(FSerializer &arc, const char *key, fixedhoriz &obj //--------------------------------------------------------------------------- // -// Double-precision implementation of `getangle()` with associated wrappers and helper functions. +// High precision vector angle function, mainly for the renderer. // //--------------------------------------------------------------------------- -inline double bradarangf(const double& vect) +inline binangle bvectangbam(int32_t x, int32_t y) { - return atan(vect) * BRadAngScale; + return radang(atan2(y, x)); } -inline double bvectangf(const int32_t& x, const int32_t& y) + + +//--------------------------------------------------------------------------- +// +// Interpolation functions for use throughout games. +// +//--------------------------------------------------------------------------- + +inline int32_t interpolatedvalue(int32_t oval, int32_t val, double const smoothratio, int const scale = 16) { - if ((x | y) == 0) - { - return 0; - } - else if (x == 0) - { - return 512 + ((y < 0) << 10); - } - else if (y == 0) - { - return ((x < 0) << 10); - } - else if (x == y) - { - return 256 + ((x < 0) << 10); - } - else if (x == -y) - { - return 768 + ((x > 0) << 10); - } - else if (abs(x) > abs(y)) - { - return fmod(bradarangf(double(y) / x) + ((x < 0) << 10), 2048.); - } - else - { - return fmod(bradarangf(double(x) / -y) + 512 + ((y < 0) << 10), 2048.); - } + return oval + MulScale(val - oval, smoothratio, scale); } -inline int32_t bvectang(const int32_t& x, const int32_t& y) + +inline double interpolatedvaluef(double oval, double val, double const smoothratio, int const scale = 16) { - return xs_CRoundToInt(bvectangf(x, y)); + return oval + MulScaleF(val - oval, smoothratio, scale); } -inline fixed_t bvectangq16(const int32_t& x, const int32_t& y) + +inline int32_t interpolatedangle(int32_t oang, int32_t ang, double const smoothratio, int const scale = 16) { - return FloatToFixed(bvectangf(x, y)); + return oang + MulScale(((ang + 1024 - oang) & 2047) - 1024, smoothratio, scale); } -inline binangle bvectangbam(const int32_t& x, const int32_t& y) + +inline binangle interpolatedangle(binangle oang, binangle ang, double const smoothratio, int const scale = 16) { - return bamang(xs_CRoundToUInt(bvectangf(x, y) * BAMUNIT)); + return bamang(oang.asbam() + MulScale(((ang.asbam() + 0x80000000 - oang.asbam()) & 0xFFFFFFFF) - 0x80000000, smoothratio, scale)); } diff --git a/source/core/cheats.cpp b/source/core/cheats.cpp index 1a7086f8b..9b670931c 100644 --- a/source/core/cheats.cpp +++ b/source/core/cheats.cpp @@ -41,7 +41,6 @@ #include "c_dispatch.h" #include "d_net.h" #include "gamestate.h" -#include "mmulti.h" #include "gstrings.h" #include "gamecontrol.h" #include "screenjob.h" @@ -242,7 +241,7 @@ void changeMap(int player, uint8_t** stream, bool skip) void endScreenJob(int player, uint8_t** stream, bool skip) { - if (!skip) EndScreenJob(); + if (!skip) gameaction = ga_endscreenjob; } //--------------------------------------------------------------------------- @@ -280,6 +279,7 @@ void DeferedStartGame(MapRecord* map, int skill, bool nostopsound) static MapRecord* levelwarp_common(FCommandLine& argv, const char *cmdname, const char *t2) { int numparm = g_gameType & (GAMEFLAG_SW | GAMEFLAG_PSEXHUMED) ? 1 : 2; // Handle games with episodic and non-episodic level order. + if (numparm == 2 && argv.argc() == 2) numparm = 1; if (argv.argc() <= numparm) { if (numparm == 2) Printf(PRINT_BOLD, "%s : %s episode 'e' and map 'm'\n", cmdname, t2); @@ -294,7 +294,7 @@ static MapRecord* levelwarp_common(FCommandLine& argv, const char *cmdname, cons Printf(PRINT_BOLD, "Invalid level! Numbers must be > 0\n"); return nullptr; } - auto map = FindMapByLevelNum(numparm == 1 ? m : levelnum(e - 1, m - 1)); + auto map = FindMapByIndex(e, m); if (!map) { if (numparm == 2) Printf(PRINT_BOLD, "Level E%s L%s not found!\n", argv[1], argv[2]); diff --git a/source/core/d_net.cpp b/source/core/d_net.cpp index 5650ea43f..30c7891d1 100644 --- a/source/core/d_net.cpp +++ b/source/core/d_net.cpp @@ -63,7 +63,6 @@ #include "vm.h" #include "gstrings.h" #include "s_music.h" -#include "mmulti.h" #include "printf.h" #include "i_time.h" #include "d_ticcmd.h" diff --git a/source/core/d_net.h b/source/core/d_net.h index 1562c1dbf..db4b11f53 100644 --- a/source/core/d_net.h +++ b/source/core/d_net.h @@ -10,6 +10,9 @@ enum MAXPLAYERS = 8 }; +extern int myconnectindex, numplayers; +extern int connecthead, connectpoint2[MAXPLAYERS]; + class FDynamicBuffer { public: diff --git a/source/core/def_.cpp b/source/core/def_.cpp deleted file mode 100644 index 1801b958c..000000000 --- a/source/core/def_.cpp +++ /dev/null @@ -1,139 +0,0 @@ -/* -** def.cpp -** Rewritten .def parser free of Build license restrictions. -** -**--------------------------------------------------------------------------- -** Copyright 2020 Christoph Oelckers -** All rights reserved. -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions -** are met: -** -** 1. Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** 2. Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** 3. The name of the author may not be used to endorse or promote products -** derived from this software without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OFf -** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -**--------------------------------------------------------------------------- -** -*/ - - -#include "build.h" -#include "compat.h" - -#include "mdsprite.h" // md3model_t -#include "buildtiles.h" -#include "bitmap.h" -#include "m_argv.h" -#include "gamecontrol.h" -#include "palettecontainer.h" -#include "mapinfo.h" -#include "sc_man.h" - - - - - -bool ParseDefFile(const char* file, FScanner *parent) -{ - int lump = fileSystem.FindFile(file); - bool success = false; - if (lump == -1) - { - if (!parent) Printf(PRINT_BOLD, "%sd: file not found\n", file); - else parent->ScriptError("%s: file not found\n", file); - return false; - } - FScanner sc; - if (parent) sc.symbols = std::move(parent->symbols); // the child parser needs to add to the parent's symbol table so transfer its ownership for the parsing run. - sc.OpenLumpNum(lump); - sc.SetCMode(true); - sc.SetNoOctals(true); - sc.SetNoFatalErrors(true); - FString str; - - while (sc.GetString()) - { - if (sc.Compare({"#include", "include"})) - { - if (!sc.MustGetString()) - ParseDefFile(sc.String, &sc); - } - else if (sc.Compare({"#includedefault", "includedefault"})) - { - ParseDefFile(G_DefaultDefFile(), &sc); - } - else if (sc.Compare({"#define", "define"})) - { - parseDefine(sc); - } - else if (sc.Compare("definetexture")) - { - parseDefineTexture(sc); - } - else if (sc.Compare("defineskybox")) - { - parseDefineSkybox(sc); - } - else if (sc.Compare("definetint")) - { - parseDefineTint(sc); - } - else if (sc.Compare("alphahack")) // why 'hack'? - { - parseAlphaHack(sc); - } - else if (sc.Compare("alphahackrange")) // why 'hack'? - { - parseAlphaHackRange(sc); - } - if (sc.Compare({"spritecol", "2dcolidxrange")) // only used by Mapster32 so just read over them and ignore the result - { - parseDiscard<3>(sc); - } - else if (sc.Compare("2dcol")) // same here - { - parseDiscard<4>(sc); - } - else if (sc.Compare("fogpal")) - { - parseFogPal(sc); - } - else if (sc.Compare("nofloorpalrange")) - { - parseNoFloorpalRange(sc); - } - else if (sc.Compare("loadgrp")) - { - parseLoadGrp(sc); - } - else if (sc.Compare("cachesize") || sc.Compare("shadefactor")) - { - parseDiscard<1>(sc); - } - else if (sc.Compare("artfile")) - { - parseArtFile(sc); - } - - } - success = true; - - if (parent) parent->symbols = std::move(sc.symbols); - return success; -} \ No newline at end of file diff --git a/source/core/defparser.cpp b/source/core/defparser.cpp new file mode 100644 index 000000000..d237eab71 --- /dev/null +++ b/source/core/defparser.cpp @@ -0,0 +1,2159 @@ + +/* +** def.cpp +** +**--------------------------------------------------------------------------- +** Copyright 2021 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +** +*/ + +#include "build.h" +#include "compat.h" + +#include "mdsprite.h" // md3model_t +#include "buildtiles.h" +#include "bitmap.h" +#include "m_argv.h" +#include "gamecontrol.h" +#include "palettecontainer.h" +#include "mapinfo.h" +#include "hw_voxels.h" + +int tileSetHightileReplacement(int picnum, int palnum, const char* filename, float alphacut, float xscale, float yscale, float specpower, float specfactor); +int tileSetSkybox(int picnum, int palnum, FString* facenames); +void tileRemoveReplacement(int num); +void AddUserMapHack(usermaphack_t&); + +static void defsparser(FScanner& sc); + +static void performInclude(FScanner* sc, const char* fn, FScriptPosition* pos) +{ + int lump = fileSystem.FindFile(fn); + if (lump == -1) + { + if (!pos) Printf("Warning: Unable to open %s\n", fn); + else pos->Message(MSG_ERROR, "Unable to open %s", fn); + } + else + { + FScanner included; + included.OpenLumpNum(lump); + if (sc) included.symbols = std::move(sc->symbols); + defsparser(included); + if (sc) sc->symbols = std::move(included.symbols); + } +} + +void parseInclude(FScanner& sc, FScriptPosition& pos) +{ + sc.MustGetString(); + performInclude(&sc, sc.String, &pos); +} + +void parseIncludeDefault(FScanner& sc, FScriptPosition& pos) +{ + performInclude(&sc, G_DefaultDefFile(), &pos); +} + +//=========================================================================== +// +// +// +//=========================================================================== + +template +void parseSkip(FScanner& sc, FScriptPosition& pos) +{ + for (int i = 0; i < cnt; i++) if (!sc.GetString()) return; +} + +void parseDefine(FScanner& sc, FScriptPosition& pos) +{ + FString name; + if (!sc.GetString(name)) return; + if (!sc.GetNumber()) return; + sc.AddSymbol(name, sc.Number); +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void parseDefineTexture(FScanner& sc, FScriptPosition& pos) +{ + int tile, palette; + + if (!sc.GetNumber(tile, true)) return; + if (!sc.GetNumber(palette, true)) return; + if (!sc.GetNumber(true)) return; //formerly x-center, unused + if (!sc.GetNumber(true)) return; //formerly y-center, unused + if (!sc.GetNumber(true)) return; //formerly x-size, unused + if (!sc.GetNumber(true)) return; //formerly y-size, unused + if (!sc.GetString()) return; + + tileSetHightileReplacement(tile, palette, sc.String, -1.0, 1.0, 1.0, 1.0, 1.0); +} + +//=========================================================================== +// +// +// +//=========================================================================== + +static void parseTexturePaletteBlock(FScanner& sc, int tile) +{ + FScanner::SavedPos blockend; + FScriptPosition pos = sc; + + int pal = -1, xsiz = 0, ysiz = 0; + FString fn; + double alphacut = -1.0, xscale = 1.0, yscale = 1.0, specpower = 1.0, specfactor = 1.0; + + if (!sc.GetNumber(pal, true)) return; + + if (sc.StartBraces(&blockend)) return; + while (!sc.FoundEndBrace(blockend)) + { + sc.GetString(); + if (sc.Compare("file")) sc.GetString(fn); + else if (sc.Compare("alphacut")) sc.GetFloat(alphacut, true); + else if (sc.Compare({ "xscale", "scale", "intensity", "detailscale" })) sc.GetFloat(xscale, true); // what's the point of all of these names? + else if (sc.Compare("yscale")) sc.GetFloat(yscale, true); + else if (sc.Compare({ "specpower", "specularpower", "parallaxscale" })) sc.GetFloat(specpower, true); + else if (sc.Compare({ "specfactor", "specularfactor", "parallaxbias" })) sc.GetFloat(specfactor, true); + else if (sc.Compare("orig_sizex")) sc.GetNumber(xsiz, true); + else if (sc.Compare("orig_sizey")) sc.GetNumber(ysiz, true); + }; + + if ((unsigned)tile < MAXUSERTILES) + { + if ((unsigned)pal >= MAXREALPAL) pos.Message(MSG_ERROR, "texture (%d): invalid palette number %d ", tile, pal); + else if (fn.IsEmpty()) pos.Message(MSG_ERROR, "texture (%d): missing file name in palette definition", tile); + else if (!fileSystem.FileExists(fn)) pos.Message(MSG_ERROR, "texture (%d): file '%s' not found in palette definition", tile, fn.GetChars()); + else + { + if (xsiz > 0 && ysiz > 0) + { + tileSetDummy(tile, xsiz, ysiz); + } + xscale = 1.0f / xscale; + yscale = 1.0f / yscale; + + tileSetHightileReplacement(tile, pal, fn, alphacut, xscale, yscale, specpower, specfactor); + } + } +} + +static void parseTextureSpecialBlock(FScanner& sc, int tile, int pal) +{ + FScanner::SavedPos blockend; + FScriptPosition pos = sc; + + FString fn; + double xscale = 1.0, yscale = 1.0, specpower = 1.0, specfactor = 1.0; + + if (sc.StartBraces(&blockend)) return; + while (!sc.FoundEndBrace(blockend)) + { + sc.GetString(); + if (sc.Compare("file")) sc.GetString(fn); + else if (sc.Compare({ "xscale", "scale", "intensity", "detailscale" })) sc.GetFloat(xscale, true); // what's the point of all of these names? + else if (sc.Compare("yscale")) sc.GetFloat(yscale, true); + else if (sc.Compare({ "specpower", "specularpower", "parallaxscale" })) sc.GetFloat(specpower, true); + else if (sc.Compare({ "specfactor", "specularfactor", "parallaxbias" })) sc.GetFloat(specfactor, true); + }; + + if ((unsigned)tile < MAXUSERTILES) + { + if (fn.IsEmpty()) pos.Message(MSG_ERROR, "texture (%d): missing file name for layer definition", tile); + else if (!fileSystem.FileExists(fn)) pos.Message(MSG_ERROR, "texture (%d): file '%s' not found in layer definition", tile, fn.GetChars()); + else + { + if (pal == DETAILPAL) + { + xscale = 1.0f / xscale; + yscale = 1.0f / yscale; + } + + tileSetHightileReplacement(tile, pal, fn, -1.f, xscale, yscale, specpower, specfactor); + } + } +} + +void parseTexture(FScanner& sc, FScriptPosition& pos) +{ + FScanner::SavedPos blockend; + int tile = -1; + + if (!sc.GetNumber(tile, true)) return; + ValidateTilenum("texture", tile, pos); // do not abort, we still need to parse over the data. + + if (sc.StartBraces(&blockend)) return; + while (!sc.FoundEndBrace(blockend)) + { + sc.MustGetString(); + if (sc.Compare("pal")) parseTexturePaletteBlock(sc, tile); + else if (sc.Compare("detail")) parseTextureSpecialBlock(sc, tile, DETAILPAL); + else if (sc.Compare("glow")) parseTextureSpecialBlock(sc, tile, GLOWPAL); + else if (sc.Compare("specular")) parseTextureSpecialBlock(sc, tile, SPECULARPAL); + else if (sc.Compare("normal")) parseTextureSpecialBlock(sc, tile, NORMALPAL); + } +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void parseUndefTexture(FScanner& sc, FScriptPosition& pos) +{ + if (!sc.GetNumber(true)) return; + if (ValidateTilenum("undeftexture", sc.Number, pos)) tileRemoveReplacement(sc.Number); +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void parseUndefTextureRange(FScanner& sc, FScriptPosition& pos) +{ + int start, end; + if (!sc.GetNumber(start, true)) return; + if (!sc.GetNumber(end, true)) return; + if (ValidateTileRange("undeftexturerange", start, end, pos)) + for (int i = start; i <= end; i++) tileRemoveReplacement(i); +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void parseTileFromTexture(FScanner& sc, FScriptPosition& pos) +{ + FScanner::SavedPos blockend; + TileImport imp; + + if (!sc.GetNumber(imp.tile, true)) return; + + if (sc.StartBraces(&blockend)) return; + while (!sc.FoundEndBrace(blockend)) + { + sc.MustGetString(); + if (sc.Compare({ "file", "name" })) sc.GetString(imp.fn); + else if (sc.Compare("alphacut")) sc.GetNumber(imp.alphacut, true); + else if (sc.Compare({ "xoff", "xoffset" })) sc.GetNumber(imp.xoffset, true); + else if (sc.Compare({ "yoff", "yoffset" })) sc.GetNumber(imp.yoffset, true); + else if (sc.Compare("texhitscan")) imp.flags |= PICANM_TEXHITSCAN_BIT; + else if (sc.Compare("nofullbright")) imp.flags |= PICANM_NOFULLBRIGHT_BIT; + else if (sc.Compare("texture")) imp.istexture = 1; + else if (sc.Compare("ifcrc")) sc.GetNumber(imp.crc32, true); + else if (sc.Compare("extra")) sc.GetNumber(imp.extra, true); + else if (sc.Compare("surface")) sc.GetNumber(imp.surface, true); + else if (sc.Compare("voxel")) sc.GetNumber(imp.vox, true); + else if (sc.Compare("shade")) sc.GetNumber(imp.shade, true); + else if (sc.Compare("view")) { sc.GetNumber(imp.extra, true); imp.extra &= 7; } + else if (sc.Compare("ifmatch")) + { + FScanner::SavedPos blockend2; + if (sc.StartBraces(&blockend2)) return; + while (!sc.FoundEndBrace(blockend2)) + { + sc.MustGetString(); + if (sc.Compare("size")) + { + sc.GetNumber(imp.sizex, true); + sc.GetNumber(imp.sizey, true); + } + else if (sc.Compare("crc32")) sc.GetNumber(imp.crc32, true); + } + } + } + processTileImport("tilefromtexture", pos, imp); +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void parseCopyTile(FScanner& sc, FScriptPosition& pos) +{ + FScanner::SavedPos blockend; + int tile = -1, source = -1; + int havetile = 0, xoffset = -1024, yoffset = -1024; + int flags = 0, tsiz = 0, temppal = -1, tempsource = -1; + + if (!sc.GetNumber(tile, true)) return; + + if (sc.StartBraces(&blockend)) return; + while (!sc.FoundEndBrace(blockend)) + { + sc.MustGetString(); + if (sc.Compare("tile")) + { + if (sc.GetNumber(tempsource, true)) + { + if (ValidateTilenum("copytile", tempsource, pos)) + { + source = tempsource; + havetile = true; + } + } + } + else if (sc.Compare("pal")) + { + // This is a bit messy because it doesn't wait until everything is parsed. Sadly that quirk needs to be replicated... + if (sc.GetNumber(temppal, true)) + { + // palettize self case + if (!havetile) + { + if (ValidateTilenum("copytile", tile, pos)) havetile = true; + } + + if ((unsigned)temppal >= MAXREALPAL) + { + pos.Message(MSG_ERROR, "copytile: palette number %d out of range (max=%d)\n", MAXREALPAL - 1); + break; + } + } + } + else if (sc.Compare({ "xoff", "xoffset" })) sc.GetNumber(xoffset, true); + else if (sc.Compare({ "yoff", "yoffset" })) sc.GetNumber(yoffset, true); + else if (sc.Compare("texhitscan")) flags |= PICANM_TEXHITSCAN_BIT; + else if (sc.Compare("nofullbright")) flags |= PICANM_NOFULLBRIGHT_BIT; + } + + if (!ValidateTilenum("copytile", tile, pos)) return; + // if !havetile, we have never confirmed a valid source + if (!havetile && !ValidateTilenum("copytile", source, pos)) return; + + tileCopy(tile, source, temppal, xoffset, yoffset, flags); +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void parseImportTile(FScanner& sc, FScriptPosition& pos) +{ + int tile; + + if (!sc.GetNumber(tile, true)) return; + if (!sc.GetString()) return; + if (!ValidateTilenum("importtile", tile, pos)) return; + + int texstatus = tileImportFromTexture(sc.String, tile, 255, 0); + if (texstatus >= 0) TileFiles.tiledata[tile].picanm = {}; +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void parseDummyTile(FScanner& sc, FScriptPosition& pos) +{ + int tile, xsiz, ysiz; + + if (!sc.GetNumber(tile, true)) return; + if (!sc.GetNumber(xsiz, true)) return; + if (!sc.GetNumber(ysiz, true)) return; + if (!ValidateTilenum("dummytile", tile, pos)) return; + tileSetDummy(tile, xsiz, ysiz); +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void parseDummyTileRange(FScanner& sc, FScriptPosition& pos) +{ + int tile1, tile2, xsiz, ysiz; + + if (!sc.GetNumber(tile1, true)) return; + if (!sc.GetNumber(tile2, true)) return; + if (!sc.GetNumber(xsiz, true)) return; + if (!sc.GetNumber(ysiz, true)) return; + if (!ValidateTileRange("dummytilerange", tile1, tile2, pos)) return; + if (xsiz < 0 || ysiz < 0) return; + + for (int i = tile1; i <= tile2; i++) tileSetDummy(i, xsiz, ysiz); +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void parseUndefineTile(FScanner& sc, FScriptPosition& pos) +{ + int tile; + + if (!sc.GetNumber(tile, true)) return; + if (ValidateTilenum("undefinetile", tile, pos)) + tileDelete(tile); +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void parseUndefineTileRange(FScanner& sc, FScriptPosition& pos) +{ + int tile1, tile2; + + if (!sc.GetNumber(tile1, true)) return; + if (!sc.GetNumber(tile2, true)) return; + if (!ValidateTileRange("undefinetilerange", tile1, tile2, pos)) return; + + for (int i = tile1; i <= tile2; i++) tileDelete(i); +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void parseDefineSkybox(FScanner& sc, FScriptPosition& pos) +{ + int tile, palette; + FString fn[6]; + + if (!sc.GetNumber(tile, true)) return; + if (!sc.GetNumber(palette, true)) return; + if (!sc.GetNumber(true)) return; //'future extension' (for what?) + for (int i = 0; i < 6; i++) + { + if (!sc.GetString()) return; + fn[i] = sc.String; + } + tileSetSkybox(tile, palette, fn); +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void parseSkybox(FScanner& sc, FScriptPosition& pos) +{ + FString faces[6]; + FScanner::SavedPos blockend; + int tile = -1, pal = 0; + + if (sc.StartBraces(&blockend)) return; + while (!sc.FoundEndBrace(blockend)) + { + sc.MustGetString(); + if (sc.Compare("tile")) sc.GetNumber(tile, true); + else if (sc.Compare("pal")) sc.GetNumber(pal, true); + else if (sc.Compare({ "ft", "front", "forward" })) sc.GetString(faces[0]); + else if (sc.Compare({ "rt", "right" })) sc.GetString(faces[1]); + else if (sc.Compare({ "bk", "back" })) sc.GetString(faces[2]); + else if (sc.Compare({ "lt", "lf", "left" })) sc.GetString(faces[3]); + else if (sc.Compare({ "up", "ceiling", "top", "ceil" })) sc.GetString(faces[4]); + else if (sc.Compare({ "dn", "floor", "bottom", "down" })) sc.GetString(faces[5]); + // skip over everything else. + } + if (tile < 0) pos.Message(MSG_ERROR, "skybox: missing tile number"); + else tileSetSkybox(tile, pal, faces); +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void parseSetupTile(FScanner& sc, FScriptPosition& pos) +{ + int tile; + if (!sc.GetNumber(tile, true)) return; + if (!ValidateTilenum("setuptile", tile, pos)) return; + auto& tiled = TileFiles.tiledata[tile]; + if (!sc.GetNumber(tiled.hiofs.xsize, true)) return; + if (!sc.GetNumber(tiled.hiofs.ysize, true)) return; + if (!sc.GetNumber(tiled.hiofs.xoffs, true)) return; + if (!sc.GetNumber(tiled.hiofs.yoffs, true)) return; +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void parseSetupTileRange(FScanner& sc, FScriptPosition& pos) +{ + int tilestart, tileend; + if (!sc.GetNumber(tilestart, true)) return; + if (!sc.GetNumber(tileend, true)) return; + if (!ValidateTileRange("setuptilerange", tilestart, tileend, pos)) return; + + TileOffs hiofs; + if (!sc.GetNumber(hiofs.xsize, true)) return; + if (!sc.GetNumber(hiofs.ysize, true)) return; + if (!sc.GetNumber(hiofs.xoffs, true)) return; + if (!sc.GetNumber(hiofs.yoffs, true)) return; + + for (int i = tilestart; i <= tileend; i++) TileFiles.tiledata[i].hiofs = hiofs; +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void parseAnimTileRange(FScanner& sc, FScriptPosition& pos) +{ + SetAnim set; + if (!sc.GetNumber(set.tile1, true)) return; + if (!sc.GetNumber(set.tile2, true)) return; + if (!sc.GetNumber(set.speed, true)) return; + if (!sc.GetNumber(set.type, true)) return; + processSetAnim("animtilerange", pos, set); +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void parseAlphahack(FScanner& sc, FScriptPosition& pos) +{ + int tile; + + if (!sc.GetNumber(tile, true)) return; + if (!sc.GetFloat(true)) return; + if ((unsigned)tile < MAXTILES) TileFiles.tiledata[tile].texture->alphaThreshold = (float)sc.Float; +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void parseAlphahackRange(FScanner& sc, FScriptPosition& pos) +{ + int tilestart, tileend; + + if (!sc.GetNumber(tilestart, true)) return; + if (!sc.GetNumber(tileend, true)) return; + if (!sc.GetFloat(true)) return; + if (!ValidateTileRange("alphahackrange", tilestart, tileend, pos)) return; + + for (int i = tilestart; i <= tileend; i++) + TileFiles.tiledata[i].texture->alphaThreshold = (float)sc.Number; +} + +//=========================================================================== +// +// +// +//=========================================================================== +static int lastvoxid = -1; + +void parseDefineVoxel(FScanner& sc, FScriptPosition& pos) +{ + sc.MustGetString(); + while (nextvoxid < MAXVOXELS && voxreserve[nextvoxid]) nextvoxid++; + + if (nextvoxid == MAXVOXELS) + { + pos.Message(MSG_ERROR, "Maximum number of voxels (%d) already defined.", MAXVOXELS); + return; + } + + if (voxDefine(nextvoxid, sc.String)) + { + pos.Message(MSG_ERROR, "Unable to load voxel file \"%s\"", sc.String); + return; + } + + lastvoxid = nextvoxid++; +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void parseDefineVoxelTiles(FScanner& sc, FScriptPosition& pos) +{ + int tilestart, tileend; + if (!sc.GetNumber(tilestart, true)) return; + if (!sc.GetNumber(tileend, true)) return; + if (!ValidateTileRange("definevoxeltiles", tilestart, tileend, pos)) return; + + if (lastvoxid < 0) + { + pos.Message(MSG_WARNING, "Warning: Ignoring voxel tiles definition without valid voxel.\n"); + return; + } + for (int i = tilestart; i <= tileend; i++) tiletovox[i] = lastvoxid; +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void parseVoxel(FScanner& sc, FScriptPosition& pos) +{ + FScanner::SavedPos blockend; + int tile0 = MAXTILES, tile1 = -1; + FString fn; + + if (!sc.GetString(fn)) return; + + while (nextvoxid < MAXVOXELS && voxreserve[nextvoxid]) nextvoxid++; + + if (nextvoxid == MAXVOXELS) + { + pos.Message(MSG_ERROR, "Maximum number of voxels (%d) already defined.", MAXVOXELS); + return; + } + + if (voxDefine(nextvoxid, fn)) + { + pos.Message(MSG_ERROR, "Unable to load voxel file \"%s\"", fn.GetChars()); + return; + } + + int lastvoxid = nextvoxid++; + + if (sc.StartBraces(&blockend)) return; + while (!sc.FoundEndBrace(blockend)) + { + sc.MustGetString(); + if (sc.Compare("tile")) + { + sc.GetNumber(true); + if (ValidateTilenum("voxel", sc.Number, pos)) tiletovox[sc.Number] = lastvoxid; + } + if (sc.Compare("tile0")) sc.GetNumber(tile0, true); + if (sc.Compare("tile1")) + { + sc.GetNumber(tile1, true); + if (ValidateTileRange("voxel", tile0, tile1, pos)) + { + for (int i = tile0; i <= tile1; i++) tiletovox[i] = lastvoxid; + } + } + if (sc.Compare("scale")) + { + sc.GetFloat(true); + voxscale[lastvoxid] = (float)sc.Float; + } + if (sc.Compare("rotate")) voxrotate.Set(lastvoxid); + } +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void parseDefineTint(FScanner& sc, FScriptPosition& pos) +{ + int pal, r, g, b, f; + + if (!sc.GetNumber(pal, true)) return; + if (!sc.GetNumber(r)) return; + if (!sc.GetNumber(g)) return; + if (!sc.GetNumber(b)) return; + if (!sc.GetNumber(f)) return; + lookups.setPaletteTint(pal, r, g, b, 0, 0, 0, f); +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void parseFogpal(FScanner& sc, FScriptPosition& pos) +{ + int pal, r, g, b; + + if (!sc.GetNumber(pal, true)) return; + if (!sc.GetNumber(r)) return; + if (!sc.GetNumber(g)) return; + if (!sc.GetNumber(b)) return; + + r = clamp(r, 0, 63); + g = clamp(g, 0, 63); + b = clamp(b, 0, 63); + + lookups.makeTable(pal, nullptr, r << 2, g << 2, b << 2, 1); +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void parseNoFloorpalRange(FScanner& sc, FScriptPosition& pos) +{ + int start, end; + if (!sc.GetNumber(start, true)) return; + if (!sc.GetNumber(end, true)) return; + if (start > 1) start = 1; + if (end > MAXPALOOKUPS - 1) end = MAXPALOOKUPS - 1; + for (int i = start; i <= end; i++) + lookups.tables[i].noFloorPal = true; +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void parseTint(FScanner& sc, FScriptPosition& pos) +{ + int red = 255, green = 255, blue = 255, shadered = 0, shadegreen = 0, shadeblue = 0, pal = -1, flags = 0; + FScanner::SavedPos blockend; + + if (sc.StartBraces(&blockend)) return; + while (!sc.FoundEndBrace(blockend)) + { + sc.MustGetString(); + if (sc.Compare("pal")) sc.GetNumber(pal, true); + else if (sc.Compare({"red", "r"})) sc.GetNumber(red); + else if (sc.Compare({ "green", "g" })) sc.GetNumber(green); + else if (sc.Compare({ "blue", "b" })) sc.GetNumber(blue); + else if (sc.Compare({ "shadered", "sr" })) sc.GetNumber(shadered); + else if (sc.Compare({ "shadegreen", "sg" })) sc.GetNumber(shadegreen); + else if (sc.Compare({ "shadeblue", "sb" })) sc.GetNumber(shadeblue); + else if (sc.Compare("flags")) sc.GetNumber(flags, true); + } + + if (pal < 0) + pos.Message(MSG_ERROR, "tint: palette number missing"); + else + lookups.setPaletteTint(pal, clamp(red, 0, 255), clamp(green, 0, 255), clamp(blue, 0, 255), + clamp(shadered, 0, 255), clamp(shadegreen, 0, 255), clamp(shadeblue, 0, 255), flags); +} + + +//=========================================================================== +// +// +// +//=========================================================================== + +void parseMusic(FScanner& sc, FScriptPosition& pos) +{ + FString id, file; + FScanner::SavedPos blockend; + + if (sc.StartBraces(&blockend)) return; + while (!sc.FoundEndBrace(blockend)) + { + sc.MustGetString(); + if (sc.Compare("id")) sc.GetString(id); + else if (sc.Compare("file")) sc.GetString(file); + } + SetMusicForMap(id, file, true); +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void parseMapinfo(FScanner& sc, FScriptPosition& pos) +{ + usermaphack_t mhk; + FScanner::SavedPos blockend; + + if (sc.StartBraces(&blockend)) return; + while (!sc.FoundEndBrace(blockend)) + { + sc.MustGetString(); + if (sc.Compare("mapfile")) sc.GetString(); + else if (sc.Compare("maptitle")) sc.GetString(mhk.title); + else if (sc.Compare("mhkfile")) sc.GetString(mhk.mhkfile); + else if (sc.Compare("mapmd4")) + { + sc.GetString(); + for (int i = 0; i < 16; i++) + { + char smallbuf[3] = { sc.String[2 * i], sc.String[2 * i + 1], 0 }; + mhk.md4[i] = strtol(smallbuf, nullptr, 16); + } + } + } + AddUserMapHack(mhk); +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void parseEcho(FScanner& sc, FScriptPosition& pos) +{ + sc.MustGetString(); + Printf("%s\n", sc.String); +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void parseMultiPsky(FScanner& sc, FScriptPosition& pos) +{ + usermaphack_t mhk; + FScanner::SavedPos blockend; + psky_t sky{}; + + sky.yscale = 65536; + if (!sc.GetNumber(sky.tilenum, true)) return; + + if (sc.StartBraces(&blockend)) return; + while (!sc.FoundEndBrace(blockend)) + { + sc.MustGetString(); + if (sc.Compare("horizfrac")) sc.GetNumber(sky.horizfrac, true); + else if (sc.Compare("yoffset")) sc.GetNumber(sky.yoffs, true); + else if (sc.Compare("lognumtiles")) sc.GetNumber(sky.lognumtiles, true); + else if (sc.Compare("yscale")) sc.GetNumber(sky.yscale, true); + else if (sc.Compare({ "tile", "panel" })) + { + int panel, offset; + sc.GetNumber(panel, true); + sc.GetNumber(offset, true); + if ((unsigned)panel < MAXPSKYTILES && (unsigned)offset <= PSKYOFF_MAX) sky.tileofs[panel] = offset; + } + } + + if (sky.tilenum != DEFAULTPSKY && (unsigned)sky.tilenum >= MAXUSERTILES) return; + if ((1 << sky.lognumtiles) > MAXPSKYTILES) return; + auto psky = tileSetupSky(sky.tilenum); + *psky = sky; +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void parseRffDefineId(FScanner& sc, FScriptPosition& pos) +{ + FString resName; + FString resType; + int resID; + + if (!sc.GetString(resName)) return; + if (!sc.GetString(resType)) return; + if (!sc.GetNumber(resID)) return; + if (!sc.GetString()) return; + resName.AppendFormat(".%s", resType.GetChars()); + fileSystem.CreatePathlessCopy(resName, resID, 0); +} + +//=========================================================================== +// +// empty stub +// +//=========================================================================== + +void parseEmptyBlock(FScanner& sc, FScriptPosition& pos) +{ + FScanner::SavedPos blockend; + + if (sc.StartBraces(&blockend)) return; + sc.RestorePos(blockend); + sc.CheckString("}"); +} + +void parseEmptyBlockWithParm(FScanner& sc, FScriptPosition& pos) +{ + FScanner::SavedPos blockend; + + sc.MustGetString(); + if (sc.StartBraces(&blockend)) return; + sc.RestorePos(blockend); + sc.CheckString("}"); +} + + +//=========================================================================== +// +// +// +//=========================================================================== + +void parseTexHitscanRange(FScanner& sc, FScriptPosition& pos) +{ + int start, end; + + if (!sc.GetNumber(start, true)) return; + if (!sc.GetNumber(end, true)) return; + + if (start < 0) start = 0; + if (end >= MAXUSERTILES) end = MAXUSERTILES - 1; + for (int i = start; i <= end; i++) + TileFiles.tiledata[i].picanm.sf |= PICANM_TEXHITSCAN_BIT; +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void parseNoFullbrightRange(FScanner& sc, FScriptPosition& pos) +{ + int start, end; + + if (!sc.GetNumber(start, true)) return; + if (!sc.GetNumber(end, true)) return; + + if (start < 0) start = 0; + if (end >= MAXUSERTILES) end = MAXUSERTILES - 1; + for (int i = start; i <= end; i++) + { + auto tex = tileGetTexture(i); + if (tex->isValid()) tex->SetDisableBrightmap(); + } +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void parseArtFile(FScanner& sc, FScriptPosition& pos) +{ + FScanner::SavedPos blockend; + FString file; + int tile = -1; + + if (sc.StartBraces(&blockend)) return; + while (!sc.FoundEndBrace(blockend)) + { + sc.MustGetString(); + if (sc.Compare("file")) sc.GetString(file); + else if (sc.Compare("tile")) sc.GetNumber(tile, true); + } + + if (file.IsEmpty()) + { + pos.Message(MSG_ERROR, "artfile: missing file name"); + } + else if (tile >= 0 && ValidateTilenum("artfile", tile, pos)) + TileFiles.LoadArtFile(file, nullptr, tile); +} + +//=========================================================================== +// +// this is only left in for compatibility purposes. +// There's way better methods to handle translucency. +// +//=========================================================================== + +static void parseBlendTableGlBlend(FScanner& sc, int id) +{ + FScanner::SavedPos blockend; + FScriptPosition pos = sc; + FString file; + + if (sc.StartBraces(&blockend)) return; + + glblend_t* const glb = glblend + id; + *glb = nullglblend; + + while (!sc.FoundEndBrace(blockend)) + { + int which = 0; + sc.MustGetString(); + if (sc.Compare("forward")) which = 1; + else if (sc.Compare("reverse")) which = 2; + else if (sc.Compare("both")) which = 3; + else continue; + + FScanner::SavedPos blockend2; + + if (sc.StartBraces(&blockend2)) return; + glblenddef_t bdef{}; + while (!sc.FoundEndBrace(blockend2)) + { + int whichb = 0; + sc.MustGetString(); + if (sc.Compare("}")) break; + if (sc.Compare({ "src", "sfactor", "top" })) whichb = 0; + else if (sc.Compare({ "dst", "dfactor", "bottom" })) whichb = 1; + else if (sc.Compare("alpha")) + { + sc.GetFloat(true); + bdef.alpha = (float)sc.Float; + continue; + } + uint8_t* const factor = whichb == 0 ? &bdef.src : &bdef.dst; + sc.MustGetString(); + if (sc.Compare("ZERO")) *factor = STYLEALPHA_Zero; + else if (sc.Compare("ONE")) *factor = STYLEALPHA_One; + else if (sc.Compare("SRC_COLOR")) *factor = STYLEALPHA_SrcCol; + else if (sc.Compare("ONE_MINUS_SRC_COLOR")) *factor = STYLEALPHA_InvSrcCol; + else if (sc.Compare("SRC_ALPHA")) *factor = STYLEALPHA_Src; + else if (sc.Compare("ONE_MINUS_SRC_ALPHA")) *factor = STYLEALPHA_InvSrc; + else if (sc.Compare("DST_ALPHA")) *factor = STYLEALPHA_Dst; + else if (sc.Compare("ONE_MINUS_DST_ALPHA")) *factor = STYLEALPHA_InvDst; + else if (sc.Compare("DST_COLOR")) *factor = STYLEALPHA_DstCol; + else if (sc.Compare("ONE_MINUS_DST_COLOR")) *factor = STYLEALPHA_InvDstCol; + else sc.ScriptMessage("Unknown blend operation %s", sc.String); + } + if (which & 1) glb->def[0] = bdef; + if (which & 2) glb->def[1] = bdef; + } +} + +void parseBlendTable(FScanner& sc, FScriptPosition& pos) +{ + FScanner::SavedPos blockend; + int id; + + if (!sc.GetNumber(id, true)) return; + + if (sc.StartBraces(&blockend)) return; + + if ((unsigned)id >= MAXBLENDTABS) + { + pos.Message(MSG_ERROR, "blendtable: Invalid blendtable number %d", id); + sc.RestorePos(blockend); + return; + } + + while (!sc.FoundEndBrace(blockend)) + { + sc.MustGetString(); + if (sc.Compare("raw")) parseEmptyBlock(sc, pos); // Raw translucency map for the software renderer. We have no use for this. + else if (sc.Compare("glblend")) parseBlendTableGlBlend(sc, id); + else if (sc.Compare("undef")) glblend[id] = defaultglblend; + else if (sc.Compare("copy")) + { + sc.GetNumber(true); + + if ((unsigned)sc.Number >= MAXBLENDTABS || sc.Number == id) + { + pos.Message(MSG_ERROR, "blendtable: Invalid source blendtable number %d in copy", sc.Number); + } + else glblend[id] = glblend[sc.Number]; + } + } +} + +//=========================================================================== +// +// thw same note as for blendtable applies here. +// +//=========================================================================== + +void parseNumAlphaTabs(FScanner& sc, FScriptPosition& pos) +{ + int value; + if (!sc.GetNumber(value)) return; + + for (int a = 1, value2 = value * 2 + (value & 1); a <= value; ++a) + { + float finv2value = 1.f / (float)value2; + + glblend_t* const glb = glblend + a; + *glb = defaultglblend; + glb->def[0].alpha = (float)(value2 - a) * finv2value; + glb->def[1].alpha = (float)a * finv2value; + } +} + +//=========================================================================== +// +// +// +//=========================================================================== + +static bool parseBasePaletteRaw(FScanner& sc, FScriptPosition& pos, int id) +{ + FScanner::SavedPos blockend; + FString fn; + int32_t offset = 0; + int32_t shiftleft = 0; + + if (sc.StartBraces(&blockend)) return false; + + while (!sc.FoundEndBrace(blockend)) + { + sc.MustGetString(); + if (sc.Compare("file")) sc.GetString(fn); + else if (sc.Compare("offset")) sc.GetNumber(offset, true); + else if (sc.Compare("shiftleft")) sc.GetNumber(shiftleft, true); + } + + if (fn.IsEmpty()) + { + pos.Message(MSG_ERROR, "basepalette: filename missing"); + } + else if (offset < 0) + { + pos.Message(MSG_ERROR, "basepalette: Invalid file offset"); + } + else if ((unsigned)shiftleft >= 8) + { + pos.Message(MSG_ERROR, "basepalette: Invalid left shift %d provided", shiftleft); + } + else + { + FileReader fil = fileSystem.OpenFileReader(fn); + if (!fil.isOpen()) + { + pos.Message(MSG_ERROR, "basepalette: Failed opening \"%s\"", fn.GetChars()); + } + else + { + fil.Seek(offset, FileReader::SeekSet); + auto palbuf = fil.Read(); + if (palbuf.Size() < 768) + { + pos.Message(MSG_ERROR, "basepalette: Read failed"); + } + else + { + if (shiftleft != 0) + { + for (bssize_t k = 0; k < 768; k++) + palbuf[k] <<= shiftleft; + } + + paletteSetColorTable(id, palbuf.Data(), false, false); + return true; + } + } + } + return false; +} + +void parseBasePalette(FScanner& sc, FScriptPosition& pos) +{ + FScanner::SavedPos blockend; + int id; + bool didLoadPal = false; + + if (!sc.GetNumber(id)) return; + + if (sc.StartBraces(&blockend)) return; + + if ((unsigned)id >= MAXBASEPALS) + { + pos.Message(MSG_ERROR, "basepalette: Invalid basepal number %d", id); + sc.RestorePos(blockend); + sc.CheckString("{"); + return; + } + + while (!sc.FoundEndBrace(blockend)) + { + sc.MustGetString(); + if (sc.Compare("raw")) didLoadPal |= parseBasePaletteRaw(sc, pos, id); + else if (sc.Compare("copy")) + { + int source = -1; + sc.GetNumber(source); + + if ((unsigned)source >= MAXBASEPALS || source == id) + { + pos.Message(MSG_ERROR, "basepalette: Invalid source basepal number %d", source); + } + else + { + auto sourcepal = GPalette.GetTranslation(Translation_BasePalettes, source); + if (sourcepal == nullptr) + { + pos.Message(MSG_ERROR, "basepalette: Source basepal %d does not exist", source); + } + else + { + GPalette.CopyTranslation(TRANSLATION(Translation_BasePalettes, id), TRANSLATION(Translation_BasePalettes, source)); + didLoadPal = true; + } + } + } + else if (sc.Compare("undef")) + { + GPalette.ClearTranslationSlot(TRANSLATION(Translation_BasePalettes, id)); + didLoadPal = 0; + if (id == 0) paletteloaded &= ~PALETTE_MAIN; + } + } + + if (didLoadPal && id == 0) + { + paletteloaded |= PALETTE_MAIN; + } +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void parseUndefBasePaletteRange(FScanner& sc, FScriptPosition& pos) +{ + int start, end; + + if (!sc.GetNumber(start)) return; + if (!sc.GetNumber(end)) return; + + if (start > end || (unsigned)start >= MAXBASEPALS || (unsigned)end >= MAXBASEPALS) + { + pos.Message(MSG_ERROR, "undefbasepaletterange: Invalid range [%d, %d]", start, end); + return; + } + for (int i = start; i <= end; i++) GPalette.ClearTranslationSlot(TRANSLATION(Translation_BasePalettes, i)); + if (start == 0) paletteloaded &= ~PALETTE_MAIN; +} + +//=========================================================================== +// +// sadly this looks broken by design with its hard coded 32 shades... +// +//=========================================================================== + +static void parsePalookupRaw(FScanner& sc, int id, int& didLoadShade) +{ + FScanner::SavedPos blockend; + FScriptPosition pos = sc; + + if (sc.StartBraces(&blockend)) return; + + FString fn; + int32_t offset = 0; + int32_t length = 256 * 32; // hardcoding 32 instead of numshades + + while (!sc.FoundEndBrace(blockend)) + { + sc.MustGetString(); + if (sc.Compare("file")) sc.GetString(fn); + else if (sc.Compare("offset")) sc.GetNumber(offset); + else if (sc.Compare("noshades")) length = 256; + } + + if (fn.IsEmpty()) + { + pos.Message(MSG_ERROR, "palookup: filename missing"); + } + else if (offset < 0) + { + pos.Message(MSG_ERROR, "palookup: Invalid file offset %d", offset); + } + else + { + FileReader fil = fileSystem.OpenFileReader(fn); + if (!fil.isOpen()) + { + pos.Message(MSG_ERROR, "palookup: Failed opening \"%s\"", fn.GetChars()); + } + else + { + fil.Seek(offset, FileReader::SeekSet); + auto palookupbuf = fil.Read(); + if (palookupbuf.Size() < 256) + { + pos.Message(MSG_ERROR, "palookup: Read failed"); + } + else if (palookupbuf.Size() >= 256 * 32) + { + didLoadShade = 1; + numshades = 32; + lookups.setTable(id, palookupbuf.Data()); + } + else + { + if (!(paletteloaded & PALETTE_SHADE)) + { + pos.Message(MSG_ERROR, "palookup: Shade tables not loaded"); + } + else + lookups.makeTable(id, palookupbuf.Data(), 0, 0, 0, lookups.tables[id].noFloorPal); + } + } + } +} + +static void parsePalookupFogpal(FScanner& sc, int id) +{ + FScanner::SavedPos blockend; + FScriptPosition pos = sc; + + if (sc.StartBraces(&blockend)) return; + + int red = 0, green = 0, blue = 0; + + while (!sc.FoundEndBrace(blockend)) + { + sc.MustGetString(); + if (sc.Compare({ "r", "red" })) sc.GetNumber(red); + else if (sc.Compare({ "g", "green" })) sc.GetNumber(green); + else if (sc.Compare({ "b", "blue" })) sc.GetNumber(blue); + } + red = clamp(red, 0, 255); + green = clamp(green, 0, 255); + blue = clamp(blue, 0, 255); + + if (!(paletteloaded & PALETTE_SHADE)) + { + pos.Message(MSG_ERROR, "palookup: Shade tables not loaded"); + } + else + lookups.makeTable(id, nullptr, red, green, blue, 1); +} + +static void parsePalookupMakePalookup(FScanner& sc, FScriptPosition& pos, int id, int& didLoadShade) +{ + FScanner::SavedPos blockend; + + if (sc.StartBraces(&blockend)) return; + + int red = 0, green = 0, blue = 0; + int remappal = -1; + + while (!sc.FoundEndBrace(blockend)) + { + sc.MustGetString(); + if (sc.Compare({ "r", "red" })) sc.GetNumber(red); + else if (sc.Compare({ "g", "green" })) sc.GetNumber(green); + else if (sc.Compare({ "b", "blue" })) sc.GetNumber(blue); + else if (sc.Compare("remappal")) sc.GetNumber(remappal, true); + else if (sc.Compare("remapself")) remappal = id; + } + red = clamp(red, 0, 255); + green = clamp(green, 0, 255); + blue = clamp(blue, 0, 255); + + if ((unsigned)remappal >= MAXPALOOKUPS) + { + pos.Message(MSG_ERROR, "palookup: Invalid remappal %d", remappal); + } + else if (!(paletteloaded & PALETTE_SHADE)) + { + pos.Message(MSG_ERROR, "palookup: Shade tables not loaded"); + } + else + lookups.makeTable(id, nullptr, red, green, blue, lookups.tables[id].noFloorPal); +} + +void parsePalookup(FScanner& sc, FScriptPosition& pos) +{ + FScanner::SavedPos blockend; + int id; + int didLoadShade = 0; + + if (!sc.GetNumber(id, true)) return; + if (sc.StartBraces(&blockend)) return; + + if ((unsigned)id >= MAXPALOOKUPS) + { + pos.Message(MSG_ERROR, "palookup: Invalid palette number %d", id); + sc.RestorePos(blockend); + return; + } + + while (!sc.FoundEndBrace(blockend)) + { + sc.MustGetString(); + if (sc.Compare("raw")) parsePalookupRaw(sc, id, didLoadShade); + else if (sc.Compare("fogpal")) parsePalookupFogpal(sc, id); + else if (sc.Compare("makepalookup")) parsePalookupMakePalookup(sc, pos, id, didLoadShade); + else if (sc.Compare("floorpal")) lookups.tables[id].noFloorPal = 0; + else if (sc.Compare("nofloorpal")) lookups.tables[id].noFloorPal = 1; + else if (sc.Compare("copy")) + { + int source; + sc.GetNumber(source, true); + + if ((unsigned)source >= MAXPALOOKUPS || source == id) + { + pos.Message(MSG_ERROR, "palookup: Invalid source pal number %d", source); + } + else if (source == 0 && !(paletteloaded & PALETTE_SHADE)) + { + pos.Message(MSG_ERROR, "palookup: Shade tables not loaded"); + } + else + { + // do not overwrite the base with an empty table. + if (lookups.checkTable(source) || id > 0) lookups.copyTable(id, source); + didLoadShade = 1; + } + } + else if (sc.Compare("undef")) + { + lookups.clearTable(id); + didLoadShade = 0; + if (id == 0) paletteloaded &= ~PALETTE_SHADE; + } + + } + if (didLoadShade && id == 0) + { + paletteloaded |= PALETTE_SHADE; + } +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void parseMakePalookup(FScanner& sc, FScriptPosition& pos) +{ + FScanner::SavedPos blockend; + int red = 0, green = 0, blue = 0, pal = -1; + int remappal = 0; + int nofloorpal = -1; + bool havepal = false, haveremappal = false, haveremapself = false; + + if (sc.StartBraces(&blockend)) return; + + while (!sc.FoundEndBrace(blockend)) + { + sc.MustGetString(); + if (sc.Compare({ "r", "red" })) sc.GetNumber(red); + else if (sc.Compare({ "g", "green" })) sc.GetNumber(green); + else if (sc.Compare({ "b", "blue" })) sc.GetNumber(blue); + else if (sc.Compare("remappal")) + { + sc.GetNumber(remappal, true); + haveremappal = true; + } + else if (sc.Compare("remapself")) + { + haveremapself = true; + } + else if (sc.Compare("nofloorpal")) sc.GetNumber(nofloorpal, true); + else if (sc.Compare("pal")) + { + havepal = true; + sc.GetNumber(pal, true); + } + } + red = clamp(red, 0, 63); + green = clamp(green, 0, 63); + blue = clamp(blue, 0, 63); + + if (!havepal) + { + pos.Message(MSG_ERROR, "makepalookup: missing palette number"); + } + else if (pal == 0 || (unsigned)pal >= MAXREALPAL) + { + pos.Message(MSG_ERROR, "makepalookup: palette number %d out of range (1 .. %d)", pal, MAXREALPAL - 1); + } + else if (haveremappal && haveremapself) + { + // will also disallow multiple remappals or remapselfs + pos.Message(MSG_ERROR, "makepalookup: must have either 'remappal' or 'remapself' but not both"); + } + else if ((haveremappal && (unsigned)remappal >= MAXREALPAL)) + { + pos.Message(MSG_ERROR, "makepalookup: remap palette number %d out of range (0 .. %d)", pal, MAXREALPAL - 1); + } + else + { + if (haveremapself) remappal = pal; + lookups.makeTable(pal, lookups.getTable(remappal), red << 2, green << 2, blue << 2, + remappal == 0 ? 1 : (nofloorpal == -1 ? lookups.tables[remappal].noFloorPal : nofloorpal)); + } +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void parseUndefPalookupRange(FScanner& sc, FScriptPosition& pos) +{ + int id0, id1; + + if (!sc.GetNumber(id0, true)) return; + if (!sc.GetNumber(id1, true)) return; + + if (id0 > id1 || (unsigned)id0 >= MAXPALOOKUPS || (unsigned)id1 >= MAXPALOOKUPS) + { + pos.Message(MSG_ERROR, "undefpalookuprange: Invalid range"); + } + else + { + for (int i = id0; i <= id1; i++) lookups.clearTable(i); + if (id0 == 0) paletteloaded &= ~PALETTE_SHADE; + } +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void parseHighpalookup(FScanner& sc, FScriptPosition& pos) +{ + FScanner::SavedPos blockend; + int basepal = -1, pal = -1; + FString fn; + + if (sc.StartBraces(&blockend)) return; + + while (!sc.FoundEndBrace(blockend)) + { + sc.MustGetString(); + if (sc.Compare("basepal")) sc.GetNumber(basepal); + else if (sc.Compare("pal")) sc.GetNumber(pal); + else if (sc.Compare("file")) sc.GetString(fn); + } + if ((unsigned)basepal >= MAXBASEPALS) + { + pos.Message(MSG_ERROR, "highpalookup: invalid base palette number %d", basepal); + } + else if ((unsigned)pal >= MAXREALPAL) + { + pos.Message(MSG_ERROR, "highpalookup: invalid palette number %d", pal); + } + else if (fn.IsEmpty()) + { + pos.Message(MSG_ERROR, "highpalookup: missing file name"); + } + else if (!fileSystem.FileExists(fn)) + { + pos.Message(MSG_ERROR, "highpalookup: file %s not found", fn.GetChars()); + } + // todo +} + + +struct ModelStatics +{ + int lastmodelid; + int modelskin, lastmodelskin; + int seenframe; +} mdglobal; + +//=========================================================================== +// +// +// +//=========================================================================== + +void parseDefineModel(FScanner& sc, FScriptPosition& pos) +{ + FString modelfn; + double scale; + int shadeoffs; + + if (!sc.GetString(modelfn)) return; + if (!sc.GetFloat(scale, true)) return; + if (!sc.GetNumber(shadeoffs, true)) return; + + mdglobal.lastmodelid = md_loadmodel(modelfn); + if (mdglobal.lastmodelid < 0) + { + pos.Message(MSG_WARNING, "definemodel: unable to load model file '%s'", modelfn.GetChars()); + } + else + { + md_setmisc(mdglobal.lastmodelid, (float)scale, shadeoffs, 0.0, 0.0, 0); + mdglobal.modelskin = mdglobal.lastmodelskin = 0; + mdglobal.seenframe = 0; + } +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void parseDefineModelFrame(FScanner& sc, FScriptPosition& pos) +{ + FString framename; + bool ok = true; + int firsttile, lasttile; + + if (!sc.GetString(framename)) return; + if (!sc.GetNumber(firsttile, true)) return; + if (!sc.GetNumber(lasttile, true)) return; + + if (!ValidateTileRange("definemodelframe", firsttile, lasttile, pos)) return; + + if (mdglobal.lastmodelid < 0) + { + pos.Message(MSG_WARNING, "definemodelframe: Ignoring frame definition outside model."); + return; + } + for (int i = firsttile; i <= lasttile && ok; i++) + { + int err = (md_defineframe(mdglobal.lastmodelid, framename, i, max(0, mdglobal.modelskin), 0.0f, 0)); + if (err < 0) ok = false; + if (err == -2) pos.Message(MSG_ERROR, "Invalid tile number %d", i); + else if (err == -3) pos.Message(MSG_ERROR, "Invalid frame name", framename.GetChars()); + } + mdglobal.seenframe = 1; +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void parseDefineModelAnim(FScanner& sc, FScriptPosition& pos) +{ + FString startframe, endframe; + int32_t flags; + double dfps; + + if (!sc.GetString(startframe)) return; + if (!sc.GetString(endframe)) return; + if (!sc.GetFloat(dfps, true)) return; + if (!sc.GetNumber(flags, true)) return; + + if (mdglobal.lastmodelid < 0) + { + pos.Message(MSG_WARNING, "definemodelframe: Ignoring animation definition outside model."); + return; + } + int err = (md_defineanimation(mdglobal.lastmodelid, startframe, endframe, (int32_t)(dfps * (65536.0 * .001)), flags)); + if (err == -2) pos.Message(MSG_ERROR, "Invalid start frame name %s", startframe.GetChars()); + else if (err == -3) pos.Message(MSG_ERROR, "Invalid end frame name %s", endframe.GetChars()); +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void parseDefineModelSkin(FScanner& sc, FScriptPosition& pos) +{ + int palnum; + FString skinfn; + + if (!sc.GetNumber(palnum, true)) return; + if (!sc.GetString(skinfn)) return; + + if (mdglobal.seenframe) { mdglobal.modelskin = ++mdglobal.lastmodelskin; } + mdglobal.seenframe = 0; + + if (!fileSystem.FileExists(skinfn)) return; + + int err = (md_defineskin(mdglobal.lastmodelid, skinfn, palnum, max(0, mdglobal.modelskin), 0, 0.0f, 1.0f, 1.0f, 0)); + if (err == -2) pos.Message(MSG_ERROR, "Invalid skin file name %s", skinfn.GetChars()); + else if (err == -3) pos.Message(MSG_ERROR, "Invalid palette %d", palnum); +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void parseSelectModelSkin(FScanner& sc, FScriptPosition& pos) +{ + sc.GetNumber(mdglobal.modelskin, true); +} + + +//=========================================================================== +// +// +// +//=========================================================================== + +void parseUndefModel(FScanner& sc, FScriptPosition& pos) +{ + int tile; + if (!sc.GetNumber(tile, true)) return; + if (!ValidateTilenum("undefmodel", tile, pos)) return; + md_undefinetile(tile); +} + +void parseUndefModelRange(FScanner& sc, FScriptPosition& pos) +{ + int start, end; + + if (!sc.GetNumber(start, true)) return; + if (!sc.GetNumber(end, true)) return; + if (!ValidateTileRange("undefmodel", start, end, pos)) return; + for (int i = start; i <= end; i++) md_undefinetile(i); +} + +void parseUndefModelOf(FScanner& sc, FScriptPosition& pos) +{ + int tile; + if (!sc.GetNumber(tile, true)) return; + if (!ValidateTilenum("undefmodelof", tile, pos)) return; + pos.Message(MSG_WARNING, "undefmodelof: currently non-functional."); +} + +//=========================================================================== +// +// +// +//=========================================================================== + +static bool parseModelFrameBlock(FScanner& sc, FixedBitArray<1024>& usedframes) +{ + FScanner::SavedPos blockend; + FScriptPosition pos = sc; + + FString framename; + bool ok = true; + int pal = -1; + int starttile = -1, endtile = -1; + double smoothduration = 0.1f; + + if (sc.StartBraces(&blockend)) return false; + while (!sc.FoundEndBrace(blockend)) + { + sc.MustGetString(); + if (sc.Compare("pal")) sc.GetNumber(pal, true); + else if (sc.Compare({ "name", "frame" })) sc.GetString(framename); + else if (sc.Compare("tile")) { sc.GetNumber(starttile, true); endtile = starttile; } + else if (sc.Compare("tile0")) sc.GetNumber(starttile, true); + else if (sc.Compare("tile1")) sc.GetNumber(endtile, true); + else if (sc.Compare("smoothduration")) sc.GetFloat(smoothduration, true); + } + + if (!ValidateTileRange("model/frame", starttile, endtile, pos)) return false; + + if (smoothduration > 1.0) + { + pos.Message(MSG_WARNING, "smoothduration out of range"); + smoothduration = 1.0; + } + for (int i = starttile; i <= endtile && ok; i++) + { + int res = md_defineframe(mdglobal.lastmodelid, framename, i, max(0, mdglobal.modelskin), smoothduration, pal); + if (res < 0) + { + ok = false; + if (res == -2) pos.Message(MSG_WARNING, "Invalid tile number %d", i); + else if (res == -3) pos.Message(MSG_WARNING, "%s: Invalid frame name", framename.GetChars()); + } + else if (res < 1024) usedframes.Set(res); + } + mdglobal.seenframe = 1; + return ok; +} + +static bool parseModelAnimBlock(FScanner& sc) +{ + FScanner::SavedPos blockend; + FScriptPosition pos = sc; + + FString startframe, endframe; + int flags = 0; + double fps = 1.0; + + if (sc.StartBraces(&blockend)) return false; + while (!sc.FoundEndBrace(blockend)) + { + sc.MustGetString(); + if (sc.Compare("frame0")) sc.GetString(startframe); + else if (sc.Compare("frame1")) sc.GetString(endframe); + else if (sc.Compare("fps")) sc.GetFloat(fps, true); + else if (sc.Compare("flags")) sc.GetNumber(flags, true); + } + + if (startframe.IsEmpty()) + { + pos.Message(MSG_ERROR, "missing start frame for anim definition"); + return false; + } + if (endframe.IsEmpty()) + { + pos.Message(MSG_ERROR, "missing end frame for anim definition"); + return false; + } + + int res = md_defineanimation(mdglobal.lastmodelid, startframe, endframe, (int)(fps * (65536.0 * .001)), flags); + if (res < 0) + { + if (res == -2) pos.Message(MSG_ERROR, "Invalid start frame name %s", startframe.GetChars()); + else if (res == -3) pos.Message(MSG_ERROR, "Invalid end frame name %s", endframe.GetChars()); + return false; + } + return true; +} + +static bool parseModelSkinBlock(FScanner& sc, int pal) +{ + FScanner::SavedPos blockend; + FScriptPosition pos = sc; + + FString filename; + int surface = 0; + double param = 1.0, specpower = 1.0, specfactor = 1.0; + int flags = 0; + + if (sc.StartBraces(&blockend)) return false; + while (!sc.FoundEndBrace(blockend)) + { + sc.MustGetString(); + if (sc.Compare("pal")) sc.GetNumber(pal, true); + else if (sc.Compare("file")) sc.GetString(filename); + else if (sc.Compare({ "surface", "surf" })) sc.GetNumber(surface, true); + else if (sc.Compare({ "intensity", "scale", "detailscale" })) sc.GetFloat(param, true); + else if (sc.Compare({ "specpower", "specularpower", "parallaxscale" })) sc.GetFloat(specpower, true); + else if (sc.Compare({ "specfactor", "specularfactor", "parallaxbias" })) sc.GetFloat(specfactor, true); + else if (sc.Compare("forcefilter")) { /* not suppoted yet*/ } + } + + + if (filename.IsEmpty()) + { + pos.Message(MSG_ERROR, "missing 'skin filename' for skin definition"); + return false; + } + + if (mdglobal.seenframe) mdglobal.modelskin = ++mdglobal.lastmodelskin; + mdglobal.seenframe = 0; + + if (!fileSystem.FileExists(filename)) + { + pos.Message(MSG_ERROR, "%s: file not found", filename.GetChars()); + return false; + } + + if (pal == DETAILPAL) param = 1. / param; + int res = md_defineskin(mdglobal.lastmodelid, filename, pal, max(0, mdglobal.modelskin), surface, param, specpower, specfactor, flags); + if (res < 0) + { + if (res == -2) pos.Message(MSG_ERROR, "Invalid skin filename %s", filename.GetChars()); + else if (res == -3) pos.Message(MSG_ERROR, "Invalid palette number %d", pal); + return false; + } + return true; +} + +static bool parseModelHudBlock(FScanner& sc) +{ + FScanner::SavedPos blockend; + FScriptPosition pos = sc; + + int starttile = -1, endtile = -1, flags = 0, fov = -1, angadd = 0; + DVector3 add{}; + + if (sc.StartBraces(&blockend)) return false; + while (!sc.FoundEndBrace(blockend)) + { + sc.MustGetString(); + if (sc.Compare("tile")) { sc.GetNumber(starttile, true); endtile = starttile; } + else if (sc.Compare("tile0")) sc.GetNumber(starttile, true); + else if (sc.Compare("tile1")) sc.GetNumber(endtile, true); + else if (sc.Compare("xadd")) sc.GetFloat(add.X, true); + else if (sc.Compare("yadd")) sc.GetFloat(add.Y, true); + else if (sc.Compare("zadd")) sc.GetFloat(add.Z, true); + else if (sc.Compare("angadd")) sc.GetNumber(angadd, true); + else if (sc.Compare("fov")) sc.GetNumber(fov, true); + else if (sc.Compare("hide")) flags |= HUDFLAG_HIDE; + else if (sc.Compare("nobob")) flags |= HUDFLAG_NOBOB; + else if (sc.Compare("flipped")) flags |= HUDFLAG_FLIPPED; + else if (sc.Compare("nodepth")) flags |= HUDFLAG_NODEPTH; + } + + if (!ValidateTileRange("hud", starttile, endtile, pos)) return false; + + for (int i = starttile; i <= endtile; i++) + { + vec3f_t addf = { (float)add.X, (float)add.Y, (float)add.Z }; + int res = md_definehud(mdglobal.lastmodelid, i, addf, angadd, flags, fov); + if (res < 0) + { + if (res == -2) pos.Message(MSG_ERROR, "Invalid tile number %d", i); + return false; + } + } + return true; +} + +void parseModel(FScanner& sc, FScriptPosition& pos) +{ + FScanner::SavedPos blockend; + + FString modelfn; + double scale = 1.0, mzadd = 0.0, myoffset = 0.0; + int32_t shadeoffs = 0, pal = 0, flags = 0; + FixedBitArray<1024> usedframes; + + usedframes.Zero(); + mdglobal.modelskin = mdglobal.lastmodelskin = 0; + mdglobal.seenframe = 0; + + if (!sc.GetString(modelfn)) return; + + if (sc.StartBraces(&blockend)) return; + + mdglobal.lastmodelid = md_loadmodel(modelfn); + if (mdglobal.lastmodelid < 0) + { + pos.Message(MSG_WARNING, "Unable to load model file '%s'", modelfn.GetChars()); + sc.RestorePos(blockend); + sc.CheckString("}"); + return; + } + + bool ok = true; + while (!sc.FoundEndBrace(blockend)) + { + sc.MustGetString(); + if (sc.Compare("scale")) sc.GetFloat(scale, true); + else if (sc.Compare("shade")) sc.GetNumber(shadeoffs, true); + else if (sc.Compare("zadd")) sc.GetFloat(mzadd, true); + else if (sc.Compare("yoffset")) sc.GetFloat(myoffset, true); + else if (sc.Compare("frame")) ok &= parseModelFrameBlock(sc, usedframes); + else if (sc.Compare("anim")) ok &= parseModelAnimBlock(sc); + else if (sc.Compare("skin")) ok &= parseModelSkinBlock(sc, 0); + else if (sc.Compare("detail")) ok &= parseModelSkinBlock(sc, DETAILPAL); + else if (sc.Compare("glow")) ok &= parseModelSkinBlock(sc, GLOWPAL); + else if (sc.Compare("specular")) ok &= parseModelSkinBlock(sc, SPECULARPAL); + else if (sc.Compare("normal")) ok &= parseModelSkinBlock(sc, NORMALPAL); + else if (sc.Compare("hud")) ok &= parseModelHudBlock(sc); + else if (sc.Compare("flags")) sc.GetNumber(flags, true); + } + + if (!ok) + { + if (mdglobal.lastmodelid >= 0) + { + pos.Message(MSG_ERROR, "Removing model %d due to errors.", mdglobal.lastmodelid); + md_undefinemodel(mdglobal.lastmodelid); + nextmodelid--; + } + } + else + { + md_setmisc(mdglobal.lastmodelid, (float)scale, shadeoffs, (float)mzadd, (float)myoffset, flags); + mdglobal.modelskin = mdglobal.lastmodelskin = 0; + mdglobal.seenframe = 0; + } +} + +//=========================================================================== +// +// +// +//=========================================================================== + +struct dispatch +{ + const char* text; + void (*handler)(FScanner& sc, FScriptPosition& pos); +}; + + +static const dispatch basetokens[] = +{ + { "include", parseInclude }, + { "#include", parseInclude }, + { "includedefault", parseIncludeDefault }, + { "#includedefault", parseIncludeDefault }, + { "define", parseDefine }, + { "#define", parseDefine }, + + // deprecated style + { "definetexture", parseDefineTexture }, + { "defineskybox", parseDefineSkybox }, + { "definetint", parseDefineTint }, + { "definemodel", parseDefineModel }, + { "definemodelframe",parseDefineModelFrame }, + { "definemodelanim", parseDefineModelAnim }, + { "definemodelskin", parseDefineModelSkin }, + { "selectmodelskin", parseSelectModelSkin }, + { "definevoxel", parseDefineVoxel }, + { "definevoxeltiles",parseDefineVoxelTiles }, + + // new style + { "model", parseModel }, + { "voxel", parseVoxel }, + { "skybox", parseSkybox }, + { "highpalookup", parseHighpalookup }, + { "tint", parseTint }, + { "makepalookup", parseMakePalookup }, + { "texture", parseTexture }, + { "tile", parseTexture }, + { "music", parseMusic }, + { "sound", parseEmptyBlock }, + { "animsounds", parseEmptyBlockWithParm }, + { "cutscene", parseEmptyBlockWithParm }, + { "nofloorpalrange", parseNoFloorpalRange }, + { "texhitscanrange", parseTexHitscanRange }, + { "nofullbrightrange", parseNoFullbrightRange }, + // other stuff + { "undefmodel", parseUndefModel }, + { "undefmodelrange", parseUndefModelRange }, + { "undefmodelof", parseUndefModelOf }, + { "undeftexture", parseUndefTexture }, + { "undeftexturerange", parseUndefTextureRange }, + { "alphahack", parseAlphahack }, + { "alphahackrange", parseAlphahackRange }, + { "spritecol", parseSkip<3> }, + { "2dcol", parseSkip<4> }, + { "2dcolidxrange", parseSkip<3> }, + { "fogpal", parseFogpal }, + { "loadgrp", parseSkip<1> }, + { "dummytile", parseDummyTile }, + { "dummytilerange", parseDummyTileRange }, + { "setuptile", parseSetupTile }, + { "setuptilerange", parseSetupTileRange }, + { "undefinetile", parseUndefineTile }, + { "undefinetilerange", parseUndefineTileRange }, + { "animtilerange", parseAnimTileRange }, + { "cachesize", parseSkip<1> }, + { "dummytilefrompic",parseImportTile }, + { "tilefromtexture", parseTileFromTexture }, + { "artfile", parseArtFile }, + { "mapinfo", parseMapinfo }, + { "echo", parseEcho }, + { "globalflags", parseSkip<1> }, + { "copytile", parseCopyTile }, + { "globalgameflags", parseSkip<1> }, + { "multipsky", parseMultiPsky }, + { "basepalette", parseBasePalette }, + { "palookup", parsePalookup }, + { "blendtable", parseBlendTable }, + { "numalphatables", parseNumAlphaTabs }, + { "undefbasepaletterange", parseUndefBasePaletteRange }, + { "undefpalookuprange", parseUndefPalookupRange }, + { "undefblendtablerange", parseSkip<2> }, + { "shadefactor", parseSkip<1> }, + { "newgamechoices", parseEmptyBlock }, + { "rffdefineid", parseRffDefineId }, +}; + +static void defsparser(FScanner& sc) +{ + int iter = 0; + + sc.SetNoFatalErrors(true); + sc.SetNoOctals(true); + while (1) + { + if (++iter >= 50) + { + Printf("."); + iter = 0; + } + FScriptPosition pos = sc; + if (!sc.GetString()) return; + int index = sc.MustMatchString(&basetokens[0].text, sizeof(basetokens[0])); + if (index != -1) basetokens[index].handler(sc, pos); + } +} + +void loaddefinitionsfile(const char* fn, bool cumulative) +{ + bool done = false; + auto parseit = [&](int lump) + { + FScanner sc; + sc.OpenLumpNum(lump); + defsparser(sc); + done = true; + Printf(PRINT_NONOTIFY, "\n"); + }; + + cycle_t deftimer; + deftimer.Reset(); + + auto printtimer = [&](const char* fn) + { + deftimer.Unclock(); + DPrintf(DMSG_SPAMMY, "Definitions file \"%s\" loaded, %f ms.\n", fn, deftimer.TimeMS()); + deftimer.Reset(); + }; + + if (!cumulative) + { + int lump = fileSystem.FindFile(fn); + if (lump >= 0) + { + Printf(PRINT_NONOTIFY, "Loading \"%s\"\n", fn); + deftimer.Clock(); + parseit(lump); + printtimer(fn); + } + } + else + { + int lump, lastlump = 0; + while ((lump = fileSystem.FindLumpFullName(fn, &lastlump)) >= 0) + { + Printf(PRINT_NONOTIFY, "Loading \"%s\"\n", fileSystem.GetFileFullPath(lump).GetChars()); + deftimer.Clock(); + parseit(lump); + printtimer(fn); + } + } +} diff --git a/source/core/g_mapinfo.cpp b/source/core/g_mapinfo.cpp new file mode 100644 index 000000000..60afb7d1b --- /dev/null +++ b/source/core/g_mapinfo.cpp @@ -0,0 +1,1321 @@ +/* +** g_level.cpp +** Parses MAPINFO +** +**--------------------------------------------------------------------------- +** Copyright 1998-2016 Randy Heit +** Copyright 2009-2021 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include +#include "mapinfo.h" +#include "g_mapinfo.h" +#include "templates.h" +#include "filesystem.h" +#include "cmdlib.h" +#include "v_video.h" +#include "gi.h" +#include "gstrings.h" +#include "autosegs.h" +#include "i_system.h" +#include "gamecontrol.h" +#include "autosegs.h" + +extern TArray clusters; +extern TArray volumes; +extern TArray> mapList; // must be allocated as pointers because it can whack the currentlLevel pointer if this was a flat array. + +static MapRecord TheDefaultLevelInfo; +static ClusterDef TheDefaultClusterInfo; + +TArray ParsedLumps(8); + +//========================================================================== +// +// +//========================================================================== + +void FMapInfoParser::ParseOpenBrace() +{ + sc.MustGetStringName("{"); + sc.SetCMode(true); +} + +//========================================================================== +// +// +//========================================================================== + +bool FMapInfoParser::ParseCloseBrace() +{ + return sc.Compare("}"); +} + +//========================================================================== +// +// +//========================================================================== + +bool FMapInfoParser::CheckAssign() +{ + return sc.CheckString("="); +} + +//========================================================================== +// +// +//========================================================================== + +void FMapInfoParser::ParseAssign() +{ + sc.MustGetStringName("="); +} + +//========================================================================== +// +// +//========================================================================== + +void FMapInfoParser::MustParseAssign() +{ + sc.MustGetStringName("="); +} + +//========================================================================== +// +// +//========================================================================== + +void FMapInfoParser::ParseComma() +{ + sc.MustGetStringName(","); +} + +//========================================================================== +// +// +//========================================================================== + +bool FMapInfoParser::CheckNumber() +{ + if (sc.CheckString(",")) + { + sc.MustGetNumber(); + return true; + } + return false; +} + +//========================================================================== +// +// +//========================================================================== + +bool FMapInfoParser::CheckFloat() +{ + if (sc.CheckString(",")) + { + sc.MustGetFloat(); + return true; + } + return false; +} + +//========================================================================== +// +// skips an entire parameter list that's separated by commas +// +//========================================================================== + +void FMapInfoParser::SkipToNext() +{ + if (sc.CheckString("=")) + { + do + { + sc.MustGetString(); + } + while (sc.CheckString(",")); + } +} + + +//========================================================================== +// +// checks if the current block was properly terminated +// +//========================================================================== + +void FMapInfoParser::CheckEndOfFile(const char *block) +{ + if (sc.End) + { + sc.ScriptError("Unexpected end of file in %s definition", block); + } +} + +//========================================================================== +// +// ParseLookupname +// +//========================================================================== + +bool FMapInfoParser::ParseLookupName(FString &dest) +{ + sc.MustGetString(); + dest = sc.String; + return true; +} + +//========================================================================== +// +// +//========================================================================== + +void FMapInfoParser::ParseLumpOrTextureName(FString &name) +{ + sc.MustGetString(); + name = sc.String; +} + + +//========================================================================== +// +// +//========================================================================== + +void FMapInfoParser::ParseMusic(FString &name, int &order) +{ + sc.MustGetString(); + name = sc.String; + if (CheckNumber()) + { + order = sc.Number; + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void FMapInfoParser::ParseCutscene(CutsceneDef& cdef) +{ + FString sound; + sc.MustGetStringName("{"); + while (!sc.CheckString("}")) + { + sc.MustGetString(); + if (sc.Compare("video")) { ParseAssign(); sc.MustGetString(); cdef.video = sc.String; cdef.function = ""; } + else if (sc.Compare("function")) { ParseAssign(); sc.SetCMode(false); sc.MustGetString(); sc.SetCMode(true); cdef.function = sc.String; cdef.video = ""; } + else if (sc.Compare("sound")) { ParseAssign(); sc.MustGetString(); cdef.soundName = sc.String; } + else if (sc.Compare("soundid")) { ParseAssign(); sc.MustGetNumber(); cdef.soundID = sc.Number; } + else if (sc.Compare("fps")) { ParseAssign(); sc.MustGetNumber(); cdef.framespersec = sc.Number; } + else if (sc.Compare("transitiononly")) cdef.transitiononly = true; + else if (sc.Compare("delete")) { cdef.function = "none"; cdef.video = ""; } // this means 'play nothing', not 'not defined'. + else if (sc.Compare("clear")) cdef = {}; + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void FMapInfoParser::ParseCluster() +{ + sc.MustGetNumber (); + auto clusterinfo = MustFindCluster(sc.Number); + + ParseOpenBrace(); + while (sc.GetString()) + { + if (sc.Compare("clear")) + { + *clusterinfo = {}; + } + else if (sc.Compare("name")) + { + ParseAssign(); + ParseLookupName(clusterinfo->name); + } + else if (sc.Compare("intro")) + { + ParseCutscene(clusterinfo->intro); + } + else if (sc.Compare("outro")) + { + ParseCutscene(clusterinfo->outro); + } + else if (sc.Compare("gameover")) + { + ParseCutscene(clusterinfo->gameover); + } + else if (sc.Compare("interbackground")) + { + ParseAssign(); + ParseLookupName(clusterinfo->InterBackground); + } + else if (!ParseCloseBrace()) + { + // Unknown + sc.ScriptMessage("Unknown property '%s' found in cluster definition\n", sc.String); + SkipToNext(); + } + else + { + break; + } + } + CheckEndOfFile("cluster"); +} + +//========================================================================== +// +// allow modification of maps defined through legacy means. +// +//========================================================================== + +bool FMapInfoParser::CheckLegacyMapDefinition(FString& mapname) +{ + if (Internal && (g_gameType & GAMEFLAG_BLOOD | GAMEFLAG_DUKECOMPAT | GAMEFLAG_SW) && sc.CheckString("{")) + { + sc.MustGetNumber(); + int vol = sc.Number; + if (!(g_gameType & GAMEFLAG_SW)) + { + // Blood and Duke use volume/level pairs + sc.MustGetStringName(","); + sc.MustGetNumber(); + int indx = sc.Number; + auto map = FindMapByIndexOnly(vol, indx); + if (!map) mapname = ""; + else mapname = map->labelName; + } + else + { + // SW only uses the level number + auto map = FindMapByLevelNum(vol); + if (!map) mapname = ""; + else mapname = map->labelName; + } + sc.MustGetStringName("}"); + return true; + } + return false; +} + +//========================================================================== +// +// ParseNextMap +// Parses a next map field +// +//========================================================================== + +void FMapInfoParser::ParseMapName(FString &mapname) +{ + if (!CheckLegacyMapDefinition(mapname)) + { + sc.MustGetString(); + mapname = ExtractFileBase(sc.String); + } +} + +//========================================================================== +// +// Map options +// +//========================================================================== + +DEFINE_MAP_OPTION(clear, true) +{ + // Save the names, reset and restore the names + FString fn = info->fileName; + FString dn = info->name; + FString ln = info->labelName; + *info = *parse.defaultinfoptr; + info->fileName = fn; + info->name = dn; + info->labelName = ln; +} + + +DEFINE_MAP_OPTION(levelnum, true) +{ + parse.ParseAssign(); + parse.sc.MustGetNumber(); + info->levelNumber = parse.sc.Number; +} + +DEFINE_MAP_OPTION(next, true) +{ + parse.ParseAssign(); + parse.ParseMapName(info->NextMap); +} + +DEFINE_MAP_OPTION(author, true) +{ + parse.ParseAssign(); + parse.sc.MustGetString(); + info->Author = parse.sc.String; +} + +DEFINE_MAP_OPTION(secretnext, true) +{ + parse.ParseAssign(); + parse.ParseMapName(info->NextSecret); +} + +DEFINE_MAP_OPTION(cluster, true) +{ + parse.ParseAssign(); + parse.sc.MustGetNumber(); + info->cluster = parse.sc.Number; + + // If this cluster hasn't been defined yet, add it. + MustFindCluster(info->cluster); +} + +DEFINE_MAP_OPTION(fade, true) +{ + parse.ParseAssign(); + parse.sc.MustGetString(); + info->fadeto = V_GetColor(nullptr, parse.sc); +} + +DEFINE_MAP_OPTION(partime, true) +{ + parse.ParseAssign(); + parse.sc.MustGetNumber(); + info->parTime = parse.sc.Number; +} + +DEFINE_MAP_OPTION(designertime, true) +{ + parse.ParseAssign(); + parse.sc.MustGetNumber(); + info->designerTime = parse.sc.Number; +} + +DEFINE_MAP_OPTION(music, true) +{ + parse.ParseAssign(); + parse.ParseMusic(info->music, info->musicorder); +} + +DEFINE_MAP_OPTION(cdtrack, true) +{ + parse.ParseAssign(); + parse.sc.MustGetNumber(); + info->cdSongId = parse.sc.Number; +} + +DEFINE_MAP_OPTION(intro, true) +{ + parse.ParseCutscene(info->intro); +} + +DEFINE_MAP_OPTION(outro, true) +{ + parse.ParseCutscene(info->outro); +} + +DEFINE_MAP_OPTION(interbackground, true) +{ + parse.ParseAssign(); + parse.sc.MustGetString(); + info->InterBackground = parse.sc.String; +} + + +/* currently all sounds are precached. This requires significant work on sound management and info collection. +DEFINE_MAP_OPTION(PrecacheSounds, true) +{ + parse.ParseAssign(); + + do + { + parse.sc.MustGetString(); + FSoundID snd = parse.sc.String; + if (snd == 0) + { + parse.sc.ScriptMessage("Unknown sound \"%s\"", parse.sc.String); + } + else + { + info->PrecacheSounds.Push(snd); + } + } while (parse.sc.CheckString(",")); +} +*/ + +DEFINE_MAP_OPTION(PrecacheTextures, true) +{ + parse.ParseAssign(); + do + { + parse.sc.MustGetString(); + //the texture manager is not initialized here so all we can do is store the texture's name. + info->PrecacheTextures.Push(parse.sc.String); + } while (parse.sc.CheckString(",")); +} + +DEFINE_MAP_OPTION(bordertexture, true) +{ + parse.ParseAssign(); + parse.ParseLumpOrTextureName(info->BorderTexture); +} + +DEFINE_MAP_OPTION(fogdensity, false) +{ + parse.ParseAssign(); + parse.sc.MustGetNumber(); + info->fogdensity = clamp(parse.sc.Number, 0, 512) >> 1; +} + +DEFINE_MAP_OPTION(skyfog, false) +{ + parse.ParseAssign(); + parse.sc.MustGetNumber(); + info->skyfog = parse.sc.Number; +} + +DEFINE_MAP_OPTION(message, false) +{ + parse.ParseAssign(); + parse.sc.MustGetNumber(); + if (parse.sc.Number < 1 || parse.sc.Number > MAX_MESSAGES) parse.sc.ScriptError("Invalid message ID %d - must be 1..32", parse.sc.Number); + int num = parse.sc.Number; + parse.ParseComma(); + parse.sc.MustGetString(); + info->messages[num] = parse.sc.String; +} + +/* stuff for later when the new renderer is done. +DEFINE_MAP_OPTION(lightmode, false) +{ + parse.ParseAssign(); + parse.sc.MustGetNumber(); + + if ((parse.sc.Number >= 0 && parse.sc.Number <= 4) || parse.sc.Number == 8 || parse.sc.Number == 16) + { + info->lightmode = ELightMode(parse.sc.Number); + } + else + { + parse.sc.ScriptMessage("Invalid light mode %d", parse.sc.Number); + } +} +*/ + +DEFINE_MAP_OPTION(skyrotate, false) +{ + parse.ParseAssign(); + parse.sc.MustGetFloat(); + info->skyrotatevector.X = (float)parse.sc.Float; + parse.sc.MustGetStringName(","); + parse.sc.MustGetFloat(); + info->skyrotatevector.Y = (float)parse.sc.Float; + parse.sc.MustGetStringName(","); + parse.sc.MustGetFloat(); + info->skyrotatevector.Z = (float)parse.sc.Float; + info->skyrotatevector.W = 0; + info->skyrotatevector.MakeUnit(); + parse.sc.MustGetStringName(","); + parse.sc.MustGetFloat(); + info->skyrotatevector.W = (float)parse.sc.Float; // W is the rotation speed. This must not be normalized +} + +DEFINE_MAP_OPTION(rr_startsound, false) +{ + parse.ParseAssign(); + parse.sc.MustGetNumber(); + info->rr_startsound = parse.sc.Number; +} + +DEFINE_MAP_OPTION(rr_mamaspawn, false) +{ + parse.ParseAssign(); + parse.sc.MustGetNumber(); + info->rr_mamaspawn = parse.sc.Number; +} + +DEFINE_MAP_OPTION(ex_ramses_horiz, false) +{ + parse.ParseAssign(); + parse.sc.MustGetNumber(); + info->ex_ramses_horiz = parse.sc.Number; +} + +DEFINE_MAP_OPTION(ex_ramses_cdtrack, false) +{ + parse.ParseAssign(); + parse.sc.MustGetNumber(); + info->ex_ramses_cdtrack = parse.sc.Number; +} + +DEFINE_MAP_OPTION(ex_ramses_pup, false) +{ + parse.ParseAssign(); + parse.sc.MustGetString(); + info->ex_ramses_pup = parse.sc.Number; +} + +DEFINE_MAP_OPTION(ex_ramses_text, false) +{ + parse.ParseAssign(); + parse.sc.MustGetString(); + info->ex_ramses_text = parse.sc.Number; +} + +int ex_ramses_horiz = 11; +int ex_ramses_cdtrack = -1; // this is not music, it is the actual dialogue! +FString ex_ramses_pup; +FString ex_ramses_text; + +//========================================================================== +// +// All flag based map options +// +//========================================================================== + +enum EMIType +{ + MITYPE_IGNORE, + MITYPE_EATNEXT, + MITYPE_SETFLAG, + MITYPE_CLRFLAG, + MITYPE_SCFLAGS, + MITYPE_SETFLAGG, + MITYPE_CLRFLAGG, + MITYPE_SCFLAGSG, + MITYPE_COMPATFLAG, +}; + +struct MapInfoFlagHandler +{ + const char *name; + EMIType type; + uint32_t data1, data2; + int gameflagmask; +} +MapFlagHandlers[] = +{ + { "nointermission", MITYPE_SETFLAG, LEVEL_NOINTERMISSION, 0, -1 }, + { "secretexitoverride", MITYPE_SETFLAG, LEVEL_SECRETEXITOVERRIDE, 0, -1 }, + { "clearinventory", MITYPE_SETFLAG, LEVEL_CLEARINVENTORY, 0, -1 }, + { "clearweapons", MITYPE_SETFLAG, LEVEL_CLEARWEAPONS, 0, -1 }, + { "forcenoeog", MITYPE_SETFLAG, LEVEL_FORCENOEOG, 0, -1 }, + { "rrra_hulkspawn", MITYPE_SETFLAGG,LEVEL_RR_HULKSPAWN, 0, GAMEFLAG_RRRA }, + { "rr_clearmoonshine", MITYPE_SETFLAGG,LEVEL_RR_CLEARMOONSHINE, 0, GAMEFLAG_RR }, + { "ex_training", MITYPE_SETFLAGG,LEVEL_EX_TRAINING, 0, GAMEFLAG_PSEXHUMED }, + { "ex_altsound", MITYPE_SETFLAGG,LEVEL_EX_ALTSOUND, 0, GAMEFLAG_PSEXHUMED }, + { "ex_countdown", MITYPE_SETFLAGG,LEVEL_EX_COUNTDOWN, 0, GAMEFLAG_PSEXHUMED }, + { "ex_multi", MITYPE_SETFLAGG,LEVEL_EX_MULTI, 0, GAMEFLAG_PSEXHUMED }, + { "sw_bossmeter_serpent", MITYPE_SETFLAGG,LEVEL_SW_BOSSMETER_SERPENT, 0, GAMEFLAG_SW }, + { "sw_bossmeter_sumo", MITYPE_SETFLAGG,LEVEL_SW_BOSSMETER_SUMO, 0, GAMEFLAG_SW }, + { "sw_bossmeter_zilla", MITYPE_SETFLAGG,LEVEL_SW_BOSSMETER_ZILLA, 0, GAMEFLAG_SW }, + { "sw_deathexit_serpent", MITYPE_SETFLAGG,LEVEL_SW_DEATHEXIT_SERPENT, 0, GAMEFLAG_SW }, + { "sw_deathexit_sumo", MITYPE_SETFLAGG,LEVEL_SW_DEATHEXIT_SUMO, 0, GAMEFLAG_SW }, + { "sw_deathexit_zilla", MITYPE_SETFLAGG,LEVEL_SW_DEATHEXIT_ZILLA, 0, GAMEFLAG_SW }, + { "sw_spawnmines", MITYPE_SETFLAGG,LEVEL_SW_SPAWNMINES, 0, GAMEFLAG_SW }, + + { NULL, MITYPE_IGNORE, 0, 0} +}; + +void PrintCutscene(const char* name, CutsceneDef& cut) +{ + if (cut.function.IsEmpty() && cut.video.IsEmpty()) return; + Printf("\t%s\n\t{\n", name); + if (cut.function.IsNotEmpty()) + { + Printf("\t\tfunction = %s\n", cut.function.GetChars()); + } + if (cut.video.IsNotEmpty()) + { + Printf("\t\tvideo = \"%s\"\n", cut.video.GetChars()); + } + if (cut.soundName.IsNotEmpty()) + { + Printf("\t\tsound = \"%s\"\n", cut.soundName.GetChars()); + } + Printf("\t}\n"); +} + +CCMD(mapinfo) +{ + for (auto& vol : volumes) + { + Printf("episode %s\n{\n", vol.startmap.GetChars()); + if (vol.name.IsNotEmpty()) Printf("\tname = \"%s\"\n", vol.name.GetChars()); + if (vol.subtitle.IsNotEmpty()) Printf("\tsubtitle = \"%s\"\n{\n", vol.subtitle.GetChars()); + Printf("}\n"); + } + for (auto& clust : clusters) + { + Printf("cluster %d\n{\n", clust.index); + if (clust.name.IsNotEmpty()) Printf("\tname = \"%s\"\n", clust.name.GetChars()); + if (clust.InterBackground.IsNotEmpty()) Printf("\tInterBackground = %s\n", clust.InterBackground.GetChars()); + PrintCutscene("intro", clust.intro); + PrintCutscene("outro", clust.outro); + PrintCutscene("gameover", clust.gameover); + Printf("}\n"); + } + for (auto& map : mapList) + { + int lump = fileSystem.FindFile(map->fileName); + if (lump >= 0) + { + int rfnum = fileSystem.GetFileContainer(lump); + Printf("map %s \"%s\"\n{\n", map->labelName.GetChars(), map->DisplayName()); + Printf("\tlevelnum = %d\n\tCluster = %d\n", map->levelNumber, map->cluster); + if (map->Author.IsNotEmpty()) + { + FString auth = map->Author; + auth.Substitute("\"", "\\\""); + Printf("\tAuthor = \"%s\"\n", auth.GetChars()); + } + if (map->NextMap.IsNotEmpty()) Printf("\tNext = %s\n", map->NextMap.GetChars()); + if (map->NextSecret.IsNotEmpty()) Printf("\tSecretNext = %s\n", map->NextSecret.GetChars()); + if (map->InterBackground.IsNotEmpty()) Printf("\tInterBackground = %s\n", map->InterBackground.GetChars()); + if (map->music.IsNotEmpty()) Printf("\tMusic = \"%s\"\n", map->music.GetChars()); + if (map->musicorder > 0) Printf("\tMusicorder = %d\n", map->musicorder); + if (map->cdSongId > 0) Printf("\tCDtrack = %d\n", map->cdSongId); + if (map->parTime) Printf("\tParTime = %d\n", map->parTime); + if (map->designerTime) Printf("\tDesignerTime = %d\n", map->designerTime); + for (int i = 0; i < MAX_MESSAGES; i++) + { + if (map->messages[i].IsNotEmpty()) Printf("\tMessage = %d, \"%s\"\n", i + 1, map->messages[i].GetChars()); + } + + for (auto& flagh : MapFlagHandlers) + { + if (flagh.type == MITYPE_SETFLAG) + { + if (map->flags & flagh.data1) Printf("\t%s\n", flagh.name); + } + if (flagh.type == MITYPE_SETFLAGG) + { + if (map->gameflags & flagh.data1) Printf("\t%s\n", flagh.name); + } + } + PrintCutscene("intro", map->intro); + PrintCutscene("outro", map->outro); + Printf("}\n"); + } + else + { + //Printf("%s - %s (defined but does not exist)\n", map->fileName.GetChars(), map->DisplayName()); + } + } +} + + +//========================================================================== +// +// ParseMapDefinition +// Parses the body of a map definition, including defaultmap etc. +// +//========================================================================== + +void FMapInfoParser::ParseMapDefinition(MapRecord &info) +{ + int index; + + ParseOpenBrace(); + + while (sc.GetString()) + { + if ((index = sc.MatchString(&MapFlagHandlers->name, sizeof(*MapFlagHandlers))) >= 0) + { + MapInfoFlagHandler *handler = &MapFlagHandlers[index]; + switch (handler->type) + { + case MITYPE_EATNEXT: + ParseAssign(); + sc.MustGetString(); + break; + + case MITYPE_IGNORE: + break; + + case MITYPE_SETFLAG: + if (!CheckAssign()) + { + info.flags |= handler->data1; + } + else + { + sc.MustGetNumber(); + if (sc.Number) info.flags |= handler->data1; + else info.flags &= ~handler->data1; + } + info.flags |= handler->data2; + break; + + case MITYPE_CLRFLAG: + info.flags &= ~handler->data1; + info.flags |= handler->data2; + break; + + case MITYPE_SCFLAGS: + info.flags = (info.flags & handler->data2) | handler->data1; + break; + + case MITYPE_SETFLAGG: + if (!CheckAssign()) + { + info.gameflags |= handler->data1; + } + else + { + sc.MustGetNumber(); + if (sc.Number) info.gameflags |= handler->data1; + else info.gameflags &= ~handler->data1; + } + info.gameflags |= handler->data2; + break; + + case MITYPE_CLRFLAGG: + info.gameflags &= ~handler->data1; + info.gameflags |= handler->data2; + break; + case MITYPE_SCFLAGSG: + info.gameflags = (info.gameflags & handler->data2) | handler->data1; + break; + + default: + // should never happen + assert(false); + break; + } + } + else + { + bool success = false; + + AutoSegs::MapInfoOptions.ForEach([this, &success, &info](FMapOptInfo* option) + { + if (sc.Compare(option->name)) + { + option->handler(*this, &info); + success = true; + return false; // break + } + + return true; // continue + }); + + if (!success) + { + if (!ParseCloseBrace()) + { + // Unknown + sc.ScriptMessage("Unknown property '%s' found in map definition\n", sc.String); + SkipToNext(); + } + else + { + break; + } + } + } + } + CheckEndOfFile("map"); +} + + +//========================================================================== +// +// GetDefaultLevelNum +// Gets a default level num from a map name. +// +//========================================================================== + +static int GetDefaultLevelNum(const char *mapname) +{ + if ((!strnicmp (mapname, "MAP", 3) || !strnicmp(mapname, "LEV", 3)) && strlen(mapname) <= 5) + { + int mapnum = atoi (mapname + 3); + + if (mapnum >= 1 && mapnum <= 99) + return mapnum; + } + else if (mapname[0] == 'E' && + mapname[1] >= '0' && mapname[1] <= '9' && + (mapname[2] == 'M' || mapname[2] == 'L') && + mapname[3] >= '0' && mapname[3] <= '9') + { + int epinum = mapname[1] - '0'; + int mapnum = mapname[3] - '0'; + return makelevelnum(epinum, mapnum); + } + return 0; +} + +//========================================================================== +// +// ParseMapHeader +// Parses the header of a map definition ('map mapxx mapname') +// +//========================================================================== +static MapRecord sink; + +MapRecord *FMapInfoParser::ParseMapHeader(MapRecord &defaultinfo) +{ + FString mapname; + MapRecord* map; + + if (!CheckLegacyMapDefinition(mapname)) + { + ParseLookupName(mapname); + } + + if (mapname.IsEmpty()) + { + map = &sink; // parse over the entire definition but discard the result. + } + else + { + map = FindMapByName(mapname); + if (!map) + { + map = AllocateMap(); + *map = defaultinfo; + DefaultExtension(mapname, ".map"); + map->SetFileName(mapname); + } + } + + if (!sc.CheckString("{")) + { + sc.MustGetString(); + map->name = sc.String; + } + else + { + if (map != &sink && map->name.IsEmpty()) sc.ScriptError("Missing level name"); + sc.UnGet(); + } + if (!map->levelNumber) map->levelNumber = GetDefaultLevelNum(map->labelName); + return map; +} + + +//========================================================================== +// +// Episode definitions start with the header "episode " +// and then can be followed by any of the following: +// +// name "Episode name as text" +// picname "Picture to display the episode name" +// key "Shortcut key for the menu" +// noskillmenu +// remove +// +//========================================================================== + +void FMapInfoParser::ParseEpisodeInfo () +{ + unsigned int i; + FString map; + FString pic; + FString name; + bool remove = false; + char key = 0; + int flags = 0; + + // Get map name + sc.MustGetString (); + map = sc.String; + + ParseOpenBrace(); + + while (sc.GetString()) + { + if (sc.Compare ("optional")) + { + flags |= VF_OPTIONAL; + } + else if (sc.Compare("sharewarelock")) + { + flags |= VF_SHAREWARELOCK; + } + else if (sc.Compare ("name")) + { + ParseAssign(); + sc.MustGetString (); + name = sc.String; + } + else if (sc.Compare ("remove")) + { + remove = true; + } + else if (sc.Compare ("key")) + { + ParseAssign(); + sc.MustGetString (); + key = sc.String[0]; + } + else if (sc.Compare("noskillmenu")) + { + flags |= VF_NOSKILL; + } + else if (!ParseCloseBrace()) + { + // Unknown + sc.ScriptMessage("Unknown property '%s' found in episode definition\n", sc.String); + SkipToNext(); + } + else + { + break; + } + } + CheckEndOfFile("episode"); + + + for (i = 0; i < volumes.Size(); i++) + { + if (volumes[i].startmap.CompareNoCase(map) == 0) + { + break; + } + } + + if (remove) + { + // If the remove property is given for an episode, remove it. + volumes.Delete(i); + } + else + { + // Only allocate a new entry if this doesn't replace an existing episode. + if (i >= volumes.Size()) + { + i = volumes.Reserve(1); + } + + auto epi = &volumes[i]; + + epi->startmap = map; + epi->name = name; + epi->shortcut = tolower(key); + epi->flags = flags; + epi->index = i; + } +} + + +//========================================================================== +// +// +// +//========================================================================== + +void FMapInfoParser::ParseCutsceneInfo() +{ + FString map; + FString pic; + FString name; + bool remove = false; + char key = 0; + int flags = 0; + + ParseOpenBrace(); + + while (sc.GetString()) + { + if (sc.Compare("intro")) + { + ParseCutscene(globalCutscenes.Intro); + } + else if (sc.Compare("defaultmapintro")) + { + ParseCutscene(globalCutscenes.DefaultMapIntro); + } + else if (sc.Compare("defaultmapoutro")) + { + ParseCutscene(globalCutscenes.DefaultMapOutro); + } + else if (sc.Compare("defaultgameover")) + { + ParseCutscene(globalCutscenes.DefaultGameover); + } + else if (sc.Compare("sharewareend")) + { + ParseCutscene(globalCutscenes.SharewareEnd); + } + else if (sc.Compare("loadscreen")) + { + ParseCutscene(globalCutscenes.LoadingScreen); + } + else if (!ParseCloseBrace()) + { + // Unknown + sc.ScriptMessage("Unknown property '%s' found in cutscene definition\n", sc.String); + SkipToNext(); + } + else + { + break; + } + } + CheckEndOfFile("cutscenes"); +} + + +//========================================================================== +// +// +// +//========================================================================== + +void FMapInfoParser::ParseGameInfo() +{ + FString map; + FString pic; + FString name; + bool remove = false; + char key = 0; + int flags = 0; + + ParseOpenBrace(); + + while (sc.GetString()) + { + if (sc.Compare("summaryscreen")) + { + ParseAssign(); + sc.SetCMode(false); + sc.MustGetString(); + sc.SetCMode(false); + globalCutscenes.SummaryScreen = sc.String; + } + else if (sc.Compare("mpsummaryscreen")) + { + ParseAssign(); + sc.SetCMode(false); + sc.MustGetString(); + sc.SetCMode(false); + globalCutscenes.MPSummaryScreen = sc.String; + } + else if (!ParseCloseBrace()) + { + // Unknown + sc.ScriptMessage("Unknown property '%s' found in gameinfo definition\n", sc.String); + SkipToNext(); + } + else + { + break; + } + } + CheckEndOfFile("cutscenes"); +} + + +//========================================================================== +// +// SetLevelNum +// Avoid duplicate levelnums. The level being set always has precedence. +// +//========================================================================== + +void SetLevelNum (MapRecord *info, int num) +{ + for (auto& map : mapList) + { + + if (map->levelNumber == num) + map->levelNumber = 0; + } + info->levelNumber = num; +} + +//========================================================================== +// +// G_DoParseMapInfo +// Parses a single MAPINFO lump +// data for wadlevelinfos and wadclusterinfos. +// +//========================================================================== + +void FMapInfoParser::ParseMapInfo (int lump, MapRecord &gamedefaults, MapRecord &defaultinfo) +{ + sc.OpenLumpNum(lump); + Internal = (fileSystem.GetFileContainer(lump) == 0); + + defaultinfo = gamedefaults; + defaultinfoptr = &defaultinfo; + + if (ParsedLumps.Find(lump) != ParsedLumps.Size()) + { + sc.ScriptMessage("MAPINFO file is processed more than once\n"); + } + else + { + ParsedLumps.Push(lump); + } + + while (sc.GetString ()) + { + if (sc.Compare("include")) + { + sc.MustGetString(); + int inclump = fileSystem.CheckNumForFullName(sc.String, true); + if (inclump < 0) + { + sc.ScriptError("include file '%s' not found", sc.String); + } + if (fileSystem.GetFileContainer(sc.LumpNum) != fileSystem.GetFileContainer(inclump)) + { + // Do not allow overriding includes from the default MAPINFO + if (fileSystem.GetFileContainer(sc.LumpNum) == 0) + { + I_FatalError("File %s is overriding core lump %s.", + fileSystem.GetResourceFileFullName(fileSystem.GetFileContainer(inclump)), sc.String); + } + } + FScanner saved_sc = sc; + ParseMapInfo(inclump, gamedefaults, defaultinfo); + sc = saved_sc; + } + else if (sc.Compare("gamedefaults")) + { + gamedefaults = {}; + ParseMapDefinition(gamedefaults); + defaultinfo = gamedefaults; + } + else if (sc.Compare("defaultmap")) + { + defaultinfo = gamedefaults; + ParseMapDefinition(defaultinfo); + } + else if (sc.Compare("adddefaultmap")) + { + // Same as above but adds to the existing definitions instead of replacing them completely + ParseMapDefinition(defaultinfo); + } + else if (sc.Compare("map")) + { + auto levelinfo = ParseMapHeader(defaultinfo); + + ParseMapDefinition(*levelinfo); + SetLevelNum (levelinfo, levelinfo->levelNumber); // Wipe out matching levelnums from other maps. + } + // clusterdef is the old keyword but the new format has enough + // structuring that 'cluster' can be handled, too. The old format does not. + else if (sc.Compare("cluster")) + { + ParseCluster(); + } + else if (sc.Compare("episode")) + { + ParseEpisodeInfo(); + } + else if (sc.Compare("clearepisodes")) + { + volumes.Clear(); + } + else if (sc.Compare("clearall")) + { + // Wipe out all legacy content to start a fresh definition. + volumes.Clear(); + mapList.Clear(); + clusters.Clear(); + } + else if (sc.Compare("cutscenes")) + { + ParseCutsceneInfo(); + } + else if (sc.Compare("gameinfo")) + { + ParseGameInfo(); + } + else if (sc.Compare("clearall")) + { + // clears all map and progression related data, so that a mod can start with a clean slate. + mapList.Clear(); + volumes.Clear(); + clusters.Clear(); + globalCutscenes.DefaultMapIntro = {}; + globalCutscenes.DefaultMapOutro = {}; + globalCutscenes.DefaultGameover = {}; + } + else + { + sc.ScriptError("%s: Unknown top level keyword", sc.String); + } + } +} + +//========================================================================== +// +// G_ParseMapInfo +// Parses the MAPINFO lumps of all loaded WADs and generates +// data for wadlevelinfos and wadclusterinfos. +// +//========================================================================== + +void G_ParseMapInfo () +{ + int lump, lastlump = 0; + MapRecord gamedefaults; + + // first parse the internal one which sets up the needed basics and patches the legacy definitions of each game. + FMapInfoParser parse; + MapRecord defaultinfo; + + // Parse internal RMAPINFOs. + while ((lump = fileSystem.FindLumpFullName("engine/rmapinfo.txt", &lastlump, false)) != -1) + { + if (fileSystem.GetFileContainer(lump) > 0) break; // only load from raze.pk3 + + FMapInfoParser parse; + MapRecord defaultinfo; + parse.ParseMapInfo(lump, gamedefaults, defaultinfo); + } + + // Parse any extra RMAPINFOs. + lastlump = 0; + while ((lump = fileSystem.FindLump ("RMAPINFO", &lastlump, false)) != -1) + { + FMapInfoParser parse; + MapRecord defaultinfo; + parse.ParseMapInfo(lump, gamedefaults, defaultinfo); + } + + if (volumes.Size() == 0) + { + I_FatalError ("No volumes defined."); + } +} diff --git a/source/core/g_mapinfo.h b/source/core/g_mapinfo.h new file mode 100644 index 000000000..6131b1e97 --- /dev/null +++ b/source/core/g_mapinfo.h @@ -0,0 +1,108 @@ +/* +** g_level.h +** +**--------------------------------------------------------------------------- +** Copyright 1998-2006 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#ifndef __G_LEVEL_H__ +#define __G_LEVEL_H__ + +#include "autosegs.h" +#include "vectors.h" +#include "sc_man.h" +#include "file_zip.h" + +struct FMapInfoParser +{ + FScanner sc; + bool Internal; + MapRecord* defaultinfoptr; + + FMapInfoParser(bool internal = false) + { + Internal = internal; + } + + bool CheckLegacyMapDefinition(FString& mapname); + bool ParseLookupName(FString &dest); + void ParseMusic(FString &name, int &order); + void ParseLumpOrTextureName(FString &name); + + void ParseCutscene(CutsceneDef& cdef); + void ParseCluster(); + void ParseMapName(FString &mapname); + MapRecord *ParseMapHeader(MapRecord &defaultinfo); + void ParseMapDefinition(MapRecord &leveldef); + void ParseEpisodeInfo (); + void ParseCutsceneInfo(); + void ParseGameInfo(); + void ParseMapInfo (int lump, MapRecord &gamedefaults, MapRecord &defaultinfo); + + void ParseOpenBrace(); + bool ParseCloseBrace(); + bool CheckAssign(); + void ParseAssign(); + void MustParseAssign(); + void ParseComma(); + bool CheckNumber(); + bool CheckFloat(); + void SkipToNext(); + void CheckEndOfFile(const char *block); +}; + +#if defined(_MSC_VER) +#pragma section(SECTION_YREG,read) +#define MSVC_YSEG __declspec(allocate(SECTION_YREG)) +#define GCC_YSEG +#else +#define MSVC_YSEG +#define GCC_YSEG __attribute__((section(SECTION_YREG))) __attribute__((used)) +#endif + +#define DEFINE_MAP_OPTION(name, old) \ + static void MapOptHandler_##name(FMapInfoParser &parse, MapRecord *info); \ + static FMapOptInfo MapOpt_##name = \ + { #name, MapOptHandler_##name, old }; \ + MSVC_YSEG FMapOptInfo *mapopt_##name GCC_YSEG = &MapOpt_##name; \ + static void MapOptHandler_##name(FMapInfoParser &parse, MapRecord *info) + + +struct FMapOptInfo +{ + const char *name; + void (*handler) (FMapInfoParser &parse, MapRecord *levelinfo); + bool old; +}; + + +void G_ParseMapInfo(); + + +#endif //__G_LEVEL_H__ diff --git a/source/core/gamecontrol.cpp b/source/core/gamecontrol.cpp index dcf45e9a9..dcc0e9da7 100644 --- a/source/core/gamecontrol.cpp +++ b/source/core/gamecontrol.cpp @@ -57,7 +57,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "c_dispatch.h" #include "glbackend/glbackend.h" #include "engineerrors.h" -#include "mmulti.h" #include "gamestate.h" #include "gstrings.h" #include "texturemanager.h" @@ -73,6 +72,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "automap.h" #include "v_draw.h" #include "gi.h" +#include "vm.h" +#include "g_mapinfo.h" +#include "gamefuncs.h" +#include "hw_voxels.h" +#include "hw_palmanager.h" CVAR(Bool, autoloadlights, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR(Bool, autoloadbrightmaps, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) @@ -101,7 +105,7 @@ CUSTOM_CVAR(Int, mouse_capturemode, 1, CVAR_GLOBALCONFIG | CVAR_ARCHIVE) // The last remains of sdlayer.cpp GameInterface* gi; int myconnectindex, numplayers; -int connecthead, connectpoint2[MAXMULTIPLAYERS]; +int connecthead, connectpoint2[MAXPLAYERS]; auto vsnprintfptr = vsnprintf; // This is an inline in Visual Studio but we need an address for it to satisfy the MinGW compiled libraries. int lastTic; @@ -134,6 +138,7 @@ void SetConsoleNotifyBuffer(); bool PreBindTexture(FRenderState* state, FGameTexture*& tex, EUpscaleFlags& flags, int& scaleflags, int& clampmode, int& translation, int& overrideshader); void PostLoadSetup(); void FontCharCreated(FGameTexture* base, FGameTexture* untranslated, FGameTexture* translated); +void LoadVoxelModels(); DBaseStatusBar* StatusBar; @@ -224,14 +229,21 @@ static bool System_DisableTextureFilter() static IntRect System_GetSceneRect() { - // Special handling so the view with a visible status bar displays properly - int height = windowxy2.y - windowxy1.y + 1, width = windowxy2.x - windowxy1.x + 1; + int viewbottom = windowxy2.y + 1; + int viewheight = viewbottom - windowxy1.y; + int viewright = windowxy2.x + 1; + int viewwidth = viewright - windowxy1.x; + + int renderheight; + + if (viewheight == screen->GetHeight()) renderheight = viewheight; + else renderheight = (viewwidth * screen->GetHeight() / screen->GetWidth()) & ~7; IntRect mSceneViewport; mSceneViewport.left = windowxy1.x; - mSceneViewport.top = windowxy1.y; - mSceneViewport.width = width; - mSceneViewport.height = height; + mSceneViewport.top = screen->GetHeight() - (renderheight + windowxy1.y - ((renderheight - viewheight) / 2)); + mSceneViewport.width = viewwidth; + mSceneViewport.height = renderheight; return mSceneViewport; } @@ -275,6 +287,11 @@ void System_CrashInfo(char* buffer, size_t bufflen, const char *lfstr) UserConfig userConfig; +DEFINE_GLOBAL(userConfig) +DEFINE_FIELD_X(UserConfigStruct, UserConfig, nomonsters) +DEFINE_FIELD_X(UserConfigStruct, UserConfig, nosound) +DEFINE_FIELD_X(UserConfigStruct, UserConfig, nologo) + void UserConfig::ProcessOptions() { // -help etc are omitted @@ -549,7 +566,7 @@ int GameMain() I_ShowFatalError(err.what()); r = -1; } - DeleteScreenJob(); + //DeleteScreenJob(); DeinitMenus(); if (StatusBar) StatusBar->Destroy(); StatusBar = nullptr; @@ -565,12 +582,12 @@ int GameMain() G_SaveConfig(); C_DeinitConsole(); V_ClearFonts(); - vox_deinit(); + voxClear(); + ClearPalManager(); TexMan.DeleteAll(); TileFiles.CloseAll(); // delete the texture data before shutting down graphics. - GLInterface.Deinit(); I_ShutdownGraphics(); - engineUnInit(); + freeallmodels(); if (gi) { delete gi; @@ -590,13 +607,17 @@ int GameMain() void SetDefaultStrings() { + // Duke 1.3 does not define its episodes through CON. if ((g_gameType & GAMEFLAG_DUKE) && fileSystem.FindFile("E4L1.MAP") < 0) { + auto vol0 = AllocateVolume(); vol0->index = 0; + auto vol1 = AllocateVolume(); vol1->index = 1; vol1->flags = VF_SHAREWARELOCK; + auto vol2 = AllocateVolume(); vol2->index = 2; vol1->flags = VF_SHAREWARELOCK; // Pre-Atomic releases do not define this. - gVolumeNames[0] = "$L.A. Meltdown"; - gVolumeNames[1] = "$Lunar Apocalypse"; - gVolumeNames[2] = "$Shrapnel City"; - if (g_gameType & GAMEFLAG_SHAREWARE) gVolumeNames[3] = "$The Birth"; + vol0->name = "$L.A. Meltdown"; + vol1->name = "$Lunar Apocalypse"; + vol2->name = "$Shrapnel City"; + gSkillNames[0] = "$Piece of Cake"; gSkillNames[1] = "$Let's Rock"; gSkillNames[2] = "$Come get Some"; @@ -648,13 +669,16 @@ static TArray SetupGame() { for (auto& str : game) { + int g = 0; for (auto& grp : groups) { if (grp.FileInfo.gameid.CompareNoCase(str) == 0) { userConfig.gamegrp = grp.FileName; + groupno = g; goto foundit; } + g++; } } } @@ -662,16 +686,18 @@ static TArray SetupGame() // If the user has specified a file name, let's see if we know it. // - if (userConfig.gamegrp.Len()) + if (groupno == -1 && userConfig.gamegrp.Len()) { - FString gamegrplower = "/" + userConfig.gamegrp.MakeLower(); + FString gamegrplower = userConfig.gamegrp.MakeLower(); + if (gamegrplower[1] != ':' || gamegrplower[2] != '/') gamegrplower.Insert(0, "/"); int g = 0; for (auto& grp : groups) { auto grplower = grp.FileName.MakeLower(); - grplower.Substitute("\\", "/"); - if (grplower.LastIndexOf(gamegrplower) == grplower.Len() - gamegrplower.Len()) + FixPathSeperator(grplower); + int pos = grplower.LastIndexOf(gamegrplower); + if (pos >= 0 && pos == grplower.Len() - gamegrplower.Len()) { groupno = g; break; @@ -992,6 +1018,7 @@ int RunGame() LoadScripts(); StartScreen->Progress(); SetDefaultStrings(); + Job_Init(); if (Args->CheckParm("-sounddebug")) C_DoCommand("stat sounddebug"); @@ -999,8 +1026,15 @@ int RunGame() SetupGameButtons(); gameinfo.mBackButton = "engine/graphics/m_back.png"; StartScreen->Progress(); + + GPalette.Init(MAXPALOOKUPS + 1); // one slot for each translation, plus a separate one for the base palettes. + gi->loadPalette(); + voxInit(); + TileFiles.LoadArtSet("tiles%03d.art"); // it's the same for all games. + engineInit(); gi->app_init(); StartScreen->Progress(); + G_ParseMapInfo(); CreateStatusBar(); SetDefaultMenuColors(); M_Init(); @@ -1012,9 +1046,24 @@ int RunGame() V_LoadTranslations(); // loading the translations must be delayed until the palettes have been fully set up. lookups.postLoadTables(); PostLoadSetup(); - videoInit(); + lookups.postLoadLookups(); + FMaterial::SetLayerCallback(setpalettelayer); if (GameStartupInfo.Name.IsNotEmpty()) I_SetWindowTitle(GameStartupInfo.Name); + V_Init2(); + twod->Begin(screen->GetWidth(), screen->GetHeight()); + twod->End(); + UpdateJoystickMenu(NULL); + UpdateVRModes(); + + setVideoMode(); + + LoadVoxelModels(); + GLInterface.Init(screen->GetWidth()); + screen->BeginFrame(); + screen->SetTextureFilterMode(); + setViewport(hud_size); + D_CheckNetGame(); UpdateGenericUI(ui_generic); MainLoop(); @@ -1061,7 +1110,7 @@ void updatePauseStatus() // //========================================================================== -void PolymostProcessVoxels(void); +void LoadVoxelModels(void); void setVideoMode() { @@ -1069,25 +1118,6 @@ void setVideoMode() ydim = screen->GetHeight(); V_UpdateModeSize(xdim, ydim); videoSetViewableArea(0, 0, xdim - 1, ydim - 1); - videoClearScreen(0); -} - -void videoInit() -{ - lookups.postLoadLookups(); - V_Init2(); - setVideoMode(); - - PolymostProcessVoxels(); - GLInterface.Init(screen->GetWidth()); - screen->BeginFrame(); - screen->SetTextureFilterMode(); - setViewport(hud_size); -} - -void G_FatalEngineError(void) -{ - I_FatalError("There was a problem initializing the engine: %s\n\nThe application will now close.", engineerrstr); } //========================================================================== @@ -1378,16 +1408,11 @@ void DrawCrosshair(int deftile, int health, double xdelta, double ydelta, double void LoadDefinitions() { - cycle_t deftimer; - deftimer.Reset(); - deftimer.Clock(); - const char* loaded = nullptr; - const char* defsfile = G_DefFile(); FString razedefsfile = defsfile; razedefsfile.Substitute(".def", "-raze.def"); - loaddefinitionsfile("engine/engine.def", false); // Internal stuff that is required. + loaddefinitionsfile("engine/engine.def"); // Internal stuff that is required. // check what we have. // user .defs override the default ones and are not cumulative. @@ -1395,31 +1420,33 @@ void LoadDefinitions() // otherwise the default rules inherited from older ports apply. if (userConfig.UserDef.IsNotEmpty()) { - if (!loaddefinitionsfile(userConfig.UserDef, true, false)) loaded = userConfig.UserDef; + loaddefinitionsfile(userConfig.UserDef, false); } else { if (fileSystem.FileExists(razedefsfile)) { - if (!loaddefinitionsfile(razedefsfile, true, true)) loaded = razedefsfile; + loaddefinitionsfile(razedefsfile, true); } - else + else if (fileSystem.FileExists(defsfile)) { - if (!loaddefinitionsfile(defsfile, true, false)) loaded = defsfile; + loaddefinitionsfile(defsfile, false); } } + if (userConfig.AddDefs) + { + for (auto& m : *userConfig.AddDefs) + { + loaddefinitionsfile(m, false); + } + userConfig.AddDefs.reset(); + } + if (GameStartupInfo.def.IsNotEmpty()) { - loaddefinitionsfile(GameStartupInfo.def, false); // Stuff from gameinfo. + loaddefinitionsfile(GameStartupInfo.def); // Stuff from gameinfo. } - - if (loaded) - { - deftimer.Unclock(); - DPrintf(DMSG_SPAMMY, "Definitions file \"%s\" loaded, %f ms.\n", loaded, deftimer.TimeMS()); - } - userConfig.AddDefs.reset(); // load the widescreen replacements last. This ensures that mods still get the correct CRCs for their own tile replacements. if (fileSystem.FindFile("engine/widescreen.def") >= 0 && !Args->CheckParm("-nowidescreen")) @@ -1481,13 +1508,69 @@ DEFINE_ACTION_FUNCTION(_Screen, GetViewWindow) return MIN(numret, 4); } -DEFINE_ACTION_FUNCTION_NATIVE(_Build, ShadeToLight, shadeToLight) +DEFINE_ACTION_FUNCTION_NATIVE(_Raze, ShadeToLight, shadeToLight) { PARAM_PROLOGUE; PARAM_INT(shade); ACTION_RETURN_INT(shadeToLight(shade)); } +DEFINE_ACTION_FUNCTION_NATIVE(_Raze, StopAllSounds, FX_StopAllSounds) +{ + FX_StopAllSounds(); + return 0; +} + +DEFINE_ACTION_FUNCTION_NATIVE(_Raze, StopMusic, Mus_Stop) +{ + Mus_Stop(); + return 0; +} + +DEFINE_ACTION_FUNCTION_NATIVE(_Raze, SoundEnabled, SoundEnabled) +{ + ACTION_RETURN_INT(SoundEnabled()); +} + +DEFINE_ACTION_FUNCTION_NATIVE(_Raze, MusicEnabled, MusicEnabled) +{ + ACTION_RETURN_INT(MusicEnabled()); +} + +DEFINE_ACTION_FUNCTION_NATIVE(_Raze, GetTimeFrac, I_GetTimeFrac) +{ + ACTION_RETURN_INT(I_GetTimeFrac()); +} + +DEFINE_ACTION_FUNCTION(_Raze, PlayerName) +{ + PARAM_PROLOGUE; + PARAM_INT(index); + ACTION_RETURN_STRING(unsigned(index) >= MAXPLAYERS ? "" : PlayerName(index)); +} + +DEFINE_ACTION_FUNCTION_NATIVE(_Raze, bsin, bsin) +{ + PARAM_PROLOGUE; + PARAM_INT(v); + PARAM_INT(shift); + ACTION_RETURN_INT(bsin(v, shift)); +} + +DEFINE_ACTION_FUNCTION_NATIVE(_Raze, bcos, bcos) +{ + PARAM_PROLOGUE; + PARAM_INT(v); + PARAM_INT(shift); + ACTION_RETURN_INT(bcos(v, shift)); +} + +DEFINE_ACTION_FUNCTION(_MapRecord, GetCluster) +{ + PARAM_SELF_STRUCT_PROLOGUE(MapRecord); + ACTION_RETURN_POINTER(FindCluster(self->cluster)); +} + extern bool demoplayback; DEFINE_GLOBAL(multiplayer) DEFINE_GLOBAL(netgame) @@ -1495,3 +1578,41 @@ DEFINE_GLOBAL(gameaction) DEFINE_GLOBAL(gamestate) DEFINE_GLOBAL(demoplayback) DEFINE_GLOBAL(consoleplayer) +DEFINE_GLOBAL(currentLevel) +DEFINE_GLOBAL(paused) + +DEFINE_FIELD_X(ClusterDef, ClusterDef, name) +DEFINE_FIELD_X(ClusterDef, ClusterDef, InterBackground) + +DEFINE_FIELD_X(MapRecord, MapRecord, parTime) +DEFINE_FIELD_X(MapRecord, MapRecord, designerTime) +DEFINE_FIELD_X(MapRecord, MapRecord, fileName) +DEFINE_FIELD_X(MapRecord, MapRecord, labelName) +DEFINE_FIELD_X(MapRecord, MapRecord, name) +DEFINE_FIELD_X(MapRecord, MapRecord, music) +DEFINE_FIELD_X(MapRecord, MapRecord, cdSongId) +DEFINE_FIELD_X(MapRecord, MapRecord, flags) +DEFINE_FIELD_X(MapRecord, MapRecord, levelNumber) +DEFINE_FIELD_X(MapRecord, MapRecord, cluster) +DEFINE_FIELD_X(MapRecord, MapRecord, NextMap) +DEFINE_FIELD_X(MapRecord, MapRecord, NextSecret) +//native readonly String messages[MAX_MESSAGES]; +DEFINE_FIELD_X(MapRecord, MapRecord, Author) +DEFINE_FIELD_X(MapRecord, MapRecord, InterBackground) + +DEFINE_FIELD_X(SummaryInfo, SummaryInfo, kills) +DEFINE_FIELD_X(SummaryInfo, SummaryInfo, maxkills) +DEFINE_FIELD_X(SummaryInfo, SummaryInfo, secrets) +DEFINE_FIELD_X(SummaryInfo, SummaryInfo, maxsecrets) +DEFINE_FIELD_X(SummaryInfo, SummaryInfo, supersecrets) +DEFINE_FIELD_X(SummaryInfo, SummaryInfo, playercount) +DEFINE_FIELD_X(SummaryInfo, SummaryInfo, time) +DEFINE_FIELD_X(SummaryInfo, SummaryInfo, cheated) +DEFINE_FIELD_X(SummaryInfo, SummaryInfo, endofgame) + + +void InitBuildTiles() +{ + // need to find a better way to handle this thing. +} + diff --git a/source/core/gamecontrol.h b/source/core/gamecontrol.h index fa2cab18a..cbf015712 100644 --- a/source/core/gamecontrol.h +++ b/source/core/gamecontrol.h @@ -70,12 +70,12 @@ extern UserConfig userConfig; extern int nomusic; extern bool nosound; -inline bool MusicEnabled() +inline int MusicEnabled() // int return is for scripting { return mus_enabled && !nomusic; } -inline bool SoundEnabled() +inline int SoundEnabled() { return snd_enabled && !nosound; } @@ -99,8 +99,15 @@ enum GAMEFLAG_POWERSLAVE = 0x00002000, GAMEFLAG_EXHUMED = 0x00004000, GAMEFLAG_PSEXHUMED = GAMEFLAG_POWERSLAVE | GAMEFLAG_EXHUMED, // the two games really are the same, except for the name and the publisher. - GAMEFLAG_WORLDTOUR = 0x00008000, - GAMEFLAG_DUKEDC = 0x00010000, + GAMEFLAG_WORLDTOUR = 0x00008000, + GAMEFLAG_DUKEDC = 0x00010000, + GAMEFLAG_DUKENW = 0x00020000, + GAMEFLAG_DUKEVACA = 0x00040000, + GAMEFLAG_BLOODCP = 0x00080000, + GAMEFLAG_ROUTE66 = 0x00100000, + GAMEFLAG_SWWANTON = 0x00200000, + GAMEFLAG_SWTWINDRAG = 0x00400000, + GAMEFLAG_DUKECOMPAT = GAMEFLAG_DUKE | GAMEFLAG_NAM | GAMEFLAG_NAPALM | GAMEFLAG_WW2GI | GAMEFLAG_RRALL, GAMEFLAGMASK = 0x0000FFFF, // flags allowed from grpinfo @@ -160,6 +167,11 @@ inline bool isWW2GI() return g_gameType & (GAMEFLAG_WW2GI); } +inline bool isDuke() +{ + return g_gameType & (GAMEFLAG_DUKE); +} + inline bool isRR() { return g_gameType & (GAMEFLAG_RRALL); @@ -195,7 +207,6 @@ void S_PauseSound(bool notmusic, bool notsfx); void S_ResumeSound(bool notsfx); void S_SetSoundPaused(int state); -void G_FatalEngineError(void); enum { MaxSmoothRatio = FRACUNIT diff --git a/source/core/gamecvars.cpp b/source/core/gamecvars.cpp index 7fea2c6f6..f4b0c3de0 100644 --- a/source/core/gamecvars.cpp +++ b/source/core/gamecvars.cpp @@ -81,6 +81,7 @@ CVARD(Bool, cl_smoothsway, false, CVAR_ARCHIVE, "move SW weapon left and right s CVARD(Bool, cl_showmagamt, false, CVAR_ARCHIVE, "show the amount of rounds left in the magazine of your weapon on the modern HUD") CVARD(Bool, cl_nomeleeblur, false, CVAR_ARCHIVE, "enable/disable blur effect with melee weapons in SW") CVARD(Bool, cl_exhumedoldturn, false, CVAR_ARCHIVE, "enable/disable legacy turning speed for Powerslave/Exhumed") +CVARD(Bool, cl_hudinterpolation, true, CVAR_ARCHIVE, "enable/disable HUD (weapon drawer) interpolation") CUSTOM_CVARD(Int, cl_autoaim, 1, CVAR_ARCHIVE|CVAR_USERINFO, "enable/disable weapon autoaim") diff --git a/source/core/gamecvars.h b/source/core/gamecvars.h index fa2115037..7e3a19259 100644 --- a/source/core/gamecvars.h +++ b/source/core/gamecvars.h @@ -26,6 +26,7 @@ EXTERN_CVAR(Bool, cl_smoothsway) EXTERN_CVAR(Bool, cl_showmagamt) EXTERN_CVAR(Bool, cl_nomeleeblur) EXTERN_CVAR(Bool, cl_exhumedoldturn) +EXTERN_CVAR(Bool, cl_hudinterpolation) EXTERN_CVAR(Bool, demorec_seeds_cvar) EXTERN_CVAR(Bool, demoplay_diffs) diff --git a/source/core/gamefuncs.cpp b/source/core/gamefuncs.cpp index ac81b680f..f34cd1249 100644 --- a/source/core/gamefuncs.cpp +++ b/source/core/gamefuncs.cpp @@ -22,6 +22,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "gamefuncs.h" #include "gamestruct.h" +#include "intvec.h" //--------------------------------------------------------------------------- @@ -146,3 +147,180 @@ bool calcChaseCamPos(int* px, int* py, int* pz, spritetype* pspr, short *psectnu return true; } + +//========================================================================== +// +// note that this returns values in renderer coordinate space with inverted sign! +// +//========================================================================== + +void PlanesAtPoint(const sectortype* sec, float dax, float day, float* pceilz, float* pflorz) +{ + float ceilz = float(sec->ceilingz); + float florz = float(sec->floorz); + + if (((sec->ceilingstat | sec->floorstat) & CSTAT_SECTOR_SLOPE) == CSTAT_SECTOR_SLOPE) + { + auto wal = &wall[sec->wallptr]; + auto wal2 = &wall[wal->point2]; + + float dx = wal2->x - wal->x; + float dy = wal2->y - wal->y; + + int i = (int)sqrt(dx * dx + dy * dy) << 5; // length of sector's first wall. + if (i != 0) + { + float const j = (dx * (day - wal->y) - dy * (dax - wal->x)) * (1.f / 8.f); + if (sec->ceilingstat & CSTAT_SECTOR_SLOPE) ceilz += (sec->ceilingheinum * j) / i; + if (sec->floorstat & CSTAT_SECTOR_SLOPE) florz += (sec->floorheinum * j) / i; + } + } + // Scale to render coordinates. + if (pceilz) *pceilz = ceilz * -(1.f / 256.f); + if (pflorz) *pflorz = florz * -(1.f / 256.f); +} + +//========================================================================== +// +// Calculate the position of a wall sprite in the world +// +//========================================================================== + +void GetWallSpritePosition(const spritetype* spr, vec2_t pos, vec2_t* out, bool render) +{ + auto tex = tileGetTexture(spr->picnum); + + int width, leftofs; + if (render && hw_hightile && TileFiles.tiledata[spr->picnum].hiofs.xsize) + { + width = TileFiles.tiledata[spr->picnum].hiofs.xsize; + leftofs = (TileFiles.tiledata[spr->picnum].hiofs.xoffs + spr->xoffset); + } + else + { + width = (int)tex->GetDisplayWidth(); + leftofs = ((int)tex->GetDisplayLeftOffset() + spr->xoffset); + } + + int x = bsin(spr->ang) * spr->xrepeat; + int y = -bcos(spr->ang) * spr->xrepeat; + + int xoff = leftofs; + if (spr->cstat & CSTAT_SPRITE_XFLIP) xoff = -xoff; + int origin = (width >> 1) + xoff; + + out[0].x = pos.x - MulScale(x, origin, 16); + out[0].y = pos.y - MulScale(y, origin, 16); + out[1].x = out[0].x + MulScale(x, width, 16); + out[1].y = out[0].y + MulScale(y, width, 16); +} + + +//========================================================================== +// +// Calculate the position of a wall sprite in the world +// +//========================================================================== + +void GetFlatSpritePosition(const spritetype* spr, vec2_t pos, vec2_t* out, bool render) +{ + auto tex = tileGetTexture(spr->picnum); + + int width, height, leftofs, topofs; + if (render && hw_hightile && TileFiles.tiledata[spr->picnum].hiofs.xsize) + { + width = TileFiles.tiledata[spr->picnum].hiofs.xsize * spr->xrepeat; + height = TileFiles.tiledata[spr->picnum].hiofs.ysize * spr->yrepeat; + leftofs = (TileFiles.tiledata[spr->picnum].hiofs.xoffs + spr->xoffset) * spr->xrepeat; + topofs = (TileFiles.tiledata[spr->picnum].hiofs.yoffs + spr->yoffset) * spr->yrepeat; + } + else + { + width = (int)tex->GetDisplayWidth() * spr->xrepeat; + height = (int)tex->GetDisplayHeight() * spr->yrepeat; + leftofs = ((int)tex->GetDisplayLeftOffset() + spr->xoffset) * spr->xrepeat; + topofs = ((int)tex->GetDisplayTopOffset() + spr->yoffset) * spr->yrepeat; + } + + if (spr->cstat & CSTAT_SPRITE_XFLIP) leftofs = -leftofs; + if (spr->cstat & CSTAT_SPRITE_YFLIP) topofs = -topofs; + + int sprcenterx = (width >> 1) + leftofs; + int sprcentery = (height >> 1) + topofs; + + int cosang = bcos(spr->ang); + int sinang = bsin(spr->ang); + + out[0].x = pos.x + DMulScale(sinang, sprcenterx, cosang, sprcentery, 16); + out[0].y = pos.y + DMulScale(sinang, sprcentery, -cosang, sprcenterx, 16); + + out[1].x = out[0].x - MulScale(sinang, width, 16); + out[1].y = out[0].y + MulScale(cosang, width, 16); + + vec2_t sub = { MulScale(cosang, height, 16), MulScale(sinang, height, 16) }; + out[2] = out[1] - sub; + out[3] = out[0] - sub; +} + + +//========================================================================== +// +// Check if some walls are set to use rotated textures. +// Ideally this should just have been done with texture rotation, +// but the effects on the render code would be too severe due to the alignment mess. +// +//========================================================================== + +void checkRotatedWalls() +{ + for (int i = 0; i < numwalls; ++i) + { + if (wall[i].cstat & CSTAT_WALL_ROTATE_90) + { + auto& w = wall[i]; + auto& tile = RotTile(w.picnum + animateoffs(w.picnum, 16384)); + + if (tile.newtile == -1 && tile.owner == -1) + { + auto owner = w.picnum + animateoffs(w.picnum, 16384); + + tile.newtile = TileFiles.tileCreateRotated(owner); + assert(tile.newtile != -1); + + RotTile(tile.newtile).owner = w.picnum + animateoffs(w.picnum, 16384); + + } + } + } +} + +//========================================================================== +// +// vector serializers +// +//========================================================================== + +FSerializer& Serialize(FSerializer& arc, const char* key, vec2_t& c, vec2_t* def) +{ + if (def && !memcmp(&c, def, sizeof(c))) return arc; + if (arc.BeginObject(key)) + { + arc("x", c.x, def ? &def->x : nullptr) + ("y", c.y, def ? &def->y : nullptr) + .EndObject(); + } + return arc; +} + +FSerializer& Serialize(FSerializer& arc, const char* key, vec3_t& c, vec3_t* def) +{ + if (def && !memcmp(&c, def, sizeof(c))) return arc; + if (arc.BeginObject(key)) + { + arc("x", c.x, def ? &def->x : nullptr) + ("y", c.y, def ? &def->y : nullptr) + ("z", c.z, def ? &def->z : nullptr) + .EndObject(); + } + return arc; +} diff --git a/source/core/gamefuncs.h b/source/core/gamefuncs.h index 0c646aa86..20a5d7ad7 100644 --- a/source/core/gamefuncs.h +++ b/source/core/gamefuncs.h @@ -1,9 +1,121 @@ #pragma once #include "gamecontrol.h" -#include "buildtypes.h" #include "binaryangle.h" +#include "build.h" extern int cameradist, cameraclock; -bool calcChaseCamPos(int* px, int* py, int* pz, spritetype* pspr, short *psectnum, binangle ang, fixedhoriz horiz, double const smoothratio); \ No newline at end of file +void loaddefinitionsfile(const char* fn, bool cumulative = false); + +bool calcChaseCamPos(int* px, int* py, int* pz, spritetype* pspr, short *psectnum, binangle ang, fixedhoriz horiz, double const smoothratio); +void PlanesAtPoint(const sectortype* sec, float dax, float day, float* ceilz, float* florz); +void setWallSectors(); +void GetWallSpritePosition(const spritetype* spr, vec2_t pos, vec2_t* out, bool render = false); +void GetFlatSpritePosition(const spritetype* spr, vec2_t pos, vec2_t* out, bool render = false); +void checkRotatedWalls(); + +// y is negated so that the orientation is the same as in GZDoom, in order to use its utilities. +// The render code should NOT use Build coordinates for anything! + +inline double WallStartX(int wallnum) +{ + return wall[wallnum].x * (1 / 16.); +} + +inline double WallStartY(int wallnum) +{ + return wall[wallnum].y * (1 / -16.); +} + +inline double WallEndX(int wallnum) +{ + return wall[wall[wallnum].point2].x * (1 / 16.); +} + +inline double WallEndY(int wallnum) +{ + return wall[wall[wallnum].point2].y * (1 / -16.); +} + +inline double WallStartX(const walltype* wallnum) +{ + return wallnum->x * (1 / 16.); +} + +inline double WallStartY(const walltype* wallnum) +{ + return wallnum->y * (1 / -16.); +} + +inline DVector2 WallStart(const walltype* wallnum) +{ + return { WallStartX(wallnum), WallStartY(wallnum) }; +} + +inline double WallEndX(const walltype* wallnum) +{ + return wall[wallnum->point2].x * (1 / 16.); +} + +inline double WallEndY(const walltype* wallnum) +{ + return wall[wallnum->point2].y * (1 / -16.); +} + +inline DVector2 WallEnd(const walltype* wallnum) +{ + return { WallEndX(wallnum), WallEndY(wallnum) }; +} + +inline DVector2 WallDelta(const walltype* wallnum) +{ + return WallEnd(wallnum) - WallStart(wallnum); +} + +inline double SpriteX(int wallnum) +{ + return sprite[wallnum].x * (1 / 16.); +} + +inline double SpriteY(int wallnum) +{ + return sprite[wallnum].y * (1 / -16.); +} + +inline double PointOnLineSide(double x, double y, double linex, double liney, double deltax, double deltay) +{ + return (x - linex) * deltay - (y - liney) * deltax; +} + +inline double PointOnLineSide(const DVector2 &pos, const walltype *line) +{ + return (pos.X - WallStartX(line)) * WallDelta(line).Y - (pos.Y - WallStartY(line)) * WallDelta(line).X; +} + +template +inline double PointOnLineSide(const TVector2& pos, const TVector2& linestart, const TVector2& lineend) +{ + return (pos.X - linestart.X) * (lineend.Y - linestart.Y) - (pos.Y - linestart.Y) * (lineend.X - linestart.X); +} + +inline int sectorofwall(int wallNum) +{ + if ((unsigned)wallNum < (unsigned)numwalls) return wall[wallNum].sector; + return -1; +} + +extern int numshades; + +// Return type is int because this gets passed to variadic functions where structs may produce undefined behavior. +inline int shadeToLight(int shade) +{ + shade = clamp(shade, 0, numshades - 1); + int light = Scale(numshades - 1 - shade, 255, numshades - 1); + return PalEntry(255, light, light, light); +} + +inline void copyfloorpal(spritetype* spr, const sectortype* sect) +{ + if (!lookups.noFloorPal(sect->floorpal)) spr->pal = sect->floorpal; +} diff --git a/source/core/gamehud.cpp b/source/core/gamehud.cpp index 7b94b95a1..e1ced1918 100644 --- a/source/core/gamehud.cpp +++ b/source/core/gamehud.cpp @@ -45,6 +45,7 @@ #include "v_draw.h" #include "v_font.h" #include "gamestruct.h" +#include "gamefuncs.h" F2DDrawer twodpsp; diff --git a/source/core/gameinput.cpp b/source/core/gameinput.cpp index 3e10991ab..8560220a2 100644 --- a/source/core/gameinput.cpp +++ b/source/core/gameinput.cpp @@ -25,6 +25,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "gameinput.h" #include "gamestruct.h" #include "serializer.h" +#include "build.h" CVARD(Bool, invertmousex, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG, "invert horizontal mouse movement") CVARD(Bool, invertmouse, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG, "invert vertical mouse movement") @@ -49,46 +50,18 @@ int getincangle(int a, int na) return na-a; } -double getincanglef(double a, double na) +binangle getincanglebam(binangle a, binangle na) { - a = fmod(a, 2048.); - na = fmod(na, 2048.); + int64_t cura = a.asbam(); + int64_t newa = na.asbam(); - if(fabs(a-na) >= 1024) + if(abs(cura-newa) > INT32_MAX) { - if(na > 1024) na -= 2048; - if(a > 1024) a -= 2048; + if(newa > INT32_MAX) newa -= UINT32_MAX; + if(cura > INT32_MAX) cura -= UINT32_MAX; } - return na-a; -} - -fixed_t getincangleq16(fixed_t a, fixed_t na) -{ - a &= 0x7FFFFFF; - na &= 0x7FFFFFF; - - if(abs(a-na) >= IntToFixed(1024)) - { - if(na > IntToFixed(1024)) na -= IntToFixed(2048); - if(a > IntToFixed(1024)) a -= IntToFixed(2048); - } - - return na-a; -} - -lookangle getincanglebam(binangle a, binangle na) -{ - int64_t cura = a.asbam() & 0xFFFFFFFF; - int64_t newa = na.asbam() & 0xFFFFFFFF; - - if(abs(cura-newa) >= BAngToBAM(1024)) - { - if(newa > BAngToBAM(1024)) newa -= BAngToBAM(2048); - if(cura > BAngToBAM(1024)) cura -= BAngToBAM(2048); - } - - return bamlook(newa-cura); + return bamang(newa-cura); } //--------------------------------------------------------------------------- @@ -271,13 +244,13 @@ void processMovement(InputPacket* currInput, InputPacket* inputBuffer, ControlIn // //--------------------------------------------------------------------------- -void sethorizon(PlayerHorizon* horizon, float const horz, ESyncBits* actions, double const scaleAdjust) +void PlayerHorizon::applyinput(float const horz, ESyncBits* actions, double const scaleAdjust) { // Process only if no targeted horizon set. - if (!horizon->targetset()) + if (!targetset()) { // Store current horizon as true pitch. - double pitch = horizon->horiz.aspitch(); + double pitch = horiz.aspitch(); if (horz) { @@ -312,20 +285,17 @@ void sethorizon(PlayerHorizon* horizon, float const horz, ESyncBits* actions, do } // clamp before converting back to horizon - horizon->horiz = q16horiz(clamp(PitchToHoriz(pitch), gi->playerHorizMin(), gi->playerHorizMax())); + horiz = q16horiz(clamp(PitchToHoriz(pitch), gi->playerHorizMin(), gi->playerHorizMax())); // return to center if conditions met. - if ((*actions & SB_CENTERVIEW) && !(*actions & (SB_LOOK_UP|SB_LOOK_DOWN))) + if ((*actions & SB_CENTERVIEW) && !(*actions & (SB_LOOK_UP|SB_LOOK_DOWN)) && horiz.asq16()) { - if (abs(horizon->horiz.asq16()) > FloatToFixed(0.25)) - { - // move horiz back to 0 - horizon->horiz -= q16horiz(xs_CRoundToInt(scaleAdjust * horizon->horiz.asq16() * (10. / GameTicRate))); - } - else + // move horiz back to 0 + horiz -= buildfhoriz(scaleAdjust * horiz.asbuildf() * (10. / GameTicRate)); + if (abs(horiz.asq16()) < (FRACUNIT >> 2)) { // not looking anymore because horiz is back at 0 - horizon->horiz = q16horiz(0); + horiz = q16horiz(0); *actions &= ~SB_CENTERVIEW; } } @@ -342,66 +312,72 @@ void sethorizon(PlayerHorizon* horizon, float const horz, ESyncBits* actions, do // //--------------------------------------------------------------------------- -void applylook(PlayerAngle* angle, float const avel, ESyncBits* actions, double const scaleAdjust) +void PlayerAngle::applyinput(float const avel, ESyncBits* actions, double const scaleAdjust) { - // return q16rotscrnang to 0 and set to 0 if less than a quarter of a unit - angle->rotscrnang -= bamlook(xs_CRoundToInt(scaleAdjust * angle->rotscrnang.asbam() * (15. / GameTicRate))); - if (abs(angle->rotscrnang.asbam()) < (BAMUNIT >> 2)) angle->rotscrnang = bamlook(0); + if (rotscrnang.asbam()) + { + // return rotscrnang to 0 + rotscrnang -= buildfang(scaleAdjust * rotscrnang.signedbuildf() * (15. / GameTicRate)); + if (abs(rotscrnang.signedbam()) < (BAMUNIT >> 2)) rotscrnang = bamang(0); + } - // return q16look_ang to 0 and set to 0 if less than a quarter of a unit - angle->look_ang -= bamlook(xs_CRoundToInt(scaleAdjust * angle->look_ang.asbam() * (7.5 / GameTicRate))); - if (abs(angle->look_ang.asbam()) < (BAMUNIT >> 2)) angle->look_ang = bamlook(0); + if (look_ang.asbam()) + { + // return look_ang to 0 + look_ang -= buildfang(scaleAdjust * look_ang.signedbuildf() * (7.5 / GameTicRate)); + if (abs(look_ang.signedbam()) < (BAMUNIT >> 2)) look_ang = bamang(0); + } if (*actions & SB_LOOK_LEFT) { // start looking left - angle->look_ang -= bamlook(xs_CRoundToInt(scaleAdjust * (4560. / GameTicRate) * BAMUNIT)); - angle->rotscrnang += bamlook(xs_CRoundToInt(scaleAdjust * (720. / GameTicRate) * BAMUNIT)); + look_ang += buildfang(scaleAdjust * -(4560. / GameTicRate)); + rotscrnang += buildfang(scaleAdjust * (720. / GameTicRate)); } if (*actions & SB_LOOK_RIGHT) { // start looking right - angle->look_ang += bamlook(xs_CRoundToInt(scaleAdjust * (4560. / GameTicRate) * BAMUNIT)); - angle->rotscrnang -= bamlook(xs_CRoundToInt(scaleAdjust * (720. / GameTicRate) * BAMUNIT)); + look_ang += buildfang(scaleAdjust * (4560. / GameTicRate)); + rotscrnang += buildfang(scaleAdjust * -(720. / GameTicRate)); } - if (!angle->targetset()) + if (!targetset()) { if (*actions & SB_TURNAROUND) { - if (angle->spin.asbam() == 0) + if (spin == 0) { // currently not spinning, so start a spin - angle->spin = buildlook(-1024); + spin = -1024.; } *actions &= ~SB_TURNAROUND; } - if (angle->spin.asbam() < 0) - { - // return spin to 0 - lookangle add = bamlook(xs_CRoundToUInt(scaleAdjust * ((!(*actions & SB_CROUCH) ? 3840. : 1920.) / GameTicRate) * BAMUNIT)); - angle->spin += add; - if (angle->spin.asbam() > 0) - { - // Don't overshoot our target. With variable factor this is possible. - add -= angle->spin; - angle->spin = bamlook(0); - } - angle->ang += bamang(add.asbam()); - } - if (avel) { // add player's input - angle->ang += degang(avel); - angle->spin = bamlook(0); + ang += degang(avel); + spin = 0; + } + + if (spin < 0) + { + // return spin to 0 + double add = scaleAdjust * ((!(*actions & SB_CROUCH) ? 3840. : 1920.) / GameTicRate); + spin += add; + if (spin > 0) + { + // Don't overshoot our target. With variable factor this is possible. + add -= spin; + spin = 0; + } + ang += buildfang(add); } } else { - angle->spin = bamlook(0); + spin = 0; } } @@ -411,7 +387,7 @@ void applylook(PlayerAngle* angle, float const avel, ESyncBits* actions, double // //--------------------------------------------------------------------------- -void calcviewpitch(vec2_t const pos, fixedhoriz* horizoff, binangle const ang, bool const aimmode, bool const canslopetilt, int const cursectnum, double const scaleAdjust, bool const climbing) +void PlayerHorizon::calcviewpitch(vec2_t const pos, binangle const ang, bool const aimmode, bool const canslopetilt, int const cursectnum, double const scaleAdjust, bool const climbing) { if (cl_slopetilting) { @@ -438,7 +414,7 @@ void calcviewpitch(vec2_t const pos, fixedhoriz* horizoff, binangle const ang, b // accordingly if (cursectnum == tempsect || (!isBlood() && abs(getflorzofslope(tempsect, x, y) - k) <= (4 << 8))) { - *horizoff += q16horiz(xs_CRoundToInt(scaleAdjust * ((j - k) * (!isBlood() ? 160 : 1408)))); + horizoff += q16horiz(xs_CRoundToInt(scaleAdjust * ((j - k) * (!isBlood() ? 160 : 1408)))); } } } @@ -446,21 +422,21 @@ void calcviewpitch(vec2_t const pos, fixedhoriz* horizoff, binangle const ang, b if (climbing) { // tilt when climbing but you can't even really tell it. - if (horizoff->asq16() < IntToFixed(100)) - *horizoff += q16horiz(xs_CRoundToInt(scaleAdjust * (((IntToFixed(100) - horizoff->asq16()) >> 3) + FRACUNIT))); + if (horizoff.asq16() < IntToFixed(100)) + horizoff += q16horiz(xs_CRoundToInt(scaleAdjust * (((IntToFixed(100) - horizoff.asq16()) >> 3) + FRACUNIT))); } else { // Make horizoff grow towards 0 since horizoff is not modified when you're not on a slope. - if (horizoff->asq16() > 0) + if (horizoff.asq16() > 0) { - *horizoff += q16horiz(xs_CRoundToInt(-scaleAdjust * ((horizoff->asq16() >> 3) + FRACUNIT))); - if (horizoff->asq16() < 0) *horizoff = q16horiz(0); + horizoff += q16horiz(xs_CRoundToInt(-scaleAdjust * ((horizoff.asq16() >> 3) + FRACUNIT))); + if (horizoff.asq16() < 0) horizoff = q16horiz(0); } - if (horizoff->asq16() < 0) + if (horizoff.asq16() < 0) { - *horizoff += q16horiz(xs_CRoundToInt(-scaleAdjust * ((horizoff->asq16() >> 3) - FRACUNIT))); - if (horizoff->asq16() > 0) *horizoff = q16horiz(0); + horizoff += q16horiz(xs_CRoundToInt(-scaleAdjust * ((horizoff.asq16() >> 3) - FRACUNIT))); + if (horizoff.asq16() > 0) horizoff = q16horiz(0); } } } diff --git a/source/core/gameinput.h b/source/core/gameinput.h index 39f9a12ff..4939d826b 100644 --- a/source/core/gameinput.h +++ b/source/core/gameinput.h @@ -7,9 +7,7 @@ #include "packet.h" int getincangle(int a, int na); -double getincanglef(double a, double na); -fixed_t getincangleq16(fixed_t a, fixed_t na); -lookangle getincanglebam(binangle a, binangle na); +binangle getincanglebam(binangle a, binangle na); struct PlayerHorizon { @@ -29,7 +27,12 @@ struct PlayerHorizon void addadjustment(double value) { - __addadjustment(q16horiz(FloatToFixed(value))); + __addadjustment(buildfhoriz(value)); + } + + void addadjustment(fixedhoriz value) + { + __addadjustment(value); } void resetadjustment() @@ -37,19 +40,14 @@ struct PlayerHorizon adjustment = 0; } - void settarget(int value, bool backup = false) - { - __settarget(buildhoriz(clamp(value, FixedToInt(gi->playerHorizMin()), FixedToInt(gi->playerHorizMax()))), backup); - } - void settarget(double value, bool backup = false) { - __settarget(buildfhoriz(clamp(value, FixedToFloat(gi->playerHorizMin()), FixedToFloat(gi->playerHorizMax()))), backup); + __settarget(buildfhoriz(value), backup); } void settarget(fixedhoriz value, bool backup = false) { - __settarget(q16horiz(clamp(value.asq16(), gi->playerHorizMin(), gi->playerHorizMax())), backup); + __settarget(value, backup); } bool targetset() @@ -61,11 +59,11 @@ struct PlayerHorizon { if (targetset()) { - auto delta = (target - horiz).asq16(); + auto delta = (target - horiz).asbuildf(); - if (abs(delta) > FRACUNIT) + if (abs(delta) > 1) { - horiz += q16horiz(xs_CRoundToInt(scaleAdjust * delta)); + horiz += buildfhoriz(scaleAdjust * delta); } else { @@ -75,7 +73,7 @@ struct PlayerHorizon } else if (adjustment) { - horiz += q16horiz(xs_CRoundToInt(scaleAdjust * adjustment)); + horiz += buildfhoriz(scaleAdjust * adjustment); } } @@ -91,12 +89,17 @@ struct PlayerHorizon fixedhoriz interpolatedsum(double const smoothratio) { - double const ratio = smoothratio * (1. / FRACUNIT); - fixed_t const prev = osum().asq16(); - fixed_t const curr = sum().asq16(); - return q16horiz(prev + xs_CRoundToInt(ratio * (curr - prev))); + return q16horiz(interpolatedvalue(osum().asq16(), sum().asq16(), smoothratio)); } + double horizsumfrac(double const smoothratio) + { + return (!SyncInput() ? sum() : interpolatedsum(smoothratio)).asbuildf() * (1. / 16.); // Used within draw code for Duke. + } + + void applyinput(float const horz, ESyncBits* actions, double const scaleAdjust = 1); + void calcviewpitch(vec2_t const pos, binangle const ang, bool const aimmode, bool const canslopetilt, int const cursectnum, double const scaleAdjust = 1, bool const climbing = false); + private: fixedhoriz target; double adjustment; @@ -105,7 +108,7 @@ private: { if (!SyncInput()) { - adjustment += value.asq16(); + adjustment += value.asbuildf(); } else { @@ -113,8 +116,10 @@ private: } } - void __settarget(fixedhoriz value, bool backup = false) + void __settarget(fixedhoriz value, bool backup) { + value = q16horiz(clamp(value.asq16(), gi->playerHorizMin(), gi->playerHorizMax())); + if (!SyncInput() && !backup) { target = value; @@ -130,8 +135,8 @@ private: struct PlayerAngle { - binangle ang, oang; - lookangle look_ang, olook_ang, rotscrnang, orotscrnang, spin; + binangle ang, oang, look_ang, olook_ang, rotscrnang, orotscrnang; + double spin; void backup() { @@ -147,24 +152,14 @@ struct PlayerAngle rotscrnang = orotscrnang; } - void addadjustment(int value) - { - __addadjustment(buildlook(value)); - } - void addadjustment(double value) { - __addadjustment(buildflook(value)); - } - - void addadjustment(lookangle value) - { - __addadjustment(value); + __addadjustment(buildfang(value)); } void addadjustment(binangle value) { - __addadjustment(bamlook(value.asbam())); + __addadjustment(value); } void resetadjustment() @@ -172,14 +167,9 @@ struct PlayerAngle adjustment = 0; } - void settarget(int value, bool backup = false) - { - __settarget(buildang(value & 2047), backup); - } - void settarget(double value, bool backup = false) { - __settarget(buildfang(fmod(value, 2048)), backup); + __settarget(buildfang(value), backup); } void settarget(binangle value, bool backup = false) @@ -196,11 +186,11 @@ struct PlayerAngle { if (targetset()) { - auto delta = getincanglebam(ang, target).asbam(); + auto delta = getincanglebam(ang, target).signedbuildf(); - if (delta > BAMUNIT) + if (abs(delta) > 1) { - ang += bamang(xs_CRoundToUInt(scaleAdjust * delta)); + ang += buildfang(scaleAdjust * delta); } else { @@ -210,7 +200,7 @@ struct PlayerAngle } else if (adjustment) { - ang += bamang(xs_CRoundToUInt(scaleAdjust * adjustment)); + ang += buildfang(scaleAdjust * adjustment); } } @@ -226,39 +216,40 @@ struct PlayerAngle binangle interpolatedsum(double const smoothratio) { - double const ratio = smoothratio * (1. / FRACUNIT); - uint32_t const dang = UINT32_MAX >> 1; - int64_t const prev = osum().asbam(); - int64_t const curr = sum().asbam(); - return bamang(prev + xs_CRoundToUInt(ratio * (((curr + dang - prev) & 0xFFFFFFFF) - dang))); + return interpolatedangle(osum(), sum(), smoothratio); } - lookangle interpolatedlookang(double const smoothratio) + binangle interpolatedlookang(double const smoothratio) { - double const ratio = smoothratio * (1. / FRACUNIT); - return bamlook(olook_ang.asbam() + xs_CRoundToInt(ratio * (look_ang - olook_ang).asbam())); + return interpolatedangle(olook_ang, look_ang, smoothratio); } - lookangle interpolatedrotscrn(double const smoothratio) + binangle interpolatedrotscrn(double const smoothratio) { - double const ratio = smoothratio * (1. / FRACUNIT); - return bamlook(orotscrnang.asbam() + xs_CRoundToInt(ratio * (rotscrnang - orotscrnang).asbam())); + return interpolatedangle(orotscrnang, rotscrnang, smoothratio); } double look_anghalf(double const smoothratio) { - return (!SyncInput() ? look_ang : interpolatedlookang(smoothratio)).asbam() * (0.5 / BAMUNIT); // Used within draw code for weapon and crosshair when looking left/right. + return (!SyncInput() ? look_ang : interpolatedlookang(smoothratio)).signedbuildf() * 0.5; // Used within draw code for weapon and crosshair when looking left/right. } + double looking_arc(double const smoothratio) + { + return fabs((!SyncInput() ? look_ang : interpolatedlookang(smoothratio)).signedbuildf()) * (1. / 9.); // Used within draw code for weapon and crosshair when looking left/right. + } + + void applyinput(float const avel, ESyncBits* actions, double const scaleAdjust = 1); + private: binangle target; double adjustment; - void __addadjustment(lookangle value) + void __addadjustment(binangle value) { if (!SyncInput()) { - adjustment += value.asbam(); + adjustment += value.signedbuildf(); } else { @@ -266,7 +257,7 @@ private: } } - void __settarget(binangle value, bool backup = false) + void __settarget(binangle value, bool backup) { if (!SyncInput() && !backup) { @@ -290,6 +281,3 @@ void updateTurnHeldAmt(double const scaleAdjust); bool const isTurboTurnTime(); void resetTurnHeldAmt(); void processMovement(InputPacket* currInput, InputPacket* inputBuffer, ControlInfo* const hidInput, double const scaleAdjust, int const drink_amt = 0, bool const allowstrafe = true, double const turnscale = 1); -void sethorizon(PlayerHorizon* horizon, float const horz, ESyncBits* actions, double const scaleAdjust = 1); -void applylook(PlayerAngle* angle, float const avel, ESyncBits* actions, double const scaleAdjust = 1); -void calcviewpitch(vec2_t const pos, fixedhoriz* horizoff, binangle const ang, bool const aimmode, bool const canslopetilt, int const cursectnum, double const scaleAdjust = 1, bool const climbing = false); diff --git a/source/core/gamestate.h b/source/core/gamestate.h index 523ead5d0..431eac31c 100644 --- a/source/core/gamestate.h +++ b/source/core/gamestate.h @@ -43,8 +43,10 @@ enum gameaction_t : int ga_nextlevel, // Actually start the next level. ga_loadgamehidecon, ga_newgamenostopsound, // start a new game + ga_endscreenjob, ga_fullconsole, }; extern gamestate_t gamestate; extern gameaction_t gameaction; +extern int intermissiondelay; diff --git a/source/core/gamestruct.h b/source/core/gamestruct.h index 4b8b95e28..97b0a857b 100644 --- a/source/core/gamestruct.h +++ b/source/core/gamestruct.h @@ -11,6 +11,8 @@ bool System_WantGuiCapture(); // During playing this tells us whether the game m #include "inputstate.h" class FSerializer; +struct FRenderViewpoint; +struct spritetype; struct GameStats { @@ -45,6 +47,19 @@ struct MapRecord; extern cycle_t drawtime, actortime, thinktime, gameupdatetime; +struct GeoEffect +{ + int* geosectorwarp; + int* geosectorwarp2; + int* geosector; + int* geox; + int* geoy; + int* geox2; + int* geoy2; + int geocnt; + +}; + struct GameInterface { virtual const char* Name() { return "$"; } @@ -64,12 +79,11 @@ struct GameInterface virtual void MenuSound(EMenuSounds snd) {} virtual bool CanSave() { return true; } virtual void CustomMenuSelection(int menu, int item) {} - virtual bool StartGame(FNewGameStartup& gs) { return false; } + virtual bool StartGame(FNewGameStartup& gs) { return true; } virtual FSavegameInfo GetSaveSig() { return { "", 0, 0}; } virtual double SmallFontScale() { return 1; } virtual void SerializeGameState(FSerializer& arc) {} virtual void DrawPlayerSprite(const DVector2& origin, bool onteam) {} - virtual void QuitToTitle() {} virtual void SetAmbience(bool on) {} virtual FString GetCoordString() { return "'stat coord' not implemented"; } virtual void ExitFromMenu() { throw CExitEvent(0); } @@ -99,6 +113,11 @@ struct GameInterface virtual int chaseCamX(binangle ang) { return 0; } virtual int chaseCamY(binangle ang) { return 0; } virtual int chaseCamZ(fixedhoriz horiz) { return 0; } + virtual void processSprites(spritetype* tsprite, int& spritesortcnt, int viewx, int viewy, int viewz, binangle viewang, double smoothRatio) = 0; + virtual void UpdateCameras(double smoothratio) {} + virtual void EnterPortal(spritetype* viewer, int type) {} + virtual void LeavePortal(spritetype* viewer, int type) {} + virtual bool GetGeoEffect(GeoEffect* eff, int viewsector) { return false; } virtual int Voxelize(int sprnum) { return -1; } virtual FString statFPS() diff --git a/source/core/interpolate.cpp b/source/core/interpolate.cpp index 8ccab4f41..ea79c8817 100644 --- a/source/core/interpolate.cpp +++ b/source/core/interpolate.cpp @@ -61,6 +61,7 @@ double Get(int index, int type) void Set(int index, int type, double val) { + int old; switch(type) { case Interp_Sect_Floorz: sector[index].floorz = xs_CRoundToInt(val); break; @@ -72,8 +73,8 @@ void Set(int index, int type, double val) case Interp_Sect_CeilingPanX: sector[index].ceilingxpan_ = float(val); break; case Interp_Sect_CeilingPanY: sector[index].ceilingypan_ = float(val); break; - case Interp_Wall_X: wall[index].x = xs_CRoundToInt(val); break; - case Interp_Wall_Y: wall[index].y = xs_CRoundToInt(val); break; + case Interp_Wall_X: old = wall[index].x; wall[index].x = xs_CRoundToInt(val); if (wall[index].x != old) sector[wall[index].sector].dirty = 255; break; + case Interp_Wall_Y: old = wall[index].y; wall[index].y = xs_CRoundToInt(val); if (wall[index].y != old) sector[wall[index].sector].dirty = 255; break; case Interp_Wall_PanX: wall[index].xpan_ = float(val); break; case Interp_Wall_PanY: wall[index].ypan_ = float(val); break; diff --git a/source/core/intvec.h b/source/core/intvec.h index 1675386f4..7c89c389f 100644 --- a/source/core/intvec.h +++ b/source/core/intvec.h @@ -1,37 +1,54 @@ #pragma once +class FSerializer; + struct vec2_16_t { int16_t x, y; }; - -#if 0 -struct vec2_t +struct vec2_t { int32_t x, y; -}; -struct vec2f_t -{ - float x, y; -}; - -struct vec2d_t -{ - double x, y; + vec2_t() = default; + vec2_t(const vec2_t&) = default; + vec2_t(int x_, int y_) : x(x_), y(y_) {} + vec2_t operator+(const vec2_t& other) const { return { x + other.x, y + other.y }; } + vec2_t operator-(const vec2_t& other) const { return { x - other.x, y - other.y }; } + vec2_t& operator+=(const vec2_t& other) { x += other.x; y += other.y; return *this; }; + vec2_t& operator-=(const vec2_t& other) { x -= other.x; y -= other.y; return *this; }; + bool operator == (const vec2_t& other) const { return x == other.x && y == other.y; }; }; struct vec3_t { - union - { - struct - { - int32_t x, y, z; - }; + union + { + struct + { + int32_t x, y, z; + }; vec2_t vec2; }; + + vec3_t() = default; + vec3_t(const vec3_t&) = default; + vec3_t(int x_, int y_, int z_) : x(x_), y(y_), z(z_) {} + vec3_t operator+(const vec3_t& other) const { return { x + other.x, y + other.y, z + other.z }; } + vec3_t operator-(const vec3_t& other) const { return { x - other.x, y - other.y, z - other.z }; } + vec3_t& operator+=(const vec3_t & other) { x += other.x; y += other.y; z += other.z; return *this; }; + vec3_t& operator-=(const vec3_t & other) { x -= other.x; y -= other.y; z += other.z; return *this; }; + +}; + + + + +#if 0 +struct vec2f_t +{ + float x, y; }; struct vec3_16_t @@ -46,3 +63,6 @@ struct vec3_16_t }; }; #endif + +FSerializer& Serialize(FSerializer& arc, const char* key, vec2_t& c, vec2_t* def); +FSerializer& Serialize(FSerializer& arc, const char* key, vec3_t& c, vec3_t* def); diff --git a/source/core/mainloop.cpp b/source/core/mainloop.cpp index 7a65c950d..ae596dcf1 100644 --- a/source/core/mainloop.cpp +++ b/source/core/mainloop.cpp @@ -74,7 +74,6 @@ #include "vm.h" #include "gamestate.h" #include "screenjob.h" -#include "mmulti.h" #include "c_console.h" #include "uiinput.h" #include "v_video.h" @@ -105,6 +104,7 @@ bool r_NoInterpolate; int entertic; int oldentertics; int gametic; +int intermissiondelay; FString BackupSaveGame; @@ -133,6 +133,20 @@ void G_BuildTiccmd(ticcmd_t* cmd) //========================================================================== bool newGameStarted; +void NewGame(MapRecord* map, int skill, bool ns = false) +{ + newGameStarted = true; + ShowIntermission(nullptr, map, nullptr, [=](bool) { + gi->NewGame(map, skill, ns); + }); +} + +//========================================================================== +// +// +// +//========================================================================== + static void GameTicker() { int i; @@ -159,7 +173,7 @@ static void GameTicker() FX_SetReverb(0); gi->FreeLevelData(); gameaction = ga_level; - gi->NewGame(g_nextmap, -1); + NewGame(g_nextmap, -1); BackupSaveGame = ""; } break; @@ -191,13 +205,12 @@ static void GameTicker() FX_StopAllSounds(); case ga_newgamenostopsound: DeleteScreenJob(); - newGameStarted = true; FX_SetReverb(0); gi->FreeLevelData(); C_FlushDisplay(); gameaction = ga_level; BackupSaveGame = ""; - gi->NewGame(g_nextmap, g_nextskill, ga == ga_newgamenostopsound); + NewGame(g_nextmap, g_nextskill, ga == ga_newgamenostopsound); break; case ga_startup: @@ -209,6 +222,7 @@ static void GameTicker() case ga_mainmenu: FX_StopAllSounds(); + if (isBlood()) Mus_Stop(); case ga_mainmenunostopsound: gi->FreeLevelData(); gamestate = GS_MENUSCREEN; @@ -253,6 +267,10 @@ static void GameTicker() gameaction = ga_nothing; break; + case ga_endscreenjob: + EndScreenJob(); + break; + // for later // case ga_recordgame, // start a new demo recording (later) // case ga_loadgameplaydemo, // load a savegame and play a demo. @@ -331,7 +349,16 @@ static void GameTicker() break; case GS_INTERMISSION: case GS_INTRO: - ScreenJobTick(); + if (intermissiondelay > 0) + { + intermissiondelay--; + break; + } + if (ScreenJobTick()) + { + // synchronize termination with the playsim. + Net_WriteByte(DEM_ENDSCREENJOB); + } break; } @@ -371,7 +398,7 @@ void Display() case GS_INTRO: case GS_INTERMISSION: // screen jobs are not bound by the game ticker so they need to be ticked in the display loop. - ScreenJobDraw(); + if (intermissiondelay <= 0) ScreenJobDraw(); break; case GS_LEVEL: @@ -633,6 +660,16 @@ void MainLoop () // Clamp the timer to TICRATE until the playloop has been entered. r_NoInterpolate = true; + if (userConfig.CommandMap.IsNotEmpty()) + { + auto maprecord = FindMapByName(userConfig.CommandMap); + userConfig.CommandMap = ""; + if (maprecord) + { + NewGame(maprecord, /*userConfig.skill*/2); // todo: fix the skill. + } + } + for (;;) { try diff --git a/source/core/maphack.cpp b/source/core/maphack.cpp index 3a62a0821..82187a799 100644 --- a/source/core/maphack.cpp +++ b/source/core/maphack.cpp @@ -35,8 +35,12 @@ #include "build.h" #include "sc_man.h" #include "printf.h" +#include "c_dispatch.h" +#include "md4.h" +#include "hw_sections.h" static TArray usermaphacks; +TArray blockingpairs[MAXWALLS]; void AddUserMapHack(usermaphack_t& mhk) { @@ -45,7 +49,9 @@ void AddUserMapHack(usermaphack_t& mhk) static int32_t LoadMapHack(const char *filename) { - int32_t currentsprite = -1; + int currentsprite = -1; + int currentwall = -1; + int currentsector = -1; FScanner sc; int lump = fileSystem.FindFile(filename); @@ -68,8 +74,30 @@ static int32_t LoadMapHack(const char *filename) return true; }; + auto validateWall = [&]() + { + if (currentwall < 0) + { + sc.ScriptMessage("Using %s without a valid wall", token.GetChars()); + return false; + } + return true; + }; + + auto validateSector = [&]() + { + if (currentsector < 0) + { + sc.ScriptMessage("Using %s without a valid sector", token.GetChars()); + return false; + } + return true; + }; + if (sc.Compare("sprite")) { + currentwall = -1; + currentsector = -1; if (sc.CheckNumber()) { currentsprite = sc.Number; @@ -81,6 +109,137 @@ static int32_t LoadMapHack(const char *filename) } else currentsprite = -1; } + if (sc.Compare("wall")) + { + currentsprite = -1; + currentsector = -1; + if (sc.CheckNumber()) + { + currentwall = sc.Number; + if (currentwall < 0 || currentwall >= MAXWALLS) + { + sc.ScriptMessage("Invalid wall number %d", currentwall); + currentwall = -1; + } + } + else currentwall = -1; + } + if (sc.Compare("sector")) + { + currentsprite = -1; + currentwall = -1; + if (sc.CheckNumber()) + { + currentsector = sc.Number; + if (currentsector < 0 || currentsector >= MAXSECTORS) + { + sc.ScriptMessage("Invalid sector number %d", currentsector); + currentsector = -1; + } + } + else currentsector = -1; + } + else if (sc.Compare("blocks")) + { + if (sc.CheckNumber() && validateWall()) + { + blockingpairs[currentwall].Push(sc.Number); + } + } + else if (sc.Compare("picnum")) + { + if (sc.CheckNumber()) + { + if (currentwall != -1 && validateWall()) + { + wall[currentwall].picnum = sc.Number; + } + else if (currentsprite != -1 && validateSprite()) + { + sprite[currentsprite].picnum = sc.Number; + } + } + } + else if (sc.Compare("overpicnum")) + { + if (sc.CheckNumber() && validateWall()) + { + wall[currentwall].overpicnum = sc.Number; + } + } + else if (sc.Compare("overpicnum")) + { + if (sc.CheckNumber() && validateWall()) + { + wall[currentwall].overpicnum = sc.Number; + } + } + else if (sc.Compare("split")) + { + int start = -1, end = -1; + if (sc.CheckNumber()) start = sc.Number; + if (sc.CheckNumber()) end = sc.Number; + if (end >= 0 && validateSector()) + { + hw_SetSplitSector(currentsector, start, end); + } + } + else if (sc.Compare("clearflags")) + { + if (currentsector != -1 && validateSector()) + { + sc.GetString(); + if (sc.Compare("floor") && sc.CheckNumber()) + { + sector[currentsector].floorstat &= ~sc.Number; + } + else if (sc.Compare("ceiling") && sc.CheckNumber()) + { + sector[currentsector].ceilingstat &= ~sc.Number; + } + else sc.ScriptError("Bad token %s", sc.String); + } + else if (sc.CheckNumber()) + { + if (currentwall != -1 && validateWall()) + { + wall[currentwall].cstat &= ~sc.Number; + } + else if (currentsprite != -1 && validateSprite()) + { + sprite[currentsprite].cstat &= ~sc.Number; + } + } + } + else if (sc.Compare("setflags")) + { + if (sc.CheckNumber()) + { + if (currentwall != -1 && validateWall()) + { + wall[currentwall].cstat |= sc.Number; + } + else if (currentsprite != -1 && validateSprite()) + { + sprite[currentsprite].cstat |= sc.Number; + } + } + } + else if (sc.Compare("lotag")) + { + if (sc.CheckNumber()) + { + if (currentwall != -1 && validateWall()) + { + wall[currentwall].lotag = sc.Number; + } + else if (currentsprite != -1 && validateSprite()) + { + sprite[currentsprite].lotag = sc.Number; + } + } + } + else if (sc.Compare("angleoff") || sc.Compare("angoff")) { if (sc.CheckNumber() && validateSprite()) @@ -201,6 +360,13 @@ static int32_t LoadMapHack(const char *filename) void G_LoadMapHack(const char* filename, const unsigned char* md4) { + for (auto& p : blockingpairs) p.Clear(); + FString internal = "engine/compatibility/"; + for (int j = 0; j < 16; ++j) + { + internal.AppendFormat("%02x", md4[j]); + } + LoadMapHack(internal + ".mhk"); FString hack = StripExtension(filename) + ".mhk"; if (LoadMapHack(hack)) @@ -215,3 +381,31 @@ void G_LoadMapHack(const char* filename, const unsigned char* md4) } } +// Map hacks use MD4 instead of MD5. Oh, well... +CCMD(md4sum) +{ + if (argv.argc() < 2) + { + Printf("Usage: md4sum ...\n"); + } + for (int i = 1; i < argv.argc(); ++i) + { + FileReader fr = fileSystem.OpenFileReader(argv[i]); + if (!fr.isOpen()) + { + Printf("%s: %s\n", argv[i], strerror(errno)); + } + else + { + auto data = fr.Read(); + uint8_t digest[16]; + md4once(data.Data(), data.Size(), digest); + for (int j = 0; j < 16; ++j) + { + Printf("%02x", digest[j]); + } + Printf(" //*%s\n", argv[i]); + } + } +} + diff --git a/source/core/mapinfo.cpp b/source/core/mapinfo.cpp index 609081f7f..3231c990a 100644 --- a/source/core/mapinfo.cpp +++ b/source/core/mapinfo.cpp @@ -41,43 +41,49 @@ #include "raze_sound.h" FString gSkillNames[MAXSKILLS]; -FString gVolumeNames[MAXVOLUMES]; -FString gVolumeSubtitles[MAXVOLUMES]; -int32_t gVolumeFlags[MAXVOLUMES]; int gDefaultVolume = 0, gDefaultSkill = 1; -MapRecord mapList[512]; // Due to how this gets used it needs to be static. EDuke defines 7 episode plus one spare episode with 64 potential levels each and relies on the static array which is freely accessible by scripts. -MapRecord *currentLevel; // level that is currently played. (The real level, not what script hacks modfifying the current level index can pretend.) +GlobalCutscenes globalCutscenes; +TArray clusters; +TArray volumes; +TArray> mapList; // must be allocated as pointers because it can whack the currentlLevel pointer if this was a flat array. +MapRecord *currentLevel; // level that is currently played. MapRecord* lastLevel; // Same here, for the last level. -unsigned int numUsedSlots; CCMD(listmaps) { - for (unsigned int i = 0; i < numUsedSlots; i++) + for (auto& map : mapList) { - int lump = fileSystem.FindFile(mapList[i].fileName); + int lump = fileSystem.FindFile(map->fileName); if (lump >= 0) { int rfnum = fileSystem.GetFileContainer(lump); - Printf("%s - %s (%s)\n", mapList[i].fileName.GetChars(), mapList[i].DisplayName(), fileSystem.GetResourceFileName(rfnum)); + Printf("%s - %s (%s)\n", map->LabelName(), map->DisplayName(), fileSystem.GetResourceFileName(rfnum)); } else { - Printf("%s - %s (defined but does not exist)\n", mapList[i].fileName.GetChars(), mapList[i].DisplayName()); + Printf("%s - %s (defined but does not exist)\n", map->fileName.GetChars(), map->DisplayName()); } } } +int CutsceneDef::GetSound() +{ + int id; + if (soundName.IsNotEmpty()) id = soundEngine->FindSound(soundName); + if (id <= 0) id = soundEngine->FindSoundByResID(soundID); + return id; +} + MapRecord *FindMapByName(const char *nm) { - for (unsigned i = 0; i < numUsedSlots; i++) + for (auto& map : mapList) { - auto &map = mapList[i]; - if (map.labelName.CompareNoCase(nm) == 0) + if (map->labelName.CompareNoCase(nm) == 0) { - return ↦ + return map.Data(); } } return nullptr; @@ -86,23 +92,79 @@ MapRecord *FindMapByName(const char *nm) MapRecord *FindMapByLevelNum(int num) { - for (unsigned i = 0; i < numUsedSlots; i++) + for (auto& map : mapList) { - auto &map = mapList[i]; - if (map.levelNumber == num) + if (map->levelNumber == num) { - return ↦ + return map.Data(); } } return nullptr; } -MapRecord *FindNextMap(MapRecord *thismap) +VolumeRecord* FindVolume(int index) { - if (thismap->nextLevel != -1) return FindMapByLevelNum(thismap->nextLevel); - return FindMapByLevelNum(thismap->levelNumber+1); + for (auto& vol : volumes) + { + if (vol.index == index) return &vol; + } + return nullptr; } +ClusterDef* FindCluster(int index) +{ + for (auto& vol : clusters) + { + if (vol.index == index) return &vol; + } + return nullptr; +} + +ClusterDef* AllocateCluster() +{ + return &clusters[clusters.Reserve(1)]; +} + +VolumeRecord* AllocateVolume() +{ + return &volumes[volumes.Reserve(1)]; +} + +MapRecord* FindMapByIndexOnly(int cluster, int num) +{ + int levelnum = makelevelnum(cluster, num); + for (auto& map : mapList) + { + if (map->levelNumber == levelnum) return map.Data(); + } + return nullptr; +} + +MapRecord* FindMapByIndex(int cluster, int num) +{ + auto map = FindMapByLevelNum(num); + if (!map && num < 1000) map = FindMapByLevelNum(makelevelnum(cluster, num)); + return map; +} + +MapRecord* FindNextMap(MapRecord* thismap) +{ + MapRecord* next = nullptr; + if (!thismap->NextMap.Compare("-")) return nullptr; // '-' means to forcibly end the game here. + if (thismap->NextMap.IsNotEmpty()) next = FindMapByName(thismap->NextMap); + if (!next) next = FindMapByLevelNum(thismap->levelNumber + 1); + return next; +} + +MapRecord* FindNextSecretMap(MapRecord* thismap) +{ + MapRecord* next = nullptr; + if (!thismap->NextSecret.Compare("-")) return nullptr; // '-' means to forcibly end the game here. + if (thismap->NextSecret.IsNotEmpty()) next = FindMapByName(thismap->NextSecret); + return next? next : FindNextMap(thismap); +} + + bool SetMusicForMap(const char* mapname, const char* music, bool namehack) { static const char* specials[] = { "intro", "briefing", "loading" }; @@ -129,7 +191,7 @@ bool SetMusicForMap(const char* mapname, const char* music, bool namehack) if (numMatches != 4 || toupper(b1) != 'E' || toupper(b2) != 'L') return false; - index = FindMapByLevelNum(levelnum(ep - 1, lev - 1)); + index = FindMapByIndexOnly(ep, lev); } if (index != nullptr) @@ -142,18 +204,19 @@ bool SetMusicForMap(const char* mapname, const char* music, bool namehack) MapRecord *AllocateMap() { - return &mapList[numUsedSlots++]; + auto&p = mapList[mapList.Reserve(1)]; + p.Alloc(); + return p.Data(); } MapRecord* SetupUserMap(const char* boardfilename, const char *defaultmusic) { - for (unsigned i = 0; i < numUsedSlots; i++) + for (auto& map : mapList) { - auto &map = mapList[i]; - if (map.fileName.CompareNoCase(boardfilename) == 0) + if (map->fileName.CompareNoCase(boardfilename) == 0) { - return ↦ + return map.Data(); } } diff --git a/source/core/mapinfo.h b/source/core/mapinfo.h index 9a1e0050d..1bbefcfa2 100644 --- a/source/core/mapinfo.h +++ b/source/core/mapinfo.h @@ -3,28 +3,58 @@ #include "gstrings.h" #include "cmdlib.h" #include "quotemgr.h" +#include "palentry.h" +#include "vectors.h" #ifdef GetMessage #undef GetMessage // Windows strikes... #endif - enum EMax { MAXSKILLS = 7, - MAXVOLUMES = 7, MAXMENUGAMEPLAYENTRIES = 7, }; enum EVolFlags { - EF_HIDEFROMSP = 1, + VF_HIDEFROMSP = 1, + VF_OPTIONAL = 2, + VF_SHAREWARELOCK = 4, // show in shareware but lock access. + VF_NOSKILL = 8, +}; + +enum EMapFlags +{ + LEVEL_NOINTERMISSION = 1, + LEVEL_SECRETEXITOVERRIDE = 2, // when given an explicit level number, override with secret exit in the map, mainly for compiling episodes out of single levels. + LEVEL_CLEARINVENTORY = 4, + LEVEL_CLEARWEAPONS = 8, + LEVEL_FORCENOEOG = 16, // RR E1L7 needs this to override its boss's death ending the game. +}; + +enum EMapGameFlags +{ + LEVEL_RR_HULKSPAWN = 1, + LEVEL_RR_CLEARMOONSHINE = 2, + + LEVEL_EX_COUNTDOWN = 4, + LEVEL_EX_TRAINING = 8, + LEVEL_EX_ALTSOUND = 16, + LEVEL_EX_MULTI = 32, + + LEVEL_SW_SPAWNMINES = 64, + LEVEL_SW_BOSSMETER_SERPENT = 128, + LEVEL_SW_BOSSMETER_SUMO = 256, + LEVEL_SW_BOSSMETER_ZILLA = 512, + LEVEL_SW_DEATHEXIT_SERPENT = 1024, + LEVEL_SW_DEATHEXIT_SUMO = 2048, + LEVEL_SW_DEATHEXIT_ZILLA = 4096, + + }; // These get filled in by the map definition parsers of the front ends. extern FString gSkillNames[MAXSKILLS]; -extern FString gVolumeNames[MAXVOLUMES]; -extern FString gVolumeSubtitles[MAXVOLUMES]; -extern int32_t gVolumeFlags[MAXVOLUMES]; extern int gDefaultVolume, gDefaultSkill; @@ -46,6 +76,57 @@ enum { MAX_MESSAGES = 32 }; +class DObject; +struct MapRecord; + +struct CutsceneDef +{ + FString video; + FString function; + FString soundName; + int soundID = -1; // ResID not SoundID! + int framespersec = 0; // only relevant for ANM. + bool transitiononly = false; // only play when transitioning between maps, but not when starting on a map or ending a game. + + void Create(DObject* runner); + bool Create(DObject* runner, MapRecord* map, bool transition); + bool isdefined() { return video.IsNotEmpty() || function.IsNotEmpty(); } + int GetSound(); +}; + +struct GlobalCutscenes +{ + CutsceneDef Intro; + CutsceneDef DefaultMapIntro; + CutsceneDef DefaultMapOutro; + CutsceneDef DefaultGameover; + CutsceneDef SharewareEnd; + CutsceneDef LoadingScreen; + FString MPSummaryScreen; + FString SummaryScreen; +}; + +struct ClusterDef +{ + FString name; // What gets displayed for this cluster. In Duke this is normally the corresponding volume name but does not have to be. + CutsceneDef intro; // plays when entering this cluster + CutsceneDef outro; // plays when leaving this cluster + CutsceneDef gameover; // when defined, plays when the player dies in this cluster + FString InterBackground; + int index = -1; + int flags = 0; // engine and common flags +}; + +struct VolumeRecord // episodes +{ + FString startmap; + FString name; + FString subtitle; + int index = -1; + int flags = 0; + int shortcut = 0; +}; + struct MapRecord { int parTime = 0; @@ -54,16 +135,38 @@ struct MapRecord FString labelName; FString name; FString music; + FString Author; + FString NextMap; + FString NextSecret; int cdSongId = -1; + int musicorder = -1; + + CutsceneDef intro; + CutsceneDef outro; int flags = 0; + int gameflags = 0; int levelNumber = -1; + int cluster = -1; + + PalEntry fadeto = 0; + int fogdensity = 0; + int skyfog = 0; + FString BorderTexture; + FString InterBackground; + TArray PrecacheTextures; + FVector4 skyrotatevector; // The rest is only used by Blood - int nextLevel = -1; - int nextSecret = -1; FString messages[MAX_MESSAGES]; - FString author; int8_t fog = -1, weather = -1; // Blood defines these but they aren't used. + + // game specific stuff + int rr_startsound = 0; + int rr_mamaspawn = 15; + int ex_ramses_horiz = 11; + int ex_ramses_cdtrack = -1; // this is not music, it is the actual dialogue! + FString ex_ramses_pup; + FString ex_ramses_text; const char* LabelName() const { @@ -97,39 +200,65 @@ struct MapRecord { messages[num] = msg; } - - }; +struct SummaryInfo +{ + int kills; + int maxkills; + int secrets; + int maxsecrets; + int supersecrets; + int time; + int playercount; + bool cheated; + bool endofgame; +}; -extern MapRecord mapList[512]; +extern GlobalCutscenes globalCutscenes; extern MapRecord *currentLevel; bool SetMusicForMap(const char* mapname, const char* music, bool namehack = false); MapRecord *FindMapByName(const char *nm); MapRecord *FindMapByLevelNum(int num); +MapRecord* FindMapByIndexOnly(int clst, int num); // this is for map setup where fallbacks are undesirable. +MapRecord* FindMapByIndex(int clst, int num); MapRecord *FindNextMap(MapRecord *thismap); +MapRecord* FindNextSecretMap(MapRecord* thismap); MapRecord* SetupUserMap(const char* boardfilename, const char *defaultmusic = nullptr); MapRecord* AllocateMap(); +VolumeRecord* FindVolume(int index); +ClusterDef* FindCluster(int index); +ClusterDef* AllocateCluster(); +VolumeRecord* AllocateVolume(); +void SetLevelNum(MapRecord* info, int num); + +inline VolumeRecord* MustFindVolume(int index) +{ + auto r = FindVolume(index); + if (r) return r; + r = AllocateVolume(); + r->index = index; + return r; +} +inline ClusterDef* MustFindCluster(int index) +{ + auto r = FindCluster(index); + if (r) return r; + r = AllocateCluster(); + r->index = index; + return r; +} + + // These should be the only places converting between level numbers and volume/map pairs -constexpr inline int levelnum(int vol, int map) +constexpr inline int makelevelnum(int vol, int map) { return vol * 1000 + map; } -constexpr inline int volfromlevelnum(int num) -{ - return num >= 0 ? num / 1000 : 0; -} - -constexpr inline int mapfromlevelnum(int num) -{ - return num >= 0 ? num % 1000 : -1; -} - - enum { RRENDSLOT = 127 diff --git a/source/core/maploader.cpp b/source/core/maploader.cpp index fb34935d1..ee6bf0d99 100644 --- a/source/core/maploader.cpp +++ b/source/core/maploader.cpp @@ -41,7 +41,10 @@ #include "inputstate.h" #include "md4.h" #include "gamecontrol.h" - +#include "gamefuncs.h" +#include "sectorgeometry.h" +#include "render.h" +#include "hw_sections.h" static void ReadSectorV7(FileReader& fr, sectortype& sect) { @@ -368,10 +371,14 @@ static void insertAllSprites(const char* filename, const vec3_t* pos, int16_t* c assert(realnumsprites == Numsprites); } +void addBlockingPairs(); void engineLoadBoard(const char* filename, int flags, vec3_t* pos, int16_t* ang, int16_t* cursectnum) { inputState.ClearAllInput(); + memset(sector, 0, sizeof(*sector) * MAXSECTORS); + memset(wall, 0, sizeof(*wall) * MAXWALLS); + memset(sprite, 0, sizeof(*sector) * MAXSPRITES); FileReader fr = fileSystem.OpenFileReader(filename); if (!fr.isOpen()) I_Error("Unable to open map %s", filename); @@ -385,7 +392,7 @@ void engineLoadBoard(const char* filename, int flags, vec3_t* pos, int16_t* ang, memset(spritesmooth, 0, sizeof(spritesmooth_t) * (MAXSPRITES + MAXUNIQHUDID)); initspritelists(); ClearAutomap(); - Polymost_prepare_loadboard(); + Polymost::Polymost_prepare_loadboard(); pos->x = fr.ReadInt32(); pos->y = fr.ReadInt32(); @@ -444,6 +451,10 @@ void engineLoadBoard(const char* filename, int flags, vec3_t* pos, int16_t* ang, unsigned char md4[16]; md4once(buffer.Data(), buffer.Size(), md4); G_LoadMapHack(filename, md4); + setWallSectors(); + hw_BuildSections(); + sectorGeometry.SetSize(numsections); + memcpy(wallbackup, wall, sizeof(wallbackup)); memcpy(sectorbackup, sector, sizeof(sectorbackup)); @@ -468,4 +479,18 @@ void loadMapBackup(const char* filename) engineLoadBoard(filename, 0, &pos, &scratch, &scratch); initspritelists(); } +} + +// Sets the sector reference for each wall. We need this for the triangulation cache. +void setWallSectors() +{ + for (int i = 0; i < numsectors; i++) + { + sector[i].dirty = 255; + sector[i].exflags = 0; + for (int w = 0; w < sector[i].wallnum; w++) + { + wall[sector[i].wallptr + w].sector = i; + } + } } \ No newline at end of file diff --git a/source/core/menu/razemenu.cpp b/source/core/menu/razemenu.cpp index ddbf0be9e..764f2f096 100644 --- a/source/core/menu/razemenu.cpp +++ b/source/core/menu/razemenu.cpp @@ -64,6 +64,7 @@ #include "i_net.h" #include "savegamehelp.h" #include "gi.h" +#include "raze_music.h" EXTERN_CVAR(Int, cl_gfxlocalization) EXTERN_CVAR(Bool, m_quickexit) @@ -83,8 +84,31 @@ bool help_disabled; FNewGameStartup NewGameStartupInfo; + //FNewGameStartup NewGameStartupInfo; +static bool DoStartGame(FNewGameStartup& gs) +{ + auto vol = FindVolume(gs.Episode); + if (!vol) return false; + + if (isShareware() && (vol->flags & VF_SHAREWARELOCK)) + { + M_StartMessage(GStrings("SHAREWARELOCK"), 1, NAME_None); + return false; + } + + auto map = FindMapByName(vol->startmap); + if (!map) return false; + soundEngine->StopAllChannels(); + + gi->StartGame(gs); // play game specific effects (like Duke/RR/SW's voice lines when starting a game.) + + DeferedStartGame(map, gs.Skill); + return true; +} + + bool M_SetSpecialMenu(FName& menu, int param) { @@ -115,13 +139,19 @@ bool M_SetSpecialMenu(FName& menu, int param) case NAME_Startgame: case NAME_StartgameNoSkill: - menu = NAME_Startgame; NewGameStartupInfo.Skill = param; - if (menu == NAME_StartgameNoSkill) NewGameStartupInfo.Episode = param; - if (gi->StartGame(NewGameStartupInfo)) + if (menu == NAME_StartgameNoSkill) + { + menu = NAME_Startgame; + NewGameStartupInfo.Episode = param; + NewGameStartupInfo.Skill = 1; + } + if (DoStartGame(NewGameStartupInfo)) { M_ClearMenus(); - STAT_StartNewGame(gVolumeNames[NewGameStartupInfo.Episode], NewGameStartupInfo.Skill); + int ep = NewGameStartupInfo.Episode; + auto vol = FindVolume(ep); + if (vol) STAT_StartNewGame(vol->name, NewGameStartupInfo.Skill); inputState.ClearAllInput(); } return false; @@ -243,7 +273,8 @@ CCMD(menu_endgame) { STAT_Cancel(); M_ClearMenus(); - gi->QuitToTitle(); + Mus_Stop(); + gameaction = ga_mainmenu; }); M_ActivateMenu(newmenu); @@ -365,6 +396,7 @@ static DMenuItemBase* CreateCustomListMenuItemText(double x, double y, int heigh // Creates the episode menu // //============================================================================= +extern TArray volumes; static void BuildEpisodeMenu() { @@ -385,22 +417,22 @@ static void BuildEpisodeMenu() ld->mSelectedItem = gDefaultVolume + ld->mItems.Size(); // account for pre-added items int y = ld->mYpos; - for (int i = 0; i < MAXVOLUMES; i++) + // Volume definitions should be sorted by intended menu order. + for (auto &vol : volumes) { - if (gVolumeNames[i].IsNotEmpty() && !(gVolumeFlags[i] & EF_HIDEFROMSP)) - + if (vol.name.IsNotEmpty() && !(vol.flags & VF_HIDEFROMSP)) { - int isShareware = ((g_gameType & GAMEFLAG_DUKE) && (g_gameType & GAMEFLAG_SHAREWARE) && i > 0); - auto it = CreateCustomListMenuItemText(ld->mXpos, y, ld->mLinespacing, gVolumeNames[i][0], - gVolumeNames[i], ld->mFont, CR_UNTRANSLATED, isShareware, NAME_Skillmenu, i); // font colors are not used, so hijack one for the shareware flag. + int isShareware = ((g_gameType & GAMEFLAG_DUKE) && (g_gameType & GAMEFLAG_SHAREWARE) && (vol.flags & VF_SHAREWARELOCK)); + auto it = CreateCustomListMenuItemText(ld->mXpos, y, ld->mLinespacing, vol.name[0], + vol.name, ld->mFont, CR_UNTRANSLATED, isShareware, NAME_Skillmenu, vol.index); // font colors are not used, so hijack one for the shareware flag. y += ld->mLinespacing; ld->mItems.Push(it); addedVolumes++; - if (gVolumeSubtitles[i].IsNotEmpty()) + if (vol.subtitle.IsNotEmpty()) { auto it = CreateCustomListMenuItemText(ld->mXpos, y, ld->mLinespacing * 6 / 10, 1, - gVolumeSubtitles[i], SmallFont, CR_GRAY, false, NAME_None, i); + vol.subtitle, SmallFont, CR_GRAY, false, NAME_None, vol.index); y += ld->mLinespacing * 6 / 10; ld->mItems.Push(it); textadded = true; @@ -731,3 +763,29 @@ DEFINE_ACTION_FUNCTION(_PlayerMenu, DrawPlayerSprite) return 0; } +#ifdef _WIN32 +EXTERN_CVAR(Bool, vr_enable_quadbuffered) +#endif + +void UpdateVRModes(bool considerQuadBuffered) +{ + FOptionValues** pVRModes = OptionValues.CheckKey("VRMode"); + if (pVRModes == nullptr) return; + + TArray& vals = (*pVRModes)->mValues; + TArray filteredValues; + int cnt = vals.Size(); + for (int i = 0; i < cnt; ++i) { + auto const& mode = vals[i]; + if (mode.Value == 7) { // Quad-buffered stereo +#ifdef _WIN32 + if (!vr_enable_quadbuffered) continue; +#else + continue; // Remove quad-buffered option on Mac and Linux +#endif + if (!considerQuadBuffered) continue; // Probably no compatible screen mode was found + } + filteredValues.Push(mode); + } + vals = filteredValues; +} diff --git a/source/core/movie/movieplayer.cpp b/source/core/movie/movieplayer.cpp index 80b6fca7b..f5dfbe0b0 100644 --- a/source/core/movie/movieplayer.cpp +++ b/source/core/movie/movieplayer.cpp @@ -49,15 +49,25 @@ #include #include #include "raze_music.h" +#include "vm.h" class MoviePlayer { +protected: + enum EMovieFlags + { + NOSOUNDCUTOFF = 1, + FIXEDVIEWPORT = 2, // Forces fixed 640x480 screen size like for Blood's intros. + }; + + int flags; public: virtual void Start() {} virtual bool Frame(uint64_t clock) = 0; virtual void Stop() {} virtual ~MoviePlayer() = default; + virtual FTextureID GetTexture() = 0; }; //--------------------------------------------------------------------------- @@ -76,16 +86,17 @@ class AnmPlayer : public MoviePlayer int frametime = 0; int nextframetime = 0; AnimTextures animtex; - const AnimSound* animSnd; - const int* frameTicks; - bool nostopsound; + const TArray animSnd; + int frameTicks[3]; public: bool isvalid() { return numframes > 0; } - AnmPlayer(FileReader& fr, const AnimSound* ans, const int *frameticks, bool nosoundcutoff) - : animSnd(ans), frameTicks(frameticks), nostopsound(nosoundcutoff) + AnmPlayer(FileReader& fr, TArray& ans, const int *frameticks, int flags_) + : animSnd(std::move(ans)) { + memcpy(frameTicks, frameticks, 3 * sizeof(int)); + flags = flags_; buffer = fr.ReadPadded(1); fr.Close(); @@ -109,17 +120,12 @@ public: if (currentclock < nextframetime - 1) { - twod->ClearScreen(); - DrawTexture(twod, animtex.GetFrame(), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_Masked, false, TAG_DONE); return true; } animtex.SetFrame(ANIM_GetPalette(&anim), ANIM_DrawFrame(&anim, curframe)); frametime = currentclock; - twod->ClearScreen(); - DrawTexture(twod, animtex.GetFrame(), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_Masked, false, TAG_DONE); - int delay = 20; if (frameTicks) { @@ -129,11 +135,12 @@ public: } nextframetime += delay; - if (animSnd) for (int i = 0; animSnd[i].framenum >= 0; i++) + bool nostopsound = (flags & NOSOUNDCUTOFF); + for (unsigned i = 0; i < animSnd.Size(); i+=2) { - if (animSnd[i].framenum == curframe) + if (animSnd[i] == curframe) { - int sound = animSnd[i].soundnum; + int sound = animSnd[i+1]; if (sound == -1) soundEngine->StopAllChannels(); else if (SoundEnabled()) @@ -147,6 +154,7 @@ public: void Stop() override { + bool nostopsound = (flags & NOSOUNDCUTOFF); if (!nostopsound) soundEngine->StopAllChannels(); } @@ -156,6 +164,11 @@ public: buffer.Reset(); animtex.Clean(); } + + FTextureID GetTexture() override + { + return animtex.GetFrameID(); + } }; //--------------------------------------------------------------------------- @@ -187,8 +200,6 @@ public: { if (failed) return false; bool playon = decoder.RunFrame(clock); - twod->ClearScreen(); - DrawTexture(twod, decoder.animTex().GetFrame(), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE); return playon; } @@ -196,6 +207,11 @@ public: { decoder.Close(); } + + FTextureID GetTexture() override + { + return decoder.animTex().GetFrameID(); + } }; //--------------------------------------------------------------------------- @@ -209,7 +225,7 @@ class VpxPlayer : public MoviePlayer bool failed = false; FileReader fr; AnimTextures animtex; - const AnimSound* animSnd; + const TArray animSnd; unsigned width, height; TArray Pic; @@ -234,10 +250,10 @@ public: public: bool isvalid() { return !failed; } - VpxPlayer(FileReader& fr_, const AnimSound* animSnd_, int origframedelay, FString& error) + VpxPlayer(FileReader& fr_, TArray& animSnd_, int flags_, int origframedelay, FString& error) : animSnd(std::move(animSnd_)) { fr = std::move(fr_); - animSnd = animSnd_; + flags = flags_; if (!ReadIVFHeader(origframedelay)) { @@ -433,30 +449,35 @@ public: framenum++; if (framenum >= numframes) stop = true; + bool nostopsound = (flags & NOSOUNDCUTOFF); int soundframe = convdenom ? Scale(framenum, convnumer, convdenom) : framenum; if (soundframe > lastsoundframe) { - if (animSnd && soundtrack == -1) for (int i = 0; animSnd[i].framenum >= 0; i++) + if (soundtrack == -1) { - if (animSnd[i].framenum == soundframe) + for (unsigned i = 0; i < animSnd.Size(); i += 2) { - int sound = animSnd[i].soundnum; - if (sound == -1) - soundEngine->StopAllChannels(); - else if (SoundEnabled()) - soundEngine->StartSound(SOURCE_None, nullptr, nullptr, CHAN_AUTO, CHANF_NONE, sound, 1.f, ATTN_NONE); + if (animSnd[i] == soundframe) + { + int sound = animSnd[i + 1]; + if (sound == -1) + soundEngine->StopAllChannels(); + else if (SoundEnabled()) + soundEngine->StartSound(SOURCE_None, nullptr, nullptr, CHAN_AUTO, nostopsound ? CHANF_UI : CHANF_NONE, sound, 1.f, ATTN_NONE); + } } } lastsoundframe = soundframe; } } - DrawTexture(twod, animtex.GetFrame(), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit, TAG_DONE); return !stop; } void Stop() { Mus_Stop(); + bool nostopsound = (flags & NOSOUNDCUTOFF); + if (!nostopsound) soundEngine->StopAllChannels(); } ~VpxPlayer() @@ -464,6 +485,11 @@ public: vpx_codec_destroy(&codec); animtex.Clean(); } + + FTextureID GetTexture() override + { + return animtex.GetFrameID(); + } }; //--------------------------------------------------------------------------- @@ -498,9 +524,10 @@ class SmkPlayer : public MoviePlayer bool fullscreenScale; uint64_t nFrameNs; int nFrame = 0; - const AnimSound* animSnd; + const TArray animSnd; FString filename; SoundStream* stream = nullptr; + bool hassound = false; public: bool isvalid() { return hSMK.isValid; } @@ -534,22 +561,21 @@ public: } - SmkPlayer(const char *fn, const AnimSound* ans, bool fixedviewport) + SmkPlayer(const char *fn, TArray& ans, int flags_) : animSnd(std::move(ans)) { hSMK = Smacker_Open(fn); if (!hSMK.isValid) { return; } + flags = flags_; Smacker_GetFrameSize(hSMK, nWidth, nHeight); pFrame.Resize(nWidth * nHeight + std::max(nWidth, nHeight)); nFrameRate = Smacker_GetFrameRate(hSMK); nFrameNs = 1'000'000'000 / nFrameRate; nFrames = Smacker_GetNumFrames(hSMK); Smacker_GetPalette(hSMK, palette); - fullscreenScale = (!fixedviewport || (nWidth <= 320 && nHeight <= 200) || nWidth >= 640 || nHeight >= 480); - bool hassound = false; numAudioTracks = Smacker_GetNumAudioTracks(hSMK); if (numAudioTracks) { @@ -562,14 +588,12 @@ public: auto read = Smacker_GetAudioData(hSMK, 0, (int16_t*)audioBuffer.Data()); if (adata.inf.bitsPerSample == 8) copy8bitSamples(read); else copy16bitSamples(read); - animSnd = nullptr; hassound = true; } } if (!hassound) { adata.inf = {}; - animSnd = ans; } } @@ -612,27 +636,21 @@ public: } } - if (fullscreenScale) - { - DrawTexture(twod, animtex.GetFrame(), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE); - } - else - { - DrawTexture(twod, animtex.GetFrame(), 320, 240, DTA_VirtualWidth, 640, DTA_VirtualHeight, 480, DTA_CenterOffset, true, TAG_DONE); - } + if (frame > nFrame) { nFrame++; Smacker_GetNextFrame(hSMK); - if (animSnd) for (int i = 0; animSnd[i].framenum >= 0; i++) + bool nostopsound = (flags & NOSOUNDCUTOFF); + if (!hassound) for (unsigned i = 0; i < animSnd.Size(); i += 2) { - if (animSnd[i].framenum == nFrame) + if (animSnd[i] == nFrame) { - int sound = animSnd[i].soundnum; + int sound = animSnd[i + 1]; if (sound == -1) soundEngine->StopAllChannels(); else if (SoundEnabled()) - soundEngine->StartSound(SOURCE_None, nullptr, nullptr, CHAN_AUTO, CHANF_NONE, sound, 1.f, ATTN_NONE); + soundEngine->StartSound(SOURCE_None, nullptr, nullptr, CHAN_AUTO, nostopsound ? CHANF_UI : CHANF_NONE, sound, 1.f, ATTN_NONE); } } } @@ -640,13 +658,24 @@ public: return nFrame < nFrames; } + void Stop() override + { + if (stream) S_StopCustomStream(stream); + bool nostopsound = (flags & NOSOUNDCUTOFF); + if (!nostopsound && !hassound) soundEngine->StopAllChannels(); + } + ~SmkPlayer() { Smacker_Close(hSMK); - if (stream) S_StopCustomStream(stream); - soundEngine->StopAllChannels(); animtex.Clean(); } + + FTextureID GetTexture() override + { + return animtex.GetFrameID(); + } + }; //--------------------------------------------------------------------------- @@ -655,61 +684,11 @@ public: // //--------------------------------------------------------------------------- -class DMoviePlayer : public DSkippableScreenJob -{ - MoviePlayer* player; - bool started = false; - -public: - DMoviePlayer(MoviePlayer* mp) - { - player = mp; - pausable = false; - } - - - void Draw(double smoothratio) override - { - if (!player) - { - state = stopped; - return; - } - if (!started) - { - started = true; - player->Start(); - } - uint64_t clock = (ticks + smoothratio) * 1'000'000'000. / GameTicRate; - if (state == running && !player->Frame(clock)) - { - state = finished; - } - } - - void OnDestroy() override - { - if (player) - { - player->Stop(); - delete player; - } - player = nullptr; - } -}; - - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -MoviePlayer* OpenMovie(const char* filename, const AnimSound* ans, const int* frameticks, bool nosoundcutoff, FString& error) +MoviePlayer* OpenMovie(const char* filename, TArray& ans, const int* frameticks, int flags, FString& error) { FileReader fr; // first try as .ivf - but only if sounds are provided - the decoder is video only. - if (ans) + if (ans.Size()) { auto fn = StripExtension(filename); DefaultExtension(fn, ".ivf"); @@ -739,7 +718,7 @@ MoviePlayer* OpenMovie(const char* filename, const AnimSound* ans, const int* fr if (!memcmp(id, "LPF ", 4)) { - auto anm = new AnmPlayer(fr, ans, frameticks, nosoundcutoff); + auto anm = new AnmPlayer(fr, ans, frameticks, flags); if (!anm->isvalid()) { error.Format("%s: invalid ANM file.\n", filename); @@ -751,7 +730,7 @@ MoviePlayer* OpenMovie(const char* filename, const AnimSound* ans, const int* fr else if (!memcmp(id, "SMK2", 4)) { fr.Close(); - auto anm = new SmkPlayer(filename, ans, true); // Fixme: Handle Blood's video scaling behavior more intelligently. + auto anm = new SmkPlayer(filename, ans, flags); if (!anm->isvalid()) { error.Format("%s: invalid SMK file.\n", filename); @@ -772,7 +751,7 @@ MoviePlayer* OpenMovie(const char* filename, const AnimSound* ans, const int* fr } else if (!memcmp(id, "DKIF\0\0 \0VP80", 12)) { - auto anm = new VpxPlayer(fr, ans, frameticks ? frameticks[1] : 0, error); + auto anm = new VpxPlayer(fr, ans, frameticks ? frameticks[1] : 0, flags, error); if (!anm->isvalid()) { delete anm; @@ -795,20 +774,53 @@ MoviePlayer* OpenMovie(const char* filename, const AnimSound* ans, const int* fr // //--------------------------------------------------------------------------- -DScreenJob* PlayVideo(const char* filename, const AnimSound* ans, const int* frameticks, bool nosoundcutoff) +DEFINE_ACTION_FUNCTION(_MoviePlayer, Create) { - if (!filename) - { - return Create(1); - } + PARAM_PROLOGUE; + PARAM_STRING(filename); + PARAM_POINTER(sndinf, TArray); + PARAM_INT(flags); + PARAM_INT(frametime); + PARAM_INT(firstframetime); + PARAM_INT(lastframetime); + FString error; - auto movie = OpenMovie(filename, ans, frameticks, nosoundcutoff, error); + if (firstframetime == -1) firstframetime = frametime; + if (lastframetime == -1) lastframetime = frametime; + int frametimes[] = { firstframetime, frametime, lastframetime }; + auto movie = OpenMovie(filename, *sndinf, frametime == -1? nullptr : frametimes, flags, error); if (!movie) { Printf(TEXTCOLOR_YELLOW, "%s", error.GetChars()); - return Create(1); } - return Create(movie); + ACTION_RETURN_POINTER(movie); } +DEFINE_ACTION_FUNCTION(_MoviePlayer, Start) +{ + PARAM_SELF_STRUCT_PROLOGUE(MoviePlayer); + self->Start(); + return 0; +} +DEFINE_ACTION_FUNCTION(_MoviePlayer, Frame) +{ + PARAM_SELF_STRUCT_PROLOGUE(MoviePlayer); + PARAM_FLOAT(clock); + ACTION_RETURN_INT(self->Frame(int64_t(clock))); + return 0; +} + +DEFINE_ACTION_FUNCTION(_MoviePlayer, Destroy) +{ + PARAM_SELF_STRUCT_PROLOGUE(MoviePlayer); + self->Stop(); + delete self; + return 0; +} + +DEFINE_ACTION_FUNCTION(_MoviePlayer, GetTexture) +{ + PARAM_SELF_STRUCT_PROLOGUE(MoviePlayer); + ACTION_RETURN_INT(self->GetTexture().GetIndex()); +} diff --git a/source/core/nodebuilder/nodebuild.cpp b/source/core/nodebuilder/nodebuild.cpp new file mode 100644 index 000000000..f355cba9c --- /dev/null +++ b/source/core/nodebuilder/nodebuild.cpp @@ -0,0 +1,1059 @@ +/* +** nodebuild.cpp +** +** The main logic for the internal node builder. +** +**--------------------------------------------------------------------------- +** Copyright 2002-2006 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** 4. When not used as part of ZDoom or a ZDoom derivative, this code will be +** covered by the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or (at +** your option) any later version. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include +#include +#include + +#include "nodebuild.h" +#include "m_bbox.h" +#include "printf.h" + +const int MaxSegs = 64; +const int SplitCost = 8; +const int AAPreference = 16; + +#if 0 +#define D(x) x +#else +#define D(x) do{}while(0) +#endif + +FNodeBuilder::FNodeBuilder (FLevel &lev) + : Level(lev), GLNodes(true), SegsStuffed(0) +{ + VertexMap = new FVertexMap (*this, Level.MinX, Level.MinY, Level.MaxX, Level.MaxY); + FindUsedVertices (Level.Vertices, Level.NumVertices); + MakeSegsFromSides (); + GroupSegPlanes (); + BuildTree (); +} + +FNodeBuilder::~FNodeBuilder() +{ + if (VertexMap != NULL) + { + delete VertexMap; + } + if (OldVertexTable != NULL) + { + delete[] OldVertexTable; + } +} + +void FNodeBuilder::BuildMini(bool makeGLNodes) +{ + GLNodes = makeGLNodes; + GroupSegPlanesSimple(); + BuildTree(); +} + +void FNodeBuilder::Clear() +{ + SegsStuffed = 0; + Nodes.Clear(); + Subsectors.Clear(); + SubsectorSets.Clear(); + Segs.Clear(); + Vertices.Clear(); + SegList.Clear(); + PlaneChecked.Clear(); + Planes.Clear(); + Touched.Clear(); + Colinear.Clear(); + SplitSharers.Clear(); + if (VertexMap == NULL) + { + VertexMap = new FVertexMapSimple(*this); + } +} + +void FNodeBuilder::BuildTree () +{ + fixed_t bbox[4]; + + HackSeg = UINT_MAX; + HackMate = UINT_MAX; + CreateNode (0, Segs.Size(), bbox); + CreateSubsectorsForReal (); +} + +int FNodeBuilder::CreateNode (uint32_t set, unsigned int count, fixed_t bbox[4]) +{ + node_t node; + int skip, selstat; + uint32_t splitseg; + + skip = int(count / MaxSegs); + + // When building GL nodes, count may not be an exact count of the number of segs + // in the set. That's okay, because we just use it to get a skip count, so an + // estimate is fine. + if ((selstat = SelectSplitter (set, node, splitseg, skip, true)) > 0 || + (skip > 0 && (selstat = SelectSplitter (set, node, splitseg, 1, true)) > 0) || + (selstat < 0 && (SelectSplitter (set, node, splitseg, skip, false) > 0 || + (skip > 0 && SelectSplitter (set, node, splitseg, 1, false)))) || + CheckSubsector (set, node, splitseg)) + { + // Create a normal node + uint32_t set1, set2; + unsigned int count1, count2; + + SplitSegs (set, node, splitseg, set1, set2, count1, count2); + D(PrintSet (1, set1)); + D(Printf (PRINT_LOG, "(%d,%d) delta (%d,%d) from seg %d\n", node.x>>16, node.y>>16, node.dx>>16, node.dy>>16, splitseg)); + D(PrintSet (2, set2)); + node.intchildren[0] = CreateNode (set1, count1, node.nb_bbox[0]); + node.intchildren[1] = CreateNode (set2, count2, node.nb_bbox[1]); + bbox[BOXTOP] = MAX (node.nb_bbox[0][BOXTOP], node.nb_bbox[1][BOXTOP]); + bbox[BOXBOTTOM] = MIN (node.nb_bbox[0][BOXBOTTOM], node.nb_bbox[1][BOXBOTTOM]); + bbox[BOXLEFT] = MIN (node.nb_bbox[0][BOXLEFT], node.nb_bbox[1][BOXLEFT]); + bbox[BOXRIGHT] = MAX (node.nb_bbox[0][BOXRIGHT], node.nb_bbox[1][BOXRIGHT]); + return (int)Nodes.Push (node); + } + else + { + return 0x80000000 | CreateSubsector (set, bbox); + } +} + +int FNodeBuilder::CreateSubsector (uint32_t set, fixed_t bbox[4]) +{ + int ssnum, count; + + bbox[BOXTOP] = bbox[BOXRIGHT] = INT_MIN; + bbox[BOXBOTTOM] = bbox[BOXLEFT] = INT_MAX; + + D(Printf (PRINT_LOG, "Subsector from set %d\n", set)); + + assert (set != UINT_MAX); + + // We cannot actually create the subsector now because the node building + // process might split a seg in this subsector (because all partner segs + // must use the same pair of vertices), adding a new seg that hasn't been + // created yet. After all the nodes are built, then we can create the + // actual subsectors using the CreateSubsectorsForReal function below. + ssnum = (int)SubsectorSets.Push (set); + + count = 0; + while (set != UINT_MAX) + { + AddSegToBBox (bbox, &Segs[set]); + set = Segs[set].next; + count++; + } + + SegsStuffed += count; + + D(Printf (PRINT_LOG, "bbox (%d,%d)-(%d,%d)\n", bbox[BOXLEFT]>>16, bbox[BOXBOTTOM]>>16, bbox[BOXRIGHT]>>16, bbox[BOXTOP]>>16)); + + return ssnum; +} + +void FNodeBuilder::CreateSubsectorsForReal () +{ + subsector_t sub; + unsigned int i; + + sub.sector = NULL; + + for (i = 0; i < SubsectorSets.Size(); ++i) + { + uint32_t set = SubsectorSets[i]; + uint32_t firstline = (uint32_t)SegList.Size(); + + while (set != UINT_MAX) + { + USegPtr ptr; + + ptr.SegPtr = &Segs[set]; + SegList.Push (ptr); + set = ptr.SegPtr->next; + } + sub.numlines = (uint32_t)(SegList.Size() - firstline); + sub.firstline = (seg_t *)(size_t)firstline; + + // Sort segs by linedef for special effects + qsort (&SegList[firstline], sub.numlines, sizeof(USegPtr), SortSegs); + + // Convert seg pointers into indices + D(Printf (PRINT_LOG, "Output subsector %d:\n", Subsectors.Size())); + for (unsigned int i = firstline; i < SegList.Size(); ++i) + { + D(Printf (PRINT_LOG, " Seg %5d%c%d(%5d,%5d)-%d(%5d,%5d) [%08x,%08x]-[%08x,%08x]\n", SegList[i].SegPtr - &Segs[0], + SegList[i].SegPtr->linedef == -1 ? '+' : ' ', + SegList[i].SegPtr->v1, + Vertices[SegList[i].SegPtr->v1].x>>16, + Vertices[SegList[i].SegPtr->v1].y>>16, + SegList[i].SegPtr->v2, + Vertices[SegList[i].SegPtr->v2].x>>16, + Vertices[SegList[i].SegPtr->v2].y>>16, + Vertices[SegList[i].SegPtr->v1].x, Vertices[SegList[i].SegPtr->v1].y, + Vertices[SegList[i].SegPtr->v2].x, Vertices[SegList[i].SegPtr->v2].y)); + SegList[i].SegNum = uint32_t(SegList[i].SegPtr - &Segs[0]); + } + Subsectors.Push (sub); + } +} + +int FNodeBuilder::SortSegs (const void *a, const void *b) +{ + const FPrivSeg *x = ((const USegPtr *)a)->SegPtr; + const FPrivSeg *y = ((const USegPtr *)b)->SegPtr; + + // Segs are grouped into three categories in this order: + // + // 1. Segs with different front and back sectors (or no back at all). + // 2. Segs with the same front and back sectors. + // 3. Minisegs. + // + // Within the first two sets, segs are also sorted by linedef. + // + // Note that when GL subsectors are written, the segs will be reordered + // so that they are in clockwise order, and extra minisegs will be added + // as needed to close the subsector. But the first seg used will still be + // the first seg chosen here. + + int xtype, ytype; + + if (x->linedef == -1) + { + xtype = 2; + } + else if (x->frontsector == x->backsector) + { + xtype = 1; + } + else + { + xtype = 0; + } + + if (y->linedef == -1) + { + ytype = 2; + } + else if (y->frontsector == y->backsector) + { + ytype = 1; + } + else + { + ytype = 0; + } + + if (xtype != ytype) + { + return xtype - ytype; + } + else if (xtype < 2) + { + return x->linedef - y->linedef; + } + else + { + return 0; + } +} + +// Given a set of segs, checks to make sure they all belong to a single +// sector. If so, false is returned, and they become a subsector. If not, +// a splitter is synthesized, and true is returned to continue processing +// down this branch of the tree. + +bool FNodeBuilder::CheckSubsector (uint32_t set, node_t &node, uint32_t &splitseg) +{ + sector_t *sec; + uint32_t seg; + + sec = NULL; + seg = set; + + do + { + D(Printf (PRINT_LOG, " - seg %d%c(%d,%d)-(%d,%d) line %d front %d back %d\n", seg, + Segs[seg].linedef == -1 ? '+' : ' ', + Vertices[Segs[seg].v1].x>>16, Vertices[Segs[seg].v1].y>>16, + Vertices[Segs[seg].v2].x>>16, Vertices[Segs[seg].v2].y>>16, + Segs[seg].linedef, + Segs[seg].frontsector == NULL ? -1 : Segs[seg].frontsector - sectors, + Segs[seg].backsector == NULL ? -1 : Segs[seg].backsector - sectors)); + if (Segs[seg].linedef != -1 && + Segs[seg].frontsector != sec + // Segs with the same front and back sectors are allowed to reside + // in a subsector with segs from a different sector, because the + // only effect they can have on the display is to place masked + // mid textures in the scene. Since minisegs only mark subsector + // boundaries, their sector information is unimportant. + // + // Update: Lines with the same front and back sectors *can* affect + // the display if their subsector does not match their front sector. + /*&& Segs[seg].frontsector != Segs[seg].backsector*/) + { + if (sec == NULL) + { + sec = Segs[seg].frontsector; + } + else + { + break; + } + } + seg = Segs[seg].next; + } while (seg != UINT_MAX); + + if (seg == UINT_MAX) + { // It's a valid non-GL subsector, and probably a valid GL subsector too. + if (GLNodes) + { + return CheckSubsectorOverlappingSegs (set, node, splitseg); + } + return false; + } + + D(Printf(PRINT_LOG, "Need to synthesize a splitter for set %d on seg %d\n", set, seg)); + splitseg = UINT_MAX; + + // This is a very simple and cheap "fix" for subsectors with segs + // from multiple sectors, and it seems ZenNode does something + // similar. It is the only technique I could find that makes the + // "transparent water" in nb_bmtrk.wad work properly. + return ShoveSegBehind (set, node, seg, UINT_MAX); +} + +// When creating GL nodes, we need to check for segs with the same start and +// end vertices and split them into two subsectors. + +bool FNodeBuilder::CheckSubsectorOverlappingSegs (uint32_t set, node_t &node, uint32_t &splitseg) +{ + int v1, v2; + uint32_t seg1, seg2; + + for (seg1 = set; seg1 != UINT_MAX; seg1 = Segs[seg1].next) + { + if (Segs[seg1].linedef == -1) + { // Do not check minisegs. + continue; + } + v1 = Segs[seg1].v1; + v2 = Segs[seg1].v2; + for (seg2 = Segs[seg1].next; seg2 != UINT_MAX; seg2 = Segs[seg2].next) + { + if (Segs[seg2].v1 == v1 && Segs[seg2].v2 == v2) + { + if (Segs[seg2].linedef == -1) + { // Do not put minisegs into a new subsector. + std::swap (seg1, seg2); + } + D(Printf(PRINT_LOG, "Need to synthesize a splitter for set %d on seg %d (ov)\n", set, seg2)); + splitseg = UINT_MAX; + + return ShoveSegBehind (set, node, seg2, seg1); + } + } + } + // It really is a good subsector. + return false; +} + +// The seg is marked to indicate that it should be forced to the +// back of the splitter. Because these segs already form a convex +// set, all the other segs will be in front of the splitter. Since +// the splitter is formed from this seg, the back of the splitter +// will have a one-dimensional subsector. SplitSegs() will add one +// or two new minisegs to close it: If mate is UINT_MAX, then a +// new seg is created to replace this one on the front of the +// splitter. Otherwise, mate takes its place. In either case, the +// seg in front of the splitter is partnered with a new miniseg on +// the back so that the back will have two segs. + +bool FNodeBuilder::ShoveSegBehind (uint32_t set, node_t &node, uint32_t seg, uint32_t mate) +{ + SetNodeFromSeg (node, &Segs[seg]); + HackSeg = seg; + HackMate = mate; + if (!Segs[seg].planefront) + { + node.x += node.dx; + node.y += node.dy; + node.dx = -node.dx; + node.dy = -node.dy; + } + return Heuristic (node, set, false) > 0; +} + +// Splitters are chosen to coincide with segs in the given set. To reduce the +// number of segs that need to be considered as splitters, segs are grouped into +// according to the planes that they lie on. Because one seg on the plane is just +// as good as any other seg on the plane at defining a split, only one seg from +// each unique plane needs to be considered as a splitter. A result of 0 means +// this set is a convex region. A result of -1 means that there were possible +// splitters, but they all split segs we want to keep intact. +int FNodeBuilder::SelectSplitter (uint32_t set, node_t &node, uint32_t &splitseg, int step, bool nosplit) +{ + int stepleft; + int bestvalue; + uint32_t bestseg; + uint32_t seg; + bool nosplitters = false; + + bestvalue = 0; + bestseg = UINT_MAX; + + seg = set; + stepleft = 0; + + memset (&PlaneChecked[0], 0, PlaneChecked.Size()); + + D(Printf (PRINT_LOG, "Processing set %d\n", set)); + + while (seg != UINT_MAX) + { + FPrivSeg *pseg = &Segs[seg]; + + if (--stepleft <= 0) + { + int l = pseg->planenum >> 3; + int r = 1 << (pseg->planenum & 7); + + if (l < 0 || (PlaneChecked[l] & r) == 0) + { + if (l >= 0) + { + PlaneChecked[l] |= r; + } + + stepleft = step; + SetNodeFromSeg (node, pseg); + + int value = Heuristic (node, set, nosplit); + + D(Printf (PRINT_LOG, "Seg %5d, ld %d (%5d,%5d)-(%5d,%5d) scores %d\n", seg, Segs[seg].linedef, node.x>>16, node.y>>16, + (node.x+node.dx)>>16, (node.y+node.dy)>>16, value)); + + if (value > bestvalue) + { + bestvalue = value; + bestseg = seg; + } + else if (value < 0) + { + nosplitters = true; + } + } + } + + seg = pseg->next; + } + + if (bestseg == UINT_MAX) + { // No lines split any others into two sets, so this is a convex region. + D(Printf (PRINT_LOG, "set %d, step %d, nosplit %d has no good splitter (%d)\n", set, step, nosplit, nosplitters)); + return nosplitters ? -1 : 0; + } + + D(Printf (PRINT_LOG, "split seg %u in set %d, score %d, step %d, nosplit %d\n", bestseg, set, bestvalue, step, nosplit)); + + splitseg = bestseg; + SetNodeFromSeg (node, &Segs[bestseg]); + return 1; +} + +// Given a splitter (node), returns a score based on how "good" the resulting +// split in a set of segs is. Higher scores are better. -1 means this splitter +// splits something it shouldn't and will only be returned if honorNoSplit is +// true. A score of 0 means that the splitter does not split any of the segs +// in the set. + +int FNodeBuilder::Heuristic (node_t &node, uint32_t set, bool honorNoSplit) +{ + // Set the initial score above 0 so that near vertex anti-weighting is less likely to produce a negative score. + int score = 1000000; + int segsInSet = 0; + int counts[2] = { 0, 0 }; + int realSegs[2] = { 0, 0 }; + int specialSegs[2] = { 0, 0 }; + uint32_t i = set; + int sidev[2]; + int side; + bool splitter = false; + unsigned int max, m2, p, q; + double frac; + + Touched.Clear (); + Colinear.Clear (); + + while (i != UINT_MAX) + { + const FPrivSeg *test = &Segs[i]; + + if (HackSeg == i) + { + side = 1; + } + else + { + side = ClassifyLine (node, &Vertices[test->v1], &Vertices[test->v2], sidev); + } + switch (side) + { + case 0: // Seg is on only one side of the partition + case 1: + // If we don't split this line, but it abuts the splitter, also reject it. + // The "right" thing to do in this case is to only reject it if there is + // another nosplit seg from the same sector at this vertex. Note that a line + // that lies exactly on top of the splitter is okay. + if (test->loopnum && honorNoSplit && (sidev[0] == 0 || sidev[1] == 0)) + { + if ((sidev[0] | sidev[1]) != 0) + { + max = Touched.Size(); + for (p = 0; p < max; ++p) + { + if (Touched[p] == test->loopnum) + { + break; + } + } + if (p == max) + { + Touched.Push (test->loopnum); + } + } + else + { + max = Colinear.Size(); + for (p = 0; p < max; ++p) + { + if (Colinear[p] == test->loopnum) + { + break; + } + } + if (p == max) + { + Colinear.Push (test->loopnum); + } + } + } + + counts[side]++; + if (test->linedef != -1) + { + realSegs[side]++; + if (test->frontsector == test->backsector) + { + specialSegs[side]++; + } + // Add some weight to the score for unsplit lines + score += SplitCost; + } + else + { + // Minisegs don't count quite as much for nosplitting + score += SplitCost / 4; + } + break; + + default: // Seg is cut by the partition + // If we are not allowed to split this seg, reject this splitter + if (test->loopnum) + { + if (honorNoSplit) + { + D(Printf (PRINT_LOG, "Splits seg %d\n", i)); + return -1; + } + else + { + splitter = true; + } + } + + // Splitters that are too close to a vertex are bad. + frac = InterceptVector (node, *test); + if (frac < 0.001 || frac > 0.999) + { + FPrivVert *v1 = &Vertices[test->v1]; + FPrivVert *v2 = &Vertices[test->v2]; + double x = v1->x, y = v1->y; + x += frac * (v2->x - x); + y += frac * (v2->y - y); + if (fabs(x - v1->x) < VERTEX_EPSILON+1 && fabs(y - v1->y) < VERTEX_EPSILON+1) + { + D(Printf("Splitter will produce same start vertex as seg %d\n", i)); + return -1; + } + if (fabs(x - v2->x) < VERTEX_EPSILON+1 && fabs(y - v2->y) < VERTEX_EPSILON+1) + { + D(Printf("Splitter will produce same end vertex as seg %d\n", i)); + return -1; + } + if (frac > 0.999) + { + frac = 1 - frac; + } + int penalty = int(1 / frac); + score = MAX(score - penalty, 1); + D(Printf ("Penalized splitter by %d for being near endpt of seg %d (%f).\n", penalty, i, frac)); + } + + counts[0]++; + counts[1]++; + if (test->linedef != -1) + { + realSegs[0]++; + realSegs[1]++; + if (test->frontsector == test->backsector) + { + specialSegs[0]++; + specialSegs[1]++; + } + } + break; + } + + segsInSet++; + i = test->next; + } + + // If this line is outside all the others, return a special score + if (counts[0] == 0 || counts[1] == 0) + { + return 0; + } + + // A splitter must have at least one real seg on each side. + // Otherwise, a subsector could be left without any way to easily + // determine which sector it lies inside. + if (realSegs[0] == 0 || realSegs[1] == 0) + { + D(Printf (PRINT_LOG, "Leaves a side with only mini segs\n")); + return -1; + } + + // Try to avoid splits that leave only "special" segs, so that the generated + // subsectors have a better chance of choosing the correct sector. This situation + // is not neccesarily bad, just undesirable. + if (honorNoSplit && (specialSegs[0] == realSegs[0] || specialSegs[1] == realSegs[1])) + { + D(Printf (PRINT_LOG, "Leaves a side with only special segs\n")); + return -1; + } + + // If this splitter intersects any vertices of segs that should not be split, + // check if it is also colinear with another seg from the same sector. If it + // is, the splitter is okay. If not, it should be rejected. Why? Assuming that + // polyobject containers are convex (which they should be), a splitter that + // is colinear with one of the sector's segs and crosses the vertex of another + // seg of that sector must be crossing the container's corner and does not + // actually split the container. + + max = Touched.Size (); + m2 = Colinear.Size (); + + // If honorNoSplit is false, then both these lists will be empty. + + // If the splitter touches some vertices without being colinear to any, we + // can skip further checks and reject this right away. + if (m2 == 0 && max > 0) + { + return -1; + } + + for (p = 0; p < max; ++p) + { + int look = Touched[p]; + for (q = 0; q < m2; ++q) + { + if (look == Colinear[q]) + { + break; + } + } + if (q == m2) + { // Not a good one + return -1; + } + } + + // Doom maps are primarily axis-aligned lines, so it's usually a good + // idea to prefer axis-aligned splitters over diagonal ones. Doom originally + // had special-casing for orthogonal lines, so they performed better. ZDoom + // does not care about the line's direction, so this is merely a choice to + // try and improve the final tree. + + if ((node.dx == 0) || (node.dy == 0)) + { + // If we have to split a seg we would prefer to keep unsplit, give + // extra precedence to orthogonal lines so that the polyobjects + // outside the entrance to MAP06 in Hexen MAP02 display properly. + if (splitter) + { + score += segsInSet*8; + } + else + { + score += segsInSet/AAPreference; + } + } + + score += (counts[0] + counts[1]) - abs(counts[0] - counts[1]); + + return score; +} + +void FNodeBuilder::SplitSegs (uint32_t set, node_t &node, uint32_t splitseg, uint32_t &outset0, uint32_t &outset1, unsigned int &count0, unsigned int &count1) +{ + unsigned int _count0 = 0; + unsigned int _count1 = 0; + outset0 = UINT_MAX; + outset1 = UINT_MAX; + + Events.DeleteAll (); + SplitSharers.Clear (); + + while (set != UINT_MAX) + { + bool hack; + FPrivSeg *seg = &Segs[set]; + int next = seg->next; + + int sidev[2], side; + + if (HackSeg == set) + { + HackSeg = UINT_MAX; + side = 1; + sidev[0] = sidev[1] = 0; + hack = true; + } + else + { + side = ClassifyLine (node, &Vertices[seg->v1], &Vertices[seg->v2], sidev); + hack = false; + } + + switch (side) + { + case 0: // seg is entirely in front + seg->next = outset0; + outset0 = set; + _count0++; + break; + + case 1: // seg is entirely in back + seg->next = outset1; + outset1 = set; + _count1++; + break; + + default: // seg needs to be split + double frac; + FPrivVert newvert; + unsigned int vertnum; + int seg2; + + frac = InterceptVector (node, *seg); + newvert.x = Vertices[seg->v1].x; + newvert.y = Vertices[seg->v1].y; + newvert.x += fixed_t(frac * (double(Vertices[seg->v2].x) - newvert.x)); + newvert.y += fixed_t(frac * (double(Vertices[seg->v2].y) - newvert.y)); + vertnum = VertexMap->SelectVertexClose (newvert); + + if (vertnum != (unsigned int)seg->v1 && vertnum != (unsigned int)seg->v2) + { + seg2 = SplitSeg(set, vertnum, sidev[0]); + + Segs[seg2].next = outset0; + outset0 = seg2; + Segs[set].next = outset1; + outset1 = set; + _count0++; + _count1++; + + // Also split the seg on the back side + if (Segs[set].partner != UINT_MAX) + { + int partner1 = Segs[set].partner; + int partner2 = SplitSeg(partner1, vertnum, sidev[1]); + // The newly created seg stays in the same set as the + // back seg because it has not been considered for splitting + // yet. If it had been, then the front seg would have already + // been split, and we would not be in this default case. + // Moreover, the back seg may not even be in the set being + // split, so we must not move its pieces into the out sets. + Segs[partner1].next = partner2; + Segs[partner2].partner = seg2; + Segs[seg2].partner = partner2; + } + + if (GLNodes) + { + AddIntersection(node, vertnum); + } + } + else + { + // all that matters here is to prevent a crash so we must make sure that we do not end up with all segs being sorted to the same side - even if this may not be correct. + // But if we do not do that this code would not be able to move on. Just discarding the seg is also not an option because it won't guarantee that we achieve an actual split. + if (_count0 == 0) + { + side = 0; + seg->next = outset0; + outset0 = set; + _count0++; + } + else + { + side = 1; + seg->next = outset1; + outset1 = set; + _count1++; + } + } + break; + } + if (side >= 0 && GLNodes) + { + if (sidev[0] == 0) + { + double dist1 = AddIntersection (node, seg->v1); + if (sidev[1] == 0) + { + double dist2 = AddIntersection (node, seg->v2); + FSplitSharer share = { dist1, set, dist2 > dist1 }; + SplitSharers.Push (share); + } + } + else if (sidev[1] == 0) + { + AddIntersection (node, seg->v2); + } + } + if (hack && GLNodes) + { + uint32_t newback, newfront; + + newback = AddMiniseg (seg->v2, seg->v1, UINT_MAX, set, splitseg); + if (HackMate == UINT_MAX) + { + newfront = AddMiniseg (Segs[set].v1, Segs[set].v2, newback, set, splitseg); + Segs[newfront].next = outset0; + outset0 = newfront; + } + else + { + newfront = HackMate; + Segs[newfront].partner = newback; + Segs[newback].partner = newfront; + } + Segs[newback].frontsector = Segs[newback].backsector = + Segs[newfront].frontsector = Segs[newfront].backsector = + Segs[set].frontsector; + + Segs[newback].next = outset1; + outset1 = newback; + } + set = next; + } + FixSplitSharers (node); + if (GLNodes) + { + AddMinisegs (node, splitseg, outset0, outset1); + } + count0 = _count0; + count1 = _count1; +} + +void FNodeBuilder::SetNodeFromSeg (node_t &node, const FPrivSeg *pseg) const +{ + if (pseg->planenum >= 0) + { + FSimpleLine *pline = &Planes[pseg->planenum]; + node.x = pline->x; + node.y = pline->y; + node.dx = pline->dx; + node.dy = pline->dy; + } + else + { + node.x = Vertices[pseg->v1].x; + node.y = Vertices[pseg->v1].y; + node.dx = Vertices[pseg->v2].x - node.x; + node.dy = Vertices[pseg->v2].y - node.y; + } +} + +uint32_t FNodeBuilder::SplitSeg (uint32_t segnum, int splitvert, int v1InFront) +{ + double dx, dy; + FPrivSeg newseg; + int newnum = (int)Segs.Size(); + + newseg = Segs[segnum]; + dx = double(Vertices[splitvert].x - Vertices[newseg.v1].x); + dy = double(Vertices[splitvert].y - Vertices[newseg.v1].y); + if (v1InFront > 0) + { + newseg.v1 = splitvert; + Segs[segnum].v2 = splitvert; + + RemoveSegFromVert2 (segnum, newseg.v2); + + newseg.nextforvert = Vertices[splitvert].segs; + Vertices[splitvert].segs = newnum; + + newseg.nextforvert2 = Vertices[newseg.v2].segs2; + Vertices[newseg.v2].segs2 = newnum; + + Segs[segnum].nextforvert2 = Vertices[splitvert].segs2; + Vertices[splitvert].segs2 = segnum; + } + else + { + Segs[segnum].v1 = splitvert; + newseg.v2 = splitvert; + + RemoveSegFromVert1 (segnum, newseg.v1); + + newseg.nextforvert = Vertices[newseg.v1].segs; + Vertices[newseg.v1].segs = newnum; + + newseg.nextforvert2 = Vertices[splitvert].segs2; + Vertices[splitvert].segs2 = newnum; + + Segs[segnum].nextforvert = Vertices[splitvert].segs; + Vertices[splitvert].segs = segnum; + } + + Segs.Push (newseg); + + D(Printf (PRINT_LOG, "Split seg %d to get seg %d\n", segnum, newnum)); + + return newnum; +} + +void FNodeBuilder::RemoveSegFromVert1 (uint32_t segnum, int vertnum) +{ + FPrivVert *v = &Vertices[vertnum]; + + if (v->segs == segnum) + { + v->segs = Segs[segnum].nextforvert; + } + else + { + uint32_t prev, curr; + prev = 0; + curr = v->segs; + while (curr != UINT_MAX && curr != segnum) + { + prev = curr; + curr = Segs[curr].nextforvert; + } + if (curr == segnum) + { + Segs[prev].nextforvert = Segs[curr].nextforvert; + } + } +} + +void FNodeBuilder::RemoveSegFromVert2 (uint32_t segnum, int vertnum) +{ + FPrivVert *v = &Vertices[vertnum]; + + if (v->segs2 == segnum) + { + v->segs2 = Segs[segnum].nextforvert2; + } + else + { + uint32_t prev, curr; + prev = 0; + curr = v->segs2; + while (curr != UINT_MAX && curr != segnum) + { + prev = curr; + curr = Segs[curr].nextforvert2; + } + if (curr == segnum) + { + Segs[prev].nextforvert2 = Segs[curr].nextforvert2; + } + } +} + +double FNodeBuilder::InterceptVector (const node_t &splitter, const FPrivSeg &seg) +{ + double v2x = (double)Vertices[seg.v1].x; + double v2y = (double)Vertices[seg.v1].y; + double v2dx = (double)Vertices[seg.v2].x - v2x; + double v2dy = (double)Vertices[seg.v2].y - v2y; + double v1dx = (double)splitter.dx; + double v1dy = (double)splitter.dy; + + double den = v1dy*v2dx - v1dx*v2dy; + + if (den == 0.0) + return 0; // parallel + + double v1x = (double)splitter.x; + double v1y = (double)splitter.y; + + double num = (v1x - v2x)*v1dy + (v2y - v1y)*v1dx; + double frac = num / den; + + return frac; +} + +void FNodeBuilder::PrintSet (int l, uint32_t set) +{ + Printf (PRINT_LOG, "set %d:\n", l); + for (; set != UINT_MAX; set = Segs[set].next) + { + Printf (PRINT_LOG, "\t%u(%d)%c%d(%d,%d)-%d(%d,%d)\n", set,0/*Segs[set].frontsector->sectornum*/, + Segs[set].linedef == -1 ? '+' : ':', + Segs[set].v1, + Vertices[Segs[set].v1].x>>16, Vertices[Segs[set].v1].y>>16, + Segs[set].v2, + Vertices[Segs[set].v2].x>>16, Vertices[Segs[set].v2].y>>16); + } + Printf (PRINT_LOG, "*\n"); +} diff --git a/source/core/nodebuilder/nodebuild.h b/source/core/nodebuilder/nodebuild.h new file mode 100644 index 000000000..0b212ad2f --- /dev/null +++ b/source/core/nodebuilder/nodebuild.h @@ -0,0 +1,470 @@ +/* +** nodebuild.cpp +** +**--------------------------------------------------------------------------- +** Copyright 2002-2016 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** 4. When not used as part of ZDoom or a ZDoom derivative, this code will be +** covered by the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or (at +** your option) any later version. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ +#pragma once + +#include "tarray.h" +#include "x86.h" +#include "build.h" + +struct FPolySeg; +struct FMiniBSP; +struct FLevelLocals; + +struct FEventInfo +{ + int Vertex; + uint32_t FrontSeg; +}; + +struct FEvent +{ + FEvent *Parent, *Left, *Right; + double Distance; + FEventInfo Info; +}; + +class FEventTree +{ +public: + FEventTree (); + ~FEventTree (); + + FEvent *GetMinimum (); + FEvent *GetSuccessor (FEvent *event) const { FEvent *node = Successor(event); return node == &Nil ? NULL : node; } + FEvent *GetPredecessor (FEvent *event) const { FEvent *node = Predecessor(event); return node == &Nil ? NULL : node; } + + FEvent *GetNewNode (); + void Insert (FEvent *event); + FEvent *FindEvent (double distance) const; + void DeleteAll (); + + void PrintTree () const { PrintTree (Root); } + +private: + FEvent Nil; + FEvent *Root; + FEvent *Spare; + + void DeletionTraverser (FEvent *event); + FEvent *Successor (FEvent *event) const; + FEvent *Predecessor (FEvent *event) const; + + void PrintTree (const FEvent *event) const; +}; + +struct FSimpleVert +{ + fixed_t x, y; +}; + +typedef int64_t fixed64_t; + +struct vertex_t +{ + DVector2 p; + + void set(fixed_t x, fixed_t y) + { + p.X = x / 65536.; + p.Y = y / 65536.; + } + + double fX() const + { + return p.X; + } + + double fY() const + { + return p.Y; + } + + fixed_t fixX() const + { + return FLOAT2FIXED(p.X); + } + + fixed_t fixY() const + { + return FLOAT2FIXED(p.Y); + } +}; + +struct side_t; + +struct line_t +{ + vertex_t* v1, * v2; // vertices, from v1 to v2 + side_t* sidedef[2]; + sectortype* frontsector, * backsector; + int linenum; + + int Index() const { return linenum; } + +}; + +struct side_t +{ + sectortype* sector; // Sector the SideDef is facing. + int sidenum; + int Index() const { return sidenum; } +}; + +struct subsector_t; +struct seg_t +{ + vertex_t* v1; + vertex_t* v2; + + side_t* sidedef; + line_t* linedef; + + // Sector references. Could be retrieved from linedef, too. + sectortype* frontsector; + sectortype* backsector; // NULL for one-sided lines + + seg_t* PartnerSeg; + subsector_t* Subsector; + + int segnum; + + int Index() const { return segnum; } +}; + +struct glseg_t : public seg_t +{ + uint32_t Partner; +}; + +struct subsector_t +{ + sectortype* sector; + seg_t* firstline; + uint32_t numlines; +}; + +struct node_t +{ + // Partition line. + fixed_t x; + fixed_t y; + fixed_t dx; + fixed_t dy; + union + { + float bbox[2][4]; // Bounding box for each child. + fixed_t nb_bbox[2][4]; // Used by nodebuilder. + }; + float len; + int nodenum; + union + { + void* children[2]; // If bit 0 is set, it's a subsector. + int intchildren[2]; // Used by nodebuilder. + }; + + int Index() const { return nodenum; } +}; + +struct FLevelLocals +{ + TArray vertexes; + TArray subsectors; + TArray nodes; + TArray segs; +}; + +enum { NO_SIDE = 0x7fffffff }; + + +class FNodeBuilder +{ + struct FPrivSeg + { + int v1, v2; + int sidedef; + int linedef; + sectortype *frontsector; + sectortype *backsector; + uint32_t next; + uint32_t nextforvert; + uint32_t nextforvert2; + int loopnum; // loop number for split avoidance (0 means splitting is okay) + uint32_t partner; // seg on back side + uint32_t storedseg; // seg # in the GL_SEGS lump + + int planenum; + bool planefront; + FPrivSeg *hashnext; + }; + struct FPrivVert : FSimpleVert + { + uint32_t segs; // segs that use this vertex as v1 + uint32_t segs2; // segs that use this vertex as v2 + + bool operator== (const FPrivVert &other) + { + return x == other.x && y == other.y; + } + }; + struct FSimpleLine + { + fixed_t x, y, dx, dy; + }; + union USegPtr + { + uint32_t SegNum; + FPrivSeg *SegPtr; + }; + struct FSplitSharer + { + double Distance; + uint32_t Seg; + bool Forward; + }; + + // Like a blockmap, but for vertices instead of lines + class IVertexMap + { + public: + virtual ~IVertexMap(); + virtual int SelectVertexExact(FPrivVert &vert) = 0; + virtual int SelectVertexClose(FPrivVert &vert) = 0; + private: + IVertexMap &operator=(const IVertexMap &); + }; + + class FVertexMap : public IVertexMap + { + public: + FVertexMap (FNodeBuilder &builder, fixed_t minx, fixed_t miny, fixed_t maxx, fixed_t maxy); + ~FVertexMap (); + + int SelectVertexExact (FPrivVert &vert); + int SelectVertexClose (FPrivVert &vert); + + private: + FNodeBuilder &MyBuilder; + TArray *VertexGrid; + + fixed64_t MinX, MinY, MaxX, MaxY; + int BlocksWide, BlocksTall; + + enum { BLOCK_SHIFT = 8 + FRACBITS }; + enum { BLOCK_SIZE = 1 << BLOCK_SHIFT }; + + int InsertVertex (FPrivVert &vert); + inline int GetBlock (fixed64_t x, fixed64_t y) + { + assert (x >= MinX); + assert (y >= MinY); + assert (x <= MaxX); + assert (y <= MaxY); + return (unsigned(x - MinX) >> BLOCK_SHIFT) + (unsigned(y - MinY) >> BLOCK_SHIFT) * BlocksWide; + } + }; + + class FVertexMapSimple : public IVertexMap + { + public: + FVertexMapSimple(FNodeBuilder &builder); + + int SelectVertexExact(FPrivVert &vert); + int SelectVertexClose(FPrivVert &vert); + private: + int InsertVertex(FPrivVert &vert); + + FNodeBuilder &MyBuilder; + }; + + friend class FVertexMap; + friend class FVertexMapSimple; + +public: + struct FLevel + { + vertex_t *Vertices; int NumVertices; + side_t *Sides; int NumSides; + line_t *Lines; int NumLines; + + fixed_t MinX, MinY, MaxX, MaxY; + + void FindMapBounds(); + void ResetMapBounds() + { + MinX = FIXED_MAX; + MinY = FIXED_MAX; + MaxX = FIXED_MIN; + MaxY = FIXED_MIN; + } + }; + + struct FPolyStart + { + int polynum; + fixed_t x, y; + }; + + FNodeBuilder (FLevel &lev); + ~FNodeBuilder (); + + void Extract(FLevelLocals &lev); + const int *GetOldVertexTable(); + + // These are used for building sub-BSP trees for polyobjects. + void Clear(); + void AddSegs(seg_t *segs, int numsegs); + void BuildMini(bool makeGLNodes); + + static angle_t PointToAngle (fixed_t dx, fixed_t dy); + + // < 0 : in front of line + // == 0 : on line + // > 0 : behind line + + static inline int PointOnSide (int x, int y, int x1, int y1, int dx, int dy); + +private: + IVertexMap *VertexMap; + int *OldVertexTable; + + TArray Nodes; + TArray Subsectors; + TArray SubsectorSets; + TArray Segs; + TArray Vertices; + TArray SegList; + TArray PlaneChecked; + TArray Planes; + + TArray Touched; // Loops a splitter touches on a vertex + TArray Colinear; // Loops with edges colinear to a splitter + FEventTree Events; // Vertices intersected by the current splitter + + TArray SplitSharers; // Segs colinear with the current splitter + + uint32_t HackSeg; // Seg to force to back of splitter + uint32_t HackMate; // Seg to use in front of hack seg + FLevel &Level; + bool GLNodes; // Add minisegs to make GL nodes? + + // Progress meter stuff + int SegsStuffed; + + void FindUsedVertices (vertex_t *vertices, int max); + void BuildTree (); + void MakeSegsFromSides (); + int CreateSeg (int linenum, int sidenum); + void GroupSegPlanes (); + void GroupSegPlanesSimple (); + void AddSegToBBox (fixed_t bbox[4], const FPrivSeg *seg); + int CreateNode (uint32_t set, unsigned int count, fixed_t bbox[4]); + int CreateSubsector (uint32_t set, fixed_t bbox[4]); + void CreateSubsectorsForReal (); + bool CheckSubsector (uint32_t set, node_t &node, uint32_t &splitseg); + bool CheckSubsectorOverlappingSegs (uint32_t set, node_t &node, uint32_t &splitseg); + bool ShoveSegBehind (uint32_t set, node_t &node, uint32_t seg, uint32_t mate); int SelectSplitter (uint32_t set, node_t &node, uint32_t &splitseg, int step, bool nosplit); + void SplitSegs (uint32_t set, node_t &node, uint32_t splitseg, uint32_t &outset0, uint32_t &outset1, unsigned int &count0, unsigned int &count1); + uint32_t SplitSeg (uint32_t segnum, int splitvert, int v1InFront); + int Heuristic (node_t &node, uint32_t set, bool honorNoSplit); + + // Returns: + // 0 = seg is in front + // 1 = seg is in back + // -1 = seg cuts the node + + int ClassifyLine (node_t &node, const FPrivVert *v1, const FPrivVert *v2, int sidev[2]); + + void FixSplitSharers (const node_t &node); + double AddIntersection (const node_t &node, int vertex); + void AddMinisegs (const node_t &node, uint32_t splitseg, uint32_t &fset, uint32_t &rset); + uint32_t CheckLoopStart (fixed_t dx, fixed_t dy, int vertex1, int vertex2); + uint32_t CheckLoopEnd (fixed_t dx, fixed_t dy, int vertex2); + void RemoveSegFromVert1 (uint32_t segnum, int vertnum); + void RemoveSegFromVert2 (uint32_t segnum, int vertnum); + uint32_t AddMiniseg (int v1, int v2, uint32_t partner, uint32_t seg1, uint32_t splitseg); + void SetNodeFromSeg (node_t &node, const FPrivSeg *pseg) const; + + int CloseSubsector (TArray &segs, int subsector, vertex_t *outVerts); + uint32_t PushGLSeg (TArray &segs, const FPrivSeg *seg, vertex_t *outVerts); + void PushConnectingGLSeg (int subsector, TArray &segs, vertex_t *v1, vertex_t *v2); + int OutputDegenerateSubsector (TArray &segs, int subsector, bool bForward, double lastdot, FPrivSeg *&prev, vertex_t *outVerts); + + static int SortSegs (const void *a, const void *b); + + double InterceptVector (const node_t &splitter, const FPrivSeg &seg); + + void PrintSet (int l, uint32_t set); + + FNodeBuilder &operator= (const FNodeBuilder &) { return *this; } +}; + +// Points within this distance of a line will be considered on the line. +// Units are in fixed_ts. +const double SIDE_EPSILON = 6.5536; + +// Vertices within this distance of each other will be considered as the same vertex. +#define VERTEX_EPSILON 6 // This is a fixed_t value + +inline int FNodeBuilder::PointOnSide (int x, int y, int x1, int y1, int dx, int dy) +{ + // For most cases, a simple dot product is enough. + double d_dx = double(dx); + double d_dy = double(dy); + double d_x = double(x); + double d_y = double(y); + double d_x1 = double(x1); + double d_y1 = double(y1); + + double s_num = (d_y1-d_y)*d_dx - (d_x1-d_x)*d_dy; + + if (fabs(s_num) < 17179869184.f) // 4<<32 + { + // Either the point is very near the line, or the segment defining + // the line is very short: Do a more expensive test to determine + // just how far from the line the point is. + double l = d_dx*d_dx + d_dy*d_dy; // double l = sqrt(d_dx*d_dx+d_dy*d_dy); + double dist = s_num * s_num / l; // double dist = fabs(s_num)/l; + if (dist < SIDE_EPSILON*SIDE_EPSILON) // if (dist < SIDE_EPSILON) + { + return 0; + } + } + return s_num > 0.0 ? -1 : 1; +} + +using sector_t = sectortype; diff --git a/source/core/nodebuilder/nodebuild_classify_nosse2.cpp b/source/core/nodebuilder/nodebuild_classify_nosse2.cpp new file mode 100644 index 000000000..90a2d02d9 --- /dev/null +++ b/source/core/nodebuilder/nodebuild_classify_nosse2.cpp @@ -0,0 +1,135 @@ +#include "nodebuild.h" + +#define FAR_ENOUGH 17179869184.f // 4<<32 + +int FNodeBuilder::ClassifyLine(node_t &node, const FPrivVert *v1, const FPrivVert *v2, int sidev[2]) +{ + double d_x1 = double(node.x); + double d_y1 = double(node.y); + double d_dx = double(node.dx); + double d_dy = double(node.dy); + double d_xv1 = double(v1->x); + double d_xv2 = double(v2->x); + double d_yv1 = double(v1->y); + double d_yv2 = double(v2->y); + + double s_num1 = (d_y1 - d_yv1) * d_dx - (d_x1 - d_xv1) * d_dy; + double s_num2 = (d_y1 - d_yv2) * d_dx - (d_x1 - d_xv2) * d_dy; + + int nears = 0; + + if (s_num1 <= -FAR_ENOUGH) + { + if (s_num2 <= -FAR_ENOUGH) + { + sidev[0] = sidev[1] = 1; + return 1; + } + if (s_num2 >= FAR_ENOUGH) + { + sidev[0] = 1; + sidev[1] = -1; + return -1; + } + nears = 1; + } + else if (s_num1 >= FAR_ENOUGH) + { + if (s_num2 >= FAR_ENOUGH) + { + sidev[0] = sidev[1] = -1; + return 0; + } + if (s_num2 <= -FAR_ENOUGH) + { + sidev[0] = -1; + sidev[1] = 1; + return -1; + } + nears = 1; + } + else + { + nears = 2 | int(fabs(s_num2) < FAR_ENOUGH); + } + + if (nears) + { + double l = 1.f / (d_dx*d_dx + d_dy*d_dy); + if (nears & 2) + { + double dist = s_num1 * s_num1 * l; + if (dist < SIDE_EPSILON*SIDE_EPSILON) + { + sidev[0] = 0; + } + else + { + sidev[0] = s_num1 > 0.0 ? -1 : 1; + } + } + else + { + sidev[0] = s_num1 > 0.0 ? -1 : 1; + } + if (nears & 1) + { + double dist = s_num2 * s_num2 * l; + if (dist < SIDE_EPSILON*SIDE_EPSILON) + { + sidev[1] = 0; + } + else + { + sidev[1] = s_num2 > 0.0 ? -1 : 1; + } + } + else + { + sidev[1] = s_num2 > 0.0 ? -1 : 1; + } + } + else + { + sidev[0] = s_num1 > 0.0 ? -1 : 1; + sidev[1] = s_num2 > 0.0 ? -1 : 1; + } + + if ((sidev[0] | sidev[1]) == 0) + { // seg is coplanar with the splitter, so use its orientation to determine + // which child it ends up in. If it faces the same direction as the splitter, + // it goes in front. Otherwise, it goes in back. + + if (node.dx != 0) + { + if ((node.dx > 0 && v2->x > v1->x) || (node.dx < 0 && v2->x < v1->x)) + { + return 0; + } + else + { + return 1; + } + } + else + { + if ((node.dy > 0 && v2->y > v1->y) || (node.dy < 0 && v2->y < v1->y)) + { + return 0; + } + else + { + return 1; + } + } + } + else if (sidev[0] <= 0 && sidev[1] <= 0) + { + return 0; + } + else if (sidev[0] >= 0 && sidev[1] >= 0) + { + return 1; + } + return -1; +} diff --git a/source/core/nodebuilder/nodebuild_events.cpp b/source/core/nodebuilder/nodebuild_events.cpp new file mode 100644 index 000000000..e615382cc --- /dev/null +++ b/source/core/nodebuilder/nodebuild_events.cpp @@ -0,0 +1,226 @@ +/* +** nodebuild_events.cpp +** +** A red-black tree for keeping track of segs that get touched by a splitter. +** +**--------------------------------------------------------------------------- +** Copyright 2002-2006 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** 4. When not used as part of ZDoom or a ZDoom derivative, this code will be +** covered by the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or (at +** your option) any later version. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include +#include "nodebuild.h" +#include "printf.h" + +FEventTree::FEventTree () +: Root (&Nil), Spare (NULL) +{ + memset (&Nil, 0, sizeof(Nil)); +} + +FEventTree::~FEventTree () +{ + FEvent *probe; + + DeleteAll (); + probe = Spare; + while (probe != NULL) + { + FEvent *next = probe->Left; + delete probe; + probe = next; + } +} + +void FEventTree::DeleteAll () +{ + DeletionTraverser (Root); + Root = &Nil; +} + +void FEventTree::DeletionTraverser (FEvent *node) +{ + if (node != &Nil && node != NULL) + { + DeletionTraverser (node->Left); + DeletionTraverser (node->Right); + node->Left = Spare; + Spare = node; + } +} + +FEvent *FEventTree::GetNewNode () +{ + FEvent *node; + + if (Spare != NULL) + { + node = Spare; + Spare = node->Left; + } + else + { + node = new FEvent; + } + return node; +} + +void FEventTree::Insert (FEvent *z) +{ + FEvent *y = &Nil; + FEvent *x = Root; + + while (x != &Nil) + { + y = x; + if (z->Distance < x->Distance) + { + x = x->Left; + } + else + { + x = x->Right; + } + } + z->Parent = y; + if (y == &Nil) + { + Root = z; + } + else if (z->Distance < y->Distance) + { + y->Left = z; + } + else + { + y->Right = z; + } + z->Left = &Nil; + z->Right = &Nil; +} + +FEvent *FEventTree::Successor (FEvent *event) const +{ + if (event->Right != &Nil) + { + event = event->Right; + while (event->Left != &Nil) + { + event = event->Left; + } + return event; + } + else + { + FEvent *y = event->Parent; + while (y != &Nil && event == y->Right) + { + event = y; + y = y->Parent; + } + return y; + } +} + +FEvent *FEventTree::Predecessor (FEvent *event) const +{ + if (event->Left != &Nil) + { + event = event->Left; + while (event->Right != &Nil) + { + event = event->Right; + } + return event; + } + else + { + FEvent *y = event->Parent; + while (y != &Nil && event == y->Left) + { + event = y; + y = y->Parent; + } + return y; + } +} + +FEvent *FEventTree::FindEvent (double key) const +{ + FEvent *node = Root; + + while (node != &Nil) + { + if (node->Distance == key) + { + return node; + } + else if (node->Distance > key) + { + node = node->Left; + } + else + { + node = node->Right; + } + } + return NULL; +} + +FEvent *FEventTree::GetMinimum () +{ + FEvent *node = Root; + + if (node == &Nil) + { + return NULL; + } + while (node->Left != &Nil) + { + node = node->Left; + } + return node; +} + +void FEventTree::PrintTree (const FEvent *event) const +{ + // Use the CRT's sprintf so that it shares the same formatting as ZDBSP's output. + char buff[100]; + if (event != &Nil) + { + PrintTree(event->Left); + sprintf(buff, " Distance %g, vertex %d, seg %u\n", + g_sqrt(event->Distance/4294967296.0), event->Info.Vertex, (unsigned)event->Info.FrontSeg); + Printf(PRINT_LOG, "%s", buff); + PrintTree(event->Right); + } +} diff --git a/source/core/nodebuilder/nodebuild_extract.cpp b/source/core/nodebuilder/nodebuild_extract.cpp new file mode 100644 index 000000000..31449e83e --- /dev/null +++ b/source/core/nodebuilder/nodebuild_extract.cpp @@ -0,0 +1,440 @@ +/* +** nodebuild_extract.cpp +** +** Converts the nodes, segs, and subsectors from the node builder's +** internal format to the format used by the rest of the game. +** +**--------------------------------------------------------------------------- +** Copyright 2002-2006 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** 4. When not used as part of ZDoom or a ZDoom derivative, this code will be +** covered by the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or (at +** your option) any later version. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include +#include + +#include "nodebuild.h" + +#if 0 +#define D(x) x +#define DD 1 +#else +#define D(x) do{}while(0) +#undef DD +#endif + + +void FNodeBuilder::Extract (FLevelLocals &theLevel) +{ + int i; + + auto &outVerts = theLevel.vertexes; + int vertCount = Vertices.Size (); + outVerts.Alloc(vertCount); + + for (i = 0; i < vertCount; ++i) + { + outVerts[i].set(Vertices[i].x, Vertices[i].y); + } + + auto &outSubs = theLevel.subsectors; + auto subCount = Subsectors.Size(); + outSubs.Alloc(subCount); + memset(&outSubs[0], 0, subCount * sizeof(subsector_t)); + + auto &outNodes = theLevel.nodes; + auto nodeCount = Nodes.Size (); + outNodes.Alloc(nodeCount); + + memcpy (&outNodes[0], &Nodes[0], nodeCount*sizeof(node_t)); + for (unsigned i = 0; i < nodeCount; ++i) + { + D(Printf(PRINT_LOG, "Node %d: Splitter[%08x,%08x] [%08x,%08x]\n", i, + outNodes[i].x, outNodes[i].y, outNodes[i].dx, outNodes[i].dy)); + // Go backwards because on 64-bit systems, both of the intchildren are + // inside the first in-game child. + for (int j = 1; j >= 0; --j) + { + if (outNodes[i].intchildren[j] & 0x80000000) + { + D(Printf(PRINT_LOG, " subsector %d\n", outNodes[i].intchildren[j] & 0x7FFFFFFF)); + outNodes[i].children[j] = (uint8_t *)(&outSubs[(outNodes[i].intchildren[j] & 0x7fffffff)]) + 1; + } + else + { + D(Printf(PRINT_LOG, " node %d\n", outNodes[i].intchildren[j])); + outNodes[i].children[j] = &outNodes[outNodes[i].intchildren[j]]; + } + } + for (int j = 0; j < 2; ++j) + { + for (int k = 0; k < 4; ++k) + { + outNodes[i].bbox[j][k] = FIXED2FLOAT(outNodes[i].nb_bbox[j][k]); + } + } + } + + auto &outSegs = theLevel.segs; + if (GLNodes) + { + TArray segs (Segs.Size()*5/4); + + for (unsigned i = 0; i < subCount; ++i) + { + uint32_t numsegs = CloseSubsector (segs, i, &outVerts[0]); + outSubs[i].numlines = numsegs; + outSubs[i].firstline = (seg_t *)(size_t)(segs.Size() - numsegs); + } + + auto segCount = segs.Size (); + outSegs.Alloc(segCount); + + for (unsigned i = 0; i < segCount; ++i) + { + outSegs[i] = *(seg_t *)&segs[i]; + + if (segs[i].Partner != UINT_MAX) + { + const uint32_t storedseg = Segs[segs[i].Partner].storedseg; + outSegs[i].PartnerSeg = UINT_MAX == storedseg ? nullptr : &outSegs[storedseg]; + } + else + { + outSegs[i].PartnerSeg = nullptr; + } + } + } + else + { + memcpy (&outSubs[0], &Subsectors[0], subCount*sizeof(subsector_t)); + auto segCount = Segs.Size (); + outSegs.Alloc(segCount); + for (unsigned i = 0; i < segCount; ++i) + { + const FPrivSeg *org = &Segs[SegList[i].SegNum]; + seg_t *out = &outSegs[i]; + + D(Printf(PRINT_LOG, "Seg %d: v1(%d) -> v2(%d)\n", i, org->v1, org->v2)); + + out->v1 = &outVerts[org->v1]; + out->v2 = &outVerts[org->v2]; + out->backsector = org->backsector; + out->frontsector = org->frontsector; + out->linedef = Level.Lines + org->linedef; + out->sidedef = Level.Sides + org->sidedef; + out->PartnerSeg = nullptr; + } + } + for (unsigned i = 0; i < subCount; ++i) + { + outSubs[i].firstline = &outSegs[(size_t)outSubs[i].firstline]; + } + + D(Printf("%i segs, %i nodes, %i subsectors\n", segCount, nodeCount, subCount)); + + for (i = 0; i < Level.NumLines; ++i) + { + Level.Lines[i].v1 = &outVerts[(size_t)Level.Lines[i].v1]; + Level.Lines[i].v2 = &outVerts[(size_t)Level.Lines[i].v2]; + } +} + +int FNodeBuilder::CloseSubsector (TArray &segs, int subsector, vertex_t *outVerts) +{ + FPrivSeg *seg, *prev; + angle_t prevAngle; + double accumx, accumy; + fixed_t midx, midy; + int firstVert; + uint32_t first, max, count, i, j; + bool diffplanes; + int firstplane; + + first = (uint32_t)(size_t)Subsectors[subsector].firstline; + max = first + Subsectors[subsector].numlines; + count = 0; + + accumx = accumy = 0.0; + diffplanes = false; + firstplane = Segs[SegList[first].SegNum].planenum; + + // Calculate the midpoint of the subsector and also check for degenerate subsectors. + // A subsector is degenerate if it exists in only one dimension, which can be + // detected when all the segs lie in the same plane. This can happen if you have + // outward-facing lines in the void that don't point toward any sector. (Some of the + // polyobjects in Hexen are constructed like this.) + for (i = first; i < max; ++i) + { + seg = &Segs[SegList[i].SegNum]; + accumx += double(Vertices[seg->v1].x) + double(Vertices[seg->v2].x); + accumy += double(Vertices[seg->v1].y) + double(Vertices[seg->v2].y); + if (firstplane != seg->planenum) + { + diffplanes = true; + } + } + + midx = fixed_t(accumx / (max - first) / 2); + midy = fixed_t(accumy / (max - first) / 2); + + seg = &Segs[SegList[first].SegNum]; + prevAngle = PointToAngle (Vertices[seg->v1].x - midx, Vertices[seg->v1].y - midy); + seg->storedseg = PushGLSeg (segs, seg, outVerts); + count = 1; + prev = seg; + firstVert = seg->v1; + +#ifdef DD + Printf(PRINT_LOG, "--%d--\n", subsector); + for (j = first; j < max; ++j) + { + seg = &Segs[SegList[j].SegNum]; + angle_t ang = PointToAngle (Vertices[seg->v1].x - midx, Vertices[seg->v1].y - midy); + Printf(PRINT_LOG, "%d%c %5d(%5d,%5d)->%5d(%5d,%5d) - %3.5f %d,%d [%08x,%08x]-[%08x,%08x]\n", j, + seg->linedef == -1 ? '+' : ':', + seg->v1, Vertices[seg->v1].x>>16, Vertices[seg->v1].y>>16, + seg->v2, Vertices[seg->v2].x>>16, Vertices[seg->v2].y>>16, + double(ang/2)*180/(1<<30), + seg->planenum, seg->planefront, + Vertices[seg->v1].x, Vertices[seg->v1].y, + Vertices[seg->v2].x, Vertices[seg->v2].y); + } +#endif + + if (diffplanes) + { // A well-behaved subsector. Output the segs sorted by the angle formed by connecting + // the subsector's center to their first vertex. + + D(Printf(PRINT_LOG, "Well behaved subsector\n")); + for (i = first + 1; i < max; ++i) + { + angle_t bestdiff = ANGLE_MAX; + FPrivSeg *bestseg = NULL; + uint32_t bestj = UINT_MAX; + j = first; + do + { + seg = &Segs[SegList[j].SegNum]; + angle_t ang = PointToAngle (Vertices[seg->v1].x - midx, Vertices[seg->v1].y - midy); + angle_t diff = prevAngle - ang; + if (seg->v1 == prev->v2) + { + bestdiff = diff; + bestseg = seg; + bestj = j; + break; + } + if (diff < bestdiff && diff > 0) + { + bestdiff = diff; + bestseg = seg; + bestj = j; + } + } + while (++j < max); + // Is a NULL bestseg actually okay? + if (bestseg != NULL) + { + seg = bestseg; + } + if (prev->v2 != seg->v1) + { + // Add a new miniseg to connect the two segs + PushConnectingGLSeg (subsector, segs, &outVerts[prev->v2], &outVerts[seg->v1]); + count++; + } +#ifdef DD + Printf(PRINT_LOG, "+%d\n", bestj); +#endif + prevAngle -= bestdiff; + seg->storedseg = PushGLSeg (segs, seg, outVerts); + count++; + prev = seg; + if (seg->v2 == firstVert) + { + prev = seg; + break; + } + } +#ifdef DD + Printf(PRINT_LOG, "\n"); +#endif + } + else + { // A degenerate subsector. These are handled in three stages: + // Stage 1. Proceed in the same direction as the start seg until we + // hit the seg furthest from it. + // Stage 2. Reverse direction and proceed until we hit the seg + // furthest from the start seg. + // Stage 3. Reverse direction again and insert segs until we get + // to the start seg. + // A dot product serves to determine distance from the start seg. + + D(Printf(PRINT_LOG, "degenerate subsector\n")); + + // Stage 1. Go forward. + count += OutputDegenerateSubsector (segs, subsector, true, 0, prev, outVerts); + + // Stage 2. Go backward. + count += OutputDegenerateSubsector (segs, subsector, false, DBL_MAX, prev, outVerts); + + // Stage 3. Go forward again. + count += OutputDegenerateSubsector (segs, subsector, true, -DBL_MAX, prev, outVerts); + } + + if (prev->v2 != firstVert) + { + PushConnectingGLSeg (subsector, segs, &outVerts[prev->v2], &outVerts[firstVert]); + count++; + } +#ifdef DD + Printf(PRINT_LOG, "Output GL subsector %d:\n", subsector); + for (i = segs.Size() - count; i < (int)segs.Size(); ++i) + { + Printf(PRINT_LOG, " Seg %5d%c(%5d,%5d)-(%5d,%5d) [%08x,%08x]-[%08x,%08x]\n", i, + segs[i].linedef == NULL ? '+' : ' ', + segs[i].v1->fixX()>>16, + segs[i].v1->fixY()>>16, + segs[i].v2->fixX()>>16, + segs[i].v2->fixY()>>16, + segs[i].v1->fixX(), + segs[i].v1->fixY(), + segs[i].v2->fixX(), + segs[i].v2->fixY()); + } +#endif + + return count; +} + +int FNodeBuilder::OutputDegenerateSubsector (TArray &segs, int subsector, bool bForward, double lastdot, FPrivSeg *&prev, vertex_t *outVerts) +{ + static const double bestinit[2] = { -DBL_MAX, DBL_MAX }; + FPrivSeg *seg; + int i, j, first, max, count; + double dot, x1, y1, dx, dy, dx2, dy2; + bool wantside; + + first = (uint32_t)(size_t)Subsectors[subsector].firstline; + max = first + Subsectors[subsector].numlines; + count = 0; + + seg = &Segs[SegList[first].SegNum]; + x1 = Vertices[seg->v1].x; + y1 = Vertices[seg->v1].y; + dx = Vertices[seg->v2].x - x1; + dy = Vertices[seg->v2].y - y1; + wantside = seg->planefront ^ !bForward; + + for (i = first + 1; i < max; ++i) + { + double bestdot = bestinit[bForward]; + FPrivSeg *bestseg = NULL; + for (j = first + 1; j < max; ++j) + { + seg = &Segs[SegList[j].SegNum]; + if (seg->planefront != wantside) + { + continue; + } + dx2 = Vertices[seg->v1].x - x1; + dy2 = Vertices[seg->v1].y - y1; + dot = dx*dx2 + dy*dy2; + + if (bForward) + { + if (dot < bestdot && dot > lastdot) + { + bestdot = dot; + bestseg = seg; + } + } + else + { + if (dot > bestdot && dot < lastdot) + { + bestdot = dot; + bestseg = seg; + } + } + } + if (bestseg != NULL) + { + if (prev->v2 != bestseg->v1) + { + PushConnectingGLSeg (subsector, segs, &outVerts[prev->v2], &outVerts[bestseg->v1]); + count++; + } + seg->storedseg = PushGLSeg (segs, bestseg, outVerts); + count++; + prev = bestseg; + lastdot = bestdot; + } + } + return count; +} + +uint32_t FNodeBuilder::PushGLSeg (TArray &segs, const FPrivSeg *seg, vertex_t *outVerts) +{ + glseg_t newseg; + + newseg.v1 = outVerts + seg->v1; + newseg.v2 = outVerts + seg->v2; + newseg.backsector = seg->backsector; + newseg.frontsector = seg->frontsector; + if (seg->linedef != -1) + { + newseg.linedef = Level.Lines + seg->linedef; + newseg.sidedef = Level.Sides + seg->sidedef; + } + else + { + newseg.linedef = NULL; + newseg.sidedef = NULL; + } + newseg.Partner = seg->partner; + return (uint32_t)segs.Push (newseg); +} + +void FNodeBuilder::PushConnectingGLSeg (int subsector, TArray &segs, vertex_t *v1, vertex_t *v2) +{ + glseg_t newseg; + + newseg.v1 = v1; + newseg.v2 = v2; + newseg.backsector = NULL; + newseg.frontsector = NULL; + newseg.linedef = NULL; + newseg.sidedef = NULL; + newseg.Partner = UINT_MAX; + segs.Push (newseg); +} diff --git a/source/core/nodebuilder/nodebuild_gl.cpp b/source/core/nodebuilder/nodebuild_gl.cpp new file mode 100644 index 000000000..279fa3fb0 --- /dev/null +++ b/source/core/nodebuilder/nodebuild_gl.cpp @@ -0,0 +1,397 @@ +/* +** nodebuild_gl.cpp +** +** Extra functions for the node builder to create minisegs. +** +**--------------------------------------------------------------------------- +** Copyright 2002-2006 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** 4. When not used as part of ZDoom or a ZDoom derivative, this code will be +** covered by the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or (at +** your option) any later version. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "nodebuild.h" + +static inline void Warn (const char *format, ...) +{ +} + +static const angle_t ANGLE_EPSILON = 5000; + +#if 0 +#define D(x) x +#else +#define D(x) do{}while(0) +#endif + +double FNodeBuilder::AddIntersection (const node_t &node, int vertex) +{ + static const FEventInfo defaultInfo = + { + -1, UINT_MAX + }; + + // Calculate signed distance of intersection vertex from start of splitter. + // Only ordering is important, so we don't need a sqrt. + FPrivVert *v = &Vertices[vertex]; + double dist = (double(v->x) - node.x)*(node.dx) + (double(v->y) - node.y)*(node.dy); + + FEvent *event = Events.FindEvent (dist); + if (event == NULL) + { + event = Events.GetNewNode (); + event->Distance = dist; + event->Info = defaultInfo; + event->Info.Vertex = vertex; + Events.Insert (event); + } + return dist; +} + +// If there are any segs on the splitter that span more than two events, they +// must be split. Alien Vendetta is one example wad that is quite bad about +// having overlapping lines. If we skip this step, these segs will still be +// split later, but minisegs will erroneously be added for them, and partner +// seg information will be messed up in the generated tree. +void FNodeBuilder::FixSplitSharers (const node_t &node) +{ + D(Printf(PRINT_LOG, "events:\n")); + D(Events.PrintTree()); + for (unsigned int i = 0; i < SplitSharers.Size(); ++i) + { + uint32_t seg = SplitSharers[i].Seg; + int v2 = Segs[seg].v2; + FEvent *event = Events.FindEvent (SplitSharers[i].Distance); + FEvent *next; + + if (event == NULL) + { // Should not happen + continue; + } + + // Use the CRT's printf so the formatting matches ZDBSP's + D(char buff[200]); + D(sprintf(buff, "Considering events on seg %d(%d[%d,%d]->%d[%d,%d]) [%g:%g]\n", seg, + Segs[seg].v1, + Vertices[Segs[seg].v1].x>>16, + Vertices[Segs[seg].v1].y>>16, + Segs[seg].v2, + Vertices[Segs[seg].v2].x>>16, + Vertices[Segs[seg].v2].y>>16, + SplitSharers[i].Distance, event->Distance)); + D(Printf(PRINT_LOG, "%s", buff)); + + if (SplitSharers[i].Forward) + { + event = Events.GetSuccessor (event); + if (event == NULL) + { + continue; + } + next = Events.GetSuccessor (event); + } + else + { + event = Events.GetPredecessor (event); + if (event == NULL) + { + continue; + } + next = Events.GetPredecessor (event); + } + + while (event != NULL && next != NULL && event->Info.Vertex != v2) + { + D(Printf(PRINT_LOG, "Forced split of seg %d(%d->%d) at %d(%d,%d)\n", seg, + Segs[seg].v1, Segs[seg].v2, + event->Info.Vertex, + Vertices[event->Info.Vertex].x>>16, + Vertices[event->Info.Vertex].y>>16)); + + uint32_t newseg = SplitSeg (seg, event->Info.Vertex, 1); + + Segs[newseg].next = Segs[seg].next; + Segs[seg].next = newseg; + + uint32_t partner = Segs[seg].partner; + if (partner != UINT_MAX) + { + int endpartner = SplitSeg (partner, event->Info.Vertex, 1); + + Segs[endpartner].next = Segs[partner].next; + Segs[partner].next = endpartner; + + Segs[seg].partner = endpartner; + Segs[partner].partner = newseg; + } + + seg = newseg; + if (SplitSharers[i].Forward) + { + event = next; + next = Events.GetSuccessor (next); + } + else + { + event = next; + next = Events.GetPredecessor (next); + } + } + } +} + +void FNodeBuilder::AddMinisegs (const node_t &node, uint32_t splitseg, uint32_t &fset, uint32_t &bset) +{ + FEvent *event = Events.GetMinimum (), *prev = NULL; + + while (event != NULL) + { + if (prev != NULL) + { + uint32_t fseg1, bseg1, fseg2, bseg2; + uint32_t fnseg, bnseg; + + // Minisegs should only be added when they can create valid loops on both the front and + // back of the splitter. This means some subsectors could be unclosed if their sectors + // are unclosed, but at least we won't be needlessly creating subsectors in void space. + // Unclosed subsectors can be closed trivially once the BSP tree is complete. + + if ((fseg1 = CheckLoopStart (node.dx, node.dy, prev->Info.Vertex, event->Info.Vertex)) != UINT_MAX && + (bseg1 = CheckLoopStart (-node.dx, -node.dy, event->Info.Vertex, prev->Info.Vertex)) != UINT_MAX && + (fseg2 = CheckLoopEnd (node.dx, node.dy, event->Info.Vertex)) != UINT_MAX && + (bseg2 = CheckLoopEnd (-node.dx, -node.dy, prev->Info.Vertex)) != UINT_MAX) + { + // Add miniseg on the front side + fnseg = AddMiniseg (prev->Info.Vertex, event->Info.Vertex, UINT_MAX, fseg1, splitseg); + Segs[fnseg].next = fset; + fset = fnseg; + + // Add miniseg on the back side + bnseg = AddMiniseg (event->Info.Vertex, prev->Info.Vertex, fnseg, bseg1, splitseg); + Segs[bnseg].next = bset; + bset = bnseg; + + sector_t *fsector, *bsector; + + fsector = Segs[fseg1].frontsector; + bsector = Segs[bseg1].frontsector; + + Segs[fnseg].frontsector = fsector; + Segs[fnseg].backsector = bsector; + Segs[bnseg].frontsector = bsector; + Segs[bnseg].backsector = fsector; + + // Only print the warning if this might be bad. + if (fsector != bsector && + fsector != Segs[fseg1].backsector && + bsector != Segs[bseg1].backsector) + { + Warn ("Sectors %d at (%d,%d) and %d at (%d,%d) don't match.\n", + Segs[fseg1].frontsector, + Vertices[prev->Info.Vertex].x>>FRACBITS, Vertices[prev->Info.Vertex].y>>FRACBITS, + Segs[bseg1].frontsector, + Vertices[event->Info.Vertex].x>>FRACBITS, Vertices[event->Info.Vertex].y>>FRACBITS + ); + } + + D(Printf (PRINT_LOG, "**Minisegs** %d/%d added %d(%d,%d)->%d(%d,%d)\n", fnseg, bnseg, + prev->Info.Vertex, + Vertices[prev->Info.Vertex].x>>16, Vertices[prev->Info.Vertex].y>>16, + event->Info.Vertex, + Vertices[event->Info.Vertex].x>>16, Vertices[event->Info.Vertex].y>>16)); + } + } + prev = event; + event = Events.GetSuccessor (event); + } +} + +uint32_t FNodeBuilder::AddMiniseg (int v1, int v2, uint32_t partner, uint32_t seg1, uint32_t splitseg) +{ + uint32_t nseg; + FPrivSeg *seg = &Segs[seg1]; + FPrivSeg newseg; + + newseg.sidedef = NO_SIDE; + newseg.linedef = -1; + newseg.loopnum = 0; + newseg.next = UINT_MAX; + newseg.planefront = true; + newseg.hashnext = NULL; + newseg.storedseg = UINT_MAX; + newseg.frontsector = NULL; + newseg.backsector = NULL; + + if (splitseg != UINT_MAX) + { + newseg.planenum = Segs[splitseg].planenum; + } + else + { + newseg.planenum = -1; + } + + newseg.v1 = v1; + newseg.v2 = v2; + newseg.nextforvert = Vertices[v1].segs; + newseg.nextforvert2 = Vertices[v2].segs2; + newseg.next = seg->next; + if (partner != UINT_MAX) + { + newseg.partner = partner; + } + else + { + newseg.partner = UINT_MAX; + } + nseg = Segs.Push (newseg); + if (newseg.partner != UINT_MAX) + { + Segs[partner].partner = nseg; + } + Vertices[v1].segs = nseg; + Vertices[v2].segs2 = nseg; + //Printf ("Between %d and %d::::\n", seg1, seg2); + return nseg; +} + +uint32_t FNodeBuilder::CheckLoopStart (fixed_t dx, fixed_t dy, int vertex, int vertex2) +{ + FPrivVert *v = &Vertices[vertex]; + angle_t splitAngle = PointToAngle (dx, dy); + uint32_t segnum; + angle_t bestang; + uint32_t bestseg; + + // Find the seg ending at this vertex that forms the smallest angle + // to the splitter. + segnum = v->segs2; + bestang = ANGLE_MAX; + bestseg = UINT_MAX; + while (segnum != UINT_MAX) + { + FPrivSeg *seg = &Segs[segnum]; + angle_t segAngle = PointToAngle (Vertices[seg->v1].x - v->x, Vertices[seg->v1].y - v->y); + angle_t diff = splitAngle - segAngle; + + if (diff < ANGLE_EPSILON && + PointOnSide (Vertices[seg->v1].x, Vertices[seg->v1].y, v->x, v->y, dx, dy) == 0) + { + // If a seg lies right on the splitter, don't count it + } + else + { + if (diff <= bestang) + { + bestang = diff; + bestseg = segnum; + } + } + segnum = seg->nextforvert2; + } + if (bestseg == UINT_MAX) + { + return UINT_MAX; + } + // Now make sure there are no segs starting at this vertex that form + // an even smaller angle to the splitter. + segnum = v->segs; + while (segnum != UINT_MAX) + { + FPrivSeg *seg = &Segs[segnum]; + if (seg->v2 == vertex2) + { + return UINT_MAX; + } + angle_t segAngle = PointToAngle (Vertices[seg->v2].x - v->x, Vertices[seg->v2].y - v->y); + angle_t diff = splitAngle - segAngle; + if (diff < bestang && seg->partner != bestseg) + { + return UINT_MAX; + } + segnum = seg->nextforvert; + } + return bestseg; +} + +uint32_t FNodeBuilder::CheckLoopEnd (fixed_t dx, fixed_t dy, int vertex) +{ + FPrivVert *v = &Vertices[vertex]; + angle_t splitAngle = PointToAngle (dx, dy) + ANGLE_180; + uint32_t segnum; + angle_t bestang; + uint32_t bestseg; + + // Find the seg starting at this vertex that forms the smallest angle + // to the splitter. + segnum = v->segs; + bestang = ANGLE_MAX; + bestseg = UINT_MAX; + while (segnum != UINT_MAX) + { + FPrivSeg *seg = &Segs[segnum]; + angle_t segAngle = PointToAngle (Vertices[seg->v2].x - v->x, Vertices[seg->v2].y - v->y); + angle_t diff = segAngle - splitAngle; + + if (diff < ANGLE_EPSILON && + PointOnSide (Vertices[seg->v1].x, Vertices[seg->v1].y, v->x, v->y, dx, dy) == 0) + { + // If a seg lies right on the splitter, don't count it + } + else + { + if (diff <= bestang) + { + bestang = diff; + bestseg = segnum; + } + } + segnum = seg->nextforvert; + } + if (bestseg == UINT_MAX) + { + return UINT_MAX; + } + // Now make sure there are no segs ending at this vertex that form + // an even smaller angle to the splitter. + segnum = v->segs2; + while (segnum != UINT_MAX) + { + FPrivSeg *seg = &Segs[segnum]; + angle_t segAngle = PointToAngle (Vertices[seg->v1].x - v->x, Vertices[seg->v1].y - v->y); + angle_t diff = segAngle - splitAngle; + if (diff < bestang && seg->partner != bestseg) + { + return UINT_MAX; + } + segnum = seg->nextforvert2; + } + return bestseg; +} diff --git a/source/core/nodebuilder/nodebuild_utility.cpp b/source/core/nodebuilder/nodebuild_utility.cpp new file mode 100644 index 000000000..afc321668 --- /dev/null +++ b/source/core/nodebuilder/nodebuild_utility.cpp @@ -0,0 +1,535 @@ +/* +** nodebuild_utility.cpp +** +** Miscellaneous node builder utility functions. +** +**--------------------------------------------------------------------------- +** Copyright 2002-2006 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** 4. When not used as part of ZDoom or a ZDoom derivative, this code will be +** covered by the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or (at +** your option) any later version. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include +#ifdef _MSC_VER +#include +#endif +#include +#include "nodebuild.h" +#include "printf.h" +#include "m_fixed.h" +#include "m_bbox.h" + +#if 0 +#define D(x) x +#else +#define D(x) do{}while(0) +#endif + +#if 0 +#define P(x) x +#else +#define P(x) do{}while(0) +#endif + +angle_t FNodeBuilder::PointToAngle (fixed_t x, fixed_t y) +{ + const double rad2bam = double(1<<30) / M_PI; + double ang = g_atan2 (double(y), double(x)); + // Convert to signed first since negative double to unsigned is undefined. + return angle_t(int(ang * rad2bam)) << 1; +} + +void FNodeBuilder::FindUsedVertices (vertex_t *oldverts, int max) +{ + int *map = new int[max]; + int i; + FPrivVert newvert; + + memset (&map[0], -1, sizeof(int)*max); + + for (i = 0; i < Level.NumLines; ++i) + { + ptrdiff_t v1 = Level.Lines[i].v1 - oldverts; + ptrdiff_t v2 = Level.Lines[i].v2 - oldverts; + + if (map[v1] == -1) + { + newvert.x = oldverts[v1].fixX(); + newvert.y = oldverts[v1].fixY(); + map[v1] = VertexMap->SelectVertexExact (newvert); + } + if (map[v2] == -1) + { + newvert.x = oldverts[v2].fixX(); + newvert.y = oldverts[v2].fixY(); + map[v2] = VertexMap->SelectVertexExact (newvert); + } + + Level.Lines[i].v1 = (vertex_t *)(size_t)map[v1]; + Level.Lines[i].v2 = (vertex_t *)(size_t)map[v2]; + } + OldVertexTable = map; +} + +// Retrieves the original vertex -> current vertex table. +// Doing so prevents the node builder from freeing it. + +const int *FNodeBuilder::GetOldVertexTable() +{ + int *table = OldVertexTable; + OldVertexTable = NULL; + return table; +} + +// For every sidedef in the map, create a corresponding seg. + +void FNodeBuilder::MakeSegsFromSides () +{ + int i, j; + + if (Level.NumLines == 0) + { + I_Error ("Map is empty.\n"); + } + + for (i = 0; i < Level.NumLines; ++i) + { + if (Level.Lines[i].sidedef[0] != NULL) + { + CreateSeg (i, 0); + } + else + { + Printf ("Linedef %d does not have a front side.\n", i); + } + + if (Level.Lines[i].sidedef[1] != NULL) + { + j = CreateSeg (i, 1); + if (Level.Lines[i].sidedef[0] != NULL) + { + Segs[j-1].partner = j; + Segs[j].partner = j-1; + } + } + } +} + +int FNodeBuilder::CreateSeg (int linenum, int sidenum) +{ + FPrivSeg seg; + int segnum; + + seg.next = UINT_MAX; + seg.loopnum = 0; + seg.partner = UINT_MAX; + seg.hashnext = NULL; + seg.planefront = false; + seg.planenum = UINT_MAX; + seg.storedseg = UINT_MAX; + + if (sidenum == 0) + { // front + seg.frontsector = Level.Lines[linenum].frontsector; + seg.backsector = Level.Lines[linenum].backsector; + seg.v1 = (int)(size_t)Level.Lines[linenum].v1; + seg.v2 = (int)(size_t)Level.Lines[linenum].v2; + } + else + { // back + seg.frontsector = Level.Lines[linenum].backsector; + seg.backsector = Level.Lines[linenum].frontsector; + seg.v2 = (int)(size_t)Level.Lines[linenum].v1; + seg.v1 = (int)(size_t)Level.Lines[linenum].v2; + } + seg.linedef = linenum; + side_t *sd = Level.Lines[linenum].sidedef[sidenum]; + seg.sidedef = sd != NULL? sd->Index() : int(NO_SIDE); + seg.nextforvert = Vertices[seg.v1].segs; + seg.nextforvert2 = Vertices[seg.v2].segs2; + + segnum = (int)Segs.Push (seg); + Vertices[seg.v1].segs = segnum; + Vertices[seg.v2].segs2 = segnum; + D(Printf(PRINT_LOG, "Seg %4d: From line %d, side %s (%5d,%5d)-(%5d,%5d) [%08x,%08x]-[%08x,%08x]\n", segnum, linenum, sidenum ? "back " : "front", + Vertices[seg.v1].x>>16, Vertices[seg.v1].y>>16, Vertices[seg.v2].x>>16, Vertices[seg.v2].y>>16, + Vertices[seg.v1].x, Vertices[seg.v1].y, Vertices[seg.v2].x, Vertices[seg.v2].y)); + + return segnum; +} + +// For every seg, create FPrivSegs and FPrivVerts. + +void FNodeBuilder::AddSegs(seg_t *segs, int numsegs) +{ + assert(numsegs > 0); + + for (int i = 0; i < numsegs; ++i) + { + FPrivSeg seg; + FPrivVert vert; + int segnum; + + seg.next = UINT_MAX; + seg.loopnum = 0; + seg.partner = UINT_MAX; + seg.hashnext = NULL; + seg.planefront = false; + seg.planenum = UINT_MAX; + seg.storedseg = UINT_MAX; + + seg.frontsector = segs[i].frontsector; + seg.backsector = segs[i].backsector; + vert.x = segs[i].v1->fixX(); + vert.y = segs[i].v1->fixY(); + seg.v1 = VertexMap->SelectVertexExact(vert); + vert.x = segs[i].v2->fixX(); + vert.y = segs[i].v2->fixY(); + seg.v2 = VertexMap->SelectVertexExact(vert); + seg.linedef = segs[i].linedef->Index(); + seg.sidedef = segs[i].sidedef != NULL ? segs[i].sidedef->Index() : int(NO_SIDE); + seg.nextforvert = Vertices[seg.v1].segs; + seg.nextforvert2 = Vertices[seg.v2].segs2; + + segnum = (int)Segs.Push(seg); + Vertices[seg.v1].segs = segnum; + Vertices[seg.v2].segs2 = segnum; + } +} + + + +// Group colinear segs together so that only one seg per line needs to be checked +// by SelectSplitter(). + +void FNodeBuilder::GroupSegPlanes () +{ + const int bucketbits = 12; + FPrivSeg *buckets[1<next = i+1; + seg->hashnext = NULL; + } + + Segs[Segs.Size()-1].next = UINT_MAX; + + for (i = planenum = 0; i < (int)Segs.Size(); ++i) + { + FPrivSeg *seg = &Segs[i]; + fixed_t x1 = Vertices[seg->v1].x; + fixed_t y1 = Vertices[seg->v1].y; + fixed_t x2 = Vertices[seg->v2].x; + fixed_t y2 = Vertices[seg->v2].y; + angle_t ang = PointToAngle (x2 - x1, y2 - y1); + + if (ang >= 1u<<31) + ang += 1u<<31; + + FPrivSeg *check = buckets[ang >>= 31-bucketbits]; + + while (check != NULL) + { + fixed_t cx1 = Vertices[check->v1].x; + fixed_t cy1 = Vertices[check->v1].y; + fixed_t cdx = Vertices[check->v2].x - cx1; + fixed_t cdy = Vertices[check->v2].y - cy1; + if (PointOnSide (x1, y1, cx1, cy1, cdx, cdy) == 0 && + PointOnSide (x2, y2, cx1, cy1, cdx, cdy) == 0) + { + break; + } + check = check->hashnext; + } + if (check != NULL) + { + seg->planenum = check->planenum; + const FSimpleLine *line = &Planes[seg->planenum]; + if (line->dx != 0) + { + if ((line->dx > 0 && x2 > x1) || (line->dx < 0 && x2 < x1)) + { + seg->planefront = true; + } + else + { + seg->planefront = false; + } + } + else + { + if ((line->dy > 0 && y2 > y1) || (line->dy < 0 && y2 < y1)) + { + seg->planefront = true; + } + else + { + seg->planefront = false; + } + } + } + else + { + seg->hashnext = buckets[ang]; + buckets[ang] = seg; + seg->planenum = planenum++; + seg->planefront = true; + + FSimpleLine pline = { Vertices[seg->v1].x, + Vertices[seg->v1].y, + Vertices[seg->v2].x - Vertices[seg->v1].x, + Vertices[seg->v2].y - Vertices[seg->v1].y }; + Planes.Push (pline); + } + } + + D(Printf ("%d planes from %d segs\n", planenum, Segs.Size())); + + PlaneChecked.Reserve ((planenum + 7) / 8); +} + +// Just create one plane per seg. Should be good enough for mini BSPs. +void FNodeBuilder::GroupSegPlanesSimple() +{ + Planes.Resize(Segs.Size()); + for (int i = 0; i < (int)Segs.Size(); ++i) + { + FPrivSeg *seg = &Segs[i]; + FSimpleLine *pline = &Planes[i]; + seg->next = i+1; + seg->hashnext = NULL; + seg->planenum = i; + seg->planefront = true; + pline->x = Vertices[seg->v1].x; + pline->y = Vertices[seg->v1].y; + pline->dx = Vertices[seg->v2].x - Vertices[seg->v1].x; + pline->dy = Vertices[seg->v2].y - Vertices[seg->v1].y; + } + Segs.Last().next = UINT_MAX; + PlaneChecked.Reserve((Segs.Size() + 7) / 8); +} + +void FNodeBuilder::AddSegToBBox (fixed_t bbox[4], const FPrivSeg *seg) +{ + FPrivVert *v1 = &Vertices[seg->v1]; + FPrivVert *v2 = &Vertices[seg->v2]; + + if (v1->x < bbox[BOXLEFT]) bbox[BOXLEFT] = v1->x; + if (v1->x > bbox[BOXRIGHT]) bbox[BOXRIGHT] = v1->x; + if (v1->y < bbox[BOXBOTTOM]) bbox[BOXBOTTOM] = v1->y; + if (v1->y > bbox[BOXTOP]) bbox[BOXTOP] = v1->y; + + if (v2->x < bbox[BOXLEFT]) bbox[BOXLEFT] = v2->x; + if (v2->x > bbox[BOXRIGHT]) bbox[BOXRIGHT] = v2->x; + if (v2->y < bbox[BOXBOTTOM]) bbox[BOXBOTTOM] = v2->y; + if (v2->y > bbox[BOXTOP]) bbox[BOXTOP] = v2->y; +} + +void FNodeBuilder::FLevel::FindMapBounds() +{ + double minx, maxx, miny, maxy; + + minx = maxx = Vertices[0].fX(); + miny = maxy = Vertices[0].fY(); + + for (int i = 1; i < NumLines; ++i) + { + for (int j = 0; j < 2; j++) + { + vertex_t *v = (j == 0 ? Lines[i].v1 : Lines[i].v2); + if (v->fX() < minx) minx = v->fX(); + else if (v->fX() > maxx) maxx = v->fX(); + if (v->fY() < miny) miny = v->fY(); + else if (v->fY() > maxy) maxy = v->fY(); + } + } + + MinX = FLOAT2FIXED(minx); + MinY = FLOAT2FIXED(miny); + MaxX = FLOAT2FIXED(maxx); + MaxY = FLOAT2FIXED(maxy); +} + +FNodeBuilder::IVertexMap::~IVertexMap() +{ +} + +FNodeBuilder::FVertexMap::FVertexMap (FNodeBuilder &builder, + fixed_t minx, fixed_t miny, fixed_t maxx, fixed_t maxy) + : MyBuilder(builder) +{ + MinX = minx; + MinY = miny; + BlocksWide = int(((double(maxx) - minx + 1) + (BLOCK_SIZE - 1)) / BLOCK_SIZE); + BlocksTall = int(((double(maxy) - miny + 1) + (BLOCK_SIZE - 1)) / BLOCK_SIZE); + MaxX = MinX + fixed64_t(BlocksWide) * BLOCK_SIZE - 1; + MaxY = MinY + fixed64_t(BlocksTall) * BLOCK_SIZE - 1; + VertexGrid = new TArray[BlocksWide * BlocksTall]; +} + +FNodeBuilder::FVertexMap::~FVertexMap () +{ + delete[] VertexGrid; +} + +int FNodeBuilder::FVertexMap::SelectVertexExact (FNodeBuilder::FPrivVert &vert) +{ + TArray &block = VertexGrid[GetBlock (vert.x, vert.y)]; + FPrivVert *vertices = &MyBuilder.Vertices[0]; + unsigned int i; + + for (i = 0; i < block.Size(); ++i) + { + if (vertices[block[i]].x == vert.x && vertices[block[i]].y == vert.y) + { + return block[i]; + } + } + + // Not present: add it! + return InsertVertex (vert); +} + +int FNodeBuilder::FVertexMap::SelectVertexClose (FNodeBuilder::FPrivVert &vert) +{ + TArray &block = VertexGrid[GetBlock (vert.x, vert.y)]; + FPrivVert *vertices = &MyBuilder.Vertices[0]; + unsigned int i; + + for (i = 0; i < block.Size(); ++i) + { +#if VERTEX_EPSILON <= 1 + if (vertices[block[i]].x == vert.x && vertices[block[i]].y == vert.y) +#else + if (abs(vertices[block[i]].x - vert.x) < VERTEX_EPSILON && + abs(vertices[block[i]].y - vert.y) < VERTEX_EPSILON) +#endif + { + return block[i]; + } + } + + // Not present: add it! + return InsertVertex (vert); +} + +int FNodeBuilder::FVertexMap::InsertVertex (FNodeBuilder::FPrivVert &vert) +{ + int vertnum; + + vert.segs = UINT_MAX; + vert.segs2 = UINT_MAX; + vertnum = (int)MyBuilder.Vertices.Push (vert); + + // If a vertex is near a block boundary, then it will be inserted on + // both sides of the boundary so that SelectVertexClose can find + // it by checking in only one block. + fixed64_t minx = MAX (MinX, fixed64_t(vert.x) - VERTEX_EPSILON); + fixed64_t maxx = MIN (MaxX, fixed64_t(vert.x) + VERTEX_EPSILON); + fixed64_t miny = MAX (MinY, fixed64_t(vert.y) - VERTEX_EPSILON); + fixed64_t maxy = MIN (MaxY, fixed64_t(vert.y) + VERTEX_EPSILON); + + int blk[4] = + { + GetBlock (minx, miny), + GetBlock (maxx, miny), + GetBlock (minx, maxy), + GetBlock (maxx, maxy) + }; + unsigned int blkcount[4] = + { + VertexGrid[blk[0]].Size(), + VertexGrid[blk[1]].Size(), + VertexGrid[blk[2]].Size(), + VertexGrid[blk[3]].Size() + }; + for (int i = 0; i < 4; ++i) + { + if (VertexGrid[blk[i]].Size() == blkcount[i]) + { + VertexGrid[blk[i]].Push (vertnum); + } + } + + return vertnum; +} + +FNodeBuilder::FVertexMapSimple::FVertexMapSimple(FNodeBuilder &builder) + : MyBuilder(builder) +{ +} + +int FNodeBuilder::FVertexMapSimple::SelectVertexExact(FNodeBuilder::FPrivVert &vert) +{ + FPrivVert *verts = &MyBuilder.Vertices[0]; + unsigned int stop = MyBuilder.Vertices.Size(); + + for (unsigned int i = 0; i < stop; ++i) + { + if (verts[i].x == vert.x && verts[i].y == vert.y) + { + return i; + } + } + // Not present: add it! + return InsertVertex(vert); +} + +int FNodeBuilder::FVertexMapSimple::SelectVertexClose(FNodeBuilder::FPrivVert &vert) +{ + FPrivVert *verts = &MyBuilder.Vertices[0]; + unsigned int stop = MyBuilder.Vertices.Size(); + + for (unsigned int i = 0; i < stop; ++i) + { +#if VERTEX_EPSILON <= 1 + if (verts[i].x == vert.x && verts[i].y == y) +#else + if (abs(verts[i].x - vert.x) < VERTEX_EPSILON && + abs(verts[i].y - vert.y) < VERTEX_EPSILON) +#endif + { + return i; + } + } + // Not present: add it! + return InsertVertex (vert); +} + +int FNodeBuilder::FVertexMapSimple::InsertVertex (FNodeBuilder::FPrivVert &vert) +{ + vert.segs = UINT_MAX; + vert.segs2 = UINT_MAX; + return (int)MyBuilder.Vertices.Push (vert); +} diff --git a/source/core/palette.cpp b/source/core/palette.cpp index 2625746b3..8d9b74179 100644 --- a/source/core/palette.cpp +++ b/source/core/palette.cpp @@ -48,6 +48,7 @@ #include "../../glbackend/glbackend.h" LookupTableInfo lookups; +int numshades; //========================================================================== // @@ -136,7 +137,6 @@ void paletteLoadFromDisk(void) void LookupTableInfo::postLoadTables(void) { - globalpal = 0; GPalette.GenerateGlobalBrightmapFromColormap(getTable(0), numshades); // Try to detect fullbright translations. Unfortunately this cannot be used to detect fade strength because of loss of color precision in the palette map. diff --git a/source/core/palette.h b/source/core/palette.h index ac6e441b7..efc60b428 100644 --- a/source/core/palette.h +++ b/source/core/palette.h @@ -17,16 +17,21 @@ #include "palentry.h" #include "templates.h" -#define MAXBASEPALS 256 -#define MAXPALOOKUPS 256 -#define MAXBLENDTABS 256 +enum +{ + MAXBASEPALS = 256, + MAXPALOOKUPS = 256, + MAXBLENDTABS = 256, + + RESERVEDPALS = 4, // don't forget to increment this when adding reserved pals + DETAILPAL = (MAXPALOOKUPS - 1), + GLOWPAL = (MAXPALOOKUPS - 2), + SPECULARPAL = (MAXPALOOKUPS - 3), + NORMALPAL = (MAXPALOOKUPS - 4), + BRIGHTPAL = (MAXPALOOKUPS), -#define RESERVEDPALS 4 // don't forget to increment this when adding reserved pals -#define DETAILPAL (MAXPALOOKUPS - 1) -#define GLOWPAL (MAXPALOOKUPS - 2) -#define SPECULARPAL (MAXPALOOKUPS - 3) -#define NORMALPAL (MAXPALOOKUPS - 4) -#define BRIGHTPAL (MAXPALOOKUPS) + MAXREALPAL = MAXPALOOKUPS - RESERVEDPALS +}; // fixme: should use the flags from the PRSFlags enum directly enum @@ -165,7 +170,7 @@ inline void videoclearFade() void videoTintBlood(int32_t r, int32_t g, int32_t b); -extern int32_t globalpal, globalfloorpal; +extern int numshades; extern void paletteLoadFromDisk(void); diff --git a/source/core/parsefuncs.h b/source/core/parsefuncs.h deleted file mode 100644 index 05b3aa87b..000000000 --- a/source/core/parsefuncs.h +++ /dev/null @@ -1,48 +0,0 @@ - -/* -** parsefuncs.h -** handlers for .def parser -** only to be included by the actual parser -** -**--------------------------------------------------------------------------- -** Copyright 2021 Christoph Oelckers -** All rights reserved. -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions -** are met: -** -** 1. Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** 2. Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** 3. The name of the author may not be used to endorse or promote products -** derived from this software without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -**--------------------------------------------------------------------------- -** -** -*/ - - -void parseAnimTileRange(FScanner& sc, FScriptPosition& pos) -{ - SetAnim set; - if (!sc.GetNumber(set.tile1, true)) return; - if (!sc.GetNumber(set.tile2, true)) return; - if (!sc.GetNumber(set.speed, true)) return; - if (!sc.GetNumber(set.type, true)) return; - processSetAnim("animtilerange", pos, set); -} - diff --git a/source/core/precache.cpp b/source/core/precache.cpp index ed660c7cd..f7aae123c 100644 --- a/source/core/precache.cpp +++ b/source/core/precache.cpp @@ -39,7 +39,10 @@ #include "hw_material.h" #include "gamestruct.h" #include "gamecontrol.h" -#include "glbackend/gl_models.h" +#include "texturemanager.h" +#include "hw_models.h" +#include "hw_voxels.h" +#include "mapinfo.h" BEGIN_BLD_NS extern short voxelIndex[MAXTILES]; @@ -128,6 +131,19 @@ void precacheMarkedTiles() int dapalnum = pair->Key >> 32; doprecache(dapicnum, dapalnum); } + + // Cache everything the map explicitly declares. + TMap cachetexmap; + for (auto& tex : currentLevel->PrecacheTextures) cachetexmap.Insert(tex, true); + + decltype(cachetexmap)::Iterator it2(cachetexmap); + decltype(cachetexmap)::Pair* pair2; + while (it2.NextPair(pair2)) + { + auto tex = TexMan.FindGameTexture(pair2->Key, ETextureType::Any); + if (tex) PrecacheTex(tex, 0); + } + cachemap.Clear(); } diff --git a/source/core/precache.h b/source/core/precache.h index bcf40813f..fab410227 100644 --- a/source/core/precache.h +++ b/source/core/precache.h @@ -2,5 +2,6 @@ void PrecacheHardwareTextures(int nTile); void markTileForPrecache(int tilenum, int palnum); +void markTextureForPrecache(const char* texname); void markVoxelForPrecache(int voxnum); void precacheMarkedTiles(); diff --git a/source/core/rendering/hw_entrypoint.cpp b/source/core/rendering/hw_entrypoint.cpp new file mode 100644 index 000000000..548a3a171 --- /dev/null +++ b/source/core/rendering/hw_entrypoint.cpp @@ -0,0 +1,400 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2004-2016 Christoph Oelckers +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//-------------------------------------------------------------------------- +// +/* +** gl_scene.cpp +** manages the rendering of the player's view +** +*/ + +#include "gi.h" +#include "build.h" +#include "v_draw.h" +//#include "a_dynlight.h" +#include "v_video.h" +#include "m_png.h" +#include "i_time.h" +#include "hw_dynlightdata.h" +#include "hw_clock.h" +#include "flatvertices.h" + +#include "hw_renderstate.h" +#include "hw_lightbuffer.h" +#include "hw_cvars.h" +#include "hw_viewpointbuffer.h" +#include "hw_clipper.h" +//#include "hwrenderer/scene/hw_portal.h" +#include "hw_vrmodes.h" + +#include "hw_drawstructs.h" +#include "hw_drawlist.h" +#include "hw_drawinfo.h" +#include "gamecvars.h" +#include "render.h" +#include "gamestruct.h" +#include "gamehud.h" + +EXTERN_CVAR(Bool, cl_capfps) + +PalEntry GlobalMapFog; +float GlobalFogDensity = 350.f; +TArray allPortals; + + +#if 0 +void CollectLights(FLevelLocals* Level) +{ + IShadowMap* sm = &screen->mShadowMap; + int lightindex = 0; + + // Todo: this should go through the blockmap in a spiral pattern around the player so that closer lights are preferred. + for (auto light = Level->lights; light; light = light->next) + { + IShadowMap::LightsProcessed++; + if (light->shadowmapped && light->IsActive() && lightindex < 1024) + { + IShadowMap::LightsShadowmapped++; + + light->mShadowmapIndex = lightindex; + sm->SetLight(lightindex, (float)light->X(), (float)light->Y(), (float)light->Z(), light->GetRadius()); + lightindex++; + } + else + { + light->mShadowmapIndex = 1024; + } + + } + + for (; lightindex < 1024; lightindex++) + { + sm->SetLight(lightindex, 0, 0, 0, 0); + } +} +#endif + + +//----------------------------------------------------------------------------- +// +// Renders one viewpoint in a scene +// +//----------------------------------------------------------------------------- + +void RenderViewpoint(FRenderViewpoint& mainvp, IntRect* bounds, float fov, float ratio, float fovratio, bool mainview, bool toscreen) +{ + auto& RenderState = *screen->RenderState(); + + /* + if (mainview && toscreen) + { + screen->SetAABBTree(camera->Level->aabbTree); + screen->mShadowMap.SetCollectLights([=] { + CollectLights(camera->Level); + }); + screen->UpdateShadowMap(); + } + */ + + // Render (potentially) multiple views for stereo 3d + // Fixme. The view offsetting should be done with a static table and not require setup of the entire render state for the mode. + auto vrmode = VRMode::GetVRMode(mainview && toscreen); + const int eyeCount = vrmode->mEyeCount; + screen->FirstEye(); + hw_int_useindexedcolortextures = eyeCount > 1? false : *hw_useindexedcolortextures; + + for (int eye_ix = 0; eye_ix < eyeCount; ++eye_ix) + { + const auto& eye = vrmode->mEyes[eye_ix]; + screen->SetViewportRects(bounds); + + if (mainview) // Bind the scene frame buffer and turn on draw buffers used by ssao + { + bool useSSAO = (gl_ssao != 0); + screen->SetSceneRenderTarget(useSSAO); + RenderState.SetPassType(useSSAO ? GBUFFER_PASS : NORMAL_PASS); + RenderState.EnableDrawBuffers(RenderState.GetPassDrawBufferCount(), true); + } + + auto di = HWDrawInfo::StartDrawInfo(nullptr, mainvp, nullptr); + auto& vp = di->Viewpoint; + vp = mainvp; + + di->Set3DViewport(RenderState); + float flash = 1.f; + di->Viewpoint.FieldOfView = fov; // Set the real FOV for the current scene (it's not necessarily the same as the global setting in r_viewpoint) + + // Stereo mode specific perspective projection + di->VPUniforms.mProjectionMatrix = eye.GetProjection(fov, ratio, fovratio); + // Stereo mode specific viewpoint adjustment + vp.Pos += eye.GetViewShift(vp.HWAngles.Yaw.Degrees); + di->SetupView(RenderState, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, false, false); + + di->ProcessScene(toscreen); + + if (mainview) + { + PostProcess.Clock(); + if (toscreen) di->EndDrawScene(RenderState); // do not call this for camera textures. + + if (RenderState.GetPassType() == GBUFFER_PASS) // Turn off ssao draw buffers + { + RenderState.SetPassType(NORMAL_PASS); + RenderState.EnableDrawBuffers(1); + } + + screen->PostProcessScene(false, CM_DEFAULT, flash, [&]() { }); + PostProcess.Unclock(); + } + di->EndDrawInfo(); + if (eyeCount - eye_ix > 1) + screen->NextEye(eyeCount); + } + hw_int_useindexedcolortextures = false; +} + +//=========================================================================== +// +// Set up the view point. +// +//=========================================================================== + +FRenderViewpoint SetupViewpoint(spritetype* cam, const vec3_t& position, int sectnum, binangle angle, fixedhoriz horizon, binangle rollang) +{ + FRenderViewpoint r_viewpoint{}; + r_viewpoint.CameraSprite = cam; + r_viewpoint.SectNums = nullptr; + r_viewpoint.SectCount = sectnum; + r_viewpoint.Pos = { position.x / 16.f, position.y / -16.f, position.z / -256.f }; + r_viewpoint.HWAngles.Yaw = -90.f + angle.asdeg(); + r_viewpoint.HWAngles.Pitch = -horizon.aspitch(); + r_viewpoint.HWAngles.Roll = -rollang.asdeg(); + r_viewpoint.FieldOfView = (float)r_fov; + r_viewpoint.RotAngle = angle.asbam(); + double FocalTangent = tan(r_viewpoint.FieldOfView.Radians() / 2); + DAngle an = 270. - r_viewpoint.HWAngles.Yaw.Degrees; + r_viewpoint.TanSin = FocalTangent * an.Sin(); + r_viewpoint.TanCos = FocalTangent * an.Cos(); + r_viewpoint.ViewVector = an.ToVector(); + return r_viewpoint; +} + + +void DoWriteSavePic(FileWriter* file, uint8_t* scr, int width, int height, bool upsidedown) +{ + int pixelsize = 3; + + int pitch = width * pixelsize; + if (upsidedown) + { + scr += ((height - 1) * width * pixelsize); + pitch *= -1; + } + + M_CreatePNG(file, scr, nullptr, SS_RGB, width, height, pitch, vid_gamma); +} + +//=========================================================================== +// +// Render the view to a savegame picture +// +// Currently a bit messy because the game side still needs to be able to +// handle Polymost. +// +//=========================================================================== +bool writingsavepic; +FileWriter* savefile; +int savewidth, saveheight; +void PM_WriteSavePic(FileWriter* file, int width, int height); +EXTERN_CVAR(Bool, testnewrenderer); + +void WriteSavePic(FileWriter* file, int width, int height) +{ + if (!testnewrenderer) + { + PM_WriteSavePic(file, width, height); + return; + } + int oldx = xdim; + int oldy = ydim; + auto oldwindowxy1 = windowxy1; + auto oldwindowxy2 = windowxy2; + + xdim = width; + ydim = height; + videoSetViewableArea(0, 0, width - 1, height - 1); + + writingsavepic = true; + savefile = file; + savewidth = width; + saveheight = height; + bool didit = gi->GenerateSavePic(); + writingsavepic = false; + xdim = oldx; + ydim = oldy; + videoSetViewableArea(oldwindowxy1.x, oldwindowxy1.y, oldwindowxy2.x, oldwindowxy2.y); +} + +void RenderToSavePic(FRenderViewpoint& vp, FileWriter* file, int width, int height) +{ + IntRect bounds; + bounds.left = 0; + bounds.top = 0; + bounds.width = width; + bounds.height = height; + auto& RenderState = *screen->RenderState(); + + // we must be sure the GPU finished reading from the buffer before we fill it with new data. + screen->WaitForCommands(false); + + // Switch to render buffers dimensioned for the savepic + screen->SetSaveBuffers(true); + screen->ImageTransitionScene(true); + + RenderState.SetVertexBuffer(screen->mVertexData); + screen->mVertexData->Reset(); + screen->mLights->Clear(); + screen->mViewpoints->Clear(); + + twodpsp.Clear(); + + RenderViewpoint(vp, &bounds, vp.FieldOfView.Degrees, 1.333f, 1.333f, true, false); + + + int numpixels = width * height; + uint8_t* scr = (uint8_t*)M_Malloc(numpixels * 3); + screen->CopyScreenToBuffer(width, height, scr); + + DoWriteSavePic(file, scr, width, height, screen->FlipSavePic()); + M_Free(scr); + + // Switch back the screen render buffers + screen->SetViewportRects(nullptr); + screen->SetSaveBuffers(false); +} + +//=========================================================================== +// +// Renders the main view +// +//=========================================================================== + +static void CheckTimer(FRenderState &state, uint64_t ShaderStartTime) +{ + // if firstFrame is not yet initialized, initialize it to current time + // if we're going to overflow a float (after ~4.6 hours, or 24 bits), re-init to regain precision + if ((state.firstFrame == 0) || (screen->FrameTime - state.firstFrame >= 1 << 24) || ShaderStartTime >= state.firstFrame) + state.firstFrame = screen->FrameTime; +} + + +void animatecamsprite(double s); + +void render_drawrooms(spritetype* playersprite, const vec3_t& position, int sectnum, binangle angle, fixedhoriz horizon, binangle rollang, double smoothratio) +{ + checkRotatedWalls(); + + if (gl_fogmode == 1) gl_fogmode = 2; // still needed? + + int16_t sect = sectnum; + updatesector(position.x, position.y, §); + if (sect >= 0) sectnum = sect; + if (sectnum < 0) return; + + iter_dlightf = iter_dlight = draw_dlight = draw_dlightf = 0; + checkBenchActive(); + + // reset statistics counters + ResetProfilingData(); + + // Get this before everything else + FRenderViewpoint r_viewpoint = SetupViewpoint(playersprite, position, sectnum, angle, horizon, rollang); + if (cl_capfps) r_viewpoint.TicFrac = 1.; + else r_viewpoint.TicFrac = smoothratio * (1/65536.); + + screen->mLights->Clear(); + screen->mViewpoints->Clear(); + screen->mVertexData->Reset(); + + if (writingsavepic) // hack alert! The save code should not go through render_drawrooms, but we can only clean up the game side when Polymost is gone for good. + { + RenderToSavePic(r_viewpoint, savefile, savewidth, saveheight); + return; + } + + // Shader start time does not need to be handled per level. Just use the one from the camera to render from. + auto RenderState = screen->RenderState(); + CheckTimer(*RenderState, 0/*ShaderStartTime*/); + + // prepare all camera textures that have been used in the last frame. + gi->UpdateCameras(r_viewpoint.TicFrac); + + RenderState->SetVertexBuffer(screen->mVertexData); + + // now render the main view + float fovratio; + float ratio = ActiveRatio(screen->GetWidth(), screen->GetHeight()); + if (ratio >= 1.33f) + { + fovratio = 1.33f; + } + else + { + fovratio = ratio; + } + + screen->ImageTransitionScene(true); // Only relevant for Vulkan. + + RenderViewpoint(r_viewpoint, nullptr, r_viewpoint.FieldOfView.Degrees, ratio, fovratio, true, true); + All.Unclock(); +} + +void render_camtex(spritetype* playersprite, const vec3_t& position, int sectnum, binangle angle, fixedhoriz horizon, binangle rollang, FGameTexture* camtex, IntRect& rect, double smoothratio) +{ + int16_t sect = sectnum; + updatesector(position.x, position.y, §); + if (sect >= 0) sectnum = sect; + if (sectnum < 0) return; + + screen->RenderState()->SetVertexBuffer(screen->mVertexData); + + // now render the main view + float ratio = camtex->GetDisplayWidth() / camtex->GetDisplayHeight(); + + FRenderViewpoint r_viewpoint = SetupViewpoint(playersprite, position, sectnum, angle, horizon, rollang); + if (cl_capfps) r_viewpoint.TicFrac = smoothratio; + + RenderViewpoint(r_viewpoint, &rect, r_viewpoint.FieldOfView.Degrees, ratio, ratio, false, false); + All.Unclock(); +} + +FSerializer& Serialize(FSerializer& arc, const char* key, PortalDesc& obj, PortalDesc* defval) +{ + if (arc.BeginObject(key)) + { + arc("type", obj.type) + ("dx", obj.dx) + ("dy", obj.dy) + ("dz", obj.dz) + ("targets", obj.targets) + .EndObject(); + } + return arc; +} diff --git a/source/glbackend/gl_models.cpp b/source/core/rendering/hw_models.cpp similarity index 97% rename from source/glbackend/gl_models.cpp rename to source/core/rendering/hw_models.cpp index 318ed7d3f..3472d6215 100644 --- a/source/glbackend/gl_models.cpp +++ b/source/core/rendering/hw_models.cpp @@ -6,7 +6,7 @@ // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or +// the Free Software Foundation, either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, @@ -33,7 +33,7 @@ #include "hwrenderer/data/buffers.h" #include "flatvertices.h" #include "hw_renderstate.h" -#include "gl_models.h" +#include "hw_models.h" //CVAR(Bool, gl_light_models, true, CVAR_ARCHIVE) diff --git a/source/glbackend/gl_models.h b/source/core/rendering/hw_models.h similarity index 97% rename from source/glbackend/gl_models.h rename to source/core/rendering/hw_models.h index c13fc7800..b533aa428 100644 --- a/source/glbackend/gl_models.h +++ b/source/core/rendering/hw_models.h @@ -6,7 +6,7 @@ // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or +// the Free Software Foundation, either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, diff --git a/source/glbackend/gl_palmanager.cpp b/source/core/rendering/hw_palmanager.cpp similarity index 87% rename from source/glbackend/gl_palmanager.cpp rename to source/core/rendering/hw_palmanager.cpp index aee5dade4..0298aa657 100644 --- a/source/glbackend/gl_palmanager.cpp +++ b/source/core/rendering/hw_palmanager.cpp @@ -35,7 +35,7 @@ #include #include "m_crc32.h" -#include "glbackend.h" +#include "hw_palmanager.h" #include "resourcefile.h" #include "imagehelpers.h" @@ -44,6 +44,8 @@ #include "build.h" #include "v_video.h" +static PaletteManager* palmanager; + //=========================================================================== // // This class manages the hardware data for the indexed render mode. @@ -138,4 +140,24 @@ IHardwareTexture* PaletteManager::GetLookup(int index) return nullptr; } +//=========================================================================== +// +// +// +//=========================================================================== +IHardwareTexture *setpalettelayer(int layer, int translation) +{ + if (!palmanager) palmanager = new PaletteManager; + if (layer == 1) + return palmanager->GetPalette(GetTranslationType(translation) - Translation_Remap); + else if (layer == 2) + return palmanager->GetLookup(GetTranslationIndex(translation)); + else return nullptr; +} + +void ClearPalManager() +{ + if (palmanager) delete palmanager; + palmanager = nullptr; +} \ No newline at end of file diff --git a/source/core/rendering/hw_palmanager.h b/source/core/rendering/hw_palmanager.h new file mode 100644 index 000000000..e308f79ab --- /dev/null +++ b/source/core/rendering/hw_palmanager.h @@ -0,0 +1,24 @@ +#pragma once + +#include "gl_hwtexture.h" + +struct palette_t; + +class PaletteManager +{ + IHardwareTexture* palettetextures[256] = {}; + IHardwareTexture* lookuptextures[256] = {}; + + unsigned FindPalswap(const uint8_t* paldata, palette_t& fadecolor); + +public: + ~PaletteManager(); + void DeleteAll(); + IHardwareTexture *GetPalette(int index); + IHardwareTexture* GetLookup(int index); +}; + + +IHardwareTexture* setpalettelayer(int layer, int translation); +void ClearPalManager(); + diff --git a/source/core/rendering/hw_sections.cpp b/source/core/rendering/hw_sections.cpp new file mode 100644 index 000000000..56a4030cc --- /dev/null +++ b/source/core/rendering/hw_sections.cpp @@ -0,0 +1,221 @@ +/* +** hw_sectiona.cpp +** For decoupling the renderer from internal Build structures +** +**--------------------------------------------------------------------------- +** Copyright 2021 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +** The sole reason for existence of this file is that Build's sector setup +** does not allow for easy splitting of sectors, either for having disjoint parts +** or requiring partial rendering. So we need to add a superstructure +** where we can shuffle around some content without disturbing the original +** order... +** +*/ + + +#include "hw_sections.h" + + +SectionLine sectionLines[MAXWALLS + (MAXWALLS >> 2)]; +Section sections[MAXSECTORS + (MAXSECTORS >> 2)]; +TArray sectionspersector[MAXSECTORS]; // reverse map, mainly for the automap +int numsections; +int numsectionlines; + +void hw_SplitSector(int sector, int startpos, int endpos); + +TArray splits; + + +void hw_BuildSections() +{ + // Initial setup just creates a 1:1 mapping of walls to section lines and sectors to sections. + numsectionlines = numwalls; + numsections = numsectors; + for (int i = 0; i < numwalls; i++) + { + sectionLines[i].startpoint = sectionLines[i].wall = i; + sectionLines[i].endpoint = wall[i].point2; + sectionLines[i].partner = wall[i].nextwall; + sectionLines[i].section = wall[i].sector; + sectionLines[i].partnersection = wall[i].nextsector; + sectionLines[i].point2index = wall[i].point2 - sector[wall[i].sector].wallptr; + } + + for (int i = 0; i < numsectors; i++) + { + sections[i].sector = i; + sections[i].lines.Resize(sector[i].wallnum); + for (int j = 0; j < sector[i].wallnum; j++) sections[i].lines[j] = sector[i].wallptr + j; + sectionspersector[i].Resize(1); + sectionspersector[i][0] = i; + } + + for (unsigned i = 0; i < splits.Size(); i += 3) + hw_SplitSector(splits[i], splits[i + 1], splits[i + 2]); +} + + +static void SplitSection(int section, int start, int end) +{ + // note: to do this, the sector's lines must be well ordered and there must only be one outline and no holes. + // This also can only apply a single split to a given sector. + int firstsection = numsections++; + int secondsection = numsections++; + + auto& sect = sections[section]; + Section* sect1 = §ions[firstsection]; + Section* sect2 = §ions[secondsection]; + sect1->sector = sect.sector; + sect2->sector = sect.sector; + sect1->lines.Clear(); + sect2->lines.Clear(); + for (int aline : sect.lines) + { + int line = sectionLines[aline].wall; + if (line < start || line >= end) + { + sect1->lines.Push(aline); + } + if (line == start) + { + sect1->lines.Push(-1); + sect2->lines.Push(-1); + } + if (line >= start && line < end) + { + sect2->lines.Push(aline); + } + } + + int firstnewline = numsectionlines; + int thisline = numsectionlines; + int splitline1, splitline2; + //numsectionlines += sect1->lines.Size() + 1; + for (unsigned i = 0; i < sect1->lines.Size(); i++)// auto& sline : sect1->lines) + { + int sline = sect1->lines[i]; + sect1->lines[i] = thisline; + if (sline != -1) + { + SectionLine& newline = sectionLines[thisline]; + newline = sectionLines[sline]; + newline.section = int16_t(sect1 - sections); + if (i != sect1->lines.Size() - 1) newline.point2index = thisline + 1 - firstnewline; + else newline.point2index = 0; + assert(newline.point2index >= 0); + + // relink the partner + auto& partnerline = sectionLines[newline.partner]; + partnerline.partner = thisline; + partnerline.partnersection = newline.section; + thisline++; + } + else + { + splitline1 = thisline++; + sectionLines[splitline1].wall = -1; + sectionLines[splitline1].section = int16_t(sect1 - sections); + sectionLines[splitline1].partnersection = int16_t(sect2 - sections); + sectionLines[splitline1].startpoint = start; + sectionLines[splitline1].endpoint = end; + sectionLines[splitline1].point2index = splitline1 + 1 - firstnewline; + } + } + + firstnewline = thisline; + for (unsigned i = 0; i < sect2->lines.Size(); i++)// auto& sline : sect1->lines) + { + int sline = sect2->lines[i]; + sect2->lines[i] = thisline; + if (sline != -1) + { + SectionLine& newline = sectionLines[thisline]; + newline = sectionLines[sline]; + newline.section = int16_t(sect2 - sections); + if (i != sect2->lines.Size() - 1) newline.point2index = thisline + 1 - firstnewline; + else newline.point2index = 0; + assert(newline.point2index >= 0); + + // relink the partner + auto& partnerline = sectionLines[newline.partner]; + partnerline.partner = thisline; + partnerline.partnersection = newline.section; + thisline++; + } + else + { + splitline2 = thisline++; + sectionLines[splitline2].wall = -1; + sectionLines[splitline2].section = int16_t(sect2 - sections); + sectionLines[splitline2].partnersection = int16_t(sect1 - sections); + sectionLines[splitline2].startpoint = end; + sectionLines[splitline2].endpoint = start; + sectionLines[splitline2].point2index = splitline2 + 1 - firstnewline; + } + } + sectionLines[splitline1].partner = splitline2; + sectionLines[splitline2].partner = splitline1; + + sectionspersector[sect.sector].Resize(2); + sectionspersector[sect.sector][0] = int16_t(sect1 - sections); + sectionspersector[sect.sector][1] = int16_t(sect2 - sections); +} + +void hw_SplitSector(int sectnum, int start, int end) +{ + int wallstart = sector[sectnum].wallptr; + int wallend = wallstart + sector[sectnum].wallnum; + if (start < wallstart || start >= wallend || end < wallstart || end >= wallend || end < start) return; + + for (unsigned i = 0; i < sectionspersector[sectnum].Size(); i++) + { + int sect = sectionspersector[sectnum][i]; + bool foundstart = false, foundend = false; + for (int aline : sections[sect].lines) + { + int line = sectionLines[aline].wall; + if (line == start) foundstart = true; + if (line == end) foundend = true; + } + if (foundstart && foundend) + { + sectionspersector->Delete(i); + SplitSection(sect, start, end); + return; + } + } +} + +void hw_SetSplitSector(int sectnum, int start, int end) +{ + splits.Push(sectnum); + splits.Push(start); + splits.Push(end); +} diff --git a/source/core/rendering/hw_sections.h b/source/core/rendering/hw_sections.h new file mode 100644 index 000000000..645d0e6e6 --- /dev/null +++ b/source/core/rendering/hw_sections.h @@ -0,0 +1,32 @@ +#pragma once + +#include "build.h" + +struct SectionLine +{ + int16_t section; + int16_t partnersection; + int16_t startpoint; + int16_t endpoint; + int16_t wall; + int16_t partner; + int16_t point2index; +}; + +struct Section +{ + int sector; + // this is the whole point of sections - instead of just having a start index and count, we have an explicit list of lines that's a lot easier to change when needed. + TArray lines; +}; + +// giving 25% more may be a bit high as normally this should be small numbers only. +extern SectionLine sectionLines[MAXWALLS + (MAXWALLS >> 2)]; +extern Section sections[MAXSECTORS + (MAXSECTORS >> 2)]; +extern TArray sectionspersector[MAXSECTORS]; // reverse map, mainly for the automap +extern int numsections; +extern int numsectionlines; + + +void hw_BuildSections(); +void hw_SetSplitSector(int sector, int startpos, int endpos); diff --git a/source/core/rendering/hw_voxels.cpp b/source/core/rendering/hw_voxels.cpp new file mode 100644 index 000000000..b48a3ef6b --- /dev/null +++ b/source/core/rendering/hw_voxels.cpp @@ -0,0 +1,114 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2021 Christoph Oelckers +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//-------------------------------------------------------------------------- +// +/* +** hw_voxels.cpp +** +** voxel handling. +** +**/ + +#include "build.h" +#include "voxels.h" +#include "hw_voxels.h" +#include "gamecontrol.h" + +int16_t tiletovox[MAXTILES]; +static int voxlumps[MAXVOXELS]; +float voxscale[MAXVOXELS]; +voxmodel_t* voxmodels[MAXVOXELS]; +FixedBitArray voxrotate; + + +void voxInit() +{ + for (auto& v : tiletovox) v = -1; + for (auto& v : voxscale) v = 1.f; + voxrotate.Zero(); +} + +void voxClear() +{ + for (auto& vox : voxmodels) + { + if (vox) delete vox; + vox = nullptr; + } +} + +int voxDefine(int voxindex, const char* filename) +{ + if ((unsigned)voxindex >= MAXVOXELS) + return -1; + + int i = fileSystem.FindFile(filename); + voxlumps[voxindex] = i; + return i < 0 ? -1 : 0; +} + +static voxmodel_t* voxload(int lumpnum) +{ + FVoxel* voxel = R_LoadKVX(lumpnum); + if (voxel != nullptr) + { + voxmodel_t* vm = new voxmodel_t; + *vm = {}; + auto pivot = voxel->Mips[0].Pivot; + vm->mdnum = 1; //VOXel model id + vm->scale = vm->bscale = 1.f; + vm->piv.x = float(pivot.X); + vm->piv.y = float(pivot.Y); + vm->piv.z = float(pivot.Z); + vm->siz.x = voxel->Mips[0].SizeX; + vm->siz.y = voxel->Mips[0].SizeY; + vm->siz.z = voxel->Mips[0].SizeZ; + vm->is8bit = true; + voxel->Mips[0].Pivot.Zero(); // Needs to be taken out of the voxel data because it gets baked into the vertex buffer which we cannot use here. + vm->model = new FVoxelModel(voxel, true); + return vm; + } + return nullptr; +} + +void LoadVoxelModels() +{ + for (int i = 0; i < MAXVOXELS; i++) + { + int lumpnum = voxlumps[i]; + if (lumpnum > 0) + { + voxmodels[i] = voxload(lumpnum); + if (voxmodels[i]) + voxmodels[i]->scale = voxscale[i]; + else + Printf("Unable to load voxel from %s\n", fileSystem.GetFileFullPath(lumpnum).GetChars()); + } + else + { + auto index = fileSystem.FindResource(i, "KVX"); + if (index >= 0) + { + voxmodels[i] = voxload(index); + } + } + } +} + diff --git a/source/core/rendering/hw_voxels.h b/source/core/rendering/hw_voxels.h new file mode 100644 index 000000000..1633d1808 --- /dev/null +++ b/source/core/rendering/hw_voxels.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include "mdsprite.h" + +// We still need the relation to mdmodel_t as long as the model code hasn't been redone. +struct voxmodel_t : public mdmodel_t +{ + FVoxelModel* model = nullptr; + vec3_t siz; + vec3f_t piv; + int32_t is8bit; +}; + + + +extern int16_t tiletovox[]; +extern float voxscale[]; +extern voxmodel_t* voxmodels[MAXVOXELS]; +extern FixedBitArray voxrotate; + +void voxInit(); +void voxClear(); +int voxDefine(int voxindex, const char* filename); diff --git a/source/core/rendering/render.h b/source/core/rendering/render.h new file mode 100644 index 000000000..256b65747 --- /dev/null +++ b/source/core/rendering/render.h @@ -0,0 +1,78 @@ +#pragma once +#include "build.h" + +class FSerializer; +struct IntRect; + +void render_drawrooms(spritetype* playersprite, const vec3_t& position, int sectnum, binangle angle, fixedhoriz horizon, binangle rollang, double smoothratio); +void render_camtex(spritetype* playersprite, const vec3_t& position, int sectnum, binangle angle, fixedhoriz horizon, binangle rollang, FGameTexture* camtex, IntRect& rect, double smoothratio); + +struct PortalDesc +{ + int type; + int dx, dy, dz; + TArray targets; +}; + +FSerializer& Serialize(FSerializer& arc, const char* key, PortalDesc& obj, PortalDesc* defval); + + +extern TArray allPortals; + +inline void portalClear() +{ + allPortals.Clear(); +} + +inline int portalAdd(int type, int target, int dx = 0, int dy = 0, int dz = 0) +{ + auto& pt = allPortals[allPortals.Reserve(1)]; + pt.type = type; + if (target >= 0) pt.targets.Push(target); + pt.dx = dx; + pt.dy = dy; + pt.dz = dz; + return allPortals.Size() - 1; +} + +// merges portals in adjoining sectors. +inline void mergePortals() +{ + //Printf("Have %d portals\n", allPortals.Size()); + bool didsomething = true; + while (didsomething) + { + didsomething = false; + for (unsigned i = 0; i < allPortals.Size(); i++) + { + auto& pt1 = allPortals[i]; + if (pt1.type == PORTAL_SECTOR_CEILING || pt1.type == PORTAL_SECTOR_FLOOR) + { + for (unsigned j = i + 1; j < allPortals.Size(); j++) + { + auto& pt2 = allPortals[j]; + if (pt1.type != pt2.type || pt1.dx != pt2.dx || pt1.dy != pt2.dy || pt1.dz != pt2.dz) continue; + for (unsigned s = 0; s < pt1.targets.Size() && pt2.targets.Size(); s++) + { + for (unsigned t = 0; t < pt2.targets.Size(); t++) + { + if (findwallbetweensectors(pt1.targets[s], pt2.targets[t]) >= 0) + { + pt1.targets.Append(pt2.targets); + pt2.targets.Reset(); + pt2.type = -1; + for (int n = 0; n < numsectors; n++) + { + //Printf("Merged %d and %d\n", i, j); + if (sector[n].portalnum == j) sector[n].portalnum = i; + } + didsomething = true; + break; + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/source/core/rendering/scene/hw_bunchdrawer.cpp b/source/core/rendering/scene/hw_bunchdrawer.cpp new file mode 100644 index 000000000..50ef2995a --- /dev/null +++ b/source/core/rendering/scene/hw_bunchdrawer.cpp @@ -0,0 +1,633 @@ +/* +** hw_bunchdrawer.cpp +** +**--------------------------------------------------------------------------- +** Copyright 2008-2021 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "hw_drawinfo.h" +#include "hw_bunchdrawer.h" +#include "hw_clipper.h" +#include "hw_clock.h" +#include "hw_drawstructs.h" +#include "automap.h" +#include "gamefuncs.h" +#include "hw_portal.h" +#include "gamestruct.h" +#include "hw_voxels.h" +#include "mapinfo.h" +#include "gamecontrol.h" +#include "hw_sections.h" + +extern TArray blockingpairs[MAXWALLS]; + +//========================================================================== +// +// +// +//========================================================================== + +void BunchDrawer::Init(HWDrawInfo *_di, Clipper* c, vec2_t& view, binangle a1, binangle a2) +{ + ang1 = a1; + ang2 = a2; + di = _di; + clipper = c; + viewx = view.x * (1/ 16.f); + viewy = view.y * -(1/ 16.f); + iview = view; + StartScene(); + clipper->SetViewpoint(view); + + gcosang = bamang(di->Viewpoint.RotAngle).fcos(); + gsinang = bamang(di->Viewpoint.RotAngle).fsin(); + + for (int i = 0; i < numwalls; i++) + { + // Precalculate the clip angles to avoid doing this repeatedly during level traversal. + // Reverse the orientation so that startangle and endangle are properly ordered. + wall[i].clipangle = clipper->PointToAngle(wall[i].pos); + } + memset(sectionstartang, -1, sizeof(sectionstartang)); + memset(sectionendang, -1, sizeof(sectionendang)); +} + +//========================================================================== +// +// +// +//========================================================================== + +void BunchDrawer::StartScene() +{ + LastBunch = 0; + StartTime = I_msTime(); + Bunches.Clear(); + CompareData.Clear(); + gotsector.Zero(); + gotsection2.Zero(); + gotwall.Zero(); + blockwall.Zero(); +} + +//========================================================================== +// +// +// +//========================================================================== + +bool BunchDrawer::StartBunch(int sectnum, int linenum, binangle startan, binangle endan, bool portal) +{ + FBunch* bunch = &Bunches[LastBunch = Bunches.Reserve(1)]; + + bunch->sectnum = sectnum; + bunch->startline = bunch->endline = linenum; + bunch->startangle = (startan.asbam() - ang1.asbam()) > ANGLE_180? ang1 :startan; + bunch->endangle = (endan.asbam() - ang2.asbam()) < ANGLE_180 ? ang2 : endan; + bunch->portal = portal; + return bunch->endangle != ang2; +} + +//========================================================================== +// +// +// +//========================================================================== + +bool BunchDrawer::AddLineToBunch(int line, binangle newan) +{ + Bunches[LastBunch].endline++; + Bunches[LastBunch].endangle = (newan.asbam() - ang2.asbam()) < ANGLE_180 ? ang2 : newan; + return Bunches[LastBunch].endangle != ang2; +} + +//========================================================================== +// +// +// +//========================================================================== + +void BunchDrawer::DeleteBunch(int index) +{ + Bunches[index] = Bunches.Last(); + Bunches.Pop(); +} + +bool BunchDrawer::CheckClip(walltype* wal) +{ + auto pt2 = &wall[wal->point2]; + sectortype* backsector = §or[wal->nextsector]; + sectortype* frontsector = §or[wall[wal->nextwall].nextsector]; + + // if one plane is sky on both sides, the line must not clip. + if (frontsector->ceilingstat & backsector->ceilingstat & CSTAT_SECTOR_SKY) return false; + if (frontsector->floorstat & backsector->floorstat & CSTAT_SECTOR_SKY) return false; + + float bs_floorheight1; + float bs_floorheight2; + float bs_ceilingheight1; + float bs_ceilingheight2; + float fs_floorheight1; + float fs_floorheight2; + float fs_ceilingheight1; + float fs_ceilingheight2; + + // Mirrors and horizons always block the view + //if (linedef->special==Line_Mirror || linedef->special==Line_Horizon) return true; + + PlanesAtPoint(frontsector, wal->x, wal->y, &fs_ceilingheight1, &fs_floorheight1); + PlanesAtPoint(frontsector, pt2->x, pt2->y, &fs_ceilingheight2, &fs_floorheight2); + + PlanesAtPoint(backsector, wal->x, wal->y, &bs_ceilingheight1, &bs_floorheight1); + PlanesAtPoint(backsector, pt2->x, pt2->y, &bs_ceilingheight2, &bs_floorheight2); + + // now check for closed sectors! No idea if we really need the sky checks. We'll see. + if (bs_ceilingheight1 <= fs_floorheight1 && bs_ceilingheight2 <= fs_floorheight2) + { + // backsector's ceiling is below frontsector's floor. + return true; + } + + if (fs_ceilingheight1 <= bs_floorheight1 && fs_ceilingheight2 <= bs_floorheight2) + { + // backsector's floor is above frontsector's ceiling + return true; + } + + if (bs_ceilingheight1 <= bs_floorheight1 && bs_ceilingheight2 <= bs_floorheight2) + { + // backsector is closed + return true; + } + + return false; +} + +//========================================================================== +// +// ClipLine +// Clips the given segment +// +//========================================================================== + +int BunchDrawer::ClipLine(int aline, bool portal) +{ + auto cline = §ionLines[aline]; + int section = cline->section; + int line = cline->wall; + + auto startAngleBam = wall[cline->startpoint].clipangle; + auto endAngleBam = wall[cline->endpoint].clipangle; + + // Back side, i.e. backface culling - read: endAngle <= startAngle! + if (startAngleBam.asbam() - endAngleBam.asbam() < ANGLE_180) + { + return CL_Skip; + } + if (line >= 0 && blockwall[line]) return CL_Draw; + + // convert to clipper coordinates and clamp to valid range. + int startAngle = startAngleBam.asbam() - ang1.asbam(); + int endAngle = endAngleBam.asbam() - ang1.asbam(); + if (startAngle < 0) startAngle = 0; + if (endAngle < 0) endAngle = INT_MAX; + + // since these values are derived from previous calls of this function they cannot be out of range. + int sectStartAngle = sectionstartang[section]; + auto sectEndAngle = sectionendang[section]; + + // check against the maximum possible viewing range of the sector. + // Todo: check if this is sufficient or if we really have to do a more costly check against the single visible segments. + if (sectStartAngle != -1) + { + if (sectStartAngle > endAngle || sectEndAngle < startAngle) + return CL_Skip; // completely outside the valid range for this sector. + if (sectStartAngle > startAngle) startAngle = sectStartAngle; + if (sectEndAngle < endAngle) endAngle = sectEndAngle; + if (endAngle <= startAngle) return CL_Skip; // can this even happen? + } + + if (!portal && !clipper->IsRangeVisible(startAngle, endAngle)) + { + return CL_Skip; + } + + auto wal = &wall[line]; + if (cline->partner == -1 || (wal->cstat & CSTAT_WALL_1WAY) || CheckClip(wal)) + { + // one-sided + if (!portal) clipper->AddClipRange(startAngle, endAngle); + return CL_Draw; + } + else + { + if (portal) clipper->RemoveClipRange(startAngle, endAngle); + + // set potentially visible viewing range for this line's back sector. + int nsection = cline->partnersection; + if (sectionstartang[nsection] == -1) + { + sectionstartang[nsection] = startAngle; + sectionendang[nsection] = endAngle; + } + else + { + if (startAngle < sectionstartang[nsection]) sectionstartang[nsection] = startAngle; + if (endAngle > sectionendang[nsection]) sectionendang[nsection] = endAngle; + } + + return CL_Draw | CL_Pass; + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void BunchDrawer::ProcessBunch(int bnch) +{ + FBunch* bunch = &Bunches[bnch]; + + ClipWall.Clock(); + for (int i = bunch->startline; i <= bunch->endline; i++) + { + int clipped = ClipLine(i, bunch->portal); + + if (clipped & CL_Draw) + { + int ww = sectionLines[i].wall; + if (ww != -1) + { + for (auto p : blockingpairs[ww]) blockwall.Set(sectionLines[p].wall); + show2dwall.Set(ww); + + if (!gotwall[i]) + { + gotwall.Set(i); + ClipWall.Unclock(); + Bsp.Unclock(); + SetupWall.Clock(); + + HWWall hwwall; + hwwall.Process(di, &wall[ww], §or[bunch->sectnum], wall[ww].nextsector < 0 ? nullptr : §or[wall[ww].nextsector]); + rendered_lines++; + + SetupWall.Unclock(); + Bsp.Clock(); + ClipWall.Clock(); + } + } + } + + if (clipped & CL_Pass) + { + ClipWall.Unclock(); + ProcessSection(sectionLines[i].partnersection, false); + ClipWall.Clock(); + } + } + ClipWall.Unclock(); +} + +//========================================================================== +// +// +// +//========================================================================== + +int BunchDrawer::WallInFront(int line1, int line2) +{ + int wall1s = sectionLines[line1].startpoint; + int wall1e = sectionLines[line1].endpoint; + int wall2s = sectionLines[line2].startpoint; + int wall2e = sectionLines[line2].endpoint; + + double x1s = WallStartX(wall1s); + double y1s = WallStartY(wall1s); + double x1e = WallStartX(wall1e); + double y1e = WallStartY(wall1e); + double x2s = WallStartX(wall2s); + double y2s = WallStartY(wall2s); + double x2e = WallStartX(wall2e); + double y2e = WallStartY(wall2e); + + double dx = x1e - x1s; + double dy = y1e - y1s; + + double t1 = PointOnLineSide(x2s, y2s, x1s, y1s, dx, dy); + double t2 = PointOnLineSide(x2e, y2e, x1s, y1s, dx, dy); + if (t1 == 0) + { + if (t2 == 0) return(-1); + t1 = t2; + } + if (t2 == 0) t2 = t1; + + if ((t1 * t2) >= 0) + { + t2 = PointOnLineSide(viewx, viewy, x1s, y1s, dx, dy); + return((t2 * t1) <= 0); + } + + dx = x2e - x2s; + dy = y2e - y2s; + t1 = PointOnLineSide(x1s, y1s, x2s, y2s, dx, dy); + t2 = PointOnLineSide(x1e, y1e, x2s, y2s, dx, dy); + if (t1 == 0) + { + if (t2 == 0) return(-1); + t1 = t2; + } + if (t2 == 0) t2 = t1; + if ((t1 * t2) >= 0) + { + t2 = PointOnLineSide(viewx, viewy, x2s, y2s, dx, dy); + return((t2 * t1) > 0); + } + return(-2); +} + +//========================================================================== +// +// This is a bit more complicated than it looks because angles can wrap +// around so we can only compare angle differences. +// +// Rules: +// 1. Any bunch can span at most 180°. +// 2. 2 bunches can never overlap at both ends +// 3. if there is an overlap one of the 2 starting points must be in the +// overlapping area. +// +//========================================================================== + +int BunchDrawer::BunchInFront(FBunch* b1, FBunch* b2) +{ + binangle anglecheck, endang; + + if (b2->startangle.asbam() - b1->startangle.asbam() < b1->endangle.asbam() - b1->startangle.asbam()) + { + // we have an overlap at b2->startangle + anglecheck = b2->startangle - b1->startangle; + + // Find the wall in b1 that overlaps b2->startangle + for (int i = b1->startline; i <= b1->endline; i++) + { + endang = wall[wall[i].point2].clipangle - b1->startangle; + if (endang.asbam() > anglecheck.asbam()) + { + // found a line + int ret = WallInFront(b2->startline, i); + return ret; + } + } + } + else if (b1->startangle.asbam() - b2->startangle.asbam() < b2->endangle.asbam() - b2->startangle.asbam()) + { + // we have an overlap at b1->startangle + anglecheck = b1->startangle - b2->startangle; + + // Find the wall in b2 that overlaps b1->startangle + for (int i = b2->startline; i <= b2->endline; i++) + { + endang = wall[wall[i].point2].clipangle - b2->startangle; + if (endang.asbam() > anglecheck.asbam()) + { + // found a line + int ret = WallInFront(i, b1->startline); + return ret; + } + } + } + // we have no overlap + return -1; +} + +//========================================================================== +// +// +// +//========================================================================== + +int BunchDrawer::FindClosestBunch() +{ + int closest = 0; //Almost works, but not quite :( + + CompareData.Clear(); + for (unsigned i = 1; i < Bunches.Size(); i++) + { + switch (BunchInFront(&Bunches[i], &Bunches[closest])) + { + case 0: // i is in front + closest = i; + continue; + + case 1: // i is behind + continue; + + default: // can't determine + CompareData.Push(i); // mark for later comparison + continue; + } + } + + // we need to do a second pass to see how the marked bunches relate to the currently closest one. + for (unsigned i = 0; i < CompareData.Size(); i++) + { + switch (BunchInFront(&Bunches[CompareData[i]], &Bunches[closest])) + { + case 0: // is in front + closest = CompareData[i]; + CompareData[i] = CompareData.Last(); + CompareData.Pop(); + i = -1; // we need to recheck everything that's still marked. -1 because this will get incremented before being used. + continue; + + case 1: // is behind + CompareData[i] = CompareData.Last(); + CompareData.Pop(); + i--; + continue; + + default: + continue; + + } + } + //Printf("picked bunch starting at %d\n", Bunches[closest].startline); + return closest; +} + +//========================================================================== +// +// +// +//========================================================================== + +void BunchDrawer::ProcessSection(int sectionnum, bool portal) +{ + if (gotsection2[sectionnum]) return; + gotsection2.Set(sectionnum); + + bool inbunch; + binangle startangle; + + SetupSprite.Clock(); + + int z; + int sectnum = sections[sectionnum].sector; + if (!gotsector[sectnum]) + { + gotsector.Set(sectnum); + SectIterator it(sectnum); + while ((z = it.NextIndex()) >= 0) + { + auto const spr = (uspriteptr_t)&sprite[z]; + + if ((spr->cstat & CSTAT_SPRITE_INVISIBLE) || spr->xrepeat == 0 || spr->yrepeat == 0) // skip invisible sprites + continue; + + int sx = spr->x - iview.x, sy = spr->y - int(iview.y); + + // this checks if the sprite is it behind the camera, which will not work if the pitch is high enough to necessitate a FOV of more than 180°. + //if ((spr->cstat & CSTAT_SPRITE_ALIGNMENT_MASK) || (hw_models && tile2model[spr->picnum].modelid >= 0) || ((sx * gcosang) + (sy * gsinang) > 0)) + { + if ((spr->cstat & (CSTAT_SPRITE_ONE_SIDED | CSTAT_SPRITE_ALIGNMENT_MASK)) != (CSTAT_SPRITE_ONE_SIDED | CSTAT_SPRITE_ALIGNMENT_WALL) || + (r_voxels && tiletovox[spr->picnum] >= 0 && voxmodels[tiletovox[spr->picnum]]) || + (r_voxels && gi->Voxelize(spr->picnum) > -1) || + DMulScale(bcos(spr->ang), -sx, bsin(spr->ang), -sy, 6) > 0) + if (renderAddTsprite(di->tsprite, di->spritesortcnt, z, sectnum)) + break; + } + } + SetupSprite.Unclock(); + } + + if (automapping) + show2dsector.Set(sectnum); + + SetupFlat.Clock(); + HWFlat flat; + flat.ProcessSector(di, §or[sectnum], sectionnum); + SetupFlat.Unclock(); + + //Todo: process subsectors + inbunch = false; + auto section = §ions[sectionnum]; + for (unsigned i = 0; i < section->lines.Size(); i++) + { + auto thisline = §ionLines[section->lines[i]]; + +#ifdef _DEBUG + // For displaying positions in debugger + //DVector2 start = { WallStartX(thiswall), WallStartY(thiswall) }; + //DVector2 end = { WallStartX(thiswall->point2), WallStartY(thiswall->point2) }; +#endif + binangle walang1 = wall[thisline->startpoint].clipangle; + binangle walang2 = wall[thisline->endpoint].clipangle; + + // outside the visible area or seen from the backside. + if ((walang1.asbam() - ang1.asbam() > ANGLE_180 && walang2.asbam() - ang1.asbam() > ANGLE_180) || + (walang1.asbam() - ang2.asbam() < ANGLE_180 && walang2.asbam() - ang2.asbam() < ANGLE_180) || + (walang1.asbam() - walang2.asbam() < ANGLE_180)) + { + inbunch = false; + } + else if (!inbunch) + { + startangle = walang1; + //Printf("Starting bunch:\n\tWall %d\n", sect->wallptr + i); + inbunch = StartBunch(sectnum, section->lines[i], walang1, walang2, portal); + } + else + { + //Printf("\tWall %d\n", sect->wallptr + i); + inbunch = AddLineToBunch(section->lines[i], walang2); + } + if (thisline->endpoint != section->lines[i] + 1) inbunch = false; + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void BunchDrawer::RenderScene(const int* viewsectors, unsigned sectcount, bool portal) +{ + //Printf("----------------------------------------- \nstart at sector %d\n", viewsectors[0]); + auto process = [&]() + { + clipper->Clear(ang1); + + for (unsigned i = 0; i < sectcount; i++) + { + for (auto j : sectionspersector[viewsectors[i]]) + { + sectionstartang[j] = 0; + sectionendang[j] = int(ang2.asbam() - ang1.asbam()); + } + } + for (unsigned i = 0; i < sectcount; i++) + { + for (auto j : sectionspersector[viewsectors[i]]) + { + ProcessSection(j, portal); + } + } + while (Bunches.Size() > 0) + { + int closest = FindClosestBunch(); + ProcessBunch(closest); + DeleteBunch(closest); + } + }; + + Bsp.Clock(); + if (ang1.asbam() != 0 || ang2.asbam() != 0) + { + process(); + } + else + { + // with a 360° field of view we need to split the scene into two halves. + // The BunchInFront check can fail with angles that may wrap around. + auto rotang = di->Viewpoint.RotAngle; + ang1 = bamang(rotang - ANGLE_90); + ang2 = bamang(rotang + ANGLE_90 - 1); + process(); + gotsection2.Zero(); + ang1 = bamang(rotang + ANGLE_90); + ang2 = bamang(rotang - ANGLE_90 - 1); + process(); + } + Bsp.Unclock(); +} diff --git a/source/core/rendering/scene/hw_bunchdrawer.h b/source/core/rendering/scene/hw_bunchdrawer.h new file mode 100644 index 000000000..685ca25bc --- /dev/null +++ b/source/core/rendering/scene/hw_bunchdrawer.h @@ -0,0 +1,63 @@ +#pragma once + +#include "tarray.h" +#include "basics.h" + +struct HWDrawInfo; +class Clipper; + +struct FBunch +{ + int sectnum; + int startline; + int endline; + bool portal; + binangle startangle; + binangle endangle; +}; + +class BunchDrawer +{ + HWDrawInfo *di; + Clipper *clipper; + int LastBunch; + int StartTime; + TArray Bunches; + TArray CompareData; + double viewx, viewy; + vec2_t iview; + float gcosang, gsinang; + FixedBitArray gotsector; + FixedBitArray gotsection2; + FixedBitArray gotwall; + FixedBitArray blockwall; + binangle ang1, ang2; + + int sectionstartang[MAXSECTORS*5/4], sectionendang[MAXSECTORS*5/4]; + +private: + + enum + { + CL_Skip = 0, + CL_Draw = 1, + CL_Pass = 2, + }; + + void StartScene(); + bool StartBunch(int sectnum, int linenum, binangle startan, binangle endan, bool portal); + bool AddLineToBunch(int line, binangle newan); + void DeleteBunch(int index); + bool CheckClip(walltype* wal); + int ClipLine(int line, bool portal); + void ProcessBunch(int bnch); + int WallInFront(int wall1, int wall2); + int BunchInFront(FBunch* b1, FBunch* b2); + int FindClosestBunch(); + void ProcessSection(int sectnum, bool portal); + +public: + void Init(HWDrawInfo* _di, Clipper* c, vec2_t& view, binangle a1, binangle a2); + void RenderScene(const int* viewsectors, unsigned sectcount, bool portal); + const FixedBitArray& GotSector() const { return gotsector; } +}; diff --git a/source/core/rendering/scene/hw_clipper.cpp b/source/core/rendering/scene/hw_clipper.cpp new file mode 100644 index 000000000..0a7eafd86 --- /dev/null +++ b/source/core/rendering/scene/hw_clipper.cpp @@ -0,0 +1,294 @@ +/* +* +** gl_clipper.cpp +** +** Handles visibility checks. +** Loosely based on the JDoom clipper. +** +**--------------------------------------------------------------------------- +** Copyright 2003 Tim Stump +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "hw_clipper.h" +#include "basics.h" +#include "build.h" +#include "printf.h" + + +//----------------------------------------------------------------------------- +// +// RemoveRange +// +//----------------------------------------------------------------------------- + +void Clipper::RemoveRange(ClipNode * range) +{ + if (range == cliphead) + { + cliphead = cliphead->next; + } + else + { + if (range->prev) range->prev->next = range->next; + if (range->next) range->next->prev = range->prev; + } + + Free(range); +} + +//----------------------------------------------------------------------------- +// +// Clear +// +//----------------------------------------------------------------------------- + +void Clipper::Clear(binangle rangestart) +{ + ClipNode *node = cliphead; + ClipNode *temp; + + while (node != nullptr) + { + temp = node; + node = node->next; + Free(temp); + } + + cliphead = nullptr; + + if (visibleStart.asbam() != 0 || visibleEnd.asbam() != 0) + { + int vstart = int(visibleStart.asbam() - rangestart.asbam()); + if (vstart > 1) AddClipRange(0, vstart - 1); + + int vend = int(visibleEnd.asbam() - rangestart.asbam()); + if (vend > 0 && vend < INT_MAX - 1) AddClipRange(vend + 1, INT_MAX); + } + + +} + +//----------------------------------------------------------------------------- +// +// IsRangeVisible +// +//----------------------------------------------------------------------------- + +bool Clipper::IsRangeVisible(int startAngle, int endAngle) +{ + ClipNode *ci; + ci = cliphead; + + if (endAngle == 0 && ci && ci->start==0) return false; + + while (ci != nullptr && ci->start < endAngle) + { + if (startAngle >= ci->start && endAngle <= ci->end) + { + return false; + } + ci = ci->next; + } + + return true; +} + +//----------------------------------------------------------------------------- +// +// AddClipRange +// +//----------------------------------------------------------------------------- + +void Clipper::AddClipRange(int start, int end) +{ + ClipNode *node, *temp, *prevNode; + + if (cliphead) + { + //check to see if range contains any old ranges + node = cliphead; + while (node != nullptr && node->start < end) + { + if (node->start >= start && node->end <= end) + { + temp = node; + node = node->next; + RemoveRange(temp); + } + else if (node->start<=start && node->end>=end) + { + return; + } + else + { + node = node->next; + } + } + + //check to see if range overlaps a range (or possibly 2) + node = cliphead; + while (node != nullptr && node->start <= end) + { + if (node->end >= start) + { + // we found the first overlapping node + if (node->start > start) + { + // the new range overlaps with this node's start point + node->start = start; + } + + if (node->end < end) + { + node->end = end; + } + + ClipNode *node2 = node->next; + while (node2 && node2->start <= node->end) + { + if (node2->end > node->end) node->end = node2->end; + ClipNode *delnode = node2; + node2 = node2->next; + RemoveRange(delnode); + } + return; + } + node = node->next; + } + + //just add range + node = cliphead; + prevNode = nullptr; + temp = NewRange(start, end); + + while (node != nullptr && node->start < end) + { + prevNode = node; + node = node->next; + } + + temp->next = node; + if (node == nullptr) + { + temp->prev = prevNode; + if (prevNode) prevNode->next = temp; + if (!cliphead) cliphead = temp; + } + else + { + if (node == cliphead) + { + cliphead->prev = temp; + cliphead = temp; + } + else + { + temp->prev = prevNode; + prevNode->next = temp; + node->prev = temp; + } + } + } + else + { + temp = NewRange(start, end); + cliphead = temp; + return; + } +} + + +//----------------------------------------------------------------------------- +// +// RemoveClipRange +// +//----------------------------------------------------------------------------- + +void Clipper::RemoveClipRange(int start, int end) +{ + ClipNode *node, *temp; + + if (cliphead) + { + //check to see if range contains any old ranges + node = cliphead; + while (node != nullptr && node->start < end) + { + if (node->start >= start && node->end <= end) + { + temp = node; + node = node->next; + RemoveRange(temp); + } + else + { + node = node->next; + } + } + + //check to see if range overlaps a range (or possibly 2) + node = cliphead; + while (node != nullptr) + { + if (node->start >= start && node->start <= end) + { + node->start = end; + break; + } + else if (node->end >= start && node->end <= end) + { + node->end=start; + } + else if (node->start < start && node->end > end) + { + temp = NewRange(end, node->end); + node->end=start; + temp->next=node->next; + temp->prev=node; + node->next=temp; + if (temp->next) temp->next->prev=temp; + break; + } + node = node->next; + } + } +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +void Clipper::DumpClipper() +{ + for (auto node = cliphead; node; node = node->next) + { + Printf("Range from %f to %f\n", bamang(node->start).asdeg(), bamang(node->end).asdeg()); + } +} diff --git a/source/core/rendering/scene/hw_clipper.h b/source/core/rendering/scene/hw_clipper.h new file mode 100644 index 000000000..34739ee79 --- /dev/null +++ b/source/core/rendering/scene/hw_clipper.h @@ -0,0 +1,115 @@ +#ifndef __GL_CLIPPER +#define __GL_CLIPPER + +#include "xs_Float.h" +#include "memarena.h" +#include "basics.h" +#include "vectors.h" +#include "binaryangle.h" +#include "intvec.h" + +class ClipNode +{ + friend class Clipper; + + ClipNode *prev, *next; + int start, end; + + bool operator== (const ClipNode &other) + { + return other.start == start && other.end == end; + } +}; + + +class Clipper +{ + FMemArena nodearena; + ClipNode * freelist = nullptr; + + ClipNode * clipnodes = nullptr; + ClipNode * cliphead = nullptr; + vec2_t viewpoint; + void RemoveRange(ClipNode* cn); + binangle visibleStart, visibleEnd; + +public: + bool IsRangeVisible(int startangle, int endangle); + void AddClipRange(int startangle, int endangle); + void RemoveClipRange(int startangle, int endangle); + +public: + + void Clear(binangle rangestart); + + void Free(ClipNode *node) + { + node->next = freelist; + freelist = node; + } + +private: + ClipNode * GetNew() + { + if (freelist) + { + ClipNode * p = freelist; + freelist = p->next; + return p; + } + else return (ClipNode*)nodearena.Alloc(sizeof(ClipNode)); + } + + ClipNode * NewRange(int start, int end) + { + ClipNode * c = GetNew(); + + c->start = start; + c->end = end; + c->next = c->prev = NULL; + return c; + } + +public: + + void SetViewpoint(const vec2_t &vp) + { + viewpoint = vp; + } + + void SetVisibleRange(angle_t a1, angle_t a2) + { + if (a2 != 0xffffffff) + { + visibleStart = bamang(a1 - a2); + visibleEnd = bamang(a1 + a2); + } + else visibleStart = visibleEnd = bamang(0); + } + + void RestrictVisibleRange(binangle a1, binangle a2) + { + if (visibleStart == visibleEnd) + { + visibleStart = a1; + visibleEnd = a2; + } + else + { + if (a1.asbam() - visibleStart.asbam() < visibleEnd.asbam() - visibleStart.asbam()) visibleStart = a1; + if (a2.asbam() - visibleStart.asbam() < visibleEnd.asbam() - visibleStart.asbam()) visibleStart = a2; + } + } + + void DumpClipper(); + + binangle PointToAngle(const vec2_t& pos) + { + vec2_t vec = pos - viewpoint; + return bvectangbam(vec.x, vec.y); + } + + +}; + +#endif diff --git a/source/core/rendering/scene/hw_drawinfo.cpp b/source/core/rendering/scene/hw_drawinfo.cpp new file mode 100644 index 000000000..b459cbfe5 --- /dev/null +++ b/source/core/rendering/scene/hw_drawinfo.cpp @@ -0,0 +1,734 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2000-2018 Christoph Oelckers +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//-------------------------------------------------------------------------- +// +/* +** gl_drawinfo.cpp +** Basic scene draw info management class +** +*/ + +#include "hw_portal.h" +#include "build.h" +#include "hw_renderstate.h" +#include "hw_drawinfo.h" +//#include "models.h" +#include "hw_clock.h" +#include "hw_cvars.h" +#include "hw_viewpointbuffer.h" +#include "flatvertices.h" +#include "hw_lightbuffer.h" +#include "hw_vrmodes.h" +#include "hw_clipper.h" +#include "v_draw.h" +#include "gamecvars.h" +#include "gamestruct.h" +#include "automap.h" +#include "hw_voxels.h" + +EXTERN_CVAR(Float, r_visibility) +CVAR(Bool, gl_no_skyclear, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) + +CVAR(Bool, gl_texture, true, 0) +CVAR(Float, gl_mask_threshold, 0.5f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Float, gl_mask_sprite_threshold, 0.5f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) + +FixedBitArray gotsector; + +//========================================================================== +// +// +// +//========================================================================== + +class FDrawInfoList +{ +public: + TDeletingArray mList; + + HWDrawInfo * GetNew(); + void Release(HWDrawInfo *); +}; + + +FDrawInfoList di_list; + +//========================================================================== +// +// Try to reuse the lists as often as possible as they contain resources that +// are expensive to create and delete. +// +// Note: If multithreading gets used, this class needs synchronization. +// +//========================================================================== + +HWDrawInfo *FDrawInfoList::GetNew() +{ + if (mList.Size() > 0) + { + HWDrawInfo *di; + mList.Pop(di); + return di; + } + return new HWDrawInfo(); +} + +void FDrawInfoList::Release(HWDrawInfo * di) +{ + di->ClearBuffers(); + mList.Push(di); +} + +//========================================================================== +// +// Sets up a new drawinfo struct +// +//========================================================================== + +HWDrawInfo *HWDrawInfo::StartDrawInfo(HWDrawInfo *parent, FRenderViewpoint &parentvp, HWViewpointUniforms *uniforms) +{ + HWDrawInfo *di = di_list.GetNew(); + di->StartScene(parentvp, uniforms); + return di; +} + + +//========================================================================== +// +// +// +//========================================================================== + +static Clipper staticClipper; // Since all scenes are processed sequentially we only need one clipper. +static HWDrawInfo * gl_drawinfo; // This is a linked list of all active DrawInfos and needed to free the memory arena after the last one goes out of scope. + +void HWDrawInfo::StartScene(FRenderViewpoint& parentvp, HWViewpointUniforms* uniforms) +{ + mClipper = &staticClipper; + + Viewpoint = parentvp; + //lightmode = Level->lightMode; + if (uniforms) + { + VPUniforms = *uniforms; + // The clip planes will never be inherited from the parent drawinfo. + VPUniforms.mClipLine.X = -1000001.f; + VPUniforms.mClipHeight = 0; + } + else + { + VPUniforms.mProjectionMatrix.loadIdentity(); + VPUniforms.mViewMatrix.loadIdentity(); + VPUniforms.mNormalViewMatrix.loadIdentity(); + //VPUniforms.mViewHeight = viewheight; + VPUniforms.mGlobVis = (2 / 65536.f) * g_visibility / r_ambientlight; + VPUniforms.mPalLightLevels = numshades | (static_cast(gl_fogmode) << 8) | (5 << 16); + + VPUniforms.mClipLine.X = -10000000.0f; + VPUniforms.mShadowmapFilter = gl_shadowmap_filter; + } + vec2_t view = { int(Viewpoint.Pos.X * 16), int(Viewpoint.Pos.Y * -16) }; + mClipper->SetViewpoint(view); + + ClearBuffers(); + + for (int i = 0; i < GLDL_TYPES; i++) drawlists[i].Reset(); + vpIndex = 0; + + // Fullbright information needs to be propagated from the main view. + if (outer != nullptr) FullbrightFlags = outer->FullbrightFlags; + else FullbrightFlags = 0; + + outer = gl_drawinfo; + gl_drawinfo = this; + +} + +//========================================================================== +// +// +// +//========================================================================== + +HWDrawInfo *HWDrawInfo::EndDrawInfo() +{ + assert(this == gl_drawinfo); + for (int i = 0; i < GLDL_TYPES; i++) drawlists[i].Reset(); + gl_drawinfo = outer; + di_list.Release(this); + if (gl_drawinfo == nullptr) + ResetRenderDataAllocator(); + return gl_drawinfo; +} + + +//========================================================================== +// +// +// +//========================================================================== + +void HWDrawInfo::ClearBuffers() +{ + spriteindex = 0; + mClipPortal = nullptr; + mCurrentPortal = nullptr; +} + +//----------------------------------------------------------------------------- +// +// R_FrustumAngle +// +//----------------------------------------------------------------------------- + +angle_t HWDrawInfo::FrustumAngle() +{ + float WidescreenRatio = 1.6666f; // fixme - this is a placeholder. + float tilt = fabs(Viewpoint.HWAngles.Pitch.Degrees); + + // If the pitch is larger than this you can look all around at a FOV of 90° + if (tilt > 46.0f) return 0xffffffff; + + // ok, this is a gross hack that barely works... + // but at least it doesn't overestimate too much... + double floatangle = 2.0 + (45.0 + ((tilt / 1.9)))*Viewpoint.FieldOfView.Degrees*48.0 / AspectMultiplier(WidescreenRatio) / 90.0; + angle_t a1 = DAngle(floatangle).BAMs(); + if (a1 >= ANGLE_90) return 0xffffffff; // it's either below 90 or bust. + return a1; +} + +//----------------------------------------------------------------------------- +// +// Setup the modelview matrix +// +//----------------------------------------------------------------------------- + +void HWDrawInfo::SetViewMatrix(const FRotator &angles, float vx, float vy, float vz, bool mirror, bool planemirror) +{ + float mult = mirror ? -1.f : 1.f; + float planemult = planemirror ? -1 : 1;// Level->info->pixelstretch : Level->info->pixelstretch; + + VPUniforms.mViewMatrix.loadIdentity(); + VPUniforms.mViewMatrix.rotate(angles.Roll.Degrees, 0.0f, 0.0f, 1.0f); + VPUniforms.mViewMatrix.rotate(angles.Pitch.Degrees, 1.0f, 0.0f, 0.0f); + VPUniforms.mViewMatrix.rotate(angles.Yaw.Degrees, 0.0f, mult, 0.0f); + VPUniforms.mViewMatrix.translate(vx * mult, -vz * planemult, -vy); + VPUniforms.mViewMatrix.scale(-mult, planemult, 1); +} + + +//----------------------------------------------------------------------------- +// +// SetupView +// Setup the view rotation matrix for the given viewpoint +// +//----------------------------------------------------------------------------- +void HWDrawInfo::SetupView(FRenderState &state, float vx, float vy, float vz, bool mirror, bool planemirror) +{ + auto &vp = Viewpoint; + //vp.SetViewAngle(r_viewwindow); // todo: need to pass in. + SetViewMatrix(vp.HWAngles, vx, vy, vz, mirror, planemirror); + SetCameraPos(vp.Pos); + VPUniforms.CalcDependencies(); + vpIndex = screen->mViewpoints->SetViewpoint(state, &VPUniforms); +} + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +HWPortal * HWDrawInfo::FindPortal(const void * src) +{ + int i = Portals.Size() - 1; + + while (i >= 0 && Portals[i] && Portals[i]->GetSource() != src) i--; + return i >= 0 ? Portals[i] : nullptr; +} + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +void HWDrawInfo::DispatchSprites() +{ + for (int i = 0; i < spritesortcnt; i++) + { + auto tspr = &tsprite[i]; + int tilenum = tspr->picnum; + int spritenum = tspr->owner; + + if (spritenum < 0 || (unsigned)tilenum >= MAXTILES) + continue; + + if (automapping == 1 && (unsigned)spritenum < MAXSPRITES) + show2dsprite.Set(spritenum); + + setgotpic(tilenum); + + if (!(spriteext[spritenum].flags & SPREXT_NOTMD)) + { + int pt = Ptile2tile(tspr->picnum, tspr->pal); + if (hw_models && tile2model[pt].modelid >= 0 && tile2model[pt].framenum >= 0) + { + //HWSprite hwsprite; + //if (hwsprite.ProcessModel(pt, tspr)) continue; + } + if (r_voxels) + { + if ((tspr->cstat & CSTAT_SPRITE_ALIGNMENT) != CSTAT_SPRITE_ALIGNMENT_SLAB && tiletovox[tspr->picnum] >= 0 && voxmodels[tiletovox[tspr->picnum]]) + { + HWSprite hwsprite; + int num = tiletovox[tspr->picnum]; + if (hwsprite.ProcessVoxel(this, voxmodels[tiletovox[tspr->picnum]], tspr, §or[tspr->sectnum], voxrotate[num])) + continue; + } + else if ((tspr->cstat & CSTAT_SPRITE_ALIGNMENT) == CSTAT_SPRITE_ALIGNMENT_SLAB && tspr->picnum < MAXVOXELS && voxmodels[tspr->picnum]) + { + HWSprite hwsprite; + int num = tspr->picnum; + hwsprite.ProcessVoxel(this, voxmodels[tspr->picnum], tspr, §or[tspr->sectnum], voxrotate[num]); + continue; + } + } + } + + if (spriteext[spritenum].flags & SPREXT_AWAY1) + { + tspr->pos.x += bcos(tspr->ang, -13); + tspr->pos.y += bsin(tspr->ang, -13); + } + else if (spriteext[spritenum].flags & SPREXT_AWAY2) + { + tspr->pos.x -= bcos(tspr->ang, -13); + tspr->pos.y -= bsin(tspr->ang, -13); + } + + tileUpdatePicnum(&tilenum, sprite->owner + 32768, 0); + tspr->picnum = tilenum; + + switch (tspr->cstat & CSTAT_SPRITE_ALIGNMENT) + { + case CSTAT_SPRITE_ALIGNMENT_FACING: + { + HWSprite sprite; + sprite.Process(this, tspr, §or[tspr->sectnum], false); + break; + } + + case CSTAT_SPRITE_ALIGNMENT_WALL: + { + HWWall wall; + wall.ProcessWallSprite(this, tspr, §or[tspr->sectnum]); + break; + } + + case CSTAT_SPRITE_ALIGNMENT_FLOOR: + { + HWFlat flat; + flat.ProcessFlatSprite(this, tspr, §or[tspr->sectnum]); + break; + } + + default: + break; + } + } +} +//----------------------------------------------------------------------------- +// +// CreateScene +// +// creates the draw lists for the current scene +// +//----------------------------------------------------------------------------- + +void HWDrawInfo::CreateScene(bool portal) +{ + const auto& vp = Viewpoint; + + angle_t a1 = FrustumAngle(); + + // reset the portal manager + portalState.StartFrame(); + + ProcessAll.Clock(); + + // clip the scene and fill the drawlists + screen->mVertexData->Map(); + screen->mLights->Map(); + + spritesortcnt = 0; + ingeo = false; + geoofs = { 0,0 }; + + vec2_t view = { int(vp.Pos.X * 16), int(vp.Pos.Y * -16) }; + + if(!portal) mClipper->SetVisibleRange(vp.RotAngle, a1); + + if (a1 != 0xffffffff) mDrawer.Init(this, mClipper, view, bamang(vp.RotAngle - a1), bamang(vp.RotAngle + a1)); + else mDrawer.Init(this, mClipper, view, bamang(0), bamang(0)); + if (vp.SectNums) + mDrawer.RenderScene(vp.SectNums, vp.SectCount, portal); + else + mDrawer.RenderScene(&vp.SectCount, 1, portal); + + SetupSprite.Clock(); + gi->processSprites(tsprite, spritesortcnt, view.x, view.y, vp.Pos.Z * -256, bamang(vp.RotAngle), vp.TicFrac * 65536); + DispatchSprites(); + SetupSprite.Unclock(); + + GeoEffect eff; + int effsect = vp.SectNums ? vp.SectNums[0] : vp.SectCount; + int drawsect = effsect; + // RR geometry hack. Ugh... + // This just adds to the existing render list, so we must offset the effect areas to the same xy-space as the main one as we cannot change the view matrix. + if (gi->GetGeoEffect(&eff, effsect)) + { + ingeo = true; + geoofs = { (float)eff.geox[0], (float)eff.geoy[0] }; + // process the first layer. + for (int i = 0; i < eff.geocnt; i++) + { + auto sect = §or[eff.geosectorwarp[i]]; + for (auto w = 0; w < sect->wallnum; w++) + { + auto wal = &wall[sect->wallptr + w]; + wal->x += eff.geox[i]; + wal->y += eff.geoy[i]; + } + sect->dirty = 255; + if (eff.geosector[i] == effsect) drawsect = eff.geosectorwarp[i]; + } + + if (a1 != 0xffffffff) mDrawer.Init(this, mClipper, view, bamang(vp.RotAngle - a1), bamang(vp.RotAngle + a1)); + else mDrawer.Init(this, mClipper, view, bamang(0), bamang(0)); + + mDrawer.RenderScene(&drawsect, 1, false); + + for (int i = 0; i < eff.geocnt; i++) + { + auto sect = §or[eff.geosectorwarp[i]]; + for (auto w = 0; w < sect->wallnum; w++) + { + auto wal = &wall[sect->wallptr + w]; + wal->x -= eff.geox[i]; + wal->y -= eff.geoy[i]; + } + } + + // Now the second layer. Same shit, different arrays. + geoofs = { (float)eff.geox2[0], (float)eff.geoy2[0] }; + for (int i = 0; i < eff.geocnt; i++) + { + auto sect = §or[eff.geosectorwarp2[i]]; + for (auto w = 0; w < sect->wallnum; w++) + { + auto wal = &wall[sect->wallptr + w]; + wal->x += eff.geox2[i]; + wal->y += eff.geoy2[i]; + } + sect->dirty = 255; + if (eff.geosector[i] == effsect) drawsect = eff.geosectorwarp2[i]; + } + + if (a1 != 0xffffffff) mDrawer.Init(this, mClipper, view, bamang(vp.RotAngle - a1), bamang(vp.RotAngle + a1)); + else mDrawer.Init(this, mClipper, view, bamang(0), bamang(0)); + mDrawer.RenderScene(&drawsect, 1, false); + + for (int i = 0; i < eff.geocnt; i++) + { + auto sect = §or[eff.geosectorwarp2[i]]; + for (auto w = 0; w < sect->wallnum; w++) + { + auto wal = &wall[sect->wallptr + w]; + wal->x -= eff.geox2[i]; + wal->y -= eff.geoy2[i]; + } + } + ingeo = false; + } + + + screen->mLights->Unmap(); + screen->mVertexData->Unmap(); + + ProcessAll.Unclock(); + +} + +//----------------------------------------------------------------------------- +// +// RenderScene +// +// Draws the current draw lists for the non GLSL renderer +// +//----------------------------------------------------------------------------- + +void HWDrawInfo::RenderScene(FRenderState &state) +{ + const auto &vp = Viewpoint; + RenderAll.Clock(); + + state.SetDepthMask(true); + + state.EnableFog(true); + state.SetRenderStyle(STYLE_Source); + + + // Part 1: solid geometry. This is set up so that there are no transparent parts + state.SetDepthFunc(DF_Less); + state.AlphaFunc(Alpha_GEqual, 0.f); + state.ClearDepthBias(); + + state.EnableTexture(gl_texture); + state.EnableBrightmap(true); + drawlists[GLDL_PLAINWALLS].DrawWalls(this, state, false); + + drawlists[GLDL_PLAINFLATS].DrawFlats(this, state, false); + + + // Part 2: masked geometry. This is set up so that only pixels with alpha>gl_mask_threshold will show + state.AlphaFunc(Alpha_GEqual, gl_mask_threshold); + + // This list is masked, non-translucent walls. + drawlists[GLDL_MASKEDWALLS].DrawWalls(this, state, false); + + // These lists must be drawn in two passes for color and depth to avoid depth fighting with overlapping entries + drawlists[GLDL_MASKEDFLATS].SortFlats(this); + drawlists[GLDL_MASKEDWALLSV].SortWallsHorz(this); + drawlists[GLDL_MASKEDWALLSH].SortWallsVert(this); + + state.SetDepthBias(-1, -128); + + // these lists are only wall and floor sprites - often attached to walls and floors - so they need to be offset from the plane they may be attached to. + drawlists[GLDL_MASKEDWALLSS].DrawWalls(this, state, false); + + // Each list must draw both its passes before the next one to ensure proper depth buffer contents. + state.SetDepthMask(false); + drawlists[GLDL_MASKEDWALLSV].DrawWalls(this, state, false); + state.SetDepthMask(true); + state.SetColorMask(false); + drawlists[GLDL_MASKEDWALLSV].DrawWalls(this, state, false); + state.SetColorMask(true); + + state.SetDepthMask(false); + drawlists[GLDL_MASKEDWALLSH].DrawWalls(this, state, false); + state.SetDepthMask(true); + state.SetColorMask(false); + drawlists[GLDL_MASKEDWALLSH].DrawWalls(this, state, false); + state.SetColorMask(true); + + state.SetDepthMask(false); + drawlists[GLDL_MASKEDFLATS].DrawFlats(this, state, false); + state.SetDepthMask(true); + state.SetColorMask(false); + drawlists[GLDL_MASKEDFLATS].DrawFlats(this, state, false); + state.SetColorMask(true); + state.ClearDepthBias(); + + drawlists[GLDL_MODELS].Draw(this, state, false); + + state.SetRenderStyle(STYLE_Translucent); + + state.SetDepthFunc(DF_LEqual); + RenderAll.Unclock(); +} + +//----------------------------------------------------------------------------- +// +// RenderTranslucent +// +//----------------------------------------------------------------------------- + +void HWDrawInfo::RenderTranslucent(FRenderState &state) +{ + RenderAll.Clock(); + + state.SetDepthBias(-1, -128); + + // final pass: translucent stuff + state.AlphaFunc(Alpha_GEqual, gl_mask_sprite_threshold); + state.SetRenderStyle(STYLE_Translucent); + + state.EnableBrightmap(true); + drawlists[GLDL_TRANSLUCENTBORDER].Draw(this, state, true); + state.SetDepthMask(false); + + drawlists[GLDL_TRANSLUCENT].DrawSorted(this, state); + state.EnableBrightmap(false); + + state.ClearDepthBias(); + state.AlphaFunc(Alpha_GEqual, 0.5f); + state.SetDepthMask(true); + + RenderAll.Unclock(); +} + + +//----------------------------------------------------------------------------- +// +// RenderTranslucent +// +//----------------------------------------------------------------------------- + +void HWDrawInfo::RenderPortal(HWPortal *p, FRenderState &state, bool usestencil) +{ + auto gp = static_cast(p); + gp->SetupStencil(this, state, usestencil); + auto new_di = StartDrawInfo(this, Viewpoint, &VPUniforms); + new_di->mCurrentPortal = gp; + state.SetLightIndex(-1); + gp->DrawContents(new_di, state); + new_di->EndDrawInfo(); + state.SetVertexBuffer(screen->mVertexData); + screen->mViewpoints->Bind(state, vpIndex); + gp->RemoveStencil(this, state, usestencil); +} + +//----------------------------------------------------------------------------- +// +// Draws player sprites and color blend +// +//----------------------------------------------------------------------------- + + +void HWDrawInfo::EndDrawScene(FRenderState &state) +{ + state.EnableFog(false); + +#if 0 + // [BB] HUD models need to be rendered here. + const bool renderHUDModel = IsHUDModelForPlayerAvailable(players[consoleplayer].camera->player); + if (renderHUDModel) + { + // [BB] The HUD model should be drawn over everything else already drawn. + state.Clear(CT_Depth); + DrawPlayerSprites(true, state); + } +#endif + + state.EnableStencil(false); + state.SetViewport(screen->mScreenViewport.left, screen->mScreenViewport.top, screen->mScreenViewport.width, screen->mScreenViewport.height); + + // Restore standard rendering state + state.SetRenderStyle(STYLE_Translucent); + state.ResetColor(); + state.EnableTexture(true); + state.SetScissor(0, 0, -1, -1); +} + + +//----------------------------------------------------------------------------- +// +// sets 3D viewport and initial state +// +//----------------------------------------------------------------------------- + +void HWDrawInfo::Set3DViewport(FRenderState &state) +{ + // Always clear all buffers with scissor test disabled. + // This is faster on newer hardware because it allows the GPU to skip + // reading from slower memory where the full buffers are stored. + state.SetScissor(0, 0, -1, -1); + state.Clear(CT_Color | CT_Depth | CT_Stencil); + + const auto &bounds = screen->mSceneViewport; + state.SetViewport(bounds.left, bounds.top, bounds.width, bounds.height); + state.SetScissor(bounds.left, bounds.top, bounds.width, bounds.height); + state.EnableMultisampling(true); + state.EnableDepthTest(true); + state.EnableStencil(true); + state.SetStencil(0, SOP_Keep, SF_AllOn); +} + +//----------------------------------------------------------------------------- +// +// gl_drawscene - this function renders the scene from the current +// viewpoint, including mirrors and skyboxes and other portals +// It is assumed that the HWPortal::EndFrame returns with the +// stencil, z-buffer and the projection matrix intact! +// +//----------------------------------------------------------------------------- + +void HWDrawInfo::DrawScene(int drawmode, bool portal) +{ + static int recursion = 0; + static int ssao_portals_available = 0; + const auto& vp = Viewpoint; + + bool applySSAO = false; + if (drawmode == DM_MAINVIEW) + { + ssao_portals_available = gl_ssao_portals; + applySSAO = true; + } + else if (drawmode == DM_OFFSCREEN) + { + ssao_portals_available = 0; + } + else if (drawmode == DM_PORTAL && ssao_portals_available > 0) + { + applySSAO = (mCurrentPortal->AllowSSAO()/* || Level->flags3&LEVEL3_SKYBOXAO*/); + ssao_portals_available--; + } + + CreateScene(portal); + auto& RenderState = *screen->RenderState(); + + RenderState.SetDepthMask(true); + + if (!gl_no_skyclear) portalState.RenderFirstSkyPortal(recursion, this, RenderState); + + RenderScene(RenderState); + + if (applySSAO && RenderState.GetPassType() == GBUFFER_PASS) + { + screen->AmbientOccludeScene(VPUniforms.mProjectionMatrix.get()[5]); + screen->mViewpoints->Bind(RenderState, vpIndex); + } + + // Handle all portals after rendering the opaque objects but before + // doing all translucent stuff + recursion++; + portalState.EndFrame(this, RenderState); + recursion--; + RenderTranslucent(RenderState); +} + + +//----------------------------------------------------------------------------- +// +// R_RenderView - renders one view - either the screen or a camera texture +// +//----------------------------------------------------------------------------- + +void HWDrawInfo::ProcessScene(bool toscreen) +{ + portalState.BeginScene(); + DrawScene(toscreen ? DM_MAINVIEW : DM_OFFSCREEN, false); + if (toscreen && isBlood()) + { + gotsector = mDrawer.GotSector(); // Blood needs this to implement some lighting effect hacks. Needs to be refactored to use better info. + } +} diff --git a/source/core/rendering/scene/hw_drawinfo.h b/source/core/rendering/scene/hw_drawinfo.h new file mode 100644 index 000000000..9e7995f0a --- /dev/null +++ b/source/core/rendering/scene/hw_drawinfo.h @@ -0,0 +1,220 @@ +#pragma once + +#include +#include +#include "build.h" +#include "vectors.h" +#include "hw_viewpointuniforms.h" +#include "v_video.h" +#include "hw_drawlist.h" +#include "hw_bunchdrawer.h" +//#include "r_viewpoint.h" + +enum EDrawMode +{ + DM_MAINVIEW, + DM_OFFSCREEN, + DM_PORTAL, + DM_SKYPORTAL +}; + +struct FSectorPortalGroup; +struct FFlatVertex; +class HWWall; +class HWFlat; +class HWSprite; +class IShadowMap; +struct FDynLightData; +class Clipper; +class HWPortal; +class FFlatVertexBuffer; +class IRenderQueue; +class HWScenePortalBase; +class FRenderState; + +struct FRenderViewpoint +{ + spritetype* CameraSprite; + DVector3 Pos; + FRotator HWAngles; + FAngle FieldOfView; + angle_t RotAngle; + int* SectNums; + int SectCount; + double TicFrac; + double TanCos, TanSin; // needed for calculating a sprite's screen depth. + DVector2 ViewVector; // direction the camera is facing. +}; +//========================================================================== +// +// these are used to link faked planes due to missing textures to a sector +// +//========================================================================== + +enum SectorRenderFlags +{ + // This is used to merge several subsectors into a single draw item + SSRF_RENDERFLOOR = 1, + SSRF_RENDERCEILING = 2, + SSRF_RENDERALL = 7, + SSRF_PROCESSED = 8, + SSRF_SEEN = 16, +}; + +enum EPortalClip +{ + PClip_InFront, + PClip_Inside, + PClip_Behind, +}; + +enum DrawListType +{ + GLDL_PLAINWALLS, + GLDL_PLAINFLATS, + GLDL_MASKEDWALLS, + GLDL_MASKEDWALLSS, // arbitrary wall sprites. + GLDL_MASKEDWALLSV, // vertical wall sprites + GLDL_MASKEDWALLSH, // horizontal wall sprites. These two lists merely exist for easier sorting. + GLDL_MASKEDFLATS, + GLDL_MODELS, + + GLDL_TRANSLUCENT, + GLDL_TRANSLUCENTBORDER, + + GLDL_TYPES, +}; + + +struct HWDrawInfo +{ + struct wallseg + { + float x1, y1, z1, x2, y2, z2; + }; + + HWDrawList drawlists[GLDL_TYPES]; + int vpIndex; + //ELightMode lightmode; + + HWDrawInfo * outer = nullptr; + int FullbrightFlags; + std::atomic spriteindex; + HWPortal *mClipPortal; + HWPortal *mCurrentPortal; + //FRotator mAngles; + BunchDrawer mDrawer; + Clipper *mClipper; + FRenderViewpoint Viewpoint; + HWViewpointUniforms VPUniforms; // per-viewpoint uniform state + TArray Portals; + spritetype tsprite[MAXSPRITESONSCREEN]; + int spritesortcnt; + + // This is needed by the BSP traverser. + bool multithread; + bool ingeo; + FVector2 geoofs; + +private: + bool inview; + sectortype *currentsector; + + void WorkerThread(); + + void UnclipSubsector(sectortype *sub); + + void AddLine(walltype *seg, bool portalclip); + void AddLines(sectortype* sector); + void AddSpecialPortalLines(sectortype * sector, walltype* line); + public: + //void RenderThings(sectortype * sub, sectortype * sector); + //void RenderParticles(sectortype *sub, sectortype *front); + void SetColor(FRenderState &state, int sectorlightlevel, int rellight, bool fullbright, const FColormap &cm, float alpha, bool weapon = false); +public: + + void SetCameraPos(const DVector3 &pos) + { + VPUniforms.mCameraPos = { (float)pos.X, (float)pos.Z, (float)pos.Y, 0.f }; + } + + void SetClipHeight(float h, float d) + { + VPUniforms.mClipHeight = h; + VPUniforms.mClipHeightDirection = d; + VPUniforms.mClipLine.X = -1000001.f; + } + + void SetClipLine(walltype *line) + { + //VPUniforms.mClipLine = { (float)line->v1->fX(), (float)line->v1->fY(), (float)line->Delta().X, (float)line->Delta().Y }; + VPUniforms.mClipHeight = 0; + } + + HWPortal * FindPortal(const void * src); + + static HWDrawInfo *StartDrawInfo(HWDrawInfo *parent, FRenderViewpoint &parentvp, HWViewpointUniforms *uniforms); + void StartScene(FRenderViewpoint &parentvp, HWViewpointUniforms *uniforms); + void ClearBuffers(); + HWDrawInfo *EndDrawInfo(); + + void DrawScene(int drawmode, bool portal); + void CreateScene(bool portal); + void DispatchSprites(); + void RenderScene(FRenderState &state); + void RenderTranslucent(FRenderState &state); + void RenderPortal(HWPortal *p, FRenderState &state, bool usestencil); + void EndDrawScene(FRenderState &state); + void Set3DViewport(FRenderState &state); + void ProcessScene(bool toscreen); + + //void GetDynSpriteLight(AActor *self, float x, float y, float z, FLightNode *node, int portalgroup, float *out); + //void GetDynSpriteLight(AActor *thing, particle_t *particle, float *out); + + void SetViewMatrix(const FRotator &angles, float vx, float vy, float vz, bool mirror, bool planemirror); + void SetupView(FRenderState &state, float vx, float vy, float vz, bool mirror, bool planemirror); + angle_t FrustumAngle(); + + void DrawPlayerSprites(bool hudModelStep, FRenderState &state); + + //void AddSubsectorToPortal(FSectorPortalGroup *portal, sectortype *sub); + + void AddWall(HWWall *w); + void AddMirrorSurface(HWWall *w); + void AddFlat(HWFlat *flat); + void AddSprite(HWSprite *sprite, bool translucent); + + + bool isSoftwareLighting() const + { + return true;// lightmode == ELightMode::ZDoomSoftware || lightmode == ELightMode::DoomSoftware || lightmode == ELightMode::Build; + } + + bool isBuildSoftwareLighting() const + { + return true;// lightmode == ELightMode::Build; + } + + bool isDoomSoftwareLighting() const + { + return false;// lightmode == ELightMode::ZDoomSoftware || lightmode == ELightMode::DoomSoftware; + } + + bool isDarkLightMode() const + { + return false;// lightmode == ELightMode::Doom || lightmode == ELightMode::DoomDark; + } + + void SetFallbackLightMode() + { + //lightmode = ELightMode::Doom; + } + +}; + +void CleanSWDrawer(); +//sectortype* RenderViewpoint(FRenderViewpoint& mainvp, AActor* camera, IntRect* bounds, float fov, float ratio, float fovratio, bool mainview, bool toscreen); +//void WriteSavePic(player_t* player, FileWriter* file, int width, int height); +//sectortype* RenderView(player_t* player); + + diff --git a/source/core/rendering/scene/hw_drawlist.cpp b/source/core/rendering/scene/hw_drawlist.cpp new file mode 100644 index 000000000..64f67a725 --- /dev/null +++ b/source/core/rendering/scene/hw_drawlist.cpp @@ -0,0 +1,1057 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2002-2016 Christoph Oelckers +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//-------------------------------------------------------------------------- +// +/* +** hw_drawlist.cpp +** The main container type for draw items. +** +*/ + +#include "hw_drawstructs.h" +#include "hw_drawlist.h" +#include "flatvertices.h" +#include "hw_clock.h" +#include "hw_renderstate.h" +#include "hw_drawinfo.h" + +FMemArena RenderDataAllocator(1024*1024); // Use large blocks to reduce allocation time. + +void ResetRenderDataAllocator() +{ + RenderDataAllocator.FreeAll(); +} + +//========================================================================== +// +// +// +//========================================================================== +class StaticSortNodeArray : public TDeletingArray +{ + unsigned usecount; +public: + unsigned Size() { return usecount; } + void Clear() { usecount=0; } + void Release(int start) { usecount=start; } + SortNode * GetNew(); +}; + + +SortNode * StaticSortNodeArray::GetNew() +{ + if (usecount==TArray::Size()) + { + Push(new SortNode); + } + return operator[](usecount++); +} + + +static StaticSortNodeArray SortNodes; + +//========================================================================== +// +// +// +//========================================================================== +void HWDrawList::Reset() +{ + if (sorted) SortNodes.Release(SortNodeStart); + sorted=NULL; + walls.Clear(); + flats.Clear(); + sprites.Clear(); + drawitems.Clear(); +} + +//========================================================================== +// +// +// +//========================================================================== +inline void SortNode::UnlinkFromChain() +{ + if (parent) parent->next=next; + if (next) next->parent=parent; + parent=next=NULL; +} + +//========================================================================== +// +// +// +//========================================================================== +inline void SortNode::Link(SortNode * hook) +{ + if (hook) + { + parent=hook->parent; + hook->parent=this; + } + next=hook; + if (parent) parent->next=this; +} + +//========================================================================== +// +// +// +//========================================================================== +inline void SortNode::AddToEqual(SortNode *child) +{ + child->UnlinkFromChain(); + child->equal=equal; + equal=child; +} + +//========================================================================== +// +// +// +//========================================================================== +inline void SortNode::AddToLeft(SortNode * child) +{ + child->UnlinkFromChain(); + child->Link(left); + left=child; +} + +//========================================================================== +// +// +// +//========================================================================== +inline void SortNode::AddToRight(SortNode * child) +{ + child->UnlinkFromChain(); + child->Link(right); + right=child; +} + + +//========================================================================== +// +// +// +//========================================================================== +void HWDrawList::MakeSortList() +{ + SortNode * p, * n, * c; + unsigned i; + + SortNodeStart=SortNodes.Size(); + p=NULL; + n=SortNodes.GetNew(); + for(i=0;iitemindex=(int)i; + n->left=n->equal=n->right=NULL; + n->parent=p; + p=n; + if (i!=drawitems.Size()-1) + { + c=SortNodes.GetNew(); + n->next=c; + n=c; + } + else + { + n->next=NULL; + } + } +} + + +//========================================================================== +// +// +// +//========================================================================== +SortNode * HWDrawList::FindSortPlane(SortNode * head) +{ + while (head->next && drawitems[head->itemindex].rendertype!=DrawType_FLAT) + head=head->next; + if (drawitems[head->itemindex].rendertype==DrawType_FLAT) return head; + return NULL; +} + + +//========================================================================== +// +// +// +//========================================================================== +SortNode * HWDrawList::FindSortWall(SortNode * head) +{ + float farthest = -FLT_MAX; + float nearest = FLT_MAX; + SortNode * best = NULL; + SortNode * node = head; + float bestdist = FLT_MAX; + + while (node) + { + HWDrawItem * it = &drawitems[node->itemindex]; + if (it->rendertype == DrawType_WALL) + { + float d = walls[it->index]->ViewDistance; + if (d > farthest) farthest = d; + if (d < nearest) nearest = d; + } + node = node->next; + } + if (farthest == INT_MIN) return NULL; + node = head; + farthest = (farthest + nearest) / 2; + while (node) + { + HWDrawItem * it = &drawitems[node->itemindex]; + if (it->rendertype == DrawType_WALL) + { + float di = fabsf(walls[it->index]->ViewDistance - farthest); + if (!best || di < bestdist) + { + best = node; + bestdist = di; + } + } + node = node->next; + } + return best; +} + +//========================================================================== +// +// Note: sloped planes are a huge problem... +// +//========================================================================== +void HWDrawList::SortPlaneIntoPlane(SortNode * head,SortNode * sort) +{ + HWFlat * fh= flats[drawitems[head->itemindex].index]; + HWFlat * fs= flats[drawitems[sort->itemindex].index]; + + if (fh->z == fs->z) + { + // Make sure we have consistent ordering with two floor sprites of the same distance if they overlap + int time1 = fh->sprite ? fh->sprite->time : -1; + int time2 = fs->sprite ? fs->sprite->time : -1; + if (time1 == time2) head->AddToEqual(sort); + else if (time2 < time1)head->AddToLeft(sort); + else head->AddToRight(sort); + } + else if ( (fh->zz && fh->plane) || (fh->z>fs->z && !fh->plane)) + head->AddToLeft(sort); + else + head->AddToRight(sort); +} + + +//========================================================================== +// +// +// +//========================================================================== +void HWDrawList::SortWallIntoPlane(SortNode * head, SortNode * sort) +{ + HWFlat * fh = flats[drawitems[head->itemindex].index]; + HWWall * ws = walls[drawitems[sort->itemindex].index]; + + bool ceiling = fh->z > SortZ; + + if ((ws->ztop[0] > fh->z || ws->ztop[1] > fh->z) && (ws->zbottom[0] < fh->z || ws->zbottom[1] < fh->z)) + { + // We have to split this wall! + + HWWall *w = NewWall(); + *w = *ws; + + // Splitting is done in the shader with clip planes, if available + if (screen->hwcaps & RFL_NO_CLIP_PLANES) + { + ws->vertcount = 0; // invalidate current vertices. + float newtexv = ws->tcs[HWWall::UPLFT].v + ((ws->tcs[HWWall::LOLFT].v - ws->tcs[HWWall::UPLFT].v) / (ws->zbottom[0] - ws->ztop[0])) * (fh->z - ws->ztop[0]); + + // I make the very big assumption here that translucent walls in sloped sectors + // and 3D-floors never coexist in the same level - If that were the case this + // code would become extremely more complicated. + if (!ceiling) + { + ws->ztop[1] = w->zbottom[1] = ws->ztop[0] = w->zbottom[0] = fh->z; + ws->tcs[HWWall::UPRGT].v = w->tcs[HWWall::LORGT].v = ws->tcs[HWWall::UPLFT].v = w->tcs[HWWall::LOLFT].v = newtexv; + } + else + { + w->ztop[1] = ws->zbottom[1] = w->ztop[0] = ws->zbottom[0] = fh->z; + w->tcs[HWWall::UPLFT].v = ws->tcs[HWWall::LOLFT].v = w->tcs[HWWall::UPRGT].v = ws->tcs[HWWall::LORGT].v = newtexv; + } + } + + SortNode * sort2 = SortNodes.GetNew(); + memset(sort2, 0, sizeof(SortNode)); + sort2->itemindex = drawitems.Size() - 1; + + head->AddToLeft(sort); + head->AddToRight(sort2); + } + else if ((ws->zbottom[0] < fh->z && !ceiling) || (ws->ztop[0] > fh->z && ceiling)) // completely on the left side + { + head->AddToLeft(sort); + } + else + { + head->AddToRight(sort); + } + +} + +//========================================================================== +// +// +// +//========================================================================== +void HWDrawList::SortSpriteIntoPlane(SortNode * head, SortNode * sort) +{ + HWFlat * fh = flats[drawitems[head->itemindex].index]; + HWSprite * ss = sprites[drawitems[sort->itemindex].index]; + + bool ceiling = fh->z > SortZ; + + auto hiz = ss->z1 > ss->z2 ? ss->z1 : ss->z2; + auto loz = ss->z1 < ss->z2 ? ss->z1 : ss->z2; + + if ((hiz > fh->z && loz < fh->z))// || ss->modelframe) + { + // We have to split this sprite + HWSprite *s = NewSprite(); + *s = *ss; + + // Splitting is done in the shader with clip planes, if available. + // The fallback here only really works for non-y-billboarded sprites. + if (screen->hwcaps & RFL_NO_CLIP_PLANES) + { + float newtexv = ss->vt + ((ss->vb - ss->vt) / (ss->z2 - ss->z1))*(fh->z - ss->z1); + + if (!ceiling) + { + ss->z1 = s->z2 = fh->z; + ss->vt = s->vb = newtexv; + } + else + { + s->z1 = ss->z2 = fh->z; + s->vt = ss->vb = newtexv; + } + } + + SortNode * sort2 = SortNodes.GetNew(); + memset(sort2, 0, sizeof(SortNode)); + sort2->itemindex = drawitems.Size() - 1; + + head->AddToLeft(sort); + head->AddToRight(sort2); + } + else if ((ss->z2z && !ceiling) || (ss->z1>fh->z && ceiling)) // completely on the left side + { + head->AddToLeft(sort); + } + else + { + head->AddToRight(sort); + } +} + +//========================================================================== +// +// +// +//========================================================================== +#define MIN_EQ (0.0005f) + +// Lines start-end and fdiv must intersect. +inline double CalcIntersectionVertex(HWWall *w1, HWWall * w2) +{ + float ax = w1->glseg.x1, ay = w1->glseg.y1; + float bx = w1->glseg.x2, by = w1->glseg.y2; + float cx = w2->glseg.x1, cy = w2->glseg.y1; + float dx = w2->glseg.x2, dy = w2->glseg.y2; + return ((ay - cy)*(dx - cx) - (ax - cx)*(dy - cy)) / ((bx - ax)*(dy - cy) - (by - ay)*(dx - cx)); +} + +void HWDrawList::SortWallIntoWall(HWDrawInfo *di, SortNode * head,SortNode * sort) +{ + HWWall * wh= walls[drawitems[head->itemindex].index]; + HWWall * ws= walls[drawitems[sort->itemindex].index]; + float v1=wh->PointOnSide(ws->glseg.x1,ws->glseg.y1); + float v2=wh->PointOnSide(ws->glseg.x2,ws->glseg.y2); + + if (fabs(v1)sprite ? wh->sprite->time : -1; + int time2 = ws->sprite ? ws->sprite->time : -1; + + if ((ws->type==RENDERWALL_FOGBOUNDARY && wh->type!=RENDERWALL_FOGBOUNDARY) || time2 < time1) + { + head->AddToRight(sort); + } + else if ((ws->type!=RENDERWALL_FOGBOUNDARY && wh->type==RENDERWALL_FOGBOUNDARY) || time2 > time1) + { + head->AddToLeft(sort); + } + else + { + head->AddToEqual(sort); + } + } + else if (v1AddToLeft(sort); + } + else if (v1>-MIN_EQ && v2>-MIN_EQ) + { + head->AddToRight(sort); + } + else + { + double r = CalcIntersectionVertex(ws, wh); + + float ix=(float)(ws->glseg.x1+r*(ws->glseg.x2-ws->glseg.x1)); + float iy=(float)(ws->glseg.y1+r*(ws->glseg.y2-ws->glseg.y1)); + float iu=(float)(ws->tcs[HWWall::UPLFT].u + r * (ws->tcs[HWWall::UPRGT].u - ws->tcs[HWWall::UPLFT].u)); + float izt=(float)(ws->ztop[0]+r*(ws->ztop[1]-ws->ztop[0])); + float izb=(float)(ws->zbottom[0]+r*(ws->zbottom[1]-ws->zbottom[0])); + + ws->vertcount = 0; // invalidate current vertices. + HWWall *w= NewWall(); + *w = *ws; + + w->glseg.x1=ws->glseg.x2=ix; + w->glseg.y1=ws->glseg.y2=iy; + w->ztop[0]=ws->ztop[1]=izt; + w->zbottom[0]=ws->zbottom[1]=izb; + w->tcs[HWWall::LOLFT].u = w->tcs[HWWall::UPLFT].u = ws->tcs[HWWall::LORGT].u = ws->tcs[HWWall::UPRGT].u = iu; + ws->MakeVertices(di, false); + w->MakeVertices(di, false); + + SortNode * sort2=SortNodes.GetNew(); + memset(sort2,0,sizeof(SortNode)); + sort2->itemindex=drawitems.Size()-1; + + if (v1>0) + { + head->AddToLeft(sort2); + head->AddToRight(sort); + } + else + { + head->AddToLeft(sort); + head->AddToRight(sort2); + } + } +} + + +//========================================================================== +// +// +// +//========================================================================== +EXTERN_CVAR(Int, gl_billboard_mode) +EXTERN_CVAR(Bool, gl_billboard_faces_camera) +EXTERN_CVAR(Bool, gl_billboard_particles) + +inline double CalcIntersectionVertex(HWSprite *s, HWWall * w2) +{ + float ax = s->x1, ay = s->y1; + float bx = s->x2, by = s->y2; + float cx = w2->glseg.x1, cy = w2->glseg.y1; + float dx = w2->glseg.x2, dy = w2->glseg.y2; + return ((ay - cy)*(dx - cx) - (ax - cx)*(dy - cy)) / ((bx - ax)*(dy - cy) - (by - ay)*(dx - cx)); +} + +void HWDrawList::SortSpriteIntoWall(HWDrawInfo *di, SortNode * head,SortNode * sort) +{ + HWWall *wh= walls[drawitems[head->itemindex].index]; + HWSprite * ss= sprites[drawitems[sort->itemindex].index]; + + float v1 = wh->PointOnSide(ss->x1, ss->y1); + float v2 = wh->PointOnSide(ss->x2, ss->y2); + + if (fabs(v1)type==RENDERWALL_FOGBOUNDARY) + { + head->AddToLeft(sort); + } + else + { + head->AddToEqual(sort); + } + } + else if (v1AddToLeft(sort); + } + else if (v1>-MIN_EQ && v2>-MIN_EQ) + { + head->AddToRight(sort); + } + else + { + const bool drawWithXYBillboard = false;// + + // [Nash] has +ROLLSPRITE + const bool rotated = false;// + + // cannot sort them at the moment. This requires more complex splitting. + /* + const bool drawBillboardFacingCamera = gl_billboard_faces_camera; + if (drawWithXYBillboard || drawBillboardFacingCamera || rotated) + { + float v1 = wh->PointOnSide(ss->x, ss->y); + if (v1 < 0) + { + head->AddToLeft(sort); + } + else + { + head->AddToRight(sort); + } + return; + } + */ + double r=CalcIntersectionVertex(ss, wh); + + float ix=(float)(ss->x1 + r * (ss->x2-ss->x1)); + float iy=(float)(ss->y1 + r * (ss->y2-ss->y1)); + float iu=(float)(ss->ul + r * (ss->ur-ss->ul)); + + HWSprite *s = NewSprite(); + *s = *ss; + + s->x1=ss->x2=ix; + s->y1=ss->y2=iy; + s->ul=ss->ur=iu; + + SortNode * sort2=SortNodes.GetNew(); + memset(sort2,0,sizeof(SortNode)); + sort2->itemindex=drawitems.Size()-1; + + if (v1>0) + { + head->AddToLeft(sort2); + head->AddToRight(sort); + } + else + { + head->AddToLeft(sort); + head->AddToRight(sort2); + } + if (screen->BuffersArePersistent()) + { + s->vertexindex = ss->vertexindex = -1; + } + else + { + s->CreateVertices(di); + ss->CreateVertices(di); + } + + } +} + + +//========================================================================== +// +// +// +//========================================================================== + +inline int HWDrawList::CompareSprites(SortNode * a,SortNode * b) +{ + HWSprite * s1= sprites[drawitems[a->itemindex].index]; + HWSprite * s2= sprites[drawitems[b->itemindex].index]; + + if (s1->depth < s2->depth) return 1; + if (s1->depth > s2->depth) return -1; + return s1->sprite->time - s2->sprite->time; +} + +//========================================================================== +// +// +// +//========================================================================== +SortNode * HWDrawList::SortSpriteList(SortNode * head) +{ + SortNode * n; + int count; + unsigned i; + + static TArray sortspritelist; + + SortNode * parent=head->parent; + + sortspritelist.Clear(); + for(count=0,n=head;n;n=n->next) sortspritelist.Push(n); + std::stable_sort(sortspritelist.begin(), sortspritelist.end(), [=](SortNode *a, SortNode *b) + { + return CompareSprites(a, b) < 0; + }); + + for(i=0;inext=NULL; + if (parent) parent->equal=sortspritelist[i]; + parent=sortspritelist[i]; + } + return sortspritelist[0]; +} + +//========================================================================== +// +// +// +//========================================================================== +SortNode * HWDrawList::DoSort(HWDrawInfo *di, SortNode * head) +{ + SortNode * node, * sn, * next; + + sn=FindSortPlane(head); + if (sn) + { + if (sn==head) head=head->next; + sn->UnlinkFromChain(); + node=head; + head=sn; + while (node) + { + next=node->next; + switch(drawitems[node->itemindex].rendertype) + { + case DrawType_FLAT: + SortPlaneIntoPlane(head,node); + break; + + case DrawType_WALL: + SortWallIntoPlane(head,node); + break; + + case DrawType_SPRITE: + SortSpriteIntoPlane(head,node); + break; + } + node=next; + } + } + else + { + sn=FindSortWall(head); + if (sn) + { + if (sn==head) head=head->next; + sn->UnlinkFromChain(); + node=head; + head=sn; + while (node) + { + next=node->next; + switch(drawitems[node->itemindex].rendertype) + { + case DrawType_WALL: + SortWallIntoWall(di, head,node); + break; + + case DrawType_SPRITE: + SortSpriteIntoWall(di, head, node); + break; + + case DrawType_FLAT: break; + } + node=next; + } + } + else + { + return SortSpriteList(head); + } + } + if (head->left) head->left=DoSort(di, head->left); + if (head->right) head->right=DoSort(di, head->right); + return sn; +} + +//========================================================================== +// +// +// +//========================================================================== +void HWDrawList::Sort(HWDrawInfo *di) +{ + reverseSort = false; + SortZ = di->Viewpoint.Pos.Z; + MakeSortList(); + sorted = DoSort(di, SortNodes[SortNodeStart]); +} + +//========================================================================== +// +// +// +//========================================================================== + +void HWDrawList::SortWallsVert(HWDrawInfo* di) +{ + auto viewy = di->Viewpoint.Pos.Y; + if (drawitems.Size() > 1) + { + TArray list1(drawitems.Size(), false); + TArray list2(drawitems.Size(), false); + + for (auto& item : drawitems) + { + HWWall* w1 = walls[item.index]; + if (w1->glseg.y1 < viewy) list1.Push(item); + else list2.Push(item); + } + + std::sort(list1.begin(), list1.end(), [=](const HWDrawItem& a, const HWDrawItem& b) + { + HWWall* w1 = walls[a.index]; + HWWall* w2 = walls[b.index]; + if (w1->glseg.y1 != w2->glseg.y1) return w1->glseg.y1 < w2->glseg.y1; + int time1 = w1->sprite ? w1->sprite->time : -1; + int time2 = w2->sprite ? w2->sprite->time : -1; + return time1 < time2; + }); + + std::sort(list2.begin(), list2.end(), [=](const HWDrawItem& a, const HWDrawItem& b) + { + HWWall* w1 = walls[a.index]; + HWWall* w2 = walls[b.index]; + if (w1->glseg.y1 != w2->glseg.y1) return w1->glseg.y1 > w2->glseg.y1; + int time1 = w1->sprite ? w1->sprite->time : -1; + int time2 = w2->sprite ? w2->sprite->time : -1; + return time1 < time2; + }); + + drawitems.Clear(); + drawitems.Append(list1); + drawitems.Append(list2); + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void HWDrawList::SortWallsHorz(HWDrawInfo* di) +{ + auto viewx = di->Viewpoint.Pos.X; + if (drawitems.Size() > 1) + { + TArray list1(drawitems.Size(), false); + TArray list2(drawitems.Size(), false); + + for (auto& item : drawitems) + { + HWWall* w1 = walls[item.index]; + if (w1->glseg.x1 < viewx) list1.Push(item); + else list2.Push(item); + } + + std::sort(list1.begin(), list1.end(), [=](const HWDrawItem& a, const HWDrawItem& b) + { + HWWall* w1 = walls[a.index]; + HWWall* w2 = walls[b.index]; + if (w1->glseg.x1 != w2->glseg.x1) return w1->glseg.x1 < w2->glseg.x1; + int time1 = w1->sprite ? w1->sprite->time : -1; + int time2 = w2->sprite ? w2->sprite->time : -1; + return time1 < time2; + }); + + std::sort(list2.begin(), list2.end(), [=](const HWDrawItem& a, const HWDrawItem& b) + { + HWWall* w1 = walls[a.index]; + HWWall* w2 = walls[b.index]; + if (w1->glseg.x1 != w2->glseg.x1) return w1->glseg.x1 > w2->glseg.x1; + int time1 = w1->sprite ? w1->sprite->time : -1; + int time2 = w2->sprite ? w2->sprite->time : -1; + return time1 < time2; + }); + + drawitems.Clear(); + drawitems.Append(list1); + drawitems.Append(list2); + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void HWDrawList::SortFlats(HWDrawInfo* di) +{ + auto viewz = di->Viewpoint.Pos.Z; + if (drawitems.Size() > 1) + { + TArray list1(drawitems.Size(), false); + TArray list2(drawitems.Size(), false); + + for (auto& item : drawitems) + { + HWFlat* w1 = flats[item.index]; + if (w1->z < viewz) list1.Push(item); + else list2.Push(item); + } + + std::sort(list1.begin(), list1.end(), [=](const HWDrawItem &a, const HWDrawItem &b) + { + HWFlat* w1 = flats[a.index]; + HWFlat* w2 = flats[b.index]; + if (w1->z != w2->z) return w1->z < w2->z; + int time1 = w1->sprite ? w1->sprite->time : -1; + int time2 = w2->sprite ? w2->sprite->time : -1; + return time1 < time2; + }); + + std::sort(list2.begin(), list2.end(), [=](const HWDrawItem& a, const HWDrawItem& b) + { + HWFlat* w1 = flats[a.index]; + HWFlat* w2 = flats[b.index]; + if (w1->z != w2->z) return w2->z < w1->z; + int time1 = w1->sprite ? w1->sprite->time : -1; + int time2 = w2->sprite ? w2->sprite->time : -1; + return time1 < time2; + }); + + drawitems.Clear(); + drawitems.Append(list1); + drawitems.Append(list2); + } +} + + +//========================================================================== +// +// +// +//========================================================================== + +HWWall *HWDrawList::NewWall() +{ + auto wall = (HWWall*)RenderDataAllocator.Alloc(sizeof(HWWall)); + drawitems.Push(HWDrawItem(DrawType_WALL, walls.Push(wall))); + return wall; +} + +//========================================================================== +// +// +// +//========================================================================== +HWFlat *HWDrawList::NewFlat() +{ + auto flat = (HWFlat*)RenderDataAllocator.Alloc(sizeof(HWFlat)); + drawitems.Push(HWDrawItem(DrawType_FLAT,flats.Push(flat))); + return flat; +} + +//========================================================================== +// +// +// +//========================================================================== +HWSprite *HWDrawList::NewSprite() +{ + auto sprite = (HWSprite*)RenderDataAllocator.Alloc(sizeof(HWSprite)); + drawitems.Push(HWDrawItem(DrawType_SPRITE, sprites.Push(sprite))); + return sprite; +} + +//========================================================================== +// +// +// +//========================================================================== +void HWDrawList::DoDraw(HWDrawInfo *di, FRenderState &state, bool translucent, int i) +{ + switch(drawitems[i].rendertype) + { + case DrawType_FLAT: + { + HWFlat * f= flats[drawitems[i].index]; + RenderFlat.Clock(); + f->DrawFlat(di, state, translucent); + RenderFlat.Unclock(); + } + break; + + case DrawType_WALL: + { + HWWall * w= walls[drawitems[i].index]; + RenderWall.Clock(); + w->DrawWall(di, state, translucent); + RenderWall.Unclock(); + } + break; + + case DrawType_SPRITE: + { + HWSprite * s= sprites[drawitems[i].index]; + RenderSprite.Clock(); + s->DrawSprite(di, state, translucent); + RenderSprite.Unclock(); + } + break; + } +} + +//========================================================================== +// +// +// +//========================================================================== +void HWDrawList::Draw(HWDrawInfo *di, FRenderState &state, bool translucent) +{ + for (unsigned i = 0; i < drawitems.Size(); i++) + { + DoDraw(di, state, translucent, i); + } +} + +//========================================================================== +// +// +// +//========================================================================== +void HWDrawList::DrawWalls(HWDrawInfo *di, FRenderState &state, bool translucent) +{ + RenderWall.Clock(); + for (auto &item : drawitems) + { + walls[item.index]->DrawWall(di, state, translucent); + } + RenderWall.Unclock(); +} + +//========================================================================== +// +// +// +//========================================================================== +void HWDrawList::DrawFlats(HWDrawInfo *di, FRenderState &state, bool translucent) +{ + RenderFlat.Clock(); + for (unsigned i = 0; iDrawFlat(di, state, translucent); + } + RenderFlat.Unclock(); +} + +//========================================================================== +// +// +// +//========================================================================== +void HWDrawList::DrawSorted(HWDrawInfo *di, FRenderState &state, SortNode * head) +{ + float clipsplit[2]; + int relation = 0; + float z = 0.f; + + state.GetClipSplit(clipsplit); + + if (drawitems[head->itemindex].rendertype == DrawType_FLAT) + { + z = flats[drawitems[head->itemindex].index]->z; + relation = z > di->Viewpoint.Pos.Z ? 1 : -1; + } + + + // left is further away, i.e. for stuff above viewz its z coordinate higher, for stuff below viewz its z coordinate is lower + if (head->left) + { + if (relation == -1) + { + state.SetClipSplit(clipsplit[0], z); // render below: set flat as top clip plane + } + else if (relation == 1) + { + state.SetClipSplit(z, clipsplit[1]); // render above: set flat as bottom clip plane + } + DrawSorted(di, state, head->left); + state.SetClipSplit(clipsplit); + } + DoDraw(di, state, true, head->itemindex); + if (head->equal) + { + SortNode * ehead = head->equal; + while (ehead) + { + DoDraw(di, state, true, ehead->itemindex); + ehead = ehead->equal; + } + } + // right is closer, i.e. for stuff above viewz its z coordinate is lower, for stuff below viewz its z coordinate is higher + if (head->right) + { + if (relation == 1) + { + state.SetClipSplit(clipsplit[0], z); // render below: set flat as top clip plane + } + else if (relation == -1) + { + state.SetClipSplit(z, clipsplit[1]); // render above: set flat as bottom clip plane + } + DrawSorted(di, state, head->right); + state.SetClipSplit(clipsplit); + } +} + +//========================================================================== +// +// +// +//========================================================================== +void HWDrawList::DrawSorted(HWDrawInfo *di, FRenderState &state) +{ + if (drawitems.Size() == 0) return; + + if (!sorted) + { + screen->mVertexData->Map(); + Sort(di); + screen->mVertexData->Unmap(); + } + state.ClearClipSplit(); + state.EnableClipDistance(1, true); + state.EnableClipDistance(2, true); + DrawSorted(di, state, sorted); + state.EnableClipDistance(1, false); + state.EnableClipDistance(2, false); + state.ClearClipSplit(); +} + diff --git a/source/core/rendering/scene/hw_drawlist.h b/source/core/rendering/scene/hw_drawlist.h new file mode 100644 index 000000000..3ddb72682 --- /dev/null +++ b/source/core/rendering/scene/hw_drawlist.h @@ -0,0 +1,124 @@ +#pragma once + +#include "memarena.h" + +extern FMemArena RenderDataAllocator; +void ResetRenderDataAllocator(); +struct HWDrawInfo; +class HWWall; +class HWFlat; +class HWSprite; +class FRenderState; + +//========================================================================== +// +// Intermediate struct to link one draw item into a draw list +// +// unfortunately this struct must not contain pointers because +// the arrays may be reallocated! +// +//========================================================================== + +enum HWDrawItemType +{ + DrawType_WALL, + DrawType_FLAT, + DrawType_SPRITE, +}; + +struct HWDrawItem +{ + HWDrawItemType rendertype; + int index; + + HWDrawItem() = default; // we need this for dynamic arrays. + HWDrawItem(HWDrawItemType _rendertype,int _index) : rendertype(_rendertype),index(_index) {} +}; + +struct SortNode +{ + int itemindex; + SortNode * parent; + SortNode * next; // unsorted successor + SortNode * left; // left side of this node + SortNode * equal; // equal to this node + SortNode * right; // right side of this node + + + void UnlinkFromChain(); + void Link(SortNode * hook); + void AddToEqual(SortNode * newnode); + void AddToLeft (SortNode * newnode); + void AddToRight(SortNode * newnode); +}; + +//========================================================================== +// +// One draw list. This contains all info for one type of rendering data +// +//========================================================================== + +struct HWDrawList +{ + //private: + TArray walls; + TArray flats; + TArray sprites; + TArray drawitems; + int SortNodeStart; + float SortZ; + SortNode * sorted; + bool reverseSort; + +public: + HWDrawList() + { + next=NULL; + SortNodeStart=-1; + sorted=NULL; + } + + ~HWDrawList() + { + Reset(); + } + + unsigned int Size() + { + return drawitems.Size(); + } + + HWWall *NewWall(); + HWFlat *NewFlat(); + HWSprite *NewSprite(); + void Reset(); + void SortWallsHorz(HWDrawInfo* di); + void SortWallsVert(HWDrawInfo* di); + void SortFlats(HWDrawInfo* di); + + + void MakeSortList(); + SortNode * FindSortPlane(SortNode * head); + SortNode * FindSortWall(SortNode * head); + void SortPlaneIntoPlane(SortNode * head,SortNode * sort); + void SortWallIntoPlane(SortNode * head,SortNode * sort); + void SortSpriteIntoPlane(SortNode * head,SortNode * sort); + void SortWallIntoWall(HWDrawInfo *di, SortNode * head,SortNode * sort); + void SortSpriteIntoWall(HWDrawInfo *di, SortNode * head,SortNode * sort); + int CompareSprites(SortNode * a,SortNode * b); + SortNode * SortSpriteList(SortNode * head); + SortNode * DoSort(HWDrawInfo *di, SortNode * head); + void Sort(HWDrawInfo *di); + + void DoDraw(HWDrawInfo *di, FRenderState &state, bool translucent, int i); + void Draw(HWDrawInfo *di, FRenderState &state, bool translucent); + void DrawWalls(HWDrawInfo *di, FRenderState &state, bool translucent); + void DrawFlats(HWDrawInfo *di, FRenderState &state, bool translucent); + + void DrawSorted(HWDrawInfo *di, FRenderState &state, SortNode * head); + void DrawSorted(HWDrawInfo *di, FRenderState &state); + + HWDrawList * next; +} ; + + diff --git a/source/core/rendering/scene/hw_drawlistadd.cpp b/source/core/rendering/scene/hw_drawlistadd.cpp new file mode 100644 index 000000000..69fb35222 --- /dev/null +++ b/source/core/rendering/scene/hw_drawlistadd.cpp @@ -0,0 +1,126 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2000-2016 Christoph Oelckers +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//-------------------------------------------------------------------------- +// + +#include "hw_dynlightdata.h" +#include "hw_cvars.h" +#include "hw_lightbuffer.h" +#include "hw_drawstructs.h" +#include "hw_drawinfo.h" +#include "hw_material.h" +#include "build.h" +#include "polymost.h" +#include "gamefuncs.h" + +EXTERN_CVAR(Bool, gl_seamless) + +//========================================================================== +// +// +// +//========================================================================== + +void HWDrawInfo::AddWall(HWWall *wall) +{ + if (wall->flags & HWWall::HWF_TRANSLUCENT) + { + auto newwall = drawlists[GLDL_TRANSLUCENT].NewWall(); + *newwall = *wall; + } + else + { + int list; + + if (wall->type != RENDERWALL_M2S) list = GLDL_PLAINWALLS; + else if (wall->sprite == nullptr) list = GLDL_MASKEDWALLS; + else if (wall->glseg.x1 == wall->glseg.x2) list = GLDL_MASKEDWALLSV; + else if (wall->glseg.y1 == wall->glseg.y2) list = GLDL_MASKEDWALLSH; + else list = GLDL_MASKEDWALLSS; + auto newwall = drawlists[list].NewWall(); + *newwall = *wall; + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void HWDrawInfo::AddMirrorSurface(HWWall *w) +{ + w->type = RENDERWALL_MIRRORSURFACE; + auto newwall = drawlists[GLDL_TRANSLUCENTBORDER].NewWall(); + *newwall = *w; + + // Invalidate vertices to allow setting of texture coordinates + newwall->vertcount = 0; + + FVector3 v = newwall->glseg.Normal(); + auto tcs = newwall->tcs; + tcs[HWWall::LOLFT].u = tcs[HWWall::LORGT].u = tcs[HWWall::UPLFT].u = tcs[HWWall::UPRGT].u = v.X; + tcs[HWWall::LOLFT].v = tcs[HWWall::LORGT].v = tcs[HWWall::UPLFT].v = tcs[HWWall::UPRGT].v = v.Z; + newwall->MakeVertices(this, false); + newwall->dynlightindex = -1; // the environment map should not be affected by lights. +} + +//========================================================================== +// +// FDrawInfo::AddFlat +// +// Checks texture, lighting and translucency settings and puts this +// plane in the appropriate render list. +// +//========================================================================== + +void HWDrawInfo::AddFlat(HWFlat *flat) +{ + int list;; + + if (flat->RenderStyle != LegacyRenderStyles[STYLE_Translucent] || flat->alpha < 1.f - FLT_EPSILON || checkTranslucentReplacement(flat->texture->GetID(), flat->palette)) + { + // translucent portals go into the translucent border list. + list = flat->sprite? GLDL_TRANSLUCENT : GLDL_TRANSLUCENTBORDER; + } + else + { + list = flat->sprite ? GLDL_MASKEDFLATS : GLDL_PLAINFLATS; + } + auto newflat = drawlists[list].NewFlat(); + *newflat = *flat; +} + + +//========================================================================== +// +// +// +//========================================================================== +void HWDrawInfo::AddSprite(HWSprite *sprite, bool translucent) +{ + int list; + if (translucent || sprite->modelframe == 0) list = GLDL_TRANSLUCENT; + else list = GLDL_MODELS; + + auto newsprt = drawlists[list].NewSprite(); + *newsprt = *sprite; +} + diff --git a/source/core/rendering/scene/hw_drawstructs.h b/source/core/rendering/scene/hw_drawstructs.h new file mode 100644 index 000000000..244bb39e1 --- /dev/null +++ b/source/core/rendering/scene/hw_drawstructs.h @@ -0,0 +1,436 @@ +#pragma once +//========================================================================== +// +// One wall segment in the draw list +// +//========================================================================== +#include "renderstyle.h" +#include "textures.h" +#include "fcolormap.h" +#include "build.h" +#include "gamefuncs.h" +#include "render.h" +#include "matrix.h" +#include "gamecontrol.h" +#include "hw_renderstate.h" +#include "hw_cvars.h" + +#ifdef _MSC_VER +#pragma warning(disable:4244) +#endif + +struct HWHorizonInfo; +struct HWSkyInfo; +class FMaterial; +struct FTexCoordInfo; +struct FSectorPortalGroup; +struct FFlatVertex; +struct FDynLightData; +class VSMatrix; +struct FSpriteModelFrame; +class FRenderState; +struct voxmodel_t; + +struct HWSectorPlane +{ + FTextureID texture; + //secplane_t plane; + float Texheight; + float Angle; + FVector2 Offs; + FVector2 Scale; + + void GetFromSector(sectortype* sec, int ceiling) + { + /* + Offs.X = (float)sec->GetXOffset(ceiling); + Offs.Y = (float)sec->GetYOffset(ceiling); + Scale.X = (float)sec->GetXScale(ceiling); + Scale.Y = (float)sec->GetYScale(ceiling); + Angle = (float)sec->GetAngle(ceiling).Degrees; + texture = sec->GetTexture(ceiling); + plane = sec->GetSecPlane(ceiling); + Texheight = (float)((ceiling == sector_t::ceiling) ? plane.fD() : -plane.fD()); + */ + } +}; + +enum HWRenderStyle +{ + STYLEHW_Normal, // default + STYLEHW_Solid, // drawn solid (needs special treatment for sprites) + STYLEHW_NoAlphaTest, // disable alpha test +}; + +enum WallTypes +{ + RENDERWALL_NONE, + RENDERWALL_TOP, + RENDERWALL_M1S, + RENDERWALL_M2S, + RENDERWALL_BOTTOM, + RENDERWALL_FOGBOUNDARY, + RENDERWALL_MIRRORSURFACE, + RENDERWALL_M2SNF, + RENDERWALL_COLOR, + // Insert new types at the end! +}; + +enum PortalTypes +{ + PORTALTYPE_SKY, + PORTALTYPE_HORIZON, + PORTALTYPE_SKYBOX, + PORTALTYPE_SECTORSTACK, + PORTALTYPE_PLANEMIRROR, + PORTALTYPE_MIRROR, + PORTALTYPE_LINETOLINE, + PORTALTYPE_LINETOSPRITE, +}; + +//========================================================================== +// +// One sector plane, still in fixed point +// +//========================================================================== + +struct HWSeg +{ + float x1,x2; + float y1,y2; + float fracleft, fracright; + + FVector3 Normal() const + { + // we do not use the vector math inlines here because they are not optimized for speed but accuracy in the playsim and this is called quite frequently. + float x = y2 - y1; + float y = x1 - x2; + float ilength = 1.f / sqrtf(x*x + y*y); + return FVector3(x * ilength, 0, y * ilength); + } +}; + +struct texcoord +{ + float u,v; +}; + +struct HWDrawInfo; + +class HWWall +{ +public: + static const char passflag[]; + + enum + { + HWF_CLAMPX=1, + HWF_CLAMPY=2, + HWF_SKYHACK=4, + HWF_NOSPLIT=64, + HWF_TRANSLUCENT = 128, + }; + + enum + { + RWF_BLANK = 0, + RWF_TEXTURED = 1, // actually not being used anymore because with buffers it's even less efficient not writing the texture coordinates - but leave it here + RWF_NORENDER = 8, + }; + + enum + { + LOLFT, + UPLFT, + UPRGT, + LORGT, + }; + + friend struct HWDrawList; + friend class HWPortal; + + FGameTexture *texture; + + HWSeg glseg; + float ztop[2],zbottom[2]; + texcoord tcs[4]; + float alpha; + + FRenderStyle RenderStyle; + + float ViewDistance; + float visibility; + short shade, palette; + + PalEntry fade; + + uint16_t flags; + uint8_t type; + + int dynlightindex; + + union + { + // it's either one of them but never more! + //FSectorPortal *secportal; // sector portal (formerly skybox) + HWSkyInfo * sky; // for normal sky + //HWHorizonInfo * horizon; // for horizon information + PortalDesc * portal; // stacked sector portals + int * planemirror; // for plane mirrors + spritetype* teleport; // SW's teleport-views + }; + + unsigned int vertindex; + unsigned int vertcount; + +public: + walltype* seg; + spritetype* sprite; + sectortype* frontsector, * backsector; +//private: + + void PutWall(HWDrawInfo *di, bool translucent); + void PutPortal(HWDrawInfo *di, int ptype, int plane); + void CheckTexturePosition(); + + void SetupLights(HWDrawInfo *di, FDynLightData &lightdata); + + void MakeVertices(HWDrawInfo *di, bool nosplit); + + void SkyPlane(HWDrawInfo *di, sectortype *sector, int plane, bool allowmirror); + void SkyLine(HWDrawInfo *di, sectortype *sec, walltype *line); + void SkyNormal(HWDrawInfo* di, sectortype* fs, FVector2& v1, FVector2& v2, float fch1, float fch2, float ffh1, float ffh2); + void SkyTop(HWDrawInfo* di, walltype* seg, sectortype* fs, sectortype* bs, FVector2& v1, FVector2& v2, float fch1, float fch2); + void SkyBottom(HWDrawInfo* di, walltype* seg, sectortype* fs, sectortype* bs, FVector2& v1, FVector2& v2, float ffh1, float ffh2); + + bool DoHorizon(HWDrawInfo *di, walltype * seg,sectortype * fs, DVector2& v1, DVector2& v2); + + bool SetWallCoordinates(walltype* seg, float topleft, float topright, float bottomleft, float bottomright); + + void DoTexture(HWDrawInfo* di, walltype* wal, walltype* refwall, float yref, float topleft, float topright, float bottomleft, float bottomright); + void DoOneSidedTexture(HWDrawInfo *di, walltype * seg, sectortype* frontsector, sectortype* backsector, float topleft,float topright, float bottomleft,float bottomright); + void DoUpperTexture(HWDrawInfo* di, walltype* seg, sectortype* frontsector, sectortype* backsector, float topleft, float topright, float bottomleft, float bottomright); + void DoLowerTexture(HWDrawInfo* di, walltype* seg, sectortype* frontsector, sectortype* backsector, float topleft, float topright, float bottomleft, float bottomright); + + void DoMidTexture(HWDrawInfo *di, walltype * seg, + sectortype * front, sectortype * back, + float fch1, float fch2, float ffh1, float ffh2, + float bch1, float bch2, float bfh1, float bfh2); + + int CreateVertices(FFlatVertex *&ptr, bool nosplit); + + //int CountVertices(); + + void RenderWall(HWDrawInfo *di, FRenderState &state, int textured); + void RenderFogBoundary(HWDrawInfo *di, FRenderState &state); + void RenderMirrorSurface(HWDrawInfo *di, FRenderState &state); + void RenderTexturedWall(HWDrawInfo *di, FRenderState &state, int rflags); + void RenderTranslucentWall(HWDrawInfo *di, FRenderState &state); + +public: + void Process(HWDrawInfo* di, walltype* seg, sectortype* frontsector, sectortype* backsector); + void ProcessWallSprite(HWDrawInfo* di, spritetype* spr, sectortype* frontsector); + + float PointOnSide(float x,float y) + { + return -((y-glseg.y1)*(glseg.x2-glseg.x1)-(x-glseg.x1)*(glseg.y2-glseg.y1)); + } + + void DrawWall(HWDrawInfo *di, FRenderState &state, bool translucent); + +}; + +//========================================================================== +// +// One flat plane in the draw list +// +//========================================================================== + +class HWFlat +{ +public: + int section; + sectortype * sec; + spritetype* sprite; // for flat sprites. + FGameTexture *texture; + + float z; // the z position of the flat (only valid for non-sloped planes) + + PalEntry fade; + int shade, palette; + float visibility; + float alpha; + FRenderStyle RenderStyle; + int iboindex; + bool stack; + FVector2 geoofs; + //int vboheight; + + int plane; + int vertindex, vertcount; // this should later use a static vertex buffer, but that'd hinder the development phase, so for now vertex data gets created on the fly. + void MakeVertices(); + + int dynlightindex; + + void CreateSkyboxVertices(FFlatVertex *buffer); + //void SetupLights(HWDrawInfo *di, FLightNode *head, FDynLightData &lightdata, int portalgroup); + + void PutFlat(HWDrawInfo* di, int whichplane); + void ProcessSector(HWDrawInfo *di, sectortype * frontsector, int sectionnum, int which = 7 /*SSRF_RENDERALL*/); // cannot use constant due to circular dependencies. + void ProcessFlatSprite(HWDrawInfo* di, spritetype* sprite, sectortype* sector); + + void DrawSubsectors(HWDrawInfo *di, FRenderState &state); + void DrawFlat(HWDrawInfo* di, FRenderState& state, bool translucent); +}; + +//========================================================================== +// +// One sprite in the draw list +// +//========================================================================== + + +class HWSprite +{ +public: + + spritetype* sprite; + PalEntry fade; + int shade, palette; + float visibility; + float alpha; + FRenderStyle RenderStyle; + int modelframe; // : sprite, 1: model, -1:voxel + voxmodel_t* voxel; + + int index; + float depth; + int vertexindex; + + float x,y,z; // needed for sorting! + + union + { + struct + { + float ul, ur; + float vt, vb; + float x1, y1, z1; + float x2, y2, z2; + }; + VSMatrix rotmat; + }; + int dynlightindex; + + FGameTexture *texture; + DRotator Angles; + + + void CalculateVertices(HWDrawInfo *di, FVector3 *v, DVector3 *vp); + +public: + + void CreateVertices(HWDrawInfo* di); + void PutSprite(HWDrawInfo *di, bool translucent); + void Process(HWDrawInfo *di, spritetype* thing,sectortype * sector, int thruportal = false); + bool ProcessVoxel(HWDrawInfo* di, voxmodel_t* voxel, spritetype* tspr, sectortype* sector, bool rotate); + + void DrawSprite(HWDrawInfo* di, FRenderState& state, bool translucent); +}; + + + +inline float Dist2(float x1,float y1,float x2,float y2) +{ + return sqrtf((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)); +} + +void hw_GetDynModelLight(AActor *self, FDynLightData &modellightdata); + +extern const float LARGE_VALUE; + +struct FDynLightData; +struct FDynamicLight; +bool GetLight(FDynLightData& dld, int group, Plane& p, FDynamicLight* light, bool checkside); +void AddLightToList(FDynLightData &dld, int group, FDynamicLight* light, bool forceAttenuate); + +inline float sectorVisibility(sectortype* sec) +{ + // Beware of wraparound madness... + int v = sec->visibility; + return v ? ((uint8_t)(v + 16)) / 16.f : 1.f; +} + +inline const float hw_density = 0.35f; + +int checkTranslucentReplacement(FTextureID picnum, int pal); + +inline bool maskWallHasTranslucency(const walltype* wall) +{ + return (wall->cstat & CSTAT_WALL_TRANSLUCENT) || checkTranslucentReplacement(tileGetTexture(wall->picnum)->GetID(), wall->pal); +} + +inline bool spriteHasTranslucency(const spritetype* tspr) +{ + if ((tspr->cstat & CSTAT_SPRITE_TRANSLUCENT) || //(tspr->clipdist & TSPR_FLAGS_DRAW_LAST) || + ((unsigned)tspr->owner < MAXSPRITES && spriteext[tspr->owner].alpha)) + return true; + + return checkTranslucentReplacement(tileGetTexture(tspr->picnum)->GetID(), tspr->pal); +} + +inline void SetSpriteTranslucency(const spritetype* sprite, float& alpha, FRenderStyle& RenderStyle) +{ + bool trans = (sprite->cstat & CSTAT_SPRITE_TRANSLUCENT); + if (trans) + { + RenderStyle = GetRenderStyle(0, !!(sprite->cstat & CSTAT_SPRITE_TRANSLUCENT_INVERT)); + alpha = GetAlphaFromBlend((sprite->cstat & CSTAT_SPRITE_TRANSLUCENT_INVERT) ? DAMETH_TRANS2 : DAMETH_TRANS1, 0); + } + else + { + RenderStyle = LegacyRenderStyles[STYLE_Translucent]; + alpha = 1.f; + } + alpha *= 1.f - spriteext[sprite->owner].alpha; +} + +//========================================================================== +// +// +// +//========================================================================== +extern PalEntry GlobalMapFog; +extern float GlobalFogDensity; + +__forceinline void SetLightAndFog(FRenderState& state, PalEntry fade, int palette, int shade, float visibility, float alpha, bool setcolor = true) +{ + // Fog must be done before the texture so that the texture selector can override it. + bool foggy = (GlobalMapFog || (fade & 0xffffff)); + auto ShadeDiv = lookups.tables[palette].ShadeFactor; + bool shadow = shade >= numshades; + + if (shadow) state.SetObjectColor(0xff000000); // make sure that nothing lights this up again. + else state.SetObjectColor(0xffffffff); + + // Disable brightmaps if non-black fog is used. + if (ShadeDiv >= 1 / 1000.f && foggy) + { + state.EnableFog(1); + float density = GlobalMapFog ? GlobalFogDensity : 350.f - Scale(numshades - shade, 150, numshades); + state.SetFog((GlobalMapFog) ? GlobalMapFog : fade, density * hw_density); + state.SetSoftLightLevel(255); + state.SetLightParms(128.f, 1 / 1000.f); + } + else + { + state.EnableFog(0); + state.SetFog(0, 0); + state.SetSoftLightLevel(gl_fogmode != 0 && ShadeDiv >= 1 / 1000.f ? 255 - Scale(shade, 255, numshades) : 255); + state.SetLightParms(visibility, ShadeDiv / (numshades - 2)); + } + + // The shade rgb from the tint is ignored here. + state.SetColor(globalr * (1 / 255.f), globalg * (1 / 255.f), globalb * (1 / 255.f), alpha); +} + diff --git a/source/core/rendering/scene/hw_flats.cpp b/source/core/rendering/scene/hw_flats.cpp new file mode 100644 index 000000000..3b1257d5e --- /dev/null +++ b/source/core/rendering/scene/hw_flats.cpp @@ -0,0 +1,365 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2000-2016 Christoph Oelckers +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//-------------------------------------------------------------------------- +// +/* +** gl_flat.cpp +** Flat processing +** +*/ + +#include "matrix.h" +#include "hw_dynlightdata.h" +#include "hw_cvars.h" +#include "hw_clock.h" +#include "hw_material.h" +#include "hw_drawinfo.h" +#include "flatvertices.h" +#include "hw_lightbuffer.h" +#include "hw_drawstructs.h" +#include "hw_renderstate.h" +#include "sectorgeometry.h" + +#ifdef _DEBUG +CVAR(Int, gl_breaksec, -1, 0) +#endif + +//========================================================================== +// +// +// +//========================================================================== +#if 0 +void HWFlat::SetupLights(HWDrawInfo *di, FLightNode * node, FDynLightData &lightdata, int portalgroup) +{ + Plane p; + + lightdata.Clear(); + if (renderstyle == STYLE_Add && !di->Level->lightadditivesurfaces) + { + dynlightindex = -1; + return; // no lights on additively blended surfaces. + } + while (node) + { + FDynamicLight * light = node->lightsource; + + if (!light->IsActive()) + { + node = node->nextLight; + continue; + } + iter_dlightf++; + + // we must do the side check here because gl_GetLight needs the correct plane orientation + // which we don't have for Legacy-style 3D-floors + double planeh = plane.plane.ZatPoint(light->Pos); + if ((planehZ() && ceiling) || (planeh>light->Z() && !ceiling)) + { + node = node->nextLight; + continue; + } + + p.Set(plane.plane.Normal(), plane.plane.fD()); + draw_dlightf += GetLight(lightdata, portalgroup, p, light, false); + node = node->nextLight; + } + + dynlightindex = screen->mLights->UploadLights(lightdata); +} +#endif + +//========================================================================== +// +// +// +//========================================================================== + +void HWFlat::MakeVertices() +{ + if (vertcount > 0) return; + bool canvas = texture->isHardwareCanvas(); + if (sprite == nullptr) + { + auto mesh = sectorGeometry.get(section, plane, geoofs); + if (!mesh) return; + auto ret = screen->mVertexData->AllocVertices(mesh->vertices.Size()); + auto vp = ret.first; + float base = (plane == 0 ? sec->floorz : sec->ceilingz) * (1/-256.f); + for (unsigned i = 0; i < mesh->vertices.Size(); i++) + { + auto& pt = mesh->vertices[i]; + auto& uv = mesh->texcoords[i]; + vp->SetVertex(pt.X, base + pt.Z, pt.Y); + vp->SetTexCoord(uv.X, canvas? 1.f - uv.Y : uv.Y); + vp++; + } + vertindex = ret.second; + vertcount = mesh->vertices.Size(); + } + else + { + vec2_t pos[4]; + GetFlatSpritePosition(sprite, sprite->pos.vec2, pos, true); + + auto ret = screen->mVertexData->AllocVertices(6); + auto vp = ret.first; + float x = !(sprite->cstat & CSTAT_SPRITE_XFLIP) ? 0.f : 1.f; + float y = !(sprite->cstat & CSTAT_SPRITE_YFLIP) ? 0.f : 1.f; + for (unsigned i = 0; i < 6; i++) + { + const static unsigned indices[] = { 0, 1, 2, 0, 2, 3 }; + int j = indices[i]; + vp->SetVertex(pos[j].x * (1 / 16.f), z, pos[j].y * (1 / -16.f)); + if (!canvas) vp->SetTexCoord(j == 1 || j == 2 ? 1.f - x : x, j == 2 || j == 3 ? 1.f - y : y); + else vp->SetTexCoord(j == 1 || j == 2 ? 1.f - x : x, j == 2 || j == 3 ? y : 1.f - y); + vp++; + } + vertindex = ret.second; + vertcount = 6; + } +} + +//========================================================================== +// +// +// +//========================================================================== +void HWFlat::DrawFlat(HWDrawInfo *di, FRenderState &state, bool translucent) +{ + if (screen->BuffersArePersistent() && !sprite) + { + MakeVertices(); + } + +#ifdef _DEBUG + if (sec - sector == gl_breaksec) + { + int a = 0; + } +#endif + + if (!sprite) + { + auto mesh = sectorGeometry.get(section, plane, geoofs); + state.SetNormal(mesh->normal); + } + else + { + if (z < di->Viewpoint.Pos.Z) state.SetNormal({ 0,1,0 }); + else state.SetNormal({ 0, -1, 0 }); + } + + SetLightAndFog(state, fade, palette, shade, visibility, alpha); + + if (translucent) + { + if (RenderStyle.BlendOp != STYLEOP_Add) + { + state.EnableBrightmap(false); + } + + state.SetRenderStyle(RenderStyle); + state.SetTextureMode(RenderStyle); + + if (texture && !checkTranslucentReplacement(texture->GetID(), palette)) state.AlphaFunc(Alpha_GEqual, texture->alphaThreshold); + else state.AlphaFunc(Alpha_GEqual, 0.f); + } + state.SetMaterial(texture, UF_Texture, 0, sprite == nullptr? CLAMP_NONE : CLAMP_XY, TRANSLATION(Translation_Remap + curbasepal, palette), -1); + + state.SetLightIndex(dynlightindex); + state.Draw(DT_Triangles, vertindex, vertcount); + vertexcount += vertcount; + + if (translucent) state.SetRenderStyle(LegacyRenderStyles[STYLE_Translucent]); + state.EnableBrightmap(true); + + //state.SetObjectColor(0xffffffff); + //state.SetAddColor(0); + //state.ApplyTextureManipulation(nullptr); +} + +//========================================================================== +// +// HWFlat::PutFlat +// +// submit to the renderer +// +//========================================================================== + +void HWFlat::PutFlat(HWDrawInfo *di, int whichplane) +{ + vertcount = 0; + plane = whichplane; + if (!screen->BuffersArePersistent() || sprite || di->ingeo) // should be made static buffer content later (when the logic is working) + { +#if 0 + if (di->Level->HasDynamicLights && texture != nullptr && !di->isFullbrightScene() && !(hacktype & (SSRF_PLANEHACK | SSRF_FLOODHACK))) + { + SetupLights(di, section->lighthead, lightdata, sector->PortalGroup); + } +#endif + MakeVertices(); + } + di->AddFlat(this); + rendered_flats++; +} + +//========================================================================== +// +// Process a sector's flats for rendering +// This function is only called once per sector. +// Subsequent subsectors are just quickly added to the ss_renderflags array +// +//========================================================================== + +void HWFlat::ProcessSector(HWDrawInfo *di, sectortype * frontsector, int section_, int which) +{ +#ifdef _DEBUG + if (frontsector - sector == gl_breaksec) + { + int a = 0; + } +#endif + + dynlightindex = -1; + + const auto &vp = di->Viewpoint; + + float florz, ceilz; + PlanesAtPoint(frontsector, vp.Pos.X * 16.f, vp.Pos.Y * -16.f, &ceilz, &florz); + + fade = lookups.getFade(frontsector->floorpal); // fog is per sector. + visibility = sectorVisibility(frontsector); + sec = frontsector; + section = section_; + sprite = nullptr; + geoofs = di->geoofs; + + // + // + // + // do floors + // + // + // + if ((which & SSRF_RENDERFLOOR) && !(frontsector->floorstat & CSTAT_SECTOR_SKY) && florz <= vp.Pos.Z) + { + // process the original floor first. + + shade = frontsector->floorshade; + palette = frontsector->floorpal; + stack = frontsector->portalflags == PORTAL_SECTOR_FLOOR || frontsector->portalflags == PORTAL_SECTOR_FLOOR_REFLECT; + + if (stack && (frontsector->floorstat & CSTAT_SECTOR_METHOD)) + { + RenderStyle = GetRenderStyle(0, !!(frontsector->floorstat & CSTAT_SECTOR_TRANS_INVERT)); + alpha = GetAlphaFromBlend((frontsector->floorstat & CSTAT_SECTOR_TRANS_INVERT) ? DAMETH_TRANS2 : DAMETH_TRANS1, 0); + } + else + { + RenderStyle = STYLE_Translucent; + alpha = 1.f; + } + + if (alpha != 0.f) + { + int tilenum = frontsector->floorpicnum; + setgotpic(tilenum); + tileUpdatePicnum(&tilenum, tilenum, 0); + texture = tileGetTexture(tilenum); + if (texture && texture->isValid()) + { + //iboindex = frontsector->iboindex[sector_t::floor]; + PutFlat(di, 0); + } + } + } + + // + // + // + // do ceilings + // + // + // + if ((which & SSRF_RENDERCEILING) && !(frontsector->ceilingstat & CSTAT_SECTOR_SKY) && ceilz >= vp.Pos.Z) + { + // process the original ceiling first. + + shade = frontsector->ceilingshade; + palette = frontsector->ceilingpal; + stack = frontsector->portalflags == PORTAL_SECTOR_CEILING || frontsector->portalflags == PORTAL_SECTOR_CEILING_REFLECT; + + if (stack && (frontsector->ceilingstat & CSTAT_SECTOR_METHOD)) + { + RenderStyle = GetRenderStyle(0, !!(frontsector->ceilingstat & CSTAT_SECTOR_TRANS_INVERT)); + alpha = GetAlphaFromBlend((frontsector->ceilingstat & CSTAT_SECTOR_TRANS_INVERT) ? DAMETH_TRANS2 : DAMETH_TRANS1, 0); + } + else + { + RenderStyle = STYLE_Translucent; + alpha = 1.f; + } + + + if (alpha != 0.f) + { + //iboindex = frontsector->iboindex[sector_t::ceiling]; + + int tilenum = frontsector->ceilingpicnum; + setgotpic(tilenum); + tileUpdatePicnum(&tilenum, tilenum, 0); + texture = tileGetTexture(tilenum); + if (texture && texture->isValid()) + { + //iboindex = frontsector->iboindex[sector_t::floor]; + PutFlat(di, 1); + } + } + } +} + +void HWFlat::ProcessFlatSprite(HWDrawInfo* di, spritetype* sprite, sectortype* sector) +{ + int tilenum = sprite->picnum; + texture = tileGetTexture(tilenum); + z = sprite->z * (1 / -256.f); + if (z == di->Viewpoint.Pos.Z) return; // looking right at the edge. + + visibility = sectorVisibility(sector) *(4.f / 5.f); // The factor comes directly from Polymost. What is it with Build and these magic factors? + + // Weird Build logic that really makes no sense. + if ((sprite->cstat & CSTAT_SPRITE_ONE_SIDED) != 0 && (di->Viewpoint.Pos.Z < z) == ((sprite->cstat & CSTAT_SPRITE_YFLIP) == 0)) + return; + + if (texture && texture->isValid()) + { + this->sprite = sprite; + sec = sector; + shade = sprite->shade; + palette = sprite->pal; + fade = lookups.getFade(sector->floorpal); // fog is per sector. + + SetSpriteTranslucency(sprite, alpha, RenderStyle); + + PutFlat(di, 0); + } +} diff --git a/source/core/rendering/scene/hw_portal.cpp b/source/core/rendering/scene/hw_portal.cpp new file mode 100644 index 000000000..1bce5d4e6 --- /dev/null +++ b/source/core/rendering/scene/hw_portal.cpp @@ -0,0 +1,977 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2004-2018 Christoph Oelckers +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//-------------------------------------------------------------------------- +// +/* +** hw_portal.cpp +** portal maintenance classes for skyboxes, horizons etc. (API independent parts) +** +*/ + +#include "c_dispatch.h" +#include "hw_portal.h" +#include "hw_clipper.h" +#include "hw_renderstate.h" +#include "flatvertices.h" +#include "hw_clock.h" +#include "texturemanager.h" +#include "gamestruct.h" + +EXTERN_CVAR(Int, r_mirror_recursions) +EXTERN_CVAR(Bool, gl_portals) + +//----------------------------------------------------------------------------- +// +// StartFrame +// +//----------------------------------------------------------------------------- + +void FPortalSceneState::StartFrame() +{ + if (renderdepth == 0) + { + inskybox = false; + screen->instack[plane_floor] = screen->instack[plane_ceiling] = 0; + } + renderdepth++; +} + +//----------------------------------------------------------------------------- +// +// printing portal info +// +//----------------------------------------------------------------------------- + +static bool gl_portalinfo; + +CCMD(gl_portalinfo) +{ + gl_portalinfo = true; +} + +static FString indent; +FPortalSceneState portalState; + +//----------------------------------------------------------------------------- +// +// EndFrame +// +//----------------------------------------------------------------------------- + +void FPortalSceneState::EndFrame(HWDrawInfo *di, FRenderState &state) +{ + HWPortal * p; + + if (gl_portalinfo) + { + Printf("%s%d portals, depth = %d\n%s{\n", indent.GetChars(), di->Portals.Size(), renderdepth, indent.GetChars()); + indent += " "; + } + + while (di->Portals.Pop(p) && p) + { + if (gl_portalinfo) + { + Printf("%sProcessing %s, depth = %d\n", indent.GetChars(), p->GetName(), renderdepth); + } + if (p->lines.Size() > 0) + { + RenderPortal(p, state, true, di); + } + delete p; + } + renderdepth--; + + if (gl_portalinfo) + { + indent.Truncate(long(indent.Len()-2)); + Printf("%s}\n", indent.GetChars()); + if (indent.Len() == 0) gl_portalinfo = false; + } +} + + +//----------------------------------------------------------------------------- +// +// Renders one sky portal without a stencil. Only useful if this is the only portal in view. +// +//----------------------------------------------------------------------------- +bool FPortalSceneState::RenderFirstSkyPortal(int recursion, HWDrawInfo *outer_di, FRenderState &state) +{ + HWPortal* best = nullptr; + unsigned bestindex = 0; + + if (recursion > 0 || outer_di->Portals.Size() != 1 || !outer_di->Portals[0]->IsSky()) return false; + + best = outer_di->Portals[0]; + outer_di->Portals.Clear(); + RenderPortal(best, state, false, outer_di); + delete best; + return true; +} + + +void FPortalSceneState::RenderPortal(HWPortal *p, FRenderState &state, bool usestencil, HWDrawInfo *outer_di) +{ + if (gl_portals) outer_di->RenderPortal(p, state, usestencil); +} + + +//----------------------------------------------------------------------------- +// +// DrawPortalStencil +// +//----------------------------------------------------------------------------- + +void HWPortal::DrawPortalStencil(FRenderState &state, int pass) +{ + if (mPrimIndices.Size() == 0) + { + mPrimIndices.Resize(2 * lines.Size()); + + for (unsigned int i = 0; i < lines.Size(); i++) + { + mPrimIndices[i * 2] = lines[i].vertindex; + mPrimIndices[i * 2 + 1] = lines[i].vertcount; + } + + if (NeedCap() && lines.Size() > 1 && planesused != 0) + { + screen->mVertexData->Map(); + if (planesused & (1 << plane_floor)) + { + auto verts = screen->mVertexData->AllocVertices(4); + auto ptr = verts.first; + ptr[0].Set((float)boundingBox.left, -32767.f, (float)boundingBox.top, 0, 0); + ptr[1].Set((float)boundingBox.right, -32767.f, (float)boundingBox.top, 0, 0); + ptr[2].Set((float)boundingBox.left, -32767.f, (float)boundingBox.bottom, 0, 0); + ptr[3].Set((float)boundingBox.right, -32767.f, (float)boundingBox.bottom, 0, 0); + mBottomCap = verts.second; + } + if (planesused & (1 << plane_ceiling)) + { + auto verts = screen->mVertexData->AllocVertices(4); + auto ptr = verts.first; + ptr[0].Set((float)boundingBox.left, 32767.f, (float)boundingBox.top, 0, 0); + ptr[1].Set((float)boundingBox.right, 32767.f, (float)boundingBox.top, 0, 0); + ptr[2].Set((float)boundingBox.left, 32767.f, (float)boundingBox.bottom, 0, 0); + ptr[3].Set((float)boundingBox.right, 32767.f, (float)boundingBox.bottom, 0, 0); + mTopCap = verts.second; + } + screen->mVertexData->Unmap(); + } + + } + + for (unsigned int i = 0; i < mPrimIndices.Size(); i += 2) + { + state.Draw(DT_TriangleFan, mPrimIndices[i], mPrimIndices[i + 1], i == 0); + } + if (NeedCap() && lines.Size() > 1) + { + // The cap's depth handling needs special treatment so that it won't block further portal caps. + if (pass == STP_DepthRestore) state.SetDepthRange(1, 1); + + if (mBottomCap != ~0u) + { + state.Draw(DT_TriangleStrip, mBottomCap, 4, false); + } + if (mTopCap != ~0u) + { + state.Draw(DT_TriangleStrip, mTopCap, 4, false); + } + + if (pass == STP_DepthRestore) state.SetDepthRange(0, 1); + } +} + + +//----------------------------------------------------------------------------- +// +// Start +// +//----------------------------------------------------------------------------- + +void HWPortal::SetupStencil(HWDrawInfo *di, FRenderState &state, bool usestencil) +{ + Clocker c(PortalAll); + + rendered_portals++; + + if (usestencil) + { + // Create stencil + state.SetStencil(0, SOP_Increment); // create stencil, increment stencil of valid pixels + state.SetColorMask(false); + state.SetEffect(EFF_STENCIL); + state.EnableTexture(false); + state.ResetColor(); + state.SetDepthFunc(DF_Less); + + if (NeedDepthBuffer()) + { + state.SetDepthMask(false); // don't write to Z-buffer! + + DrawPortalStencil(state, STP_Stencil); + + // Clear Z-buffer + state.SetStencil(1, SOP_Keep); // draw sky into stencil. This stage doesn't modify the stencil. + state.SetDepthMask(true); // enable z-buffer again + state.SetDepthRange(1, 1); + state.SetDepthFunc(DF_Always); + DrawPortalStencil(state, STP_DepthClear); + + // set normal drawing mode + state.EnableTexture(true); + state.SetDepthRange(0, 1); + state.SetDepthFunc(DF_Less); + state.SetColorMask(true); + state.SetEffect(EFF_NONE); + } + else + { + // No z-buffer is needed therefore we can skip all the complicated stuff that is involved + // Note: We must draw the stencil with z-write enabled here because there is no second pass! + + state.SetDepthMask(true); + DrawPortalStencil(state, STP_AllInOne); + state.SetStencil(1, SOP_Keep); // draw sky into stencil. This stage doesn't modify the stencil. + state.EnableTexture(true); + state.SetColorMask(true); + state.SetEffect(EFF_NONE); + state.EnableDepthTest(false); + state.SetDepthMask(false); // don't write to Z-buffer! + } + screen->stencilValue++; + + + } + else + { + if (!NeedDepthBuffer()) + { + state.SetDepthMask(false); + state.EnableDepthTest(false); + } + } + + // save viewpoint + //savedvisibility = di->Viewpoint. +} + + +//----------------------------------------------------------------------------- +// +// End +// +//----------------------------------------------------------------------------- +void HWPortal::RemoveStencil(HWDrawInfo *di, FRenderState &state, bool usestencil) +{ + Clocker c(PortalAll); + bool needdepth = NeedDepthBuffer(); + + // Restore the old view + auto &vp = di->Viewpoint; + //if (vp.camera != nullptr) vp.camera->renderflags = (vp.camera->renderflags & ~RF_MAYBEINVISIBLE) | savedvisibility; + + if (usestencil) + { + + state.SetColorMask(false); // no graphics + state.SetEffect(EFF_NONE); + state.ResetColor(); + state.EnableTexture(false); + + if (needdepth) + { + // first step: reset the depth buffer to max. depth + state.SetDepthRange(1, 1); // always + state.SetDepthFunc(DF_Always); // write the farthest depth value + DrawPortalStencil(state, STP_DepthClear); + } + else + { + state.EnableDepthTest(true); + } + + // second step: restore the depth buffer to the previous values and reset the stencil + state.SetDepthFunc(DF_LEqual); + state.SetDepthRange(0, 1); + state.SetStencil(0, SOP_Decrement); + DrawPortalStencil(state, STP_DepthRestore); + state.SetDepthFunc(DF_Less); + + + state.EnableTexture(true); + state.SetEffect(EFF_NONE); + state.SetColorMask(true); + screen->stencilValue--; + + // restore old stencil op. + state.SetStencil(0, SOP_Keep); + } + else + { + if (needdepth) + { + state.Clear(CT_Depth); + } + else + { + state.EnableDepthTest(true); + state.SetDepthMask(true); + } + + // This draws a valid z-buffer into the stencil's contents to ensure it + // doesn't get overwritten by the level's geometry. + + state.ResetColor(); + state.SetDepthFunc(DF_LEqual); + state.SetDepthRange(0, 1); + state.SetColorMask(0, 0, 0, 1); // mark portal in alpha channel but don't touch color + state.SetEffect(EFF_STENCIL); + state.EnableTexture(false); + state.SetRenderStyle(STYLE_Source); + DrawPortalStencil(state, STP_DepthRestore); + state.SetEffect(EFF_NONE); + state.EnableTexture(true); + state.SetColorMask(true); + state.SetDepthFunc(DF_Less); + } +} + + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +void HWScenePortalBase::DrawContents(HWDrawInfo* di, FRenderState& state) +{ + if (Setup(di, state, di->mClipper)) + { + auto type = GetType(); + gi->EnterPortal(di->Viewpoint.CameraSprite, type); + di->DrawScene(DM_PORTAL, type == PORTAL_SECTOR_CEILING); + gi->LeavePortal(di->Viewpoint.CameraSprite, type); + Shutdown(di, state); + } + else state.ClearScreen(); +} + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +void HWScenePortalBase::ClearClipper(HWDrawInfo *di, Clipper *clipper) +{ + clipper->SetVisibleRange(di->Viewpoint.RotAngle, di->FrustumAngle()); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// +// +// Common code for line to line and mirror portals +// +// +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +inline int P_GetLineSide(const DVector2& pos, const walltype* line) +{ + auto delta = WallDelta(line); + double v = (pos.Y - WallStartY(line) * delta.X + WallStartX(line) - pos.X) * delta.Y; + return v < -1. / 65536. ? -1 : v > 1. / 65536 ? 1 : 0; +} + +bool P_ClipLineToPortal(walltype* line, walltype* portal, DVector2 view) +{ + int behind1 = P_GetLineSide(WallStart(line), portal); + int behind2 = P_GetLineSide(WallEnd(line), portal); + + if (behind1 == 0 && behind2 == 0) + { + // The line is parallel to the portal and cannot possibly be visible. + return true; + } + // If one point lies on the same straight line than the portal, the other vertex will determine sidedness alone. + else if (behind2 == 0) behind2 = behind1; + else if (behind1 == 0) behind1 = behind2; + + if (behind1 > 0 && behind2 > 0) + { + // The line is behind the portal, i.e. between viewer and portal line, and must be rejected + return true; + } + else if (behind1 < 0 && behind2 < 0) + { + // The line is in front of the portal, i.e. the portal is between viewer and line. This line must not be rejected + return false; + } + else + { + // The line intersects with the portal straight, so we need to do another check to see how both ends of the portal lie in relation to the viewer. + int viewside = P_GetLineSide(view, line); + int p1side = P_GetLineSide(WallStart(portal), line); + int p2side = P_GetLineSide(WallEnd(portal), line); + // Do the same handling of points on the portal straight as above. + if (p1side == 0) p1side = p2side; + else if (p2side == 0) p2side = p1side; + // If the portal is on the other side of the line than the viewpoint, there is no possibility to see this line inside the portal. + return (p1side == p2side && viewside != p1side); + } +} + +int HWLinePortal::ClipSeg(walltype *seg, const DVector3 &viewpos) +{ + return P_ClipLineToPortal(seg, line, viewpos) ? PClip_InFront : PClip_Inside; +} + +int HWLinePortal::ClipSector(sectortype *sub) +{ + // this seg is completely behind the mirror + for (unsigned int i = 0; iwallnum; i++) + { + if (PointOnLineSide(WallStart(&wall[sub->wallptr]), line) == 0) return PClip_Inside; + } + return PClip_InFront; +} + +int HWLinePortal::ClipPoint(const DVector2 &pos) +{ + if (PointOnLineSide(pos, line)) + { + return PClip_InFront; + } + return PClip_Inside; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// +// +// Mirror Portal +// +// +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +bool HWMirrorPortal::Setup(HWDrawInfo *di, FRenderState &rstate, Clipper *clipper) +{ + auto state = mState; + if (state->renderdepth > r_mirror_recursions) + { + return false; + } + + auto &vp = di->Viewpoint; + + di->mClipPortal = this; + + int x = line->x; + int y = line->y; + int dx = wall[line->point2].x - x; + int dy = wall[line->point2].y - y; + + // this can overflow so use 64 bit math. + const int64_t j = int64_t(dx) * dx + int64_t(dy) * dy; + if (j == 0) + return false; + + vec2_t view = { int(vp.Pos.X * 16), int(vp.Pos.Y * -16) }; + + int64_t i = ((int64_t(view.x) - x) * dx + (int64_t(view.y) - y) * dy) << 1; + + int newx = int((x << 1) + Scale(dx, i, j) - view.x); + int newy = int((y << 1) + Scale(dy, i, j) - view.y); + + auto myan = bvectangbam(dx, dy); + auto newan = myan + myan - bamang(vp.RotAngle); + + vp.RotAngle = newan.asbam(); + vp.SectNums = nullptr; + vp.SectCount = line->sector; + + vp.Pos.X = newx / 16.f; + vp.Pos.Y = newy / -16.f; + vp.HWAngles.Yaw = -90.f + newan.asdeg(); + + double FocalTangent = tan(vp.FieldOfView.Radians() / 2); + DAngle an = 270. - vp.HWAngles.Yaw.Degrees; + vp.TanSin = FocalTangent * an.Sin(); + vp.TanCos = FocalTangent * an.Cos(); + vp.ViewVector = an.ToVector(); + + state->MirrorFlag++; + di->SetClipLine(line); + di->SetupView(rstate, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(state->MirrorFlag & 1), !!(state->PlaneMirrorFlag & 1)); + + ClearClipper(di, clipper); + + auto startan = bvectangbam(line->x - newx, line->y - newy); + auto endan = bvectangbam(wall[line->point2].x - newx, wall[line->point2].y - newy); + clipper->RestrictVisibleRange(endan, startan); // we check the line from the backside so angles are reversed. + return true; +} + +void HWMirrorPortal::Shutdown(HWDrawInfo *di, FRenderState &rstate) +{ + mState->MirrorFlag--; +} + +const char *HWMirrorPortal::GetName() { return "Mirror"; } + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// +// +// Line to line Portal +// +// +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// +// only used by Blood - they can not change the angle. +// +//----------------------------------------------------------------------------- +bool HWLineToLinePortal::Setup(HWDrawInfo *di, FRenderState &rstate, Clipper *clipper) +{ + // TODO: Handle recursion more intelligently + auto &state = mState; + if (state->renderdepth>r_mirror_recursions) + { + return false; + } + auto &vp = di->Viewpoint; + di->mClipPortal = this; + + auto srccenter = (WallStart(origin) + WallEnd(origin)) / 2; + auto destcenter = (WallStart(line) + WallEnd(line)) / 2; + DVector2 npos = vp.Pos - srccenter + destcenter; + +#if 0 // Blood does not rotate these. Needs map checking to make sure it can be added. + int dx = wall[origin->point2].x - origin->x; + int dy = wall[origin->point2].y - origin->y; + int dx2 = wall[line->point2].x - line->x; + int dy2 = wall[line->point2].y - line->y; + + auto srcang = bvectangbam(dx, dy); + auto destang = bvectangbam(-dx, -dy); + + vp.RotAngle += (destang - srcang).asbam(); +#endif + + // Nothing in the entire setup mandates that both lines have the same length. + // So we need to calculate the clip range from the origin line, not the destination, because that is what determines the visible part of the scene. + int origx = vp.Pos.X * 16; + int origy = vp.Pos.Y * -16; + + vp.SectNums = nullptr; + vp.SectCount = line->sector; + vp.Pos.X = npos.X; + vp.Pos.Y = npos.Y; + + di->SetClipLine(line); + di->SetupView(rstate, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(state->MirrorFlag & 1), !!(state->PlaneMirrorFlag & 1)); + + ClearClipper(di, clipper); + + auto startan = bvectangbam(origin->x - origx, origin->y - origy); + auto endan = bvectangbam(wall[origin->point2].x - origx, wall[origin->point2].y - origy); + clipper->RestrictVisibleRange(startan, endan); + return true; +} + +const char *HWLineToLinePortal::GetName() { return "LineToLine"; } + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// +// +// Line to sprite Portal +// +// +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// +// only used by SW. +// +//----------------------------------------------------------------------------- +bool HWLineToSpritePortal::Setup(HWDrawInfo* di, FRenderState& rstate, Clipper* clipper) +{ + // TODO: Handle recursion more intelligently + auto& state = mState; + if (state->renderdepth > r_mirror_recursions) + { + return false; + } + auto& vp = di->Viewpoint; + di->mClipPortal = this; + + auto srccenter = (WallStart(origin) + WallEnd(origin)) / 2; + DVector2 destcenter ={ camera->x / 16.f, camera->y / -16.f }; + DVector2 npos = vp.Pos - srccenter + destcenter; + + int origx = vp.Pos.X * 16; + int origy = vp.Pos.Y * -16; + + vp.SectNums = nullptr; + vp.SectCount = camera->sectnum; + vp.Pos.X = npos.X; + vp.Pos.Y = npos.Y; + + di->SetClipLine(line); + di->SetupView(rstate, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(state->MirrorFlag & 1), !!(state->PlaneMirrorFlag & 1)); + + ClearClipper(di, clipper); + + auto startan = bvectangbam(origin->x - origx, origin->y - origy); + auto endan = bvectangbam(wall[origin->point2].x - origx, wall[origin->point2].y - origy); + clipper->RestrictVisibleRange(startan, endan); + return true; +} + +const char* HWLineToSpritePortal::GetName() { return "LineToSprite"; } + + +#if 0 // currently none of the games has any support for this. Maybe later. +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// +// +// Skybox Portal +// +// +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// +// GLSkyboxPortal::DrawContents +// +//----------------------------------------------------------------------------- + +bool HWSkyboxPortal::Setup(HWDrawInfo *di, FRenderState &rstate, Clipper *clipper) +{ + auto state = mState; + old_pm = state->PlaneMirrorMode; + + if (mState->skyboxrecursion >= 3) + { + return false; + } + auto &vp = di->Viewpoint; + + state->skyboxrecursion++; + state->PlaneMirrorMode = 0; + state->inskybox = true; + + AActor *origin = portal->mSkybox; + portal->mFlags |= PORTSF_INSKYBOX; + vp.extralight = 0; + + oldclamp = rstate.SetDepthClamp(false); + vp.Pos = origin->InterpolatedPosition(vp.TicFrac); + vp.ActorPos = origin->Pos(); + vp.Angles.Yaw += (origin->PrevAngles.Yaw + deltaangle(origin->PrevAngles.Yaw, origin->Angles.Yaw) * vp.TicFrac); + + // Don't let the viewpoint be too close to a floor or ceiling + double floorh = origin->Sector->floorplane.ZatPoint(origin->Pos()); + double ceilh = origin->Sector->ceilingplane.ZatPoint(origin->Pos()); + if (vp.Pos.Z < floorh + 4) vp.Pos.Z = floorh + 4; + if (vp.Pos.Z > ceilh - 4) vp.Pos.Z = ceilh - 4; + + vp.ViewActor = origin; + + di->SetupView(rstate, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(state->MirrorFlag & 1), !!(state->PlaneMirrorFlag & 1)); + di->SetViewArea(); + ClearClipper(di, clipper); + di->UpdateCurrentMapSection(); + return true; +} + + +void HWSkyboxPortal::Shutdown(HWDrawInfo *di, FRenderState &rstate) +{ + rstate.SetDepthClamp(oldclamp); + + auto state = mState; + portal->mFlags &= ~PORTSF_INSKYBOX; + state->inskybox = false; + state->skyboxrecursion--; + state->PlaneMirrorMode = old_pm; +} + +const char *HWSkyboxPortal::GetName() { return "Skybox"; } +bool HWSkyboxPortal::AllowSSAO() { return false; } // [MK] sector skyboxes don't allow SSAO by default +#endif + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// +// +// Sector stack Portal +// +// +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// +// GLSectorStackPortal::DrawContents +// +//----------------------------------------------------------------------------- +bool HWSectorStackPortal::Setup(HWDrawInfo *di, FRenderState &rstate, Clipper *clipper) +{ + // TODO: Handle recursion more intelligently + auto& state = mState; + if (state->renderdepth > r_mirror_recursions) + { + return false; + } + auto portal = origin; + auto &vp = di->Viewpoint; + + vp.Pos += DVector3(portal->dx / 16., portal->dy / -16., portal->dz / -256.); + vp.SectNums = portal->targets.Data(); + vp.SectCount = portal->targets.Size(); + type = origin->type; + state->insectorportal = true; + + // avoid recursions! + screen->instack[type == PORTAL_SECTOR_CEILING ? 1 : 0]++; + + di->SetupView(rstate, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(state->MirrorFlag & 1), !!(state->PlaneMirrorFlag & 1)); + //SetupCoverage(di); + ClearClipper(di, clipper); + + return true; +} + + +void HWSectorStackPortal::Shutdown(HWDrawInfo *di, FRenderState &rstate) +{ + screen->instack[type == PORTAL_SECTOR_CEILING ? 1 : 0]--; + mState->insectorportal = false; +} + +const char *HWSectorStackPortal::GetName() { return "Sectorstack"; } + +#if 0 +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// +// +// Plane Mirror Portal (currently not needed, Witchaven 2 is the only Build game using such a feature) +// +// +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// +// GLPlaneMirrorPortal::DrawContents +// +//----------------------------------------------------------------------------- + +bool HWPlaneMirrorPortal::Setup(HWDrawInfo *di, FRenderState &rstate, Clipper *clipper) +{ + auto state = mState; + if (state->renderdepth > r_mirror_recursions) + { + return false; + } + // A plane mirror needs to flip the portal exclusion logic because inside the mirror, up is down and down is up. + std::swap(screen->instack[plane_floor], screen->instack[plane_ceiling]); + + auto &vp = di->Viewpoint; + old_pm = state->PlaneMirrorMode; + + // the player is always visible in a mirror. + vp.showviewer = true; + + double planez = origin->ZatPoint(vp.Pos); + vp.Pos.Z = 2 * planez - vp.Pos.Z; + vp.ViewActor = nullptr; + state->PlaneMirrorMode = origin->fC() < 0 ? -1 : 1; + + state->PlaneMirrorFlag++; + di->SetClipHeight(planez, state->PlaneMirrorMode < 0 ? -1.f : 1.f); + di->SetupView(rstate, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(state->MirrorFlag & 1), !!(state->PlaneMirrorFlag & 1)); + ClearClipper(di, clipper); + + di->UpdateCurrentMapSection(); + return true; +} + +void HWPlaneMirrorPortal::Shutdown(HWDrawInfo *di, FRenderState &rstate) +{ + auto state = mState; + state->PlaneMirrorFlag--; + state->PlaneMirrorMode = old_pm; + std::swap(screen->instack[plane_floor], screen->instack[plane_ceiling]); +} + +const char *HWPlaneMirrorPortal::GetName() { return origin->fC() < 0? "Planemirror ceiling" : "Planemirror floor"; } +#endif + +#if 0 +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// +// +// Horizon Portal +// +// +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +HWHorizonPortal::HWHorizonPortal(FPortalSceneState *s, HWHorizonInfo * pt, FRenderViewpoint &vp, bool local) +: HWPortal(s, local) +{ + origin = pt; + + // create the vertex data for this horizon portal. + HWSectorPlane * sp = &origin->plane; + const float vx = vp.Pos.X; + const float vy = vp.Pos.Y; + const float vz = vp.Pos.Z; + const float z = sp->Texheight; + const float tz = (z - vz); + + // Draw to some far away boundary + // This is not drawn as larger strips because it causes visual glitches. + auto verts = screen->mVertexData->AllocVertices(1024 + 10); + auto ptr = verts.first; + for (int xx = -32768; xx < 32768; xx += 4096) + { + float x = xx + vx; + for (int yy = -32768; yy < 32768; yy += 4096) + { + float y = yy + vy; + ptr->Set(x, z, y, x / 64, -y / 64); + ptr++; + ptr->Set(x + 4096, z, y, x / 64 + 64, -y / 64); + ptr++; + ptr->Set(x, z, y + 4096, x / 64, -y / 64 - 64); + ptr++; + ptr->Set(x + 4096, z, y + 4096, x / 64 + 64, -y / 64 - 64); + ptr++; + } + } + + // fill the gap between the polygon and the true horizon + // Since I can't draw into infinity there can always be a + // small gap + ptr->Set(-32768 + vx, z, -32768 + vy, 512.f, 0); + ptr++; + ptr->Set(-32768 + vx, vz, -32768 + vy, 512.f, tz); + ptr++; + ptr->Set(-32768 + vx, z, 32768 + vy, -512.f, 0); + ptr++; + ptr->Set(-32768 + vx, vz, 32768 + vy, -512.f, tz); + ptr++; + ptr->Set(32768 + vx, z, 32768 + vy, 512.f, 0); + ptr++; + ptr->Set(32768 + vx, vz, 32768 + vy, 512.f, tz); + ptr++; + ptr->Set(32768 + vx, z, -32768 + vy, -512.f, 0); + ptr++; + ptr->Set(32768 + vx, vz, -32768 + vy, -512.f, tz); + ptr++; + ptr->Set(-32768 + vx, z, -32768 + vy, 512.f, 0); + ptr++; + ptr->Set(-32768 + vx, vz, -32768 + vy, 512.f, tz); + ptr++; + + voffset = verts.second; + vcount = 1024; + +} + +//----------------------------------------------------------------------------- +// +// HWHorizonPortal::DrawContents +// +//----------------------------------------------------------------------------- +void HWHorizonPortal::DrawContents(HWDrawInfo *di, FRenderState &state) +{ + Clocker c(PortalAll); + + HWSectorPlane * sp = &origin->plane; + auto &vp = di->Viewpoint; + + auto texture = TexMan.GetGameTexture(sp->texture, true); + + if (!texture || !texture->isValid()) + { + state.ClearScreen(); + return; + } + di->SetCameraPos(vp.Pos); + + + if (texture->isFullbright()) + { + // glowing textures are always drawn full bright without color + di->SetColor(state, 255, 0, false, origin->colormap, 1.f); + di->SetFog(state, 255, 0, false, &origin->colormap, false); + } + else + { + int rel = getExtraLight(); + di->SetColor(state, origin->lightlevel, rel, di->isFullbrightScene(), origin->colormap, 1.0f); + di->SetFog(state, origin->lightlevel, rel, di->isFullbrightScene(), &origin->colormap, false); + } + + + state.SetMaterial(texture, UF_Texture, 0, CLAMP_NONE, 0, -1); + state.SetObjectColor(origin->specialcolor); + + SetPlaneTextureRotation(state, sp, texture); + state.AlphaFunc(Alpha_GEqual, 0.f); + state.SetRenderStyle(STYLE_Source); + + for (unsigned i = 0; i < vcount; i += 4) + { + state.Draw(DT_TriangleStrip, voffset + i, 4, true);// i == 0); + } + state.Draw(DT_TriangleStrip, voffset + vcount, 10, false); + + state.EnableTextureMatrix(false); +} +#endif diff --git a/source/core/rendering/scene/hw_portal.h b/source/core/rendering/scene/hw_portal.h new file mode 100644 index 000000000..77278edfb --- /dev/null +++ b/source/core/rendering/scene/hw_portal.h @@ -0,0 +1,434 @@ +#pragma once + +#include "tarray.h" +#include "hw_drawinfo.h" +#include "hw_drawstructs.h" +#include "hw_renderstate.h" +#include "hw_material.h" +#include "render.h" + +class FSkyBox; + +enum +{ + plane_floor, + plane_ceiling +}; + + + +struct HWSkyInfo +{ + float x_offset; + float y_offset; + float y_scale; + int shade; + bool cloudy; + FGameTexture * texture; + PalEntry fadecolor; + + bool operator==(const HWSkyInfo & inf) + { + return !memcmp(this, &inf, sizeof(*this)); + } + bool operator!=(const HWSkyInfo & inf) + { + return !!memcmp(this, &inf, sizeof(*this)); + } +}; + +struct HWHorizonInfo +{ + HWSectorPlane plane; + int lightlevel; + FColormap colormap; + PalEntry specialcolor; +}; + +struct BoundingRect +{ + double left, top, right, bottom; + + BoundingRect() = default; + BoundingRect(bool) + { + setEmpty(); + } + + void setEmpty() + { + left = top = FLT_MAX; + bottom = right = -FLT_MAX; + } + + bool contains(const BoundingRect& other) const + { + return left <= other.left && top <= other.top && right >= other.right && bottom >= other.bottom; + } + + bool contains(double x, double y) const + { + return left <= x && top <= y && right >= x && bottom >= y; + } + + template + bool contains(const T& vec) const + { + return left <= vec.X && top <= vec.Y && right >= vec.X && bottom >= vec.Y; + } + + bool intersects(const BoundingRect& other) const + { + return !(other.left > right || + other.right < left || + other.top > bottom || + other.bottom < top); + } + + void Union(const BoundingRect& other) + { + if (other.left < left) left = other.left; + if (other.right > right) right = other.right; + if (other.top < top) top = other.top; + if (other.bottom > bottom) bottom = other.bottom; + } + + double distanceTo(const BoundingRect& other) const + { + if (intersects(other)) return 0; + return std::max(std::min(fabs(left - other.right), fabs(right - other.left)), + std::min(fabs(top - other.bottom), fabs(bottom - other.top))); + } + + void addVertex(double x, double y) + { + if (x < left) left = x; + if (x > right) right = x; + if (y < top) top = y; + if (y > bottom) bottom = y; + } + + bool operator == (const BoundingRect& other) const + { + return left == other.left && top == other.top && right == other.right && bottom == other.bottom; + } + +}; + +struct secplane_t +{ + double a, b, c, d, ic; +}; + +struct FPortalSceneState; + +class HWPortal +{ + friend struct FPortalSceneState; + + enum + { + STP_Stencil, + STP_DepthClear, + STP_DepthRestore, + STP_AllInOne + }; + + TArray mPrimIndices; + unsigned int mTopCap = ~0u, mBottomCap = ~0u; + + void DrawPortalStencil(FRenderState &state, int pass); + +public: + FPortalSceneState * mState; + TArray lines; + BoundingRect boundingBox; + int planesused = 0; + + HWPortal(FPortalSceneState *s, bool local = false) : mState(s), boundingBox(false) + { + } + virtual ~HWPortal() {} + virtual int ClipSeg(walltype *seg, const DVector3 &viewpos) { return PClip_Inside; } + virtual int ClipSector(sectortype *sub) { return PClip_Inside; } + virtual int ClipPoint(const DVector2 &pos) { return PClip_Inside; } + virtual walltype *ClipLine() { return nullptr; } + virtual void * GetSource() const = 0; // GetSource MUST be implemented! + virtual const char *GetName() = 0; + virtual bool AllowSSAO() { return true; } + virtual bool IsSky() { return false; } + virtual bool NeedCap() { return true; } + virtual bool NeedDepthBuffer() { return true; } + virtual void DrawContents(HWDrawInfo *di, FRenderState &state) = 0; + virtual void RenderAttached(HWDrawInfo *di) {} + virtual int GetType() { return -1; } + void SetupStencil(HWDrawInfo *di, FRenderState &state, bool usestencil); + void RemoveStencil(HWDrawInfo *di, FRenderState &state, bool usestencil); + + void AddLine(HWWall * l) + { + lines.Push(*l); + boundingBox.addVertex(l->glseg.x1, l->glseg.y1); + boundingBox.addVertex(l->glseg.x2, l->glseg.y2); + } + + +}; + + +struct FPortalSceneState +{ + int MirrorFlag = 0; + int PlaneMirrorFlag = 0; + int renderdepth = 0; + + int PlaneMirrorMode = 0; + bool inskybox = 0; + bool insectorportal = false; + + UniqueList UniqueSkies; + UniqueList UniqueHorizons; + UniqueList UniquePlaneMirrors; + + int skyboxrecursion = 0; + + void BeginScene() + { + UniqueSkies.Clear(); + UniqueHorizons.Clear(); + UniquePlaneMirrors.Clear(); + } + + bool isMirrored() const + { + return !!((MirrorFlag ^ PlaneMirrorFlag) & 1); + } + + void StartFrame(); + bool RenderFirstSkyPortal(int recursion, HWDrawInfo *outer_di, FRenderState &state); + void EndFrame(HWDrawInfo *outer_di, FRenderState &state); + void RenderPortal(HWPortal *p, FRenderState &state, bool usestencil, HWDrawInfo *outer_di); +}; + +extern FPortalSceneState portalState; + + +class HWScenePortalBase : public HWPortal +{ +protected: + HWScenePortalBase(FPortalSceneState *state) : HWPortal(state, false) + { + + } +public: + void ClearClipper(HWDrawInfo *di, Clipper *clipper); + virtual bool NeedDepthBuffer() { return true; } + virtual void DrawContents(HWDrawInfo* di, FRenderState& state); + virtual bool Setup(HWDrawInfo *di, FRenderState &rstate, Clipper *clipper) = 0; + virtual void Shutdown(HWDrawInfo *di, FRenderState &rstate) {} +}; + +struct HWLinePortal : public HWScenePortalBase +{ +protected: + // this must be the same as at the start of line_t, so that we can pass in this structure directly to P_ClipLineToPortal. + walltype* line; + /* + vec2_t *v1, *v2; // vertices, from v1 to v2 + DVector2 delta; // precalculated v2 - v1 for side checking + + angle_t angv1, angv2; // for quick comparisons with a line or subsector + */ + + HWLinePortal(FPortalSceneState *state, walltype *line) : HWScenePortalBase(state) + { + this->line = line; + //v1 = &line->pos; + //v2 = &wall[line->point2].pos; + //CalcDelta(); + } + + void CalcDelta() + { + //delta = v2->fPos() - v1->fPos(); + } + + int ClipSeg(walltype *seg, const DVector3 &viewpos) override; + int ClipSector(sectortype *sub) override; + int ClipPoint(const DVector2 &pos); + bool NeedCap() override { return false; } +}; + +struct HWMirrorPortal : public HWLinePortal +{ +protected: + bool Setup(HWDrawInfo *di, FRenderState &rstate, Clipper *clipper) override; + void Shutdown(HWDrawInfo *di, FRenderState &rstate) override; + void * GetSource() const override { return line; } + const char *GetName() override; + virtual int GetType() { return PORTAL_WALL_MIRROR; } + +public: + + HWMirrorPortal(FPortalSceneState *state, walltype * line) + : HWLinePortal(state, line) + { + } +}; + + +struct HWLineToLinePortal : public HWLinePortal +{ + walltype* origin; +protected: + bool Setup(HWDrawInfo *di, FRenderState &rstate, Clipper *clipper) override; + virtual void * GetSource() const override { return origin; } + virtual const char *GetName() override; + virtual walltype *ClipLine() override { return line; } + virtual int GetType() { return PORTAL_WALL_VIEW; } + +public: + + HWLineToLinePortal(FPortalSceneState *state, walltype* from, walltype *to) + : HWLinePortal(state, to), origin(from) + { + } +}; + +struct HWLineToSpritePortal : public HWLinePortal +{ + walltype* origin; + spritetype* camera; +protected: + bool Setup(HWDrawInfo* di, FRenderState& rstate, Clipper* clipper) override; + virtual void* GetSource() const override { return origin; } + virtual const char* GetName() override; + virtual walltype* ClipLine() override { return line; } + virtual int GetType() { return PORTAL_WALL_TO_SPRITE; } + +public: + + HWLineToSpritePortal(FPortalSceneState* state, walltype* from, spritetype* to) + : HWLinePortal(state, &wall[numwalls]), origin(from), camera(to) + { + // todo: set up two fake walls at the end of the walls array to be used for backside clipping. + // Not really needed for vanilla support but maybe later for feature enhancement. + } +}; + + +#if 0 +struct HWSkyboxPortal : public HWScenePortalBase +{ + bool oldclamp; + int old_pm; + spritetype * portal; + +protected: + bool Setup(HWDrawInfo *di, FRenderState &rstate, Clipper *clipper) override; + void Shutdown(HWDrawInfo *di, FRenderState &rstate) override; + virtual void * GetSource() const { return portal; } + virtual bool IsSky() { return true; } + virtual const char *GetName(); + virtual bool AllowSSAO() override; + +public: + + + HWSkyboxPortal(FPortalSceneState *state, spritetype * pt) : HWScenePortalBase(state) + { + portal = pt; + } + +}; +#endif + + +struct HWSectorStackPortal : public HWScenePortalBase +{ + TArray sectors; + int type = -1; +protected: + bool Setup(HWDrawInfo *di, FRenderState &rstate, Clipper *clipper) override; + void Shutdown(HWDrawInfo *di, FRenderState &rstate) override; + virtual void * GetSource() const { return origin; } + virtual const char *GetName(); + virtual int GetType() { return PORTAL_SECTOR_CEILING; } + PortalDesc *origin; + +public: + + HWSectorStackPortal(FPortalSceneState *state, PortalDesc *pt) : HWScenePortalBase(state) + { + origin = pt; + } + //void SetupCoverage(HWDrawInfo *di); + +}; + +#if 0 +struct HWPlaneMirrorPortal : public HWScenePortalBase +{ + int old_pm; +protected: + bool Setup(HWDrawInfo *di, FRenderState &rstate, Clipper *clipper) override; + void Shutdown(HWDrawInfo *di, FRenderState &rstate) override; + virtual void * GetSource() const { return origin; } + virtual const char *GetName(); + secplane_t * origin; + +public: + + HWPlaneMirrorPortal(FPortalSceneState *state, secplane_t * pt) : HWScenePortalBase(state) + { + origin = pt; + } + +}; +#endif + +#if 0 +struct HWHorizonPortal : public HWPortal +{ + HWHorizonInfo * origin; + unsigned int voffset; + unsigned int vcount; + friend struct HWEEHorizonPortal; + +protected: + virtual void DrawContents(HWDrawInfo *di, FRenderState &state); + virtual void * GetSource() const { return origin; } + virtual bool NeedDepthBuffer() { return false; } + virtual bool NeedCap() { return false; } + virtual const char *GetName(); + +public: + + HWHorizonPortal(FPortalSceneState *state, HWHorizonInfo * pt, FRenderViewpoint &vp, bool local = false); +}; +#endif + +struct HWSkyPortal : public HWPortal +{ + HWSkyInfo * origin; + FSkyVertexBuffer *vertexBuffer; + friend struct HWEEHorizonPortal; + +protected: + virtual void DrawContents(HWDrawInfo *di, FRenderState &state); + virtual void * GetSource() const { return origin; } + virtual bool IsSky() { return true; } + virtual bool NeedDepthBuffer() { return false; } + virtual const char *GetName(); + +public: + + + HWSkyPortal(FSkyVertexBuffer *vertexbuffer, FPortalSceneState *state, HWSkyInfo * pt, bool local = false) + : HWPortal(state, local) + { + origin = pt; + vertexBuffer = vertexbuffer; + } + +}; diff --git a/source/core/rendering/scene/hw_sky.cpp b/source/core/rendering/scene/hw_sky.cpp new file mode 100644 index 000000000..1a008ece9 --- /dev/null +++ b/source/core/rendering/scene/hw_sky.cpp @@ -0,0 +1,245 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2002-2016 Christoph Oelckers +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//-------------------------------------------------------------------------- +// + +#include "texturemanager.h" +#include "hw_drawinfo.h" +#include "hw_drawstructs.h" +#include "hw_portal.h" +//#include "hw_lighting.h" +#include "hw_material.h" +#include "gamefuncs.h" +#include "build.h" +#include "cmdlib.h" + +CVAR(Bool,gl_noskyboxes, false, 0) +FGameTexture* GetSkyTexture(int basetile, int lognumtiles, const int16_t* tilemap, int remap); +FGameTexture* SkyboxReplacement(FTextureID picnum, int palnum); + +//========================================================================== +// +// Set up the skyinfo struct +// +//========================================================================== + +void initSkyInfo(HWDrawInfo *di, HWSkyInfo* sky, sectortype* sector, int plane, PalEntry FadeColor) +{ + int picnum = plane == plane_ceiling ? sector->ceilingpicnum : sector->floorpicnum; + tileUpdatePicnum(&picnum, 0, 0); + int palette = plane == plane_ceiling ? sector->ceilingpal : sector->floorpal; + + int32_t dapyscale = 0, dapskybits = 0, dapyoffs = 0, daptileyscale = 0; + FGameTexture* skytex = SkyboxReplacement(tileGetTexture(picnum)->GetID(), palette); + int realskybits = 0; + // todo: check for skybox replacement. + if (!skytex) + { + int remap = TRANSLATION(Translation_Remap + curbasepal, palette); + + int16_t const* dapskyoff = getpsky(picnum, &dapyscale, &dapskybits, &dapyoffs, &daptileyscale); + int tw = tileWidth(picnum); + if ((1 << sizeToBits(tw)) < tw) dapskybits--; // Build math is weird. + + skytex = GetSkyTexture(picnum, dapskybits, dapskyoff, remap); + realskybits = dapskybits; + if (skytex) dapskybits = 0; + else skytex = tileGetTexture(picnum); + } + + float xpanning = plane == plane_ceiling ? sector->ceilingxpan_ : sector->floorxpan_; + float ypanning = plane == plane_ceiling ? sector->ceilingypan_ : sector->floorypan_; + + // dapyscale is not relvant for a sky dome. + sky->y_scale = FixedToFloat(daptileyscale); + sky->cloudy = !!(sector->exflags & SECTOREX_CLOUDSCROLL); + if (!sky->cloudy) + { + sky->y_offset = dapyoffs * 1.5; + sky->x_offset = xpanning / (1 << (realskybits - dapskybits)); + } + else + { + sky->y_offset = ypanning; + sky->x_offset = 2 * xpanning / (1 << (realskybits - dapskybits)); + } + sky->fadecolor = FadeColor; + sky->shade = 0;// clamp(plane == plane_ceiling ? sector->ceilingshade : sector->floorshade, 0, numshades - 1); + sky->texture = skytex; +} + + +//========================================================================== +// +// Calculate sky texture for ceiling or floor +// +//========================================================================== + +void HWWall::SkyPlane(HWDrawInfo *di, sectortype *sector, int plane, bool allowreflect) +{ + int ptype; + + if ((sector->portalflags == PORTAL_SECTOR_CEILING && plane == plane_ceiling) || (sector->portalflags == PORTAL_SECTOR_FLOOR && plane == plane_floor)) + { + if (screen->instack[1 - plane] || sector->portalnum >= (int)allPortals.Size()) return; + portal = &allPortals[sector->portalnum]; + PutPortal(di, PORTALTYPE_SECTORSTACK, plane); + } + else if ((sector->portalflags == PORTAL_SECTOR_CEILING_REFLECT && plane == plane_ceiling) || (sector->portalflags == PORTAL_SECTOR_FLOOR_REFLECT && plane == plane_floor)) + { + ptype = PORTALTYPE_PLANEMIRROR; + if (plane == plane_ceiling && (sector->ceilingstat & CSTAT_SECTOR_SLOPE)) return; + if (plane == plane_floor && (sector->floorstat & CSTAT_SECTOR_SLOPE)) return; + planemirror = plane == plane_floor ? §or->floorz : §or->ceilingz; + PutPortal(di, PORTALTYPE_PLANEMIRROR, plane); + } + else + { + ptype = PORTALTYPE_SKY; + HWSkyInfo skyinfo; + initSkyInfo(di, &skyinfo, sector, plane, fade); + ptype = PORTALTYPE_SKY; + sky = &skyinfo; + PutPortal(di, ptype, plane); + } +} + + +//========================================================================== +// +// Skies on one sided walls +// +//========================================================================== + +void HWWall::SkyNormal(HWDrawInfo* di, sectortype* fs, FVector2& v1, FVector2& v2, float fch1, float fch2, float ffh1, float ffh2) +{ + if ((fs->ceilingstat & CSTAT_SECTOR_SKY) || fs->portalflags == PORTAL_SECTOR_CEILING || fs->portalflags == PORTAL_SECTOR_CEILING_REFLECT) + { + ztop[0] = ztop[1] = 32768.0f; + zbottom[0] = fch1; + zbottom[1] = fch2; + SkyPlane(di, fs, plane_ceiling, true); + } + + if ((fs->floorstat & CSTAT_SECTOR_SKY) || fs->portalflags == PORTAL_SECTOR_FLOOR || fs->portalflags == PORTAL_SECTOR_FLOOR_REFLECT) + { + ztop[0] = ffh1; + ztop[1] = ffh2; + zbottom[0] = zbottom[1] = -32768.0f; + SkyPlane(di, fs, plane_floor, true); + } +} + +//========================================================================== +// +// Upper Skies on two sided walls +// +//========================================================================== + +void HWWall::SkyTop(HWDrawInfo *di, walltype * seg,sectortype * fs,sectortype * bs, FVector2& v1, FVector2& v2, float fch1, float fch2) +{ + if (fs->portalflags == PORTAL_SECTOR_CEILING || fs->portalflags == PORTAL_SECTOR_CEILING_REFLECT) + { + if (fs->portalflags == PORTAL_SECTOR_CEILING_REFLECT) + { + + /* + float backreflect = bs->GetReflect(sectortype::ceiling); + if (backreflect > 0 && bs->ceilingplane.fD() == fs->ceilingplane.fD() && !bs->isClosed()) + { + // Don't add intra-portal line to the portal. + return; + } + */ + } + else + { + if (bs->portalflags == PORTAL_SECTOR_CEILING && bs->portalnum == fs->portalnum) + { + return; + } + } + } + else if (fs->ceilingstat & CSTAT_SECTOR_SKY) + { + if (bs->ceilingstat & CSTAT_SECTOR_SKY) + { + return; + } + + flags |= HWF_SKYHACK; // mid textures on such lines need special treatment! + } + else return; + + ztop[0] = ztop[1] = 32768.0f; + zbottom[0] = fch1; + zbottom[1] = fch2; + SkyPlane(di, fs, plane_ceiling, true); +} + + +//========================================================================== +// +// Lower Skies on two sided walls +// +//========================================================================== + +void HWWall::SkyBottom(HWDrawInfo *di, walltype * seg,sectortype * fs,sectortype * bs, FVector2& v1, FVector2& v2, float ffh1, float ffh2) +{ + if (fs->portalflags == PORTAL_SECTOR_FLOOR || fs->portalflags == PORTAL_SECTOR_FLOOR_REFLECT) + { + if (fs->portalflags == PORTAL_SECTOR_FLOOR_REFLECT) + { + + /* + float backreflect = bs->GetReflect(sectortype::floor); + if (backreflect > 0 && bs->ceilingplane.fD() == fs->ceilingplane.fD() && !bs->isClosed()) + { + // Don't add intra-portal line to the portal. + return; + } + */ + } + else + { + if (bs->portalflags == PORTAL_SECTOR_FLOOR && bs->portalnum == fs->portalnum) + { + return; + } + } + // stacked sectors + } + else if (fs->floorstat & CSTAT_SECTOR_SKY) + { + + if (bs->floorstat & CSTAT_SECTOR_SKY) + { + return; + } + flags |= HWF_SKYHACK; // mid textures on such lines need special treatment! + } + else return; + + zbottom[0] = zbottom[1] = -32768.0f; + ztop[0] = ffh1; + ztop[1] = ffh2; + SkyPlane(di, fs, plane_floor, true); +} + diff --git a/source/core/rendering/scene/hw_skyportal.cpp b/source/core/rendering/scene/hw_skyportal.cpp new file mode 100644 index 000000000..457b97fe1 --- /dev/null +++ b/source/core/rendering/scene/hw_skyportal.cpp @@ -0,0 +1,110 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2003-2016 Christoph Oelckers +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//-------------------------------------------------------------------------- +// + +#include "filesystem.h" +#include "hw_skydome.h" +#include "hw_portal.h" +#include "hw_renderstate.h" +#include "skyboxtexture.h" + +CVAR(Float, skyoffsettest, 0, 0) + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- +void HWSkyPortal::DrawContents(HWDrawInfo *di, FRenderState &state) +{ + int indexed = hw_int_useindexedcolortextures; + hw_int_useindexedcolortextures = false; // this code does not work with indexed textures. + bool drawBoth = false; + auto &vp = di->Viewpoint; + + if (di->isSoftwareLighting()) + { + //di->SetFallbackLightMode(); + state.SetNoSoftLightLevel(); + } + + state.ResetColor(); + state.EnableFog(false); + state.AlphaFunc(Alpha_GEqual, 0.f); + state.SetRenderStyle(STYLE_Translucent); + bool oldClamp = state.SetDepthClamp(true); + + di->SetupView(state, 0, 0, 0, !!(mState->MirrorFlag & 1), !!(mState->PlaneMirrorFlag & 1)); + + state.SetVertexBuffer(vertexBuffer); + state.SetTextureMode(TM_OPAQUE); + auto skybox = origin->texture ? dynamic_cast(origin->texture->GetTexture()) : nullptr; + if (skybox) + { + vertexBuffer->RenderBox(state, skybox, origin->x_offset, false, /*di->Level->info->pixelstretch*/1, { 0, 0, 1 }, { 0, 0, 1 }); + } + else if (!origin->cloudy) + { + auto tex = origin->texture; + float texw = tex->GetDisplayWidth(); + float texh = tex->GetDisplayHeight(); + auto& modelMatrix = state.mModelMatrix; + auto& textureMatrix = state.mTextureMatrix; + auto texskyoffset = tex->GetSkyOffset() + skyoffsettest; + if (!(g_gameType & GAMEFLAG_PSEXHUMED)) texskyoffset += origin->y_offset; + + float repeat_fac = 1; + if (texh <= 256) + { + repeat_fac = 336.f / texh; + texh = 336; + } + modelMatrix.loadIdentity(); + modelMatrix.rotate(-180.0f + origin->x_offset, 0.f, 1.f, 0.f); + modelMatrix.translate(0.f, -40 + texskyoffset * skyoffsetfactor, 0.f); + modelMatrix.scale(1.f, texh / 400.f, 1.f); + textureMatrix.loadIdentity(); + state.EnableTextureMatrix(true); + textureMatrix.scale(1.f, repeat_fac, 1.f); + vertexBuffer->RenderDome(state, origin->texture, FSkyVertexBuffer::SKYMODE_MAINLAYER, true); + state.EnableTextureMatrix(false); + } + else + { + vertexBuffer->RenderDome(state, origin->texture, -origin->x_offset, origin->y_offset, false, FSkyVertexBuffer::SKYMODE_MAINLAYER, true); + } + state.SetTextureMode(TM_NORMAL); + if (origin->fadecolor & 0xffffff) + { + PalEntry FadeColor = origin->fadecolor; + state.EnableTexture(false); + state.SetObjectColor(FadeColor); + state.Draw(DT_Triangles, 0, 12); + state.EnableTexture(true); + state.SetObjectColor(0xffffffff); + } + + //di->lightmode = oldlightmode; + state.SetDepthClamp(oldClamp); + hw_int_useindexedcolortextures = indexed; + } + +const char *HWSkyPortal::GetName() { return "Sky"; } diff --git a/source/core/rendering/scene/hw_sprites.cpp b/source/core/rendering/scene/hw_sprites.cpp new file mode 100644 index 000000000..31ec01a47 --- /dev/null +++ b/source/core/rendering/scene/hw_sprites.cpp @@ -0,0 +1,534 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2002-2016 Christoph Oelckers +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//-------------------------------------------------------------------------- +// +/* +** gl_sprite.cpp +** Sprite/Particle rendering +** +*/ + +#include "matrix.h" +//#include "models.h" +#include "vectors.h" +#include "texturemanager.h" +#include "basics.h" + +//#include "hw_models.h" +#include "hw_drawstructs.h" +#include "hw_drawinfo.h" +#include "hw_portal.h" +#include "flatvertices.h" +#include "hw_cvars.h" +#include "hw_clock.h" +#include "hw_material.h" +#include "hw_dynlightdata.h" +#include "hw_lightbuffer.h" +#include "hw_renderstate.h" +#include "hw_models.h" +#include "hw_viewpointbuffer.h" +#include "hw_voxels.h" + +//========================================================================== +// +// +// +//========================================================================== + +void HWSprite::DrawSprite(HWDrawInfo* di, FRenderState& state, bool translucent) +{ + bool additivefog = false; + bool foglayer = false; + auto& vp = di->Viewpoint; + + if (translucent) + { + // The translucent pass requires special setup for the various modes. + + // for special render styles brightmaps would not look good - especially for subtractive. + if (RenderStyle.BlendOp != STYLEOP_Add) + { + state.EnableBrightmap(false); + } + + state.SetRenderStyle(RenderStyle); + state.SetTextureMode(RenderStyle); + + if (texture && !checkTranslucentReplacement(texture->GetID(), palette)) state.AlphaFunc(Alpha_GEqual, texture->alphaThreshold); + else state.AlphaFunc(Alpha_Greater, 0.f); + + if (RenderStyle.BlendOp == STYLEOP_Add && RenderStyle.DestAlpha == STYLEALPHA_One) + { + additivefog = true; + } + } + + if (dynlightindex == -1) // only set if we got no light buffer index. This covers all cases where sprite lighting is used. + { + float out[3] = {}; + //di->GetDynSpriteLight(gl_light_sprites ? actor : nullptr, gl_light_particles ? particle : nullptr, out); + //state.SetDynLight(out[0], out[1], out[2]); + } + + + if (RenderStyle.Flags & STYLEF_FadeToBlack) + { + fade = 0; + additivefog = true; + } + + if (RenderStyle.BlendOp == STYLEOP_RevSub || RenderStyle.BlendOp == STYLEOP_Sub) + { + if (!modelframe) + { + // non-black fog with subtractive style needs special treatment + if (fade != 0) + { + foglayer = true; + // Due to the two-layer approach we need to force an alpha test that lets everything pass + state.AlphaFunc(Alpha_Greater, 0); + } + } + else RenderStyle.BlendOp = STYLEOP_Fuzz; // subtractive with models is not going to work. + } + + SetLightAndFog(state, fade, palette, shade, visibility, alpha, this->shade <= numshades); + + if (modelframe == 0) + { + state.SetMaterial(texture, UF_Texture, 0, CLAMP_XY, TRANSLATION(Translation_Remap + curbasepal, palette), -1); + state.SetNormal(0, 0, 0); + + + if (screen->BuffersArePersistent()) + { + CreateVertices(di); + } + state.SetLightIndex(-1); + state.Draw(DT_TriangleStrip, vertexindex, 4); + + if (foglayer) + { + bool foggy = (GlobalMapFog || (fade & 0xffffff)); + auto ShadeDiv = lookups.tables[palette].ShadeFactor; + if (ShadeDiv >= 1 / 1000.f && foggy) + { + // If we get here we know that we have colored fog and no fixed colormap. + float density = GlobalMapFog ? GlobalFogDensity : 350.f - Scale(numshades - shade, 150, numshades); + state.SetFog((GlobalMapFog) ? GlobalMapFog : fade, density * hw_density); + state.SetTextureMode(TM_FOGLAYER); + state.SetRenderStyle(STYLE_Translucent); + state.Draw(DT_TriangleStrip, vertexindex, 4); + state.SetTextureMode(TM_NORMAL); + } + } + } + else + { + state.EnableModelMatrix(true); + state.mModelMatrix = rotmat; + FHWModelRenderer mr(state, dynlightindex); + if (modelframe < 0) + { + auto model = voxel->model; + state.SetDepthFunc(DF_LEqual); + state.EnableTexture(true); + model->BuildVertexBuffer(&mr); + mr.SetupFrame(model, 0, 0, 0); + model->RenderFrame(&mr, TexMan.GetGameTexture(model->GetPaletteTexture()), 0, 0, 0.f, TRANSLATION(Translation_Remap + curbasepal, palette)); + state.SetDepthFunc(DF_Less); + state.SetVertexBuffer(screen->mVertexData); + + } + else + { + //RenderModel(&renderer, x, y, z, modelframe, actor, di->Viewpoint.TicFrac); + } + state.SetVertexBuffer(screen->mVertexData); + state.EnableModelMatrix(false); + } + + if (translucent) + { + state.EnableBrightmap(true); + state.SetRenderStyle(STYLE_Translucent); + state.SetTextureMode(TM_NORMAL); + } + + state.SetObjectColor(0xffffffff); + state.SetAddColor(0); + state.EnableTexture(true); + state.SetDynLight(0, 0, 0); +} + + +//========================================================================== +// +// +// +//========================================================================== + +void HWSprite::CalculateVertices(HWDrawInfo* di, FVector3* v, DVector3* vp) +{ + const auto& HWAngles = di->Viewpoint.HWAngles; + +#if 0 // maybe later, it's a bit more tricky with Build. + // [BB] Billboard stuff + const bool drawWithXYBillboard = false; + const bool drawBillboardFacingCamera = false; + + + // [Nash] check for special sprite drawing modes + if (drawWithXYBillboard || drawBillboardFacingCamera) + { + // Compute center of sprite + float xcenter = (x1 + x2) * 0.5; + float ycenter = (y1 + y2) * 0.5; + float zcenter = (z1 + z2) * 0.5; + float xx = -xcenter + x; + float zz = -zcenter + z; + float yy = -ycenter + y; + Matrix3x4 mat; + mat.MakeIdentity(); + mat.Translate(xcenter, zcenter, ycenter); // move to sprite center + + // Order of rotations matters. Perform yaw rotation (Y, face camera) before pitch (X, tilt up/down). + if (drawBillboardFacingCamera) + { + // [CMB] Rotate relative to camera XY position, not just camera direction, + // which is nicer in VR + float xrel = xcenter - vp->X; + float yrel = ycenter - vp->Y; + float absAngleDeg = RAD2DEG(atan2(-yrel, xrel)); + float counterRotationDeg = 270. - HWAngles.Yaw.Degrees; // counteracts existing sprite rotation + float relAngleDeg = counterRotationDeg + absAngleDeg; + + mat.Rotate(0, 1, 0, relAngleDeg); + } + + // [fgsfds] calculate yaw vectors + float yawvecX = 0, yawvecY = 0, rollDegrees = 0; + float angleRad = (270. - HWAngles.Yaw).Radians(); + + if (drawWithXYBillboard) + { + // Rotate the sprite about the vector starting at the center of the sprite + // triangle strip and with direction orthogonal to where the player is looking + // in the x/y plane. + mat.Rotate(-sin(angleRad), 0, cos(angleRad), -HWAngles.Pitch.Degrees); + } + + mat.Translate(-xcenter, -zcenter, -ycenter); // retreat from sprite center + v[0] = mat * FVector3(x1, z1, y1); + v[1] = mat * FVector3(x2, z1, y2); + v[2] = mat * FVector3(x1, z2, y1); + v[3] = mat * FVector3(x2, z2, y2); + } + else // traditional "Y" billboard mode +#endif + { + v[0] = FVector3(x1, z1, y1); + v[1] = FVector3(x2, z1, y2); + v[2] = FVector3(x1, z2, y1); + v[3] = FVector3(x2, z2, y2); + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void HWSprite::CreateVertices(HWDrawInfo* di) +{ + if (modelframe == 0) + { + FVector3 v[4]; + CalculateVertices(di, v, &di->Viewpoint.Pos); + auto vert = screen->mVertexData->AllocVertices(4); + auto vp = vert.first; + vertexindex = vert.second; + + vp[0].Set(v[0][0], v[0][1], v[0][2], ul, vt); + vp[1].Set(v[1][0], v[1][1], v[1][2], ur, vt); + vp[2].Set(v[2][0], v[2][1], v[2][2], ul, vb); + vp[3].Set(v[3][0], v[3][1], v[3][2], ur, vb); + } +} + +//========================================================================== +// +// +// +//========================================================================== + +inline void HWSprite::PutSprite(HWDrawInfo* di, bool translucent) +{ + // That's a lot of checks... + /* + if (modelframe == 1 && gl_light_sprites) + { + hw_GetDynModelLight(actor, lightdata); + dynlightindex = screen->mLights->UploadLights(lightdata); + } + else*/ + dynlightindex = -1; + + vertexindex = -1; + if (!screen->BuffersArePersistent()) + { + CreateVertices(di); + } + di->AddSprite(this, translucent); +} + +//========================================================================== +// +// +// +//========================================================================== + +void HWSprite::Process(HWDrawInfo* di, spritetype* spr, sectortype* sector, int thruportal) +{ + if (spr == nullptr) + return; + + auto tex = tileGetTexture(spr->picnum); + if (!tex || !tex->isValid()) return; + + texture = tex; + sprite = spr; + + modelframe = 0; + dynlightindex = -1; + shade = spr->shade; + palette = spr->pal; + fade = lookups.getFade(sector->floorpal); // fog is per sector. + visibility = sectorVisibility(sector); + + SetSpriteTranslucency(spr, alpha, RenderStyle); + + x = spr->x * (1 / 16.f); + z = spr->z * (1 / -256.f); + y = spr->y * (1 / -16.f); + auto vp = di->Viewpoint; + + if ((vp.Pos.XY() - DVector2(x, y)).LengthSquared() < 0.125) return; + + if (modelframe == 0) + { + int flags = spr->cstat; + int tilenum = spr->picnum; + + int xsize, ysize, tilexoff, tileyoff; + if (hw_hightile && TileFiles.tiledata[tilenum].hiofs.xsize) + { + xsize = TileFiles.tiledata[tilenum].hiofs.xsize; + ysize = TileFiles.tiledata[tilenum].hiofs.ysize; + tilexoff = TileFiles.tiledata[tilenum].hiofs.xoffs; + tileyoff = TileFiles.tiledata[tilenum].hiofs.yoffs; + } + else + { + xsize = (int)tex->GetDisplayWidth(); + ysize = (int)tex->GetDisplayHeight(); + tilexoff = (int)tex->GetDisplayLeftOffset(); + tileyoff = (int)tex->GetDisplayTopOffset(); + + } + + int heinum = 0; // tspriteGetSlope(tspr); // todo: slope sprites + + if (heinum == 0) + { + tilexoff += spr->xoffset; + tileyoff += spr->yoffset; + } + + // convert to render space. + float width = (xsize * spr->xrepeat) * (0.2f / 16.f); // weird Build fuckery. Face sprites are rendered at 80% width only. + float height = (ysize * spr->yrepeat) * (0.25f / 16.f); + float xoff = (tilexoff * spr->xrepeat) * (0.2f / 16.f); + float yoff = (tileyoff * spr->yrepeat) * (0.25f / 16.f); + + if (xsize & 1) xoff -= spr->xrepeat * (0.1f / 16.f); // Odd xspans (taken from polymost as-is) + + if (spr->cstat & CSTAT_SPRITE_YCENTER) + { + yoff -= height * 0.5; + if (ysize & 1) yoff -= spr->yrepeat * (0.125f / 16.f); // Odd yspans (taken from polymost as-is) + } + + if (flags & CSTAT_SPRITE_XFLIP) xoff = -xoff; + //if (flags & CSTAT_SPRITE_YFLIP) yoff = -yoff; According to Polymost this must not be done. + + ul = (spr->cstat & CSTAT_SPRITE_XFLIP) ? 0.f : 1.f; + ur = (spr->cstat & CSTAT_SPRITE_XFLIP) ? 1.f : 0.f; + vt = (spr->cstat & CSTAT_SPRITE_YFLIP) ? 0.f : 1.f; + vb = (spr->cstat & CSTAT_SPRITE_YFLIP) ? 1.f : 0.f; + if (tex->isHardwareCanvas()) std::swap(vt, vb); + + float viewvecX = vp.ViewVector.X; + float viewvecY = vp.ViewVector.Y; + + x = spr->x * (1 / 16.f); + y = spr->y * (1 / -16.f); + z = spr->z * (1 / -256.f); + + x1 = x - viewvecY * (xoff - (width * 0.5f)); + x2 = x - viewvecY * (xoff + (width * 0.5f)); + y1 = y + viewvecX * (xoff - (width * 0.5f)); + y2 = y + viewvecX * (xoff + (width * 0.5f)); + + z1 = z + yoff; + z2 = z + height + yoff; + if (z1 < z2) + { + // Make sure that z1 is the higher one. Some utilities expect it to be oriented this way. + std::swap(z1, z2); + std::swap(vt, vb); + } + } + else + { + x1 = x2 = x; + y1 = y2 = y; + z1 = z2 = z; + texture = nullptr; + } + + depth = (float)((x - vp.Pos.X) * vp.TanCos + (y - vp.Pos.Y) * vp.TanSin); + +#if 0 // huh? + if ((texture && texture->GetTranslucency()) || (RenderStyle.Flags & STYLEF_RedIsAlpha) || (modelframe && spr->RenderStyle != DefaultRenderStyle())) + { + if (hw_styleflags == STYLEHW_Solid) + { + RenderStyle.SrcAlpha = STYLEALPHA_Src; + RenderStyle.DestAlpha = STYLEALPHA_InvSrc; + } + hw_styleflags = STYLEHW_NoAlphaTest; + } +#endif + + PutSprite(di, true); + rendered_sprites++; +} + + +//========================================================================== +// +// +// +//========================================================================== + +bool HWSprite::ProcessVoxel(HWDrawInfo* di, voxmodel_t* vox, spritetype* spr, sectortype* sector, bool rotate) +{ + sprite = spr; + auto sprext = &spriteext[spr->owner]; + + texture = nullptr; + modelframe = -1; + dynlightindex = -1; + shade = spr->shade; + palette = spr->pal; + fade = lookups.getFade(sector->floorpal); // fog is per sector. + visibility = sectorVisibility(sector); + voxel = vox; + + auto ang = spr->ang + sprext->angoff; + if ((spr->cstat & CSTAT_SPRITE_MDLROTATE) || rotate) + { + int myclock = (PlayClock << 3) + MulScale(4 << 3, (int)di->Viewpoint.TicFrac, 16); + ang = (ang + myclock) & 2047; + } + + + if (!vox || (spr->cstat & CSTAT_SPRITE_ALIGNMENT) == CSTAT_SPRITE_ALIGNMENT_FLOOR) return false; + + SetSpriteTranslucency(spr, alpha, RenderStyle); + + + FVector3 scalevec = { voxel->scale, voxel->scale, voxel->scale }; + FVector3 translatevec = { 0, 0, voxel->zadd * voxel->scale }; + + float basescale = voxel->bscale / 64.f; + float sprxscale = (float)spr->xrepeat * (256.f / 320.f) * basescale; + if ((::sprite[spr->owner].cstat & CSTAT_SPRITE_ALIGNMENT) == CSTAT_SPRITE_ALIGNMENT_WALL) + { + sprxscale *= 1.25f; + translatevec.Y -= spr->xoffset * bcosf(sprext->angoff, -20); + translatevec.X += spr->xoffset * bsinf(sprext->angoff, -20); + } + + if (spr->cstat & CSTAT_SPRITE_YFLIP) + { + scalevec.Z = -scalevec.Z; + translatevec.Z = -translatevec.Z; + } + if (spr->cstat & CSTAT_SPRITE_XFLIP) + { + scalevec.X = -scalevec.X; + translatevec.X = -translatevec.X; + translatevec.Y = -translatevec.Y; + } + + scalevec.X *= sprxscale; + translatevec.X *= sprxscale; + scalevec.Y *= sprxscale; + translatevec.Y *= sprxscale; + float sprzscale = (float)spr->yrepeat * basescale; + scalevec.Z *= sprzscale; + translatevec.Z *= sprzscale; + + float zpos = (float)(spr->z + sprext->position_offset.z); + float zscale = ((spr->cstat & CSTAT_SPRITE_YFLIP) && (::sprite[spr->owner].cstat & CSTAT_SPRITE_ALIGNMENT) != 0) ? -4.f : 4.f; + zpos -= (spr->yoffset * spr->yrepeat) * zscale * voxel->bscale; + + x = (spr->x + sprext->position_offset.x) * (1 / 16.f); + z = zpos * (1 / -256.f); + y = (spr->y + sprext->position_offset.y) * (1 / -16.f); + + float zoff = voxel->siz.z * .5f; + if (!(spr->cstat & CSTAT_SPRITE_YCENTER)) + zoff += voxel->piv.z; + else if ((spr->cstat & CSTAT_SPRITE_ALIGNMENT) != CSTAT_SPRITE_ALIGNMENT_SLAB) + { + zoff += voxel->piv.z; + zoff -= voxel->siz.z * .5f; + } + if (spr->cstat & CSTAT_SPRITE_YFLIP) zoff = voxel->siz.z - zoff; + + rotmat.loadIdentity(); + rotmat.translate(x + translatevec.X, z - translatevec.Z, y - translatevec.Y); + rotmat.rotate(buildang(ang).asdeg() - 90.f, 0, 1, 0); + rotmat.scale(scalevec.X, scalevec.Z, scalevec.Y); + // Apply pivot last + rotmat.translate(-voxel->piv.x, zoff, voxel->piv.y); + + + + auto vp = di->Viewpoint; + depth = (float)((x - vp.Pos.X) * vp.TanCos + (y - vp.Pos.Y) * vp.TanSin); + PutSprite(di, spriteHasTranslucency(sprite)); + rendered_sprites++; + return true; +} diff --git a/source/core/rendering/scene/hw_walls.cpp b/source/core/rendering/scene/hw_walls.cpp new file mode 100644 index 000000000..1bdc3a944 --- /dev/null +++ b/source/core/rendering/scene/hw_walls.cpp @@ -0,0 +1,1126 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2000-2016 Christoph Oelckers +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//-------------------------------------------------------------------------- +// + +#include "texturemanager.h" +#include "hw_dynlightdata.h" +#include "hw_material.h" +#include "hw_cvars.h" +#include "hw_clock.h" +//#include "hw_lighting.h" +#include "hw_drawinfo.h" +#include "hw_portal.h" +#include "hw_lightbuffer.h" +#include "hw_renderstate.h" +#include "hw_skydome.h" +#include "hw_drawstructs.h" +#include "gamefuncs.h" +#include "cmdlib.h" + +#include "v_video.h" +#include "flatvertices.h" +#include "glbackend/glbackend.h" + +//========================================================================== +// +// Create vertices for one wall +// +//========================================================================== + +int HWWall::CreateVertices(FFlatVertex*& ptr, bool split) +{ + auto oo = ptr; + ptr->Set(glseg.x1, zbottom[0], glseg.y1, tcs[LOLFT].u, tcs[LOLFT].v); + ptr++; + ptr->Set(glseg.x1, ztop[0], glseg.y1, tcs[UPLFT].u, tcs[UPLFT].v); + ptr++; + ptr->Set(glseg.x2, ztop[1], glseg.y2, tcs[UPRGT].u, tcs[UPRGT].v); + ptr++; + ptr->Set(glseg.x2, zbottom[1], glseg.y2, tcs[LORGT].u, tcs[LORGT].v); + ptr++; + return int(ptr - oo); +} + +//========================================================================== +// +// build the vertices for this wall +// +//========================================================================== + +void HWWall::MakeVertices(HWDrawInfo* di, bool nosplit) +{ + if (vertcount == 0) + { + auto ret = screen->mVertexData->AllocVertices(4); + vertindex = ret.second; + vertcount = CreateVertices(ret.first, false); + } +} + +//========================================================================== +// +// General purpose wall rendering function +// everything goes through here +// +//========================================================================== + +void HWWall::RenderWall(HWDrawInfo *di, FRenderState &state, int textured) +{ + assert(vertcount > 0); + state.SetLightIndex(dynlightindex); + state.Draw(DT_TriangleFan, vertindex, vertcount); + vertexcount += vertcount; +} + +//========================================================================== +// +// +// +//========================================================================== + +void HWWall::RenderFogBoundary(HWDrawInfo *di, FRenderState &state) +{ + if (gl_fogmode)// && !di->isFullbrightScene()) + { + state.EnableDrawBufferAttachments(false); + SetLightAndFog(state, fade, palette, shade, visibility, alpha); + state.SetEffect(EFF_FOGBOUNDARY); + state.AlphaFunc(Alpha_GEqual, 0.f); + state.SetDepthBias(-1, -128); + RenderWall(di, state, HWWall::RWF_BLANK); + state.ClearDepthBias(); + state.SetEffect(EFF_NONE); + state.EnableDrawBufferAttachments(true); + } +} + +//========================================================================== +// +// +// +//========================================================================== +void HWWall::RenderMirrorSurface(HWDrawInfo *di, FRenderState &state) +{ + if (!TexMan.mirrorTexture.isValid()) return; + + state.SetDepthFunc(DF_LEqual); + + // we use texture coordinates and texture matrix to pass the normal stuff to the shader so that the default vertex buffer format can be used as is. + state.EnableTextureMatrix(true); + + // Use sphere mapping for this + state.SetEffect(EFF_SPHEREMAP); + SetLightAndFog(state, fade, palette, shade, visibility, alpha, false); + state.SetColor(PalEntry(25, globalr >> 1, globalg >> 1, globalb >> 1)); + + state.SetRenderStyle(STYLE_Add); + state.AlphaFunc(Alpha_Greater, 0); + + auto tex = TexMan.GetGameTexture(TexMan.mirrorTexture, false); + state.SetMaterial(tex, UF_None, 0, CLAMP_NONE, 0, -1); // do not upscale the mirror texture. + + RenderWall(di, state, HWWall::RWF_BLANK); + + state.EnableTextureMatrix(false); + state.SetEffect(EFF_NONE); + state.AlphaFunc(Alpha_GEqual, 0.5f); + + state.SetDepthFunc(DF_Less); + state.SetRenderStyle(STYLE_Translucent); +} + +//========================================================================== +// +// +// +//========================================================================== + +void HWWall::RenderTexturedWall(HWDrawInfo *di, FRenderState &state, int rflags) +{ + SetLightAndFog(state, fade, palette, shade, visibility, alpha); + state.SetMaterial(texture, UF_Texture, 0, (flags & (HWF_CLAMPX | HWF_CLAMPY)), TRANSLATION(Translation_Remap + curbasepal, palette), -1); + + if (sprite == nullptr) + { + int h = (int)texture->GetDisplayHeight(); + int h2 = 1 << sizeToBits(h); + if (h2 < h) h2 *= 2; + if (h != h2) + { + float xOffset = 1.f / texture->GetDisplayWidth(); + state.SetNpotEmulation(float(h2) / h, xOffset); + } + } + + RenderWall(di, state, rflags); + + state.SetNpotEmulation(0.f, 0.f); + /* none of these functions is in use. + state.SetObjectColor(0xffffffff); + state.SetObjectColor2(0); + state.SetAddColor(0); + state.SetTextureMode(tmode); + state.EnableGlow(false); + state.EnableGradient(false); + state.ApplyTextureManipulation(nullptr); + */ +} + +//========================================================================== +// +// +// +//========================================================================== + +void HWWall::RenderTranslucentWall(HWDrawInfo *di, FRenderState &state) +{ + if (RenderStyle.BlendOp != STYLEOP_Add) + { + state.EnableBrightmap(false); + } + + state.SetRenderStyle(RenderStyle); + state.SetTextureMode(RenderStyle); + if (texture && !checkTranslucentReplacement(texture->GetID(), palette)) state.AlphaFunc(Alpha_GEqual, texture->alphaThreshold); + else state.AlphaFunc(Alpha_GEqual, 0.f); + RenderTexturedWall(di, state, HWWall::RWF_TEXTURED); + state.SetRenderStyle(STYLE_Translucent); + state.EnableBrightmap(true); +} + +//========================================================================== +// +// +// +//========================================================================== +void HWWall::DrawWall(HWDrawInfo *di, FRenderState &state, bool translucent) +{ + if (screen->BuffersArePersistent()) + { + /* + if (di->Level->HasDynamicLights && !di->isFullbrightScene() && texture != nullptr) + { + SetupLights(di, lightdata); + } + */ + MakeVertices(di, !!(flags & HWWall::HWF_TRANSLUCENT)); + } + + state.SetNormal(glseg.Normal()); + if (!translucent) + { + RenderTexturedWall(di, state, HWWall::RWF_TEXTURED); + } + else + { + switch (type) + { + case RENDERWALL_MIRRORSURFACE: + RenderMirrorSurface(di, state); + break; + + case RENDERWALL_FOGBOUNDARY: + RenderFogBoundary(di, state); + break; + + default: + RenderTranslucentWall(di, state); + break; + } + } +} + +//========================================================================== +// +// Collect lights for shader +// +//========================================================================== +#if 0 +void HWWall::SetupLights(HWDrawInfo *di, FDynLightData &lightdata) +{ + lightdata.Clear(); + + if (RenderStyle == STYLE_Add && !di->Level->lightadditivesurfaces) return; // no lights on additively blended surfaces. + + // check for wall types which cannot have dynamic lights on them (portal types never get here so they don't need to be checked.) + switch (type) + { + case RENDERWALL_FOGBOUNDARY: + case RENDERWALL_MIRRORSURFACE: + case RENDERWALL_COLOR: + return; + } + + float vtx[]={glseg.x1,zbottom[0],glseg.y1, glseg.x1,ztop[0],glseg.y1, glseg.x2,ztop[1],glseg.y2, glseg.x2,zbottom[1],glseg.y2}; + Plane p; + + + auto normal = glseg.Normal(); + p.Set(normal, -normal.X * glseg.x1 - normal.Z * glseg.y1); + + FLightNode *node; + if (seg->sidedef == NULL) + { + node = NULL; + } + else if (!(seg->sidedef->Flags & WALLF_POLYOBJ)) + { + node = seg->sidedef->lighthead; + } + else if (sub) + { + // Polobject segs cannot be checked per sidedef so use the subsector instead. + node = sub->section->lighthead; + } + else node = NULL; + + // Iterate through all dynamic lights which touch this wall and render them + while (node) + { + if (node->lightsource->IsActive()) + { + iter_dlight++; + + DVector3 posrel = node->lightsource->PosRelative(seg->frontsector->PortalGroup); + float x = posrel.X; + float y = posrel.Y; + float z = posrel.Z; + float dist = fabsf(p.DistToPoint(x, z, y)); + float radius = node->lightsource->GetRadius(); + float scale = 1.0f / ((2.f * radius) - dist); + FVector3 fn, pos; + + if (radius > 0.f && dist < radius) + { + FVector3 nearPt, up, right; + + pos = { x, z, y }; + fn = p.Normal(); + + fn.GetRightUp(right, up); + + FVector3 tmpVec = fn * dist; + nearPt = pos + tmpVec; + + FVector3 t1; + int outcnt[4]={0,0,0,0}; + texcoord tcs[4]; + + // do a quick check whether the light touches this polygon + for(int i=0;i<4;i++) + { + t1 = FVector3(&vtx[i*3]); + FVector3 nearToVert = t1 - nearPt; + tcs[i].u = ((nearToVert | right) * scale) + 0.5f; + tcs[i].v = ((nearToVert | up) * scale) + 0.5f; + + if (tcs[i].u<0) outcnt[0]++; + if (tcs[i].u>1) outcnt[1]++; + if (tcs[i].v<0) outcnt[2]++; + if (tcs[i].v>1) outcnt[3]++; + + } + if (outcnt[0]!=4 && outcnt[1]!=4 && outcnt[2]!=4 && outcnt[3]!=4) + { + draw_dlight += GetLight(lightdata, seg->frontsector->PortalGroup, p, node->lightsource, true); + } + } + } + node = node->nextLight; + } + dynlightindex = screen->mLights->UploadLights(lightdata); +} +#endif + +//========================================================================== +// +// +// +//========================================================================== +void HWWall::PutWall(HWDrawInfo *di, bool translucent) +{ + if (translucent || (texture && texture->GetTranslucency() && type == RENDERWALL_M2S)) + { + flags |= HWF_TRANSLUCENT; + ViewDistance = (di->Viewpoint.Pos.XY() - DVector2((glseg.x1 + glseg.x2) * 0.5f, (glseg.y1 + glseg.y2) * 0.5f)).LengthSquared(); + } + + if (texture->isHardwareCanvas()) + { + tcs[UPLFT].v = 1.f - tcs[UPLFT].v; + tcs[LOLFT].v = 1.f - tcs[LOLFT].v; + tcs[UPRGT].v = 1.f - tcs[UPRGT].v; + tcs[LORGT].v = 1.f - tcs[LORGT].v; + } + + + if (!screen->BuffersArePersistent()) + { + /* + if (di->Level->HasDynamicLights && !di->isFullbrightScene() && texture != nullptr) + { + SetupLights(di, lightdata); + } + */ + MakeVertices(di, translucent); + } + + di->AddWall(this); + // make sure that following parts of the same linedef do not get this one's vertex and lighting info. + vertcount = 0; + dynlightindex = -1; + flags &= ~(HWF_TRANSLUCENT|HWF_CLAMPX|HWF_CLAMPY); +} + +//========================================================================== +// +// will be done later. +// +//========================================================================== + +void HWWall::PutPortal(HWDrawInfo *di, int ptype, int plane) +{ + HWPortal * portal = nullptr; + + MakeVertices(di, false); + switch (ptype) + { +#if 0 + // portals don't go into the draw list. + // Instead they are added to the portal manager + case PORTALTYPE_HORIZON: + horizon = portalState.UniqueHorizons.Get(horizon); + portal = di->FindPortal(horizon); + if (!portal) + { + portal = new HWHorizonPortal(&portalState, horizon, di->Viewpoint); + di->Portals.Push(portal); + } + portal->AddLine(this); + break; + + case PORTALTYPE_SKYBOX: + portal = di->FindPortal(secportal); + if (!portal) + { + // either a regular skybox or an Eternity-style horizon + if (secportal->mType != PORTS_SKYVIEWPOINT) portal = new HWEEHorizonPortal(&portalState, secportal); + else + { + portal = new HWSkyboxPortal(&portalState, secportal); + di->Portals.Push(portal); + } + } + portal->AddLine(this); + break; +#endif + + case PORTALTYPE_SECTORSTACK: + portal = di->FindPortal(this->portal); + if (!portal) + { + portal = new HWSectorStackPortal(&portalState, this->portal); + di->Portals.Push(portal); + } + portal->AddLine(this); + break; + +#if 0 + case PORTALTYPE_PLANEMIRROR: + if (portalState.PlaneMirrorMode * planemirror->fC() <= 0) + { + planemirror = portalState.UniquePlaneMirrors.Get(planemirror); + portal = di->FindPortal(planemirror); + if (!portal) + { + portal = new HWPlaneMirrorPortal(&portalState, planemirror); + di->Portals.Push(portal); + } + portal->AddLine(this); + } + break; +#endif + case PORTALTYPE_MIRROR: + // These are unique. No need to look existing ones up. + portal = new HWMirrorPortal(&portalState, seg); + di->Portals.Push(portal); + portal->AddLine(this); + if (gl_mirror_envmap) + { + // draw a reflective layer over the mirror + di->AddMirrorSurface(this); + } + break; + + case PORTALTYPE_LINETOLINE: + // These are also unique. + portal = new HWLineToLinePortal(&portalState, seg, &wall[seg->portalnum]); + di->Portals.Push(portal); + portal->AddLine(this); + break; + + case PORTALTYPE_LINETOSPRITE: + // These are also unique. + portal = new HWLineToSpritePortal(&portalState, seg, &sprite[seg->portalnum]); + di->Portals.Push(portal); + portal->AddLine(this); + break; + + case PORTALTYPE_SKY: + sky = portalState.UniqueSkies.Get(sky); + portal = di->FindPortal(sky); + if (!portal) + { + portal = new HWSkyPortal(screen->mSkyData, &portalState, sky); + di->Portals.Push(portal); + } + portal->AddLine(this); + break; + } + vertcount = 0; + + if (plane != -1 && portal) + { + portal->planesused |= (1<GetPlaneTexZ(sector_t::ceiling); + zbottom[1] = zbottom[0] = fs->GetPlaneTexZ(sector_t::floor); + + auto vpz = di->Viewpoint.Pos.Z; + if (vpz < fs->GetPlaneTexZ(sector_t::ceiling)) + { + if (vpz > fs->GetPlaneTexZ(sector_t::floor)) + zbottom[1] = zbottom[0] = vpz; + + if (fs->GetTexture(sector_t::ceiling) == skyflatnum) + { + SkyPlane(di, fs, sector_t::ceiling, false); + } + else + { + horizon = &hi; + PutPortal(di, PORTALTYPE_HORIZON, -1); + } + ztop[1] = ztop[0] = zbottom[0]; + } + + if (vpz > fs->GetPlaneTexZ(sector_t::floor)) + { + zbottom[1] = zbottom[0] = fs->GetPlaneTexZ(sector_t::floor); + if (fs->GetTexture(sector_t::floor) == skyflatnum) + { + SkyPlane(di, fs, sector_t::floor, false); + } + else + { + horizon = &hi; + PutPortal(di, PORTALTYPE_HORIZON, -1); + } + } +#endif + return true; +} + +//========================================================================== +// +// +// +//========================================================================== + +bool HWWall::SetWallCoordinates(walltype * seg, float topleft, float topright, float bottomleft, float bottomright) +{ + // + // + // set up coordinates for the left side of the polygon + // + // check left side for intersections + if (topleft >= bottomleft) + { + // normal case + ztop[0] = topleft; + zbottom[0] = bottomleft; + } + else + { + // ceiling below floor - clip to the visible part of the wall + float dch = topright - topleft; + float dfh = bottomright - bottomleft; + float inter_x = (bottomleft - topleft) / (dch - dfh); + float inter_y = topleft + inter_x * dch; + + glseg.x1 = glseg.x1 + inter_x * (glseg.x2 - glseg.x1); + glseg.y1 = glseg.y1 + inter_x * (glseg.y2 - glseg.y1); + glseg.fracleft = inter_x; + + zbottom[0] = ztop[0] = inter_y; + } + + // + // + // set up coordinates for the right side of the polygon + // + // check left side for intersections + if (topright >= bottomright) + { + // normal case + ztop[1] = topright; + zbottom[1] = bottomright; + } + else + { + // ceiling below floor - clip to the visible part of the wall + float dch = topright - topleft; + float dfh = bottomright - bottomleft; + float inter_x = (bottomleft - topleft) / (dch - dfh); + + float inter_y = topleft + inter_x * dch; + + glseg.x2 = glseg.x1 + inter_x * (glseg.x2 - glseg.x1); + glseg.y2 = glseg.y1 + inter_x * (glseg.y2 - glseg.y1); + glseg.fracright = inter_x; + + zbottom[1] = ztop[1] = inter_y; + } + + return true; +} + +//========================================================================== +// +// Do some tweaks with the texture coordinates to reduce visual glitches +// +//========================================================================== + +void HWWall::CheckTexturePosition() +{ + float sub; + + if (texture->isHardwareCanvas()) return; + + // clamp texture coordinates to a reasonable range. + // Extremely large values can cause visual problems + if (tcs[UPLFT].v < tcs[LOLFT].v || tcs[UPRGT].v < tcs[LORGT].v) + { + if (tcs[UPLFT].v < tcs[UPRGT].v) + { + sub = float(xs_FloorToInt(tcs[UPLFT].v)); + } + else + { + sub = float(xs_FloorToInt(tcs[UPRGT].v)); + } + tcs[UPLFT].v -= sub; + tcs[UPRGT].v -= sub; + tcs[LOLFT].v -= sub; + tcs[LORGT].v -= sub; + } + else + { + if (tcs[LOLFT].v < tcs[LORGT].v) + { + sub = float(xs_FloorToInt(tcs[LOLFT].v)); + } + else + { + sub = float(xs_FloorToInt(tcs[LORGT].v)); + } + tcs[UPLFT].v -= sub; + tcs[UPRGT].v -= sub; + tcs[LOLFT].v -= sub; + tcs[LORGT].v -= sub; + } + if (tcs[UPLFT].u >= 0.f && tcs[UPRGT].u >= 0.f && tcs[LOLFT].u >= 0.f && tcs[LORGT].u >= 0.f && + tcs[UPLFT].u <= 1.f && tcs[UPRGT].u <= 1.f && tcs[LOLFT].u <= 1.f && tcs[LORGT].u <= 1.f) + { + flags |= HWF_CLAMPX; + } + + if (tcs[UPLFT].v >= 0.f && tcs[UPRGT].v >= 0.f && tcs[LOLFT].v >= 0.f && tcs[LORGT].v >= 0.f && + tcs[UPLFT].v <= 1.f && tcs[UPRGT].v <= 1.f && tcs[LOLFT].v <= 1.f && tcs[LORGT].v <= 1.f) + { + flags |= HWF_CLAMPY; + } + +} + + +//========================================================================== +// +// Common part of wall drawers +// +//========================================================================== + +void HWWall::DoTexture(HWDrawInfo* di, walltype* wal, walltype* refwall, float refheight, float topleft, float topright, float bottomleft, float bottomright) +{ + auto glsave = glseg; + SetWallCoordinates(wal, topleft, topright, bottomleft, bottomright); + + bool xflipped = (wal->cstat & CSTAT_WALL_XFLIP); + float leftdist = xflipped ? 1.f - glseg.fracleft : glseg.fracleft; + float rightdist = xflipped ? 1.f - glseg.fracright : glseg.fracright; + + float tw = texture->GetDisplayWidth(); + float th = texture->GetDisplayHeight(); + int pow2size = 1 << sizeToBits(th); + if (pow2size < th) pow2size *= 2; + float ypanning = refwall->ypan_ ? pow2size * refwall->ypan_ / (256.0f * th) : 0; + + tcs[LOLFT].u = tcs[UPLFT].u = ((leftdist * 8.f * wal->xrepeat) + refwall->xpan_) / tw; + tcs[LORGT].u = tcs[UPRGT].u = ((rightdist * 8.f * wal->xrepeat) + refwall->xpan_) / tw; + + auto setv = [=](float hl, float hr, float frac) -> float + { + float h = hl + (hr - hl) * frac; + h = (-(float)(refheight + (h * 256)) / ((th * 2048.0f) / (float)(wal->yrepeat))) + ypanning; + if (wal->cstat & CSTAT_WALL_YFLIP) h = -h; + return h; + }; + + tcs[UPLFT].v = setv(topleft, topright, glseg.fracleft); + tcs[LOLFT].v = setv(bottomleft, bottomright, glseg.fracleft); + tcs[UPRGT].v = setv(topleft, topright, glseg.fracright); + tcs[LORGT].v = setv(bottomleft, bottomright, glseg.fracright); + if (th == pow2size) CheckTexturePosition(); // for NPOT textures this adjustment can break things. + bool trans = type == RENDERWALL_M2S && maskWallHasTranslucency(wal); + if (trans) + { + RenderStyle = GetRenderStyle(0, !!(wal->cstat & CSTAT_WALL_TRANS_FLIP)); + alpha = GetAlphaFromBlend((wal->cstat & CSTAT_WALL_TRANS_FLIP) ? DAMETH_TRANS2 : DAMETH_TRANS1, 0); + } + PutWall(di, trans); + flags = 0; + glseg = glsave; +} + +//========================================================================== +// +// Handle one sided walls +// +//========================================================================== + +void HWWall::DoOneSidedTexture(HWDrawInfo* di, walltype* wal, sectortype* frontsector, sectortype* backsector, + float topleft, float topright, float bottomleft, float bottomright) +{ + // get the alignment reference position. + int refheight; + + if ((wal->cstat & CSTAT_WALL_1WAY) && backsector) + { + if ((!(wal->cstat & CSTAT_WALL_BOTTOM_SWAP) && (wal->cstat & CSTAT_WALL_1WAY)) || + ((wal->cstat & CSTAT_WALL_BOTTOM_SWAP) && (wall[wal->nextwall].cstat & CSTAT_WALL_ALIGN_BOTTOM))) + refheight = frontsector->ceilingz; + else + refheight = backsector->floorz; + } + else + { + refheight = (wal->cstat & CSTAT_WALL_ALIGN_BOTTOM) ? frontsector->floorz : frontsector->ceilingz; + } + + type = RENDERWALL_M1S; + DoTexture(di, wal, wal, refheight, topleft, topright, bottomleft, bottomright); +} + +//========================================================================== +// +// +// +//========================================================================== + +void HWWall::DoUpperTexture(HWDrawInfo* di, walltype* wal, sectortype* frontsector, sectortype* backsector, + float topleft, float topright, float bottomleft, float bottomright) +{ + // get the alignment reference position. + int refheight = (wal->cstat & CSTAT_WALL_ALIGN_BOTTOM) ? frontsector->ceilingz : backsector->ceilingz; + + type = RENDERWALL_TOP; + DoTexture(di, wal, wal, refheight, topleft, topright, bottomleft, bottomright); +} + +//========================================================================== +// +// +// +//========================================================================== + +void HWWall::DoLowerTexture(HWDrawInfo* di, walltype* wal, sectortype* frontsector, sectortype* backsector, + float topleft, float topright, float bottomleft, float bottomright) +{ + // get the alignment reference position. + int refheight; + auto refwall = (wal->cstat & CSTAT_WALL_BOTTOM_SWAP) ? &wall[wal->nextwall] : wal; + refheight = (refwall->cstat & CSTAT_WALL_ALIGN_BOTTOM) ? frontsector->ceilingz : backsector->floorz; + + shade = refwall->shade; + palette = refwall->pal; + type = RENDERWALL_BOTTOM; + DoTexture(di, wal, refwall, refheight, topleft, topright, bottomleft, bottomright); +} + +//========================================================================== +// +// +// +//========================================================================== + +void HWWall::DoMidTexture(HWDrawInfo* di, walltype* wal, + sectortype* front, sectortype* back, + float fch1, float fch2, float ffh1, float ffh2, + float bch1, float bch2, float bfh1, float bfh2) +{ + float topleft,bottomleft,topright,bottomright; + int refheight; + + const int swapit = (wal->cstat & CSTAT_WALL_ALIGN_BOTTOM); + + if (wal->cstat & CSTAT_WALL_1WAY) + { + // 1-sided wall + refheight = swapit ? front->ceilingz : back->ceilingz; + } + else + { + // masked wall + if (swapit) + refheight = min(front->floorz, back->floorz); + else + refheight = max(front->ceilingz, back->ceilingz); + } + + topleft = std::min(bch1,fch1); + topright = std::min(bch2,fch2); + bottomleft = std::max(bfh1,ffh1); + bottomright = std::max(bfh2,ffh2); + if (topleft<=bottomleft && topright<=bottomright) return; + type = seg->cstat & CSTAT_WALL_1WAY ? RENDERWALL_M1S : RENDERWALL_M2S; + + // todo: transparency. + + DoTexture(di, wal, wal, refheight, topleft, topright, bottomleft, bottomright); + RenderStyle = STYLE_Translucent; + alpha = 1.f; +} + + +//========================================================================== +// +// +// +//========================================================================== +void HWWall::Process(HWDrawInfo* di, walltype* wal, sectortype* frontsector, sectortype* backsector) +{ + auto backwall = wal->nextwall >= 0 && wal->nextwall < numwalls ? &wall[wal->nextwall] : nullptr; + auto p2wall = &wall[wal->point2]; + + float fch1; + float ffh1; + float fch2; + float ffh2; + + FVector2 v1(WallStartX(wal), WallStartY(wal)); + FVector2 v2(WallEndX(wal), WallEndY(wal)); + + PlanesAtPoint(frontsector, wal->x, wal->y, &fch1, &ffh1); + PlanesAtPoint(frontsector, p2wall->x, p2wall->y, &fch2, &ffh2); + + +#ifdef _DEBUG + if (wal - wall == 788) + { + int a = 0; + } +#endif + + this->seg = wal; + this->frontsector = frontsector; + this->backsector = backsector; + sprite = nullptr; + vertindex = 0; + vertcount = 0; + + glseg.x1 = v1.X; + glseg.y1 = v1.Y; + glseg.x2 = v2.X; + glseg.y2 = v2.Y; + glseg.fracleft = 0; + glseg.fracright = 1; + flags = 0; + dynlightindex = -1; + shade = wal->shade; + palette = wal->pal; + fade = lookups.getFade(frontsector->floorpal); // fog is per sector. + visibility = sectorVisibility(frontsector); + + alpha = 1.0f; + RenderStyle = STYLE_Translucent; + texture = NULL; + + /* + if (wal->linedef->special == Line_Horizon) + { + SkyNormal(di, frontsector, v1, v2); + DoHorizon(di, wal, frontsector, v1, v2); + return; + } + */ + + bool isportal = false;// wal->linedef->isVisualPortal() && wal->sidedef == wal->linedef->sidedef[0]; + + if (seg->portalflags) + { + int ptype = -1; + if (seg->portalflags == PORTAL_WALL_MIRROR) ptype = PORTALTYPE_MIRROR; + else if (seg->portalflags == PORTAL_WALL_VIEW) ptype = PORTALTYPE_LINETOLINE; + else if (seg->portalflags == PORTAL_WALL_TO_SPRITE) ptype = PORTALTYPE_LINETOSPRITE; + if (ptype != -1) + { + ztop[0] = fch1; + ztop[1] = fch2; + zbottom[0] = ffh1; + zbottom[1] = ffh2; + PutPortal(di, ptype, -1); + return; + } + } + + + if (!backsector || !backwall) + { + // sector's sky + SkyNormal(di, frontsector, v1, v2, fch1, fch2, ffh1, ffh2); + + // normal texture + + int tilenum = ((wal->cstat & CSTAT_WALL_1WAY) && wal->nextwall != -1) ? wal->overpicnum : wal->picnum; + setgotpic(tilenum); + tileUpdatePicnum(&tilenum, int(wal-wall) + 16384, wal->cstat); + texture = tileGetTexture(tilenum); + if (texture && texture->isValid()) + { + DoOneSidedTexture(di, wal, frontsector, backsector, fch1, fch2, ffh1, ffh2); + } + } + else // two sided + { + float bfh1; + float bfh2; + float bch1; + float bch2; + PlanesAtPoint(backsector, wal->x, wal->y, &bch1, &bfh1); + PlanesAtPoint(backsector, p2wall->x, p2wall->y, &bch2, &bfh2); + + float zalign = 0.f; + + SkyTop(di, wal, frontsector, backsector, v1, v2, fch1, fch2); + SkyBottom(di, wal, frontsector, backsector, v1, v2, ffh1, ffh2); + + // upper texture + if (!(frontsector->ceilingstat & backsector->ceilingstat & CSTAT_SECTOR_SKY)) + { + float bch1a = bch1; + float bch2a = bch2; + if (ffh1 > bch1 || ffh2 > bch2) + { + // the back sector's floor obstructs part of this wall. Todo: Handle the portal case better. + if ((ffh1 > bch1 && ffh2 > bch2) || frontsector->portalflags == PORTAL_SECTOR_FLOOR) + { + bch2a = ffh2; + bch1a = ffh1; + } + } + + if (bch1a < fch1 || bch2a < fch2) + { + int tilenum = wal->picnum; + setgotpic(tilenum); + tileUpdatePicnum(&tilenum, int(wal - wall) + 16384, wal->cstat); + texture = tileGetTexture(tilenum); + if (texture && texture->isValid()) + { + DoUpperTexture(di, wal, frontsector, backsector, fch1, fch2, bch1a, bch2a); + } + } + } + + if (wal->cstat & (CSTAT_WALL_MASKED | CSTAT_WALL_1WAY)) + { + int tilenum = wal->overpicnum; + setgotpic(tilenum); + tileUpdatePicnum(&tilenum, int(wal - wall) + 16384, wal->cstat); + texture = tileGetTexture(tilenum); + if (texture && texture->isValid()) + { + DoMidTexture(di, wal, frontsector, backsector, fch1, fch2, ffh1, ffh2, bch1, bch2, bfh1, bfh2); + } + } + + // lower texture + if (!(frontsector->floorstat & backsector->floorstat & CSTAT_SECTOR_SKY)) + { + if (fch1 < bfh1 || fch2 < bfh2) + { + // the back sector's ceiling obstructs part of this wall. Todo: Handle the portal case better. + if ((fch1 < bfh1 && fch2 < bfh2) || frontsector->portalflags == PORTAL_SECTOR_CEILING) + { + bfh1 = fch1; + bfh2 = fch2; + } + } + + if (bfh1 > ffh1 || bfh2 > ffh2) + { + auto w = (wal->cstat & CSTAT_WALL_BOTTOM_SWAP) ? backwall : wal; + int tilenum = w->picnum; + setgotpic(tilenum); + tileUpdatePicnum(&tilenum, int(wal - wall) + 16384, w->cstat); + texture = tileGetTexture(tilenum); + if (texture && texture->isValid()) + { + DoLowerTexture(di, wal, frontsector, backsector, bfh1, bfh2, ffh1, ffh2); + } + } + } + } +} + +void HWWall::ProcessWallSprite(HWDrawInfo* di, spritetype* spr, sectortype* sector) +{ + auto tex = tileGetTexture(spr->picnum); + if (!tex || !tex->isValid()) return; + + seg = nullptr; + sprite = spr; + vec2_t pos[2]; + int sprz = spr->pos.z; + + GetWallSpritePosition(spr, spr->pos.vec2, pos, true); + glseg.x1 = pos[0].x * (1 / 16.f); + glseg.y1 = pos[0].y * (1 / -16.f); + glseg.x2 = pos[1].x * (1 / 16.f); + glseg.y2 = pos[1].y * (1 / -16.f); + + if (spr->cstat & CSTAT_SPRITE_ONE_SIDED) + { + if (PointOnLineSide(di->Viewpoint.Pos.X, di->Viewpoint.Pos.Y, glseg.x1, glseg.y1, glseg.x2 - glseg.x1, glseg.y2 - glseg.y1) <= 0) + { + return; + } + } + + vertindex = 0; + vertcount = 0; + type = RENDERWALL_M2S; + frontsector = sector; + backsector = sector; + texture = tex; + + flags = HWF_CLAMPX|HWF_CLAMPY; + dynlightindex = -1; + shade = spr->shade; + palette = spr->pal; + fade = lookups.getFade(sector->floorpal); // fog is per sector. + visibility = sectorVisibility(sector); + + SetSpriteTranslucency(sprite, alpha, RenderStyle); + + int height, topofs; + if (hw_hightile && TileFiles.tiledata[spr->picnum].hiofs.xsize) + { + height = TileFiles.tiledata[spr->picnum].hiofs.ysize; + topofs = (TileFiles.tiledata[spr->picnum].hiofs.yoffs + spr->yoffset); + } + else + { + height = (int)tex->GetDisplayHeight(); + topofs = ((int)tex->GetDisplayTopOffset() + spr->yoffset); + } + + if (spr->cstat & CSTAT_SPRITE_YFLIP) + topofs = -topofs; + + sprz -= ((topofs * spr->yrepeat) << 2); + + if (spr->cstat & CSTAT_SPRITE_YCENTER) + { + sprz += ((height * spr->yrepeat) << 1); + if (height & 1) sprz += (spr->yrepeat << 1); // Odd yspans (taken from polymost as-is) + } + + glseg.fracleft = 0; + glseg.fracright = 1; + tcs[LOLFT].u = tcs[UPLFT].u = (spr->cstat & CSTAT_SPRITE_XFLIP) ? 1.f : 0.f; + tcs[LORGT].u = tcs[UPRGT].u = (spr->cstat & CSTAT_SPRITE_XFLIP) ? 0.f : 1.f; + tcs[UPLFT].v = tcs[UPRGT].v = (spr->cstat & CSTAT_SPRITE_YFLIP) ? 1.f : 0.f; + tcs[LOLFT].v = tcs[LORGT].v = (spr->cstat & CSTAT_SPRITE_YFLIP) ? 0.f : 1.f; + + zbottom[0] = zbottom[1] = (sprz) * (1 / -256.); + ztop[0] = ztop[1] = (sprz - ((height * spr->yrepeat) << 2)) * (1 / -256.); + + // Clip sprites to ceilings/floors + float origz = ztop[0]; + float polyh = (zbottom[0] - origz); + if (!(sector->ceilingstat & CSTAT_SECTOR_SKY)) + { + float ceilingz = sector->ceilingz * (1 / -256.f); + if (ceilingz < ztop[0] && ceilingz > zbottom[0]) + { + float newv = (ceilingz - origz) / polyh; + tcs[UPLFT].v = tcs[UPRGT].v = newv; + ztop[0] = ztop[1] = ceilingz; + } + } + if (!(sector->floorstat & CSTAT_SECTOR_SKY)) + { + float floorz = sector->floorz * (1 / -256.f); + if (floorz < ztop[0] && floorz > zbottom[0]) + { + float newv = (floorz - origz) / polyh; + tcs[LOLFT].v = tcs[LORGT].v = newv; + zbottom[0] = zbottom[1] = floorz; + } + } + + // If the sprite is backward, flip it around so that we have guaranteed orientation when this is about to be sorted. + if (PointOnLineSide(di->Viewpoint.Pos.XY(), DVector2(glseg.x1, glseg.y1), DVector2(glseg.x2, glseg.y2)) < 0) + { + std::swap(glseg.x1, glseg.x2); + std::swap(glseg.y1, glseg.y2); + // z is always the same on both sides. + std::swap(tcs[LOLFT], tcs[LORGT]); + std::swap(tcs[UPLFT], tcs[UPRGT]); + } + + PutWall(di, spriteHasTranslucency(sprite)); +} \ No newline at end of file diff --git a/source/core/savegamehelp.cpp b/source/core/savegamehelp.cpp index c344ef58e..9d6573417 100644 --- a/source/core/savegamehelp.cpp +++ b/source/core/savegamehelp.cpp @@ -55,6 +55,11 @@ #include "gamestate.h" #include "razemenu.h" #include "interpolate.h" +#include "gamefuncs.h" +#include "render.h" +#include "hw_sections.h" +#include "sectorgeometry.h" +#include "d_net.h" #include @@ -236,7 +241,7 @@ bool WriteSavegame(const char* filename, const char *name) if (test != nullptr) { delete test; - return true; + return true; } } return false; @@ -418,7 +423,6 @@ FString G_BuildSaveName (const char *prefix) } #include "build.h" -#include "mmulti.h" #define V(x) x static spritetype zsp; @@ -453,6 +457,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, spritetype &c, spritet ("hitag", c.hitag, def->hitag) ("extra", c.extra, def->extra) ("detail", c.detail, def->detail) + ("time", c.time, def->time) .EndObject(); } return arc; @@ -512,6 +517,8 @@ FSerializer &Serialize(FSerializer &arc, const char *key, sectortype &c, sectort ("lotag", c.lotag, def->lotag) ("hitag", c.hitag, def->hitag) ("extra", c.extra, def->extra) + ("portalflags", c.portalflags, def->portalflags) + ("portalnum", c.portalnum, def->portalnum) .EndObject(); } return arc; @@ -538,6 +545,8 @@ FSerializer &Serialize(FSerializer &arc, const char *key, walltype &c, walltype ("lotag", c.lotag, def->lotag) ("hitag", c.hitag, def->hitag) ("extra", c.extra, def->extra) + ("portalflags", c.portalflags, def->portalflags) + ("portalnum", c.portalnum, def->portalnum) .EndObject(); } return arc; @@ -598,12 +607,12 @@ void SerializeMap(FSerializer& arc) ("numshades", numshades) // is this really needed? ("visibility", g_visibility) ("parallaxtype", parallaxtype) - ("parallaxvisibility", parallaxvisibility) ("parallaxyo", parallaxyoffs_override) ("parallaxys", parallaxyscale_override) ("pskybits", pskybits_override) ("numsprites", Numsprites) - ("gamesetinput", gamesetinput); + ("gamesetinput", gamesetinput) + ("allportals", allPortals); SerializeInterpolations(arc); @@ -629,6 +638,12 @@ void SerializeMap(FSerializer& arc) if (prevspritestat[i] == -2) prevspritestat[i] = i - 1; if (prevspritesect[i] == -2) prevspritesect[i] = i - 1; } + if (arc.isReading()) + { + setWallSectors(); + hw_BuildSections(); + sectorGeometry.SetSize(numsections); + } } //============================================================================= @@ -660,10 +675,10 @@ static int nextquicksave = -1; { if (ReadSavegame(name)) { - gameaction = ga_level; - } - else - { + gameaction = ga_level; + } + else + { I_Error("%s: Failed to open savegame", name); } } @@ -681,11 +696,11 @@ static int nextquicksave = -1; { if (WriteSavegame(fn, desc)) { - savegameManager.NotifyNewSave(fn, desc, ok4q, forceq); - Printf(PRINT_NOTIFY, "%s\n", GStrings("GAME SAVED")); - BackupSaveGame = fn; + savegameManager.NotifyNewSave(fn, desc, ok4q, forceq); + Printf(PRINT_NOTIFY, "%s\n", GStrings("GAME SAVED")); + BackupSaveGame = fn; + } } - } void M_Autosave() diff --git a/source/core/screenjob.cpp b/source/core/screenjob.cpp index f2417dc5b..993c97ce1 100644 --- a/source/core/screenjob.cpp +++ b/source/core/screenjob.cpp @@ -51,59 +51,371 @@ #include #include #include "raze_music.h" +#include "vm.h" +#include "mapinfo.h" +static DObject* runner; +static SummaryInfo sinfo; +static PClass* runnerclass; +static PType* runnerclasstype; +static PType* maprecordtype; +static PType* summaryinfotype; +static CompletionFunc completion; +static int ticks; +static SummaryInfo summaryinfo; -IMPLEMENT_CLASS(DScreenJob, true, false) -IMPLEMENT_CLASS(DImageScreen, true, false) - - -bool DSkippableScreenJob::OnEvent(event_t* evt) -{ - if (evt->type == EV_KeyDown && !specialKeyEvent(evt)) - { - state = skipped; - Skipped(); - } - return true; -} - -void DBlackScreen::OnTick() -{ - if (cleared) - { - int span = ticks * 1000 / GameTicRate; - if (span > wait) state = finished; - } -} - -void DBlackScreen::Draw(double) -{ - cleared = true; - twod->ClearScreen(); -} - -//--------------------------------------------------------------------------- +//============================================================================= // // // -//--------------------------------------------------------------------------- +//============================================================================= -void DImageScreen::OnTick() +void Job_Init() { - if (cleared) + static bool done = false; + if (!done) { - int span = ticks * 1000 / GameTicRate; - if (span > waittime) state = finished; + done = true; + GC::AddMarkerFunc([] { GC::Mark(runner); }); + } + runnerclass = PClass::FindClass("ScreenJobRunner"); + if (!runnerclass) I_FatalError("ScreenJobRunner not defined"); + runnerclasstype = NewPointer(runnerclass); + + maprecordtype = NewPointer(NewStruct("MapRecord", nullptr, true)); + summaryinfotype = NewPointer(NewStruct("SummaryInfo", nullptr, true)); +} + +//============================================================================= +// +// +// +//============================================================================= + +static VMFunction* LookupFunction(const char* qname, bool validate = true) +{ + int p = strcspn(qname, "."); + if (p == 0) I_Error("Call to undefined function %s", qname); + FString clsname(qname, p); + FString funcname = qname + p + 1; + + auto func = PClass::FindFunction(clsname, funcname); + if (func == nullptr) I_Error("Call to undefined function %s", qname); + if (validate) + { + // these conditions must be met by all functions for this interface. + if (func->Proto->ReturnTypes.Size() != 0) I_Error("Bad cutscene function %s. Return value not allowed", qname); + if (func->ImplicitArgs != 0) I_Error("Bad cutscene function %s. Must be static", qname); + } + return func; +} + +//============================================================================= +// +// +// +//============================================================================= + +void CallCreateFunction(const char* qname, DObject* runner) +{ + auto func = LookupFunction(qname); + if (func->Proto->ArgumentTypes.Size() != 1) I_Error("Bad cutscene function %s. Must receive precisely one argument.", qname); + if (func->Proto->ArgumentTypes[0] != runnerclasstype) I_Error("Bad cutscene function %s. Must receive ScreenJobRunner reference.", qname); + VMValue val = runner; + VMCall(func, &val, 1, nullptr, 0); +} + +//============================================================================= +// +// +// +//============================================================================= + +void CallCreateMapFunction(const char* qname, DObject* runner, MapRecord* map) +{ + auto func = LookupFunction(qname); + if (func->Proto->ArgumentTypes.Size() == 1) return CallCreateFunction(qname, runner); // accept functions without map parameter as well here. + if (func->Proto->ArgumentTypes.Size() != 2) I_Error("Bad map-cutscene function %s. Must receive precisely two arguments.", qname); + if (func->Proto->ArgumentTypes[0] != runnerclasstype && func->Proto->ArgumentTypes[1] != maprecordtype) + I_Error("Bad cutscene function %s. Must receive ScreenJobRunner and MapRecord reference.", qname); + VMValue val[2] = { runner, map }; + VMCall(func, val, 2, nullptr, 0); +} + +//============================================================================= +// +// +// +//============================================================================= + +void CallCreateSummaryFunction(const char* qname, DObject* runner, MapRecord* map, SummaryInfo* info, MapRecord* map2) +{ + auto func = LookupFunction(qname); + auto s = func->Proto->ArgumentTypes.Size(); + auto at = func->Proto->ArgumentTypes.Data(); + if (s != 3 && s != 4) I_Error("Bad map-cutscene function %s. Must receive precisely three or four arguments.", qname); + if (at[0] != runnerclasstype && at[1] != maprecordtype && at[2] != summaryinfotype && (s == 3 || at[3] == maprecordtype)) + I_Error("Bad cutscene function %s. Must receive ScreenJobRunner, MapRecord and SummaryInfo reference,", qname); + if (info) summaryinfo = *info; // must be copied to a persistent location. + else summaryinfo = {}; + VMValue val[] = { runner, map, &summaryinfo, map2 }; + VMCall(func, val, s, nullptr, 0); +} + +//============================================================================= +// +// +// +//============================================================================= + +DObject* CreateRunner(bool clearbefore = true) +{ + auto obj = runnerclass->CreateNew(); + auto func = LookupFunction("ScreenJobRunner.Init", false); + VMValue val[3] = { obj, clearbefore, false }; + VMCall(func, val, 3, nullptr, 0); + return obj; +} + +//============================================================================= +// +// +// +//============================================================================= + +void AddGenericVideo(DObject* runner, const FString& fn, int soundid, int fps) +{ + auto obj = runnerclass->CreateNew(); + auto func = LookupFunction("ScreenJobRunner.AddGenericVideo", false); + VMValue val[] = { runner, &fn, soundid, fps }; + VMCall(func, val, 4, nullptr, 0); +} + +//============================================================================= +// +// +// +//============================================================================= + +void CutsceneDef::Create(DObject* runner) +{ + if (function.IsNotEmpty()) + { + CallCreateFunction(function, runner); + } + else if (video.IsNotEmpty()) + { + AddGenericVideo(runner, video, GetSound(), framespersec); } } +//============================================================================= +// +// +// +//============================================================================= -void DImageScreen::Draw(double smoothratio) +bool CutsceneDef::Create(DObject* runner, MapRecord* map, bool transition) { - if (tilenum > 0) tex = tileGetTexture(tilenum, true); - twod->ClearScreen(); - if (tex) DrawTexture(twod, tex, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, DTA_TranslationIndex, trans, TAG_DONE); - cleared = true; + if (!transition && transitiononly) return false; + if (function.CompareNoCase("none") == 0) + return true; // play nothing but return as being validated + if (function.IsNotEmpty()) + { + CallCreateMapFunction(function, runner, map); + return true; + } + else if (video.IsNotEmpty()) + { + AddGenericVideo(runner, video, GetSound(), framespersec); + return true; + } + return false; +} + +//============================================================================= +// +// +// +//============================================================================= + +void DeleteScreenJob() +{ + if (runner) runner->Destroy(); + runner = nullptr; +} + +void EndScreenJob() +{ + DeleteScreenJob(); + if (completion) completion(false); + completion = nullptr; +} + + +//============================================================================= +// +// +// +//============================================================================= + +bool ScreenJobResponder(event_t* ev) +{ + if (ev->type == EV_KeyDown) + { + // We never reach the key binding checks in G_Responder, so for the console we have to check for ourselves here. + auto binding = Bindings.GetBinding(ev->data1); + if (binding.CompareNoCase("toggleconsole") == 0) + { + C_ToggleConsole(); + return true; + } + } + FInputEvent evt = ev; + if (runner) + { + IFVIRTUALPTRNAME(runner, NAME_ScreenJobRunner, OnEvent) + { + int result = 0; + VMValue parm[] = { runner, &evt }; + VMReturn ret(&result); + VMCall(func, parm, 2, &ret, 1); + return result; + } + } + return false; +} + +//============================================================================= +// +// +// +//============================================================================= + +bool ScreenJobTick() +{ + ticks++; + if (runner) + { + IFVIRTUALPTRNAME(runner, NAME_ScreenJobRunner, OnTick) + { + int result = 0; + VMValue parm[] = { runner }; + VMReturn ret(&result); + VMCall(func, parm, 1, &ret, 1); + return result; + } + } + return false; +} + +//============================================================================= +// +// +// +//============================================================================= + +void ScreenJobDraw() +{ + double smoothratio = I_GetTimeFrac(); + + if (runner) + { + twod->ClearScreen(); + IFVIRTUALPTRNAME(runner, NAME_ScreenJobRunner, RunFrame) + { + VMValue parm[] = { runner, smoothratio }; + VMCall(func, parm, 2, nullptr, 0); + } + } +} + +//============================================================================= +// +// +// +//============================================================================= + +bool ScreenJobValidate() +{ + if (runner) + { + IFVIRTUALPTRNAME(runner, NAME_ScreenJobRunner, Validate) + { + int res; + VMValue parm[] = { runner }; + VMReturn ret(&res); + VMCall(func, parm, 2, &ret, 1); + return res; + } + } + return false; +} + +//============================================================================= +// +// +// +//============================================================================= + +bool StartCutscene(CutsceneDef& cs, int flags, const CompletionFunc& completion_) +{ + if ((cs.function.IsNotEmpty() || cs.video.IsNotEmpty()) && cs.function.CompareNoCase("none") != 0) + { + completion = completion_; + runner = CreateRunner(); + GC::WriteBarrier(runner); + try + { + cs.Create(runner); + if (!ScreenJobValidate()) + { + runner->Destroy(); + runner = nullptr; + return false; + } + if (flags & SJ_DELAY) intermissiondelay = 10; // need to wait a bit at the start to let the timer catch up. + else intermissiondelay = 0; + gameaction = (flags & SJ_BLOCKUI) ? ga_intro : ga_intermission; + } + catch (...) + { + if (runner) runner->Destroy(); + runner = nullptr; + throw; + } + return true; + } + return false; +} + +bool StartCutscene(const char* s, int flags, const CompletionFunc& completion) +{ + CutsceneDef def; + def.function = s; + return StartCutscene(def, 0, completion); +} + +//============================================================================= +// +// +// +//============================================================================= + +void PlayLogos(gameaction_t complete_ga, gameaction_t def_ga, bool stopmusic) +{ + Mus_Stop(); + FX_StopAllSounds(); // JBF 20031228 + if (userConfig.nologo) + { + gameaction = def_ga; + } + else + { + if (!StartCutscene(globalCutscenes.Intro, SJ_BLOCKUI|SJ_DELAY, [=](bool) { + gameaction = complete_ga; + })) gameaction = def_ga; + } } //--------------------------------------------------------------------------- @@ -112,269 +424,137 @@ void DImageScreen::Draw(double smoothratio) // //--------------------------------------------------------------------------- -class ScreenJobRunner +void ShowScoreboard(int numplayers, const CompletionFunc& completion_) { - enum - { - State_Clear, - State_Run, - State_Fadeout - }; - TArray jobs; - CompletionFunc completion; - int index = -1; - float screenfade; - bool clearbefore; - int actionState; - int terminateState; - int fadeticks = 0; - int last_paused_tic = -1; - -public: - ScreenJobRunner(JobDesc* jobs_, int count, CompletionFunc completion_, bool clearbefore_) - : completion(std::move(completion_)), clearbefore(clearbefore_) - { - jobs.Resize(count); - memcpy(jobs.Data(), jobs_, count * sizeof(JobDesc)); - // Release all jobs from the garbage collector - the code as it is cannot deal with them getting collected. This should be removed later once the GC is working. - for (int i = 0; i < count; i++) - { - jobs[i].job->Release(); - } - AdvanceJob(false); - } - - ~ScreenJobRunner() - { - DeleteJobs(); - } - - void DeleteJobs() - { - for (auto& job : jobs) - { - job.job->ObjectFlags |= OF_YesReallyDelete; - delete job.job; - } - jobs.Clear(); - } - - void AdvanceJob(bool skip) - { - if (index >= 0) - { - if (jobs[index].postAction) jobs[index].postAction(); - jobs[index].job->Destroy(); - } - index++; - while (index < jobs.Size() && (jobs[index].job == nullptr || (skip && jobs[index].ignoreifskipped))) - { - if (jobs[index].job != nullptr) jobs[index].job->Destroy(); - index++; - } - actionState = clearbefore ? State_Clear : State_Run; - if (index < jobs.Size()) - { - jobs[index].job->fadestate = !paused && jobs[index].job->fadestyle & DScreenJob::fadein? DScreenJob::fadein : DScreenJob::visible; - jobs[index].job->Start(); - } - inputState.ClearAllInput(); - } - - int DisplayFrame(double smoothratio) - { - auto& job = jobs[index]; - auto now = I_GetTimeNS(); - bool processed = job.job->ProcessInput(); - - if (job.job->fadestate == DScreenJob::fadein) - { - double ms = (job.job->ticks + smoothratio) * 1000 / GameTicRate / job.job->fadetime; - float screenfade = (float)clamp(ms, 0., 1.); - twod->SetScreenFade(screenfade); - if (screenfade == 1.f) job.job->fadestate = DScreenJob::visible; - } - int state = job.job->DrawFrame(smoothratio); - twod->SetScreenFade(1.f); - return state; - } - - int FadeoutFrame(double smoothratio) - { - auto& job = jobs[index]; - double ms = (fadeticks + smoothratio) * 1000 / GameTicRate / job.job->fadetime; - float screenfade = 1.f - (float)clamp(ms, 0., 1.); - twod->SetScreenFade(screenfade); - job.job->DrawFrame(1.); - return (screenfade > 0.f); - } - - bool OnEvent(event_t* ev) - { - if (paused || index >= jobs.Size()) return false; - - if (ev->type == EV_KeyDown) - { - // We never reach the key binding checks in G_Responder, so for the console we have to check for ourselves here. - auto binding = Bindings.GetBinding(ev->data1); - if (binding.CompareNoCase("toggleconsole") == 0) - { - C_ToggleConsole(); - return true; - } - } - - if (jobs[index].job->state != DScreenJob::running) return false; - - return jobs[index].job->OnEvent(ev); - } - - void OnFinished() + completion = completion_; + runner = CreateRunner(); + GC::WriteBarrier(runner); + + const char* qname = globalCutscenes.MPSummaryScreen; + auto func = LookupFunction(qname); + if (func->Proto->ArgumentTypes.Size() != 2) I_Error("Bad map-cutscene function %s. Must receive precisely two arguments.", qname); + if (func->Proto->ArgumentTypes[0] != runnerclasstype && func->Proto->ArgumentTypes[1] != TypeSInt32) + I_Error("Bad cutscene function %s. Must receive ScreenJobRunner reference and integer.", qname); + VMValue val[2] = { runner, numplayers }; + VMCall(func, val, 2, nullptr, 0); + if (!ScreenJobValidate()) { + runner->Destroy(); + runner = nullptr; if (completion) completion(false); - completion = nullptr; // only finish once. + completion = nullptr; + return; } + gameaction = ga_intermission; +} - void OnTick() +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +void ShowIntermission(MapRecord* fromMap, MapRecord* toMap, SummaryInfo* info, CompletionFunc completion_) +{ + completion = completion_; + runner = CreateRunner(); + GC::WriteBarrier(runner); + + // retrieve cluster relations for cluster-based cutscenes. + ClusterDef* fromcluster = nullptr, *tocluster = nullptr; + if (fromMap) fromcluster = FindCluster(fromMap->cluster); + if (toMap) tocluster = FindCluster(toMap->cluster); + if (fromcluster == tocluster) fromcluster = tocluster = nullptr; + + + try { - if (paused) return; - if (index >= jobs.Size()) + if (fromMap) { - //DeleteJobs(); - //twod->SetScreenFade(1); - //twod->ClearScreen(); // This must not leave the 2d buffer empty. - //if (gamestate == GS_INTRO) OnFinished(); - //else Net_WriteByte(DEM_ENDSCREENJOB); // intermissions must be terminated synchronously. + if (!fromMap->outro.Create(runner, fromMap, !!toMap)) + { + if (fromcluster == nullptr || !fromcluster->outro.Create(runner, fromMap, !!toMap)) + globalCutscenes.DefaultMapOutro.Create(runner, fromMap, !!toMap); + } + } + if (fromMap || (g_gameType & GAMEFLAG_PSEXHUMED)) + CallCreateSummaryFunction(globalCutscenes.SummaryScreen, runner, fromMap, info, toMap); + + if (toMap) + { + if (!toMap->intro.Create(runner, toMap, !!fromMap)) + { + if (tocluster == nullptr || !tocluster->intro.Create(runner, toMap, !!fromMap)) + globalCutscenes.DefaultMapIntro.Create(runner, toMap, !!fromMap); + } + globalCutscenes.LoadingScreen.Create(runner, toMap, true); + } + else if (isShareware()) + { + globalCutscenes.SharewareEnd.Create(runner); + } + if (!ScreenJobValidate()) + { + runner->Destroy(); + runner = nullptr; + if (completion) completion(false); + completion = nullptr; + return; + } + gameaction = ga_intermission; + } + catch (...) + { + if (runner) runner->Destroy(); + runner = nullptr; + throw; + } +} + +CCMD(testcutscene) +{ + if (argv.argc() < 2) + { + Printf("Usage: testcutscene \n"); + return; + } + try + { + if (StartCutscene(argv[1], 0, [](bool) {})) + { + C_HideConsole(); + } + } + catch (const CRecoverableError& err) + { + Printf(TEXTCOLOR_RED "Unable to play cutscene: %s\n", err.what()); + } +} + + + +/* +Blood: + if (!userConfig.nologo && gGameOptions.nGameType == 0) playlogos(); else { - if (jobs[index].job->state == DScreenJob::running) + gameaction = ga_mainmenu; + } + RunScreenJob(jobs, [](bool) { + Mus_Stop(); + gameaction = ga_mainmenu; + }, SJ_BLOCKUI); + +Exhumed: + if (!userConfig.nologo) DoTitle([](bool) { gameaction = ga_mainmenu; }); + else gameaction = ga_mainmenu; + +SW: + if (!userConfig.nologo) Logo([](bool) { - jobs[index].job->ticks++; - jobs[index].job->OnTick(); - } - else if (jobs[index].job->state == DScreenJob::stopping) - { - fadeticks++; - } - } - } - - bool RunFrame() - { - if (index >= jobs.Size()) - { - DeleteJobs(); - twod->SetScreenFade(1); - twod->ClearScreen(); // This must not leave the 2d buffer empty. - if (completion) completion(false); - return false; - } - - // ensure that we won't go back in time if the menu is dismissed without advancing our ticker - bool menuon = paused; - if (menuon) last_paused_tic = jobs[index].job->ticks; - else if (last_paused_tic == jobs[index].job->ticks) menuon = true; - double smoothratio = menuon ? 1. : I_GetTimeFrac(); - - if (actionState == State_Clear) - { - actionState = State_Run; - twod->ClearScreen(); - } - else if (actionState == State_Run) - { - terminateState = DisplayFrame(smoothratio); - if (terminateState < 1) - { - // Must lock before displaying. - if (jobs[index].job->fadestyle & DScreenJob::fadeout) - { - jobs[index].job->fadestate = DScreenJob::fadeout; - jobs[index].job->state = DScreenJob::stopping; - actionState = State_Fadeout; - fadeticks = 0; - } - else - { - AdvanceJob(terminateState < 0); - } - } - } - else if (actionState == State_Fadeout) - { - int ended = FadeoutFrame(smoothratio); - if (ended < 1) - { - jobs[index].job->state = DScreenJob::stopped; - AdvanceJob(terminateState < 0); - } - } - return true; - } -}; - -ScreenJobRunner *runner; - -void RunScreenJob(JobDesc* jobs, int count, CompletionFunc completion, bool clearbefore, bool blockingui) -{ - assert(completion != nullptr); - videoclearFade(); - if (count) - { - runner = new ScreenJobRunner(jobs, count, completion, clearbefore); - gameaction = blockingui? ga_intro : ga_intermission; - } - else - { - completion(false); - } -} - -void DeleteScreenJob() -{ - if (runner) - { - delete runner; - runner = nullptr; - } - twod->SetScreenFade(1); -} - -void EndScreenJob() -{ - if (runner) runner->OnFinished(); - DeleteScreenJob(); -} - - -bool ScreenJobResponder(event_t* ev) -{ - if (runner) return runner->OnEvent(ev); - return false; -} - -void ScreenJobTick() -{ - if (runner) runner->OnTick(); -} - -bool ScreenJobDraw() -{ - // we cannot recover from this because we have no completion callback to call. - if (!runner) - { - // We can get here before a gameaction has been processed. In that case just draw a black screen and wait. - if (gameaction == ga_nothing) I_Error("Trying to run a non-existent screen job"); - twod->ClearScreen(); - return false; - } - auto res = runner->RunFrame(); - if (!res) - { - assert((gamestate != GS_INTERMISSION && gamestate != GS_INTRO) || gameaction != ga_nothing); - DeleteScreenJob(); - } - return res; -} + gameaction = ga_mainmenunostopsound; + }); + else gameaction = ga_mainmenu; +*/ \ No newline at end of file diff --git a/source/core/screenjob.h b/source/core/screenjob.h index b0a48b058..94c445158 100644 --- a/source/core/screenjob.h +++ b/source/core/screenjob.h @@ -3,153 +3,29 @@ #include "dobject.h" #include "v_2ddrawer.h" #include "d_eventbase.h" +#include "s_soundinternal.h" +#include "gamestate.h" using CompletionFunc = std::function; -struct JobDesc; -class ScreenJobRunner; -class DScreenJob : public DObject +void Job_Init(); + +enum { - DECLARE_CLASS(DScreenJob, DObject) - const int fadestyle; - const float fadetime; // in milliseconds - int fadestate = fadein; - - friend class ScreenJobRunner; -protected: - int ticks = 0; - int state = running; - bool pausable = true; - -public: - enum - { - running = 1, // normal operation - skipped = 2, // finished by user skipping - finished = 3, // finished by completing its sequence - stopping = 4, // running ending animations / fadeout, etc. Will not accept more input. - stopped = 5, // we're done here. - }; - enum - { - visible = 0, - fadein = 1, - fadeout = 2, - }; - - DScreenJob(int fade = 0, float fadet = 250.f) : fadestyle(fade), fadetime(fadet) {} - - virtual bool ProcessInput() - { - return false; - } - - virtual void Start() {} - virtual bool OnEvent(event_t* evt) { return false; } - virtual void OnTick() { /*state = finished;*/ } - virtual void Draw(double smoothratio) {} - - int DrawFrame(double smoothratio) - { - if (state != running) smoothratio = 1; // this is necessary because the ticker won't be incremented anymore to avoid having a negative time span. - Draw(smoothratio); - if (state == skipped) return -1; - if (state == finished) return 0; - return 1; - } - - int GetFadeState() const { return fadestate; } - + SJ_BLOCKUI = 1, + SJ_DELAY = 2, }; -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -class DSkippableScreenJob : public DScreenJob -{ -protected: - DSkippableScreenJob(int fade = 0, float fadet = 250.f) : DScreenJob(fade, fadet) - {} - - bool OnEvent(event_t* evt) override; - virtual void Skipped() {} -}; - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -class DBlackScreen : public DScreenJob -{ - int wait; - bool cleared = false; - -public: - DBlackScreen(int w) : wait(w) {} - void OnTick() override; - void Draw(double smooth) override; -}; - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -class DImageScreen : public DSkippableScreenJob -{ - DECLARE_CLASS(DImageScreen, DScreenJob) - - int tilenum = -1; - int trans; - int waittime; // in ms. - bool cleared = false; - FGameTexture* tex = nullptr; - -public: - DImageScreen(FGameTexture* tile, int fade = DScreenJob::fadein | DScreenJob::fadeout, int wait = 3000, int translation = 0) : DSkippableScreenJob(fade), waittime(wait) - { - tex = tile; - trans = translation; - } - - DImageScreen(int tile, int fade = DScreenJob::fadein | DScreenJob::fadeout, int wait = 3000, int translation = 0) : DSkippableScreenJob(fade), waittime(wait) - { - tilenum = tile; - trans = translation; - } - - void OnTick() override; - void Draw(double smooth) override; -}; - - - - -struct JobDesc -{ - DScreenJob* job; - void (*postAction)(); - bool ignoreifskipped; -}; - - -void RunScreenJob(JobDesc *jobs, int count, CompletionFunc completion, bool clearbefore = true, bool blockingui = false); void EndScreenJob(); void DeleteScreenJob(); bool ScreenJobResponder(event_t* ev); -void ScreenJobTick(); -bool ScreenJobDraw(); +bool ScreenJobTick(); +void ScreenJobDraw(); -struct AnimSound -{ - int framenum; - int soundnum; -}; - -DScreenJob *PlayVideo(const char *filename, const AnimSound *ans = nullptr, const int *frameticks = nullptr, bool nosoundstop = false); +struct CutsceneDef; +struct MapRecord; +struct SummaryInfo; +bool StartCutscene(const char* s, int flags, const CompletionFunc& completion); +void PlayLogos(gameaction_t complete_ga, gameaction_t def_ga, bool stopmusic); +void ShowScoreboard(int numplayers, const CompletionFunc& completion_); +void ShowIntermission(MapRecord* fromMap, MapRecord* toMap, SummaryInfo* info, CompletionFunc completion_); diff --git a/source/core/searchpaths.cpp b/source/core/searchpaths.cpp index dd3a73af9..99c974399 100644 --- a/source/core/searchpaths.cpp +++ b/source/core/searchpaths.cpp @@ -398,6 +398,12 @@ static TArray ParseGrpInfo(const char *fn, FileReader &fr, TMapFindSection("Episode1")) { diff --git a/source/core/sectorgeometry.cpp b/source/core/sectorgeometry.cpp new file mode 100644 index 000000000..4db5b3617 --- /dev/null +++ b/source/core/sectorgeometry.cpp @@ -0,0 +1,467 @@ +/* +** sectorgeometry.cpp +** +** caches the triangle meshes used for rendering sector planes. +** +**--------------------------------------------------------------------------- +** Copyright 2021 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +** +*/ + +#include "sectorgeometry.h" +#include "build.h" +#include "gamefuncs.h" +#include "texturemanager.h" +#include "earcut.hpp" +#include "hw_sections.h" +#include "nodebuilder/nodebuild.h" + +SectorGeometry sectorGeometry; + +//========================================================================== +// +// CalcPlane fixme - this should be stored in the sector, not be recalculated each frame. +// +//========================================================================== + +static FVector3 CalcNormal(sectortype* sector, int plane) +{ + FVector3 pt[3]; + + auto wal = &wall[sector->wallptr]; + auto wal2 = &wall[wal->point2]; + + pt[0] = { (float)WallStartX(wal), (float)WallStartY(wal), 0 }; + pt[1] = { (float)WallEndX(wal), (float)WallEndY(wal), 0 }; + PlanesAtPoint(sector, wal->x, wal->y, plane ? &pt[0].Z : nullptr, plane? nullptr : &pt[0].Z); + PlanesAtPoint(sector, wal2->x, wal2->y, plane ? &pt[1].Z : nullptr, plane ? nullptr : &pt[1].Z); + + if (pt[0].X == pt[1].X) + { + if (pt[0].Y == pt[1].Y) return { 0.f, 0.f, plane ? -1.f : 1.f }; + pt[2].X = pt[0].X + 4; + pt[2].Y = pt[0].Y; + } + else + { + pt[2].X = pt[0].X; + pt[2].Y = pt[0].Y + 4; + } + PlanesAtPoint(sector, pt[2].X * 16, pt[2].Y * 16, plane ? &pt[2].Z : nullptr, plane ? nullptr : &pt[2].Z); + + auto normal = (pt[2] - pt[0]) ^ (pt[1] - pt[0]); + + if ((pt[2].Z < 0 && !plane) || (pt[2].Z > 0 && plane)) return -pt[2]; + return pt[2]; +} + +//========================================================================== +// +// The math used here to calculate texture positioning was derived from +// Polymer but required several fixes for correctness. +// +//========================================================================== +class UVCalculator +{ + sectortype* sect; + int myplane; + int stat; + float z1; + int ix1; + int iy1; + int ix2; + int iy2; + float sinalign, cosalign; + FGameTexture* tex; + float xpanning, ypanning; + float xscaled, yscaled; + FVector2 offset; + +public: + + // Moved in from pragmas.h + UVCalculator(sectortype* sec, int plane, FGameTexture* tx, const FVector2& off) + { + float xpan, ypan; + + sect = sec; + tex = tx; + myplane = plane; + offset = off; + + auto firstwall = &wall[sec->wallptr]; + ix1 = firstwall->x; + iy1 = firstwall->y; + ix2 = wall[firstwall->point2].x; + iy2 = wall[firstwall->point2].y; + + if (plane == 0) + { + stat = sec->floorstat; + xpan = sec->floorxpan_; + ypan = sec->floorypan_; + PlanesAtPoint(sec, ix1, iy1, nullptr, &z1); + } + else + { + stat = sec->ceilingstat; + xpan = sec->ceilingxpan_; + ypan = sec->ceilingypan_; + PlanesAtPoint(sec, ix1, iy1, &z1, nullptr); + } + + DVector2 dv = { double(ix2 - ix1), -double(iy2 - iy1) }; + auto vang = dv.Angle() - 90.; + + cosalign = vang.Cos(); + sinalign = vang.Sin(); + + int pow2width = 1 << sizeToBits((int)tx->GetDisplayWidth()); + if (pow2width < (int)tx->GetDisplayWidth()) pow2width *= 2; + + int pow2height = 1 << sizeToBits((int)tx->GetDisplayHeight()); + if (pow2height < (int)tx->GetDisplayHeight()) pow2height *= 2; + + xpanning = pow2width * xpan / (256.f * tx->GetDisplayWidth()); + ypanning = pow2height * ypan / (256.f * tx->GetDisplayHeight()); + + float scalefactor = (stat & CSTAT_SECTOR_TEXHALF) ? 8.0f : 16.0f; + + if ((stat & (CSTAT_SECTOR_SLOPE | CSTAT_SECTOR_ALIGN)) == (CSTAT_SECTOR_ALIGN)) + { + // This is necessary to adjust for some imprecisions in the math. + // To calculate the inverse Build performs an integer division with significant loss of precision + // that can cause the texture to be shifted by multiple pixels. + // The code below calculates the amount of this deviation so that it can be added back to the formula. + int len = ksqrt(uhypsq(ix2 - ix1, iy2 - iy1)); + if (len != 0) + { + int i = 1048576 / len; + scalefactor *= 1048576.f / (i * len); + } + } + + xscaled = scalefactor * (int)tx->GetDisplayWidth(); + yscaled = scalefactor * (int)tx->GetDisplayHeight(); + } + + FVector2 GetUV(int x, int y, float z) + { + float tv, tu; + + if (stat & CSTAT_SECTOR_ALIGN) + { + float dx = (float)(x - ix1); + float dy = (float)(y - iy1); + + tu = -(dx * sinalign + dy * cosalign); + tv = (dx * cosalign - dy * sinalign); + + if (stat & CSTAT_SECTOR_SLOPE) + { + float dz = (z - z1) * 16; + float newtv = sqrt(tv * tv + dz * dz); + tv = tv < 0 ? -newtv : newtv; + } + } + else + { + tu = x - offset.X; + tv = -y - offset.Y; + } + + if (stat & CSTAT_SECTOR_SWAPXY) + std::swap(tu, tv); + + if (stat & CSTAT_SECTOR_XFLIP) tu = -tu; + if (stat & CSTAT_SECTOR_YFLIP) tv = -tv; + + + + return { tu / xscaled + xpanning, tv / yscaled + ypanning }; + + } +}; + + +//========================================================================== +// +// +// +//========================================================================== + +bool SectorGeometry::MakeVertices(unsigned int secnum, int plane, const FVector2& offset) +{ + auto sec = §ions[secnum]; + auto sectorp = §or[sec->sector]; + int numvertices = sec->lines.Size(); + + TArray points(numvertices, true); + using Point = std::pair; + std::vector> polygon; + std::vector* curPoly; + + polygon.resize(1); + curPoly = &polygon.back(); + FixedBitArray done; + + int fz = sectorp->floorz, cz = sectorp->ceilingz; + + int vertstoadd = numvertices; + + done.Zero(); + while (vertstoadd > 0) + { + int start = 0; + while (done[start] && start < numvertices) start++; + int s = start; + if (start < numvertices) + { + while (!done[start]) + { + auto sline = §ionLines[sec->lines[start]]; + auto wallp = &wall[sline->startpoint]; + float X = WallStartX(wallp); + float Y = WallStartY(wallp); + if (fabs(X) > 32768. || fabs(Y) > 32768.) + { + // If we get here there's some fuckery going around with the coordinates. Let's better abort and wait for things to realign. + // Do not try alternative methods if this happens. + return true; + } + curPoly->push_back(std::make_pair(X, Y)); + done.Set(start); + vertstoadd--; + start = sline->point2index; + } + polygon.resize(polygon.size() + 1); + curPoly = &polygon.back(); + if (start != s) return false; // means the sector is badly defined. RRRA'S E1L3 triggers this. + } + } + // Now make sure that the outer boundary is the first polygon by picking a point that's as much to the outside as possible. + int outer = 0; + float minx = FLT_MAX; + float miny = FLT_MAX; + for (size_t a = 0; a < polygon.size(); a++) + { + for (auto& pt : polygon[a]) + { + if (pt.first < minx || (pt.first == minx && pt.second < miny)) + { + minx = pt.first; + miny = pt.second; + outer = a; + } + } + } + if (outer != 0) std::swap(polygon[0], polygon[outer]); + auto indices = mapbox::earcut(polygon); + if (indices.size() < 3 * (sec->lines.Size() - 2)) + { + // this means that full triangulation failed. + return false; + } + sectorp->floorz = sectorp->ceilingz = 0; + + int p = 0; + for (size_t a = 0; a < polygon.size(); a++) + { + for (auto& pt : polygon[a]) + { + float planez; + PlanesAtPoint(sectorp, (pt.first * 16), (pt.second * -16), plane ? &planez : nullptr, !plane ? &planez : nullptr); + FVector3 point = { pt.first, pt.second, planez }; + points[p++] = point; + } + } + + auto& entry = data[secnum].planes[plane]; + entry.vertices.Resize(indices.size()); + entry.texcoords.Resize(indices.size()); + entry.normal = CalcNormal(sectorp, plane); + + auto texture = tileGetTexture(plane ? sectorp->ceilingpicnum : sectorp->floorpicnum); + + UVCalculator uvcalc(sectorp, plane, texture, offset); + + for(unsigned i = 0; i < entry.vertices.Size(); i++) + { + auto& pt = points[indices[i]]; + entry.vertices[i] = pt; + entry.texcoords[i] = uvcalc.GetUV(int(pt.X * 16), int(pt.Y * -16), pt.Z); + } + + sectorp->floorz = fz; + sectorp->ceilingz = cz; + return true; +} + +//========================================================================== +// +// Use ZDoom's node builder if the simple approach fails. +// This will create something usable in the vast majority of cases, +// even if the result is less efficient. +// +//========================================================================== + +bool SectorGeometry::MakeVertices2(unsigned int secnum, int plane, const FVector2& offset) +{ + auto sec = §ions[secnum]; + auto sectorp = §or[sec->sector]; + int numvertices = sec->lines.Size(); + + // Convert our sector into something the node builder understands + TArray vertexes(sectorp->wallnum, true); + TArray lines(numvertices, true); + TArray sides(numvertices, true); + for (int i = 0; i < numvertices; i++) + { + auto sline = §ionLines[sec->lines[i]]; + auto wal = &wall[sline->startpoint]; + vertexes[i].p = { wal->x * (1 / 16.), wal->y * (1 / -16.) }; + + lines[i].backsector = nullptr; + lines[i].frontsector = sectorp; + lines[i].linenum = i; + lines[i].sidedef[0] = &sides[i]; + lines[i].sidedef[1] = nullptr; + lines[i].v1 = &vertexes[i]; + lines[i].v2 = &vertexes[sline->point2index]; + + sides[i].sidenum = i; + sides[i].sector = sectorp; + } + + + FNodeBuilder::FLevel leveldata = + { + &vertexes[0], (int)vertexes.Size(), + &sides[0], (int)sides.Size(), + &lines[0], (int)lines.Size(), + 0, 0, 0, 0 + }; + leveldata.FindMapBounds(); + FNodeBuilder builder(leveldata); + + FLevelLocals Level; + builder.Extract(Level); + + // Now turn the generated subsectors into triangle meshes + + auto& entry = data[secnum].planes[plane]; + entry.vertices.Clear(); + entry.texcoords.Clear(); + + int fz = sectorp->floorz, cz = sectorp->ceilingz; + sectorp->floorz = sectorp->ceilingz = 0; + + for (auto& sub : Level.subsectors) + { + auto v0 = sub.firstline->v1; + for (unsigned i = 1; i < sub.numlines-1; i++) + { + auto v1 = sub.firstline[i].v1; + auto v2 = sub.firstline[i].v2; + + entry.vertices.Push({ (float)v0->fX(), (float)v0->fY(), 0 }); + entry.vertices.Push({ (float)v1->fX(), (float)v1->fY(), 0 }); + entry.vertices.Push({ (float)v2->fX(), (float)v2->fY(), 0 }); + + } + } + + // calculate the rest. + auto texture = tileGetTexture(plane ? sectorp->ceilingpicnum : sectorp->floorpicnum); + + UVCalculator uvcalc(sectorp, plane, texture, offset); + + entry.texcoords.Resize(entry.vertices.Size()); + for (unsigned i = 0; i < entry.vertices.Size(); i++) + { + auto& pt = entry.vertices[i]; + + float planez; + PlanesAtPoint(sectorp, (pt.X * 16), (pt.Y * -16), plane ? &planez : nullptr, !plane ? &planez : nullptr); + entry.vertices[i].Z = planez; + entry.texcoords[i] = uvcalc.GetUV(int(pt.X * 16.), int(pt.Y * -16.), pt.Z); + } + entry.normal = CalcNormal(sectorp, plane); + sectorp->floorz = fz; + sectorp->ceilingz = cz; + return true; +} + +//========================================================================== +// +// +// +//========================================================================== + +void SectorGeometry::ValidateSector(unsigned int secnum, int plane, const FVector2& offset) +{ + auto sec = §or[sections[secnum].sector]; + + auto compare = &data[secnum].compare[plane]; + if (plane == 0) + { + if (sec->floorheinum == compare->floorheinum && + sec->floorpicnum == compare->floorpicnum && + ((sec->floorstat ^ compare->floorstat) & (CSTAT_SECTOR_ALIGN | CSTAT_SECTOR_YFLIP | CSTAT_SECTOR_XFLIP | CSTAT_SECTOR_TEXHALF | CSTAT_SECTOR_SWAPXY)) == 0 && + sec->floorxpan_ == compare->floorxpan_ && + sec->floorypan_ == compare->floorypan_ && + wall[sec->wallptr].pos == data[secnum].poscompare[0] && + wall[wall[sec->wallptr].point2].pos == data[secnum].poscompare2[0] && + !(sec->dirty & 1) && data[secnum].planes[plane].vertices.Size() ) return; + + sec->dirty &= ~1; + } + else + { + if (sec->ceilingheinum == compare->ceilingheinum && + sec->ceilingpicnum == compare->ceilingpicnum && + ((sec->ceilingstat ^ compare->ceilingstat) & (CSTAT_SECTOR_ALIGN | CSTAT_SECTOR_YFLIP | CSTAT_SECTOR_XFLIP | CSTAT_SECTOR_TEXHALF | CSTAT_SECTOR_SWAPXY)) == 0 && + sec->ceilingxpan_ == compare->ceilingxpan_ && + sec->ceilingypan_ == compare->ceilingypan_ && + wall[sec->wallptr].pos == data[secnum].poscompare[1] && + wall[wall[sec->wallptr].point2].pos == data[secnum].poscompare2[1] && + !(sec->dirty & 2) && data[secnum].planes[1].vertices.Size()) return; + + sec->dirty &= ~2; + } + *compare = *sec; + data[secnum].poscompare[plane] = wall[sec->wallptr].pos; + data[secnum].poscompare2[plane] = wall[wall[sec->wallptr].point2].pos; + if (data[secnum].degenerate || !MakeVertices(secnum, plane, offset)) + { + data[secnum].degenerate = true; + //Printf(TEXTCOLOR_YELLOW "Normal triangulation failed for sector %d. Retrying with alternative approach\n", secnum); + MakeVertices2(secnum, plane, offset); + } +} diff --git a/source/core/sectorgeometry.h b/source/core/sectorgeometry.h new file mode 100644 index 000000000..32ce750f8 --- /dev/null +++ b/source/core/sectorgeometry.h @@ -0,0 +1,48 @@ +#pragma once + +#include "tarray.h" +#include "vectors.h" +#include "build.h" + +struct SectorGeometryPlane +{ + TArray vertices; + TArray texcoords; + FVector3 normal{}; +}; + +struct SectorGeometryData +{ + SectorGeometryPlane planes[2]; + sectortype compare[2] = {}; + vec2_t poscompare[2] = {}; + vec2_t poscompare2[2] = {}; + bool degenerate = false; +}; + +class SectorGeometry +{ + TArray data; + + void ValidateSector(unsigned sectnum, int plane, const FVector2& offset); + bool MakeVertices(unsigned sectnum, int plane, const FVector2& offset); + bool MakeVertices2(unsigned sectnum, int plane, const FVector2& offset); + +public: + SectorGeometryPlane* get(unsigned sectnum, int plane, const FVector2& offset) + { + if (sectnum >= data.Size()) return nullptr; + ValidateSector(sectnum, plane, offset); + return &data[sectnum].planes[plane]; + } + + void SetSize(unsigned sectcount) + { + data.Clear(); // delete old content + data.Resize(sectcount); + } +}; + +extern SectorGeometry sectorGeometry; + + \ No newline at end of file diff --git a/source/core/statusbar.h b/source/core/statusbar.h index 3edeb862e..086b4c383 100644 --- a/source/core/statusbar.h +++ b/source/core/statusbar.h @@ -136,7 +136,7 @@ void ST_Clear(); extern FGameTexture *CrosshairImage; -void SBar_DrawString(DStatusBarCore* self, DHUDFont* font, const FString& string, double x, double y, int flags, int trans, double alpha, int wrapwidth, int linespacing, double scaleX, double scaleY, int pt = 0); +void SBar_DrawString(DStatusBarCore* self, DHUDFont* font, const FString& string, double x, double y, int flags, int trans, double alpha, int wrapwidth, int linespacing, double scaleX, double scaleY, int pt = 0, int style = STYLE_Translucent); void setViewport(int viewSize); struct MapRecord; void setLevelStarted(MapRecord *); diff --git a/source/core/statusbar2.cpp b/source/core/statusbar2.cpp index 7ba06d80a..cce7f8f90 100644 --- a/source/core/statusbar2.cpp +++ b/source/core/statusbar2.cpp @@ -240,7 +240,9 @@ void DBaseStatusBar::PrintAutomapInfo(FLevelStats& stats, bool forcetextfont) { y = 200 - stats.screenbottomspace - spacing; } - const auto &volname = gVolumeNames[volfromlevelnum(lev->levelNumber)]; + auto cluster = FindCluster(lev->cluster); + FString volname; + if (cluster) volname = cluster->name; if (volname.IsEmpty() && am_nameontop) y = 1; DrawText(twod, stats.font, stats.standardColor, 2 * hud_statscale, y, mapname, DTA_FullscreenScale, FSMode_ScaleToHeight, DTA_VirtualWidth, 320, DTA_VirtualHeight, 200, diff --git a/source/core/textures/buildtiles.cpp b/source/core/textures/buildtiles.cpp index 6d2d6ec7a..5cd2b27c0 100644 --- a/source/core/textures/buildtiles.cpp +++ b/source/core/textures/buildtiles.cpp @@ -47,6 +47,7 @@ #include "c_dispatch.h" #include "sc_man.h" #include "gamestruct.h" +#include "hw_voxels.h" #include "hw_renderstate.h" @@ -59,7 +60,7 @@ enum BuildTiles TileFiles; -int tileSetHightileReplacement(int picnum, int palnum, const char* filename, float alphacut, float xscale, float yscale, float specpower, float specfactor, uint8_t flags); +int tileSetHightileReplacement(int picnum, int palnum, const char* filename, float alphacut, float xscale, float yscale, float specpower, float specfactor); //========================================================================== // @@ -144,7 +145,7 @@ void BuildTiles::Init() tile.RotTile = { -1,-1 }; tile.replacement = ReplacementType::Art; tile.alphaThreshold = 0.5; - tile.h_xsize = 0; + tile.hiofs = {}; } } @@ -513,7 +514,7 @@ int tileImportFromTexture(const char* fn, int tilenum, int alphacut, int istextu TexMan.AddGameTexture(tex); TileFiles.tiledata[tilenum].backup = TileFiles.tiledata[tilenum].texture = tex; if (istexture) - tileSetHightileReplacement(tilenum, 0, fn, (float)(255 - alphacut) * (1.f / 255.f), 1.0f, 1.0f, 1.0, 1.0, 0); + tileSetHightileReplacement(tilenum, 0, fn, (float)(255 - alphacut) * (1.f / 255.f), 1.0f, 1.0f, 1.0, 1.0); return 0; } @@ -526,8 +527,6 @@ int tileImportFromTexture(const char* fn, int tilenum, int alphacut, int istextu void tileCopy(int tile, int source, int pal, int xoffset, int yoffset, int flags) { - // Todo. Since I do not know if some mod needs this it's of low priority now. - // Let's get things working first. picanm_t* picanm = nullptr; picanm_t* sourceanm = nullptr; int srcxo, srcyo; @@ -633,7 +632,7 @@ void artSetupMapArt(const char* filename) } - for (bssize_t i = 0; i < MAXARTFILES_TOTAL - MAXARTFILES_BASE; i++) + for (int i = 0; i < MAXARTFILES_TOTAL - MAXARTFILES_BASE; i++) { FStringf fullname("%s_%02d.art", lcfilename.GetChars(), i); TileFiles.LoadArtFile(fullname, filename); @@ -649,7 +648,7 @@ void artSetupMapArt(const char* filename) void tileDelete(int tile) { TileFiles.tiledata[tile].texture = TileFiles.tiledata[tile].backup = TexMan.GameByIndex(0); - vox_undefine(tile); + tiletovox[tile] = -1; // clear the link but don't clear the voxel. It may be in use for another tile. md_undefinetile(tile); } @@ -888,6 +887,8 @@ void processTileImport(const char *cmd, FScriptPosition& pos, TileImport& imp) if (imp.sizex != INT_MAX && tileWidth(imp.tile) != imp.sizex && tileHeight(imp.tile) != imp.sizey) return; + imp.alphacut = clamp(imp.alphacut, 0, 255); + gi->SetTileProps(imp.tile, imp.surface, imp.vox, imp.shade); if (imp.fn.IsNotEmpty() && tileImportFromTexture(imp.fn, imp.tile, imp.alphacut, imp.istexture) < 0) return; @@ -897,10 +898,19 @@ void processTileImport(const char *cmd, FScriptPosition& pos, TileImport& imp) // 1: Since these are texture properties now, there's no need to clear them. // 2: The original code assumed that an imported texture cannot have an offset. But this can import Doom patches and PNGs with grAb, so the situation is very different. if (imp.xoffset == INT_MAX) imp.xoffset = tileLeftOffset(imp.tile); + else imp.xoffset = clamp(imp.xoffset, -128, 127); if (imp.yoffset == INT_MAX) imp.yoffset = tileTopOffset(imp.tile); + else imp.yoffset = clamp(imp.yoffset, -128, 127); auto tex = tileGetTexture(imp.tile); - if (tex) tex->SetOffsets(imp.xoffset, imp.yoffset); + if (tex) + { + tex->SetOffsets(imp.xoffset, imp.yoffset); + if (imp.flags & PICANM_NOFULLBRIGHT_BIT) + { + tex->SetDisableBrightmap(); + } + } if (imp.extra != INT_MAX) TileFiles.tiledata[imp.tile].picanm.extra = imp.extra; } diff --git a/source/core/textures/buildtiles.h b/source/core/textures/buildtiles.h index 28dab021b..77b928f2e 100644 --- a/source/core/textures/buildtiles.h +++ b/source/core/textures/buildtiles.h @@ -256,6 +256,11 @@ struct RawCacheNode uint64_t lastUseTime; }; +struct TileOffs +{ + int xsize, ysize, xoffs, yoffs; +}; + struct TileDesc { FGameTexture* texture; // the currently active tile @@ -268,8 +273,7 @@ struct TileDesc float alphaThreshold; // Sprite offset hackery for hires replacements. This only gets used for sprites in the 3D view, nothing else. - uint16_t h_xsize, h_ysize; - int8_t h_xoffs, h_yoffs; + TileOffs hiofs; }; diff --git a/source/core/textures/hightile.cpp b/source/core/textures/hightile.cpp index f5d240233..36f8e6315 100644 --- a/source/core/textures/hightile.cpp +++ b/source/core/textures/hightile.cpp @@ -48,15 +48,17 @@ #include "sc_man.h" #include "gamestruct.h" #include "hw_renderstate.h" +#include "skyboxtexture.h" CVARD(Bool, hw_shadeinterpolate, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG, "enable/disable shade interpolation") struct HightileReplacement { - FGameTexture* faces[6]; // only one gets used by a texture, the other 5 are for skyboxes only + FGameTexture* image; FVector2 scale; float alphacut, specpower, specfactor; - uint16_t palnum, flags; + uint16_t palnum; + bool issky; }; static TMap> tileReplacements; @@ -91,7 +93,7 @@ static void AddReplacement(int picnum, const HightileReplacement& replace) auto& Hightiles = tileReplacements[picnum]; for (auto& ht : Hightiles) { - if (replace.palnum == ht.palnum && (replace.faces[1] == nullptr) == (ht.faces[1] == nullptr)) + if (replace.palnum == ht.palnum && replace.issky == ht.issky) { ht = replace; return; @@ -125,7 +127,7 @@ static HightileReplacement* FindReplacement(FTextureID picnum, int palnum, bool { for (auto& rep : *Hightiles) { - if (rep.palnum == palnum && (rep.faces[1] != nullptr) == skybox) return &rep; + if (rep.palnum == palnum && rep.issky == skybox) return &rep; } if (!palnum || palnum >= MAXPALOOKUPS - RESERVEDPALS) break; palnum = 0; @@ -137,11 +139,19 @@ int checkTranslucentReplacement(FTextureID picnum, int pal) { FGameTexture* tex = nullptr; auto si = FindReplacement(picnum, pal, 0); - if (si && hw_hightile) tex = si->faces[0]; + if (si && hw_hightile) tex = si->image; if (!tex || tex->GetTexelWidth() == 0 || tex->GetTexelHeight() == 0) return false; return tex && tex->GetTranslucency(); } +FGameTexture* SkyboxReplacement(FTextureID picnum, int palnum) +{ + auto hr = FindReplacement(picnum, palnum, true); + if (!hr) return nullptr; + return hr->image; +} + + //========================================================================== // // Processes data from .def files into the textures @@ -163,19 +173,19 @@ void PostLoadSetup() { if (rep.palnum == GLOWPAL) { - glowTex = rep.faces[0]; + glowTex = rep.image; } if (rep.palnum == NORMALPAL) { - normalTex = rep.faces[0]; + normalTex = rep.image; } if (rep.palnum == SPECULARPAL) { - specTex = rep.faces[0]; + specTex = rep.image; } if (rep.palnum == DETAILPAL) { - detailTex = rep.faces[0]; + detailTex = rep.image; scalex = rep.scale.X; scaley = rep.scale.Y; } @@ -185,10 +195,10 @@ void PostLoadSetup() { for (auto& rep : *Hightile) { - if (rep.faces[1]) continue; // do not muck around with skyboxes (yet) + if (rep.issky) continue; // do not muck around with skyboxes (yet) if (rep.palnum < NORMALPAL) { - auto tex = rep.faces[0]; + auto tex = rep.image; // Make a copy so that multiple appearances of the same texture with different layers can be handled. They will all refer to the same internal texture anyway. tex = MakeGameTexture(tex->GetTexture(), "", ETextureType::Any); if (glowTex) tex->SetGlowmap(glowTex->GetTexture()); @@ -196,7 +206,7 @@ void PostLoadSetup() if (normalTex) tex->SetNormalmap(normalTex->GetTexture()); if (specTex) tex->SetSpecularmap(specTex->GetTexture()); tex->SetDetailScale(scalex, scaley); - rep.faces[0] = tex; + rep.image = tex; } } } @@ -237,7 +247,7 @@ void PostLoadSetup() // //========================================================================== -int tileSetHightileReplacement(int picnum, int palnum, const char* filename, float alphacut, float xscale, float yscale, float specpower, float specfactor, uint8_t flags) +int tileSetHightileReplacement(int picnum, int palnum, const char* filename, float alphacut, float xscale, float yscale, float specpower, float specfactor) { if ((uint32_t)picnum >= (uint32_t)MAXTILES) return -1; if ((uint32_t)palnum >= (uint32_t)MAXPALOOKUPS) return -1; @@ -257,13 +267,12 @@ int tileSetHightileReplacement(int picnum, int palnum, const char* filename, flo return -1; } - replace.faces[0] = TexMan.GetGameTexture(texid); - if (replace.faces[0] == nullptr) + replace.image = TexMan.GetGameTexture(texid); replace.alphacut = min(alphacut,1.f); replace.scale = { xscale, yscale }; replace.specpower = specpower; // currently unused replace.specfactor = specfactor; // currently unused - replace.flags = flags; + replace.issky = 0; replace.palnum = (uint16_t)palnum; AddReplacement(picnum, replace); return 0; @@ -276,10 +285,10 @@ int tileSetHightileReplacement(int picnum, int palnum, const char* filename, flo // //========================================================================== -int tileSetSkybox(int picnum, int palnum, const char **facenames, int flags ) +int tileSetSkybox(int picnum, int palnum, FString* facenames) { - if ((uint32_t)picnum >= (uint32_t)MAXTILES) return -1; - if ((uint32_t)palnum >= (uint32_t)MAXPALOOKUPS) return -1; + if ((uint32_t)picnum >= (uint32_t)MAXTILES) return -1; + if ((uint32_t)palnum >= (uint32_t)MAXPALOOKUPS) return -1; auto tex = tileGetTexture(picnum); if (tex->GetTexelWidth() <= 0 || tex->GetTexelHeight() <= 0) @@ -288,18 +297,25 @@ int tileSetSkybox(int picnum, int palnum, const char **facenames, int flags ) return -1; // cannot add replacements to empty tiles, must create one beforehand } HightileReplacement replace = {}; - - for (auto &face : replace.faces) + + FGameTexture *faces[6]; + for (int i = 0; i < 6; i++) { - FTextureID texid = TexMan.CheckForTexture(*facenames, ETextureType::Any); + FTextureID texid = TexMan.CheckForTexture(facenames[i], ETextureType::Any); if (!texid.isValid()) { - Printf("%s: Skybox image for tile %d does not exist or is invalid\n", *facenames, picnum); + Printf("%s: Skybox image for tile %d does not exist or is invalid\n", facenames[i].GetChars(), picnum); return -1; } - face = TexMan.GetGameTexture(texid); + faces[i] = TexMan.GetGameTexture(texid); } - replace.flags = flags; + FSkyBox* sbtex = new FSkyBox(""); + memcpy(sbtex->faces, faces, sizeof(faces)); + sbtex->previous = faces[0]; // won't ever be used, just to be safe. + sbtex->fliptop = true; + replace.image = MakeGameTexture(sbtex, "", ETextureType::Override); + TexMan.AddGameTexture(replace.image, false); + replace.issky = 1; replace.palnum = (uint16_t)palnum; AddReplacement(picnum, replace); return 0; @@ -341,7 +357,7 @@ bool PickTexture(FRenderState *state, FGameTexture* tex, int paletteid, TextureP if (rep) { - tex = rep->faces[0]; + tex = rep->image; } if (!rep || rep->palnum != hipalswap || (h.tintFlags & TINTF_APPLYOVERALTPAL)) applytint = true; diff --git a/source/core/textures/skytexture.cpp b/source/core/textures/skytexture.cpp index 7dbde726a..55c38c571 100644 --- a/source/core/textures/skytexture.cpp +++ b/source/core/textures/skytexture.cpp @@ -41,24 +41,23 @@ #include "texturemanager.h" #include "buildtiles.h" -FGameTexture* GetSkyTexture(int basetile, int lognumtiles, const int16_t *tilemap) +FGameTexture* GetSkyTexture(int basetile, int lognumtiles, const int16_t *tilemap, int remap) { - char synthname[60]; + FString synthname; - if (lognumtiles == 0 || lognumtiles > 4) + if ((lognumtiles == 0 && remap == 0) || lognumtiles > 4) { // no special handling - let the old code do its job as-is return nullptr; } int numtiles = 1 << lognumtiles; - mysnprintf(synthname, 60, "%04x", basetile); + synthname.Format("Sky%04x%02x", basetile, remap); for(int i = 0; i < numtiles; i++) { - synthname[4+i] = 'A' + tilemap[i]; + synthname += 'A' + tilemap[i]; }; - synthname[4+numtiles] = 0; auto tex = TexMan.FindGameTexture(synthname); if (tex) return tex; @@ -70,6 +69,7 @@ FGameTexture* GetSkyTexture(int basetile, int lognumtiles, const int16_t *tilema if (!tex || !tex->isValid() || tex->GetTexture() == 0) return nullptr; build[i].TexImage = static_cast(tex->GetTexture()); build[i].OriginX = tilewidth * i; + build[i].Translation = GPalette.GetTranslation(GetTranslationType(remap), GetTranslationIndex(remap)); } auto tt = MakeGameTexture(new FImageTexture(new FMultiPatchTexture(tilewidth*numtiles, tileHeight(basetile), build, false, false)), synthname, ETextureType::Override); TexMan.AddGameTexture(tt, true); diff --git a/source/core/version.h b/source/core/version.h index c8a5949a0..440be91c8 100644 --- a/source/core/version.h +++ b/source/core/version.h @@ -41,7 +41,7 @@ const char *GetVersionString(); /** Lots of different version numbers **/ -#define VERSIONSTR "1.0.0-pre" +#define VERSIONSTR "0.10.0-preview" // The version as seen in the Windows resource #define RC_FILEVERSION 0,9,0,0 diff --git a/source/games/blood/all.cpp b/source/games/blood/all.cpp index 23658ca4d..46e731887 100644 --- a/source/games/blood/all.cpp +++ b/source/games/blood/all.cpp @@ -31,7 +31,6 @@ #include "src/callback.cpp" #include "src/choke.cpp" #include "src/controls.cpp" -#include "src/credits.cpp" #include "src/db.cpp" #include "src/dude.cpp" #include "src/endgame.cpp" @@ -66,5 +65,9 @@ #include "src/view.cpp" #include "src/warp.cpp" #include "src/weapon.cpp" + +#include "src/_polymost.cpp" + // This includes the VM so it is last #include "src/d_menu.cpp" + diff --git a/source/games/blood/src/_polymost.cpp b/source/games/blood/src/_polymost.cpp new file mode 100644 index 000000000..417da88b0 --- /dev/null +++ b/source/games/blood/src/_polymost.cpp @@ -0,0 +1,337 @@ +#include "polymost.h" + +BEGIN_BLD_NS + + +// leftover bits needed to keep Polymost running through the transition. +// This is mainly the game side part of the portal renderer. + +void collectTSpritesForPortal(int x, int y, int i, int interpolation) +{ + int nSector = mirror[i].link; + int nSector2 = mirror[i].wallnum; + int nSprite; + SectIterator it(nSector); + while ((nSprite = it.NextIndex()) >= 0) + { + spritetype* pSprite = &sprite[nSprite]; + if (pSprite == gView->pSprite) + continue; + int top, bottom; + GetSpriteExtents(pSprite, &top, &bottom); + int zCeil, zFloor; + getzsofslope(nSector, pSprite->x, pSprite->y, &zCeil, &zFloor); + if (pSprite->statnum == kStatDude && (top < zCeil || bottom > zFloor)) + { + int j = i; + if (mirror[i].type == 2) + j++; + else + j--; + int dx = mirror[j].dx; + int dy = mirror[j].dy; + int dz = mirror[j].dz; + tspritetype* pTSprite = &pm_tsprite[pm_spritesortcnt++]; + *pTSprite = {}; + pTSprite->type = pSprite->type; + pTSprite->index = pSprite->index; + pTSprite->sectnum = nSector2; + pTSprite->x = pSprite->x + dx; + pTSprite->y = pSprite->y + dy; + pTSprite->z = pSprite->z + dz; + pTSprite->ang = pSprite->ang; + pTSprite->picnum = pSprite->picnum; + pTSprite->shade = pSprite->shade; + pTSprite->pal = pSprite->pal; + pTSprite->xrepeat = pSprite->xrepeat; + pTSprite->yrepeat = pSprite->yrepeat; + pTSprite->xoffset = pSprite->xoffset; + pTSprite->yoffset = pSprite->yoffset; + pTSprite->cstat = pSprite->cstat; + pTSprite->statnum = kStatDecoration; + pTSprite->owner = pSprite->index; + pTSprite->extra = pSprite->extra; + pTSprite->flags = pSprite->hitag | 0x200; + pTSprite->x = dx + interpolatedvalue(pSprite->ox, pSprite->x, interpolation); + pTSprite->y = dy + interpolatedvalue(pSprite->oy, pSprite->y, interpolation); + pTSprite->z = dz + interpolatedvalue(pSprite->oz, pSprite->z, interpolation); + pTSprite->ang = pSprite->interpolatedang(interpolation); + + int nAnim = 0; + switch (picanm[pTSprite->picnum].extra & 7) + { + case 1: + { + int dX = x - pTSprite->x; + int dY = y - pTSprite->y; + RotateVector(&dX, &dY, 128 - pTSprite->ang); + nAnim = GetOctant(dX, dY); + if (nAnim <= 4) + { + pTSprite->cstat &= ~4; + } + else + { + nAnim = 8 - nAnim; + pTSprite->cstat |= 4; + } + break; + } + case 2: + { + int dX = x - pTSprite->x; + int dY = y - pTSprite->y; + RotateVector(&dX, &dY, 128 - pTSprite->ang); + nAnim = GetOctant(dX, dY); + break; + } + } + while (nAnim > 0) + { + pTSprite->picnum += picanm[pTSprite->picnum].num + 1; + nAnim--; + } + + pm_spritesortcnt++; + } + } + +} + +void processSpritesOnOtherSideOfPortal(int x, int y, int interpolation) +{ + if (pm_spritesortcnt == 0) return; + int nViewSprites = pm_spritesortcnt-1; + for (int nTSprite = nViewSprites; nTSprite >= 0; nTSprite--) + { + tspritetype *pTSprite = &pm_tsprite[nTSprite]; + pTSprite->xrepeat = pTSprite->yrepeat = 0; + } + for (int i = mirrorcnt-1; i >= 0; i--) + { + int nTile = 4080+i; + if (TestBitString(gotpic, nTile)) + { + if (mirror[i].type == 1 || mirror[i].type == 2) + { + collectTSpritesForPortal(x, y, i, interpolation); + } + } + } +} + + +void render3DViewPolymost(int nSectnum, int cX, int cY, int cZ, binangle cA, fixedhoriz cH) +{ + int yxAspect = yxaspect; + int viewingRange = viewingrange; + videoSetCorrectedAspect(); + + int v1 = xs_CRoundToInt(double(viewingrange) * tan(r_fov * (pi::pi() / 360.))); + + renderSetAspect(v1, yxaspect); + + + int ceilingZ, floorZ; + getzsofslope(nSectnum, cX, cY, &ceilingZ, &floorZ); + if (cZ >= floorZ) + { + cZ = floorZ - (gUpperLink[nSectnum] >= 0 ? 0 : (8 << 8)); + } + if (cZ <= ceilingZ) + { + cZ = ceilingZ + (gLowerLink[nSectnum] >= 0 ? 0 : (8 << 8)); + } + cH = q16horiz(ClipRange(cH.asq16(), gi->playerHorizMin(), gi->playerHorizMax())); +RORHACK: + int ror_status[16]; + for (int i = 0; i < 16; i++) + ror_status[i] = TestBitString(gotpic, 4080 + i); + fixed_t deliriumPitchI = interpolatedvalue(IntToFixed(deliriumPitchO), IntToFixed(deliriumPitch), gInterpolate); + DrawMirrors(cX, cY, cZ, cA.asq16(), cH.asq16() + deliriumPitchI, gInterpolate, gViewIndex); + int bakCstat = gView->pSprite->cstat; + if (gViewPos == 0) + { + gView->pSprite->cstat |= 32768; + } + else + { + gView->pSprite->cstat |= 514; + } + + renderDrawRoomsQ16(cX, cY, cZ, cA.asq16(), cH.asq16() + deliriumPitchI, nSectnum); + viewProcessSprites(pm_tsprite, pm_spritesortcnt, cX, cY, cZ, cA.asbuild(), gInterpolate); + bool do_ror_hack = false; + for (int i = 0; i < 16; i++) + if (ror_status[i] != TestBitString(gotpic, 4080 + i)) + do_ror_hack = true; + if (do_ror_hack) + { + gView->pSprite->cstat = bakCstat; + pm_spritesortcnt = 0; + goto RORHACK; + } + setPortalFlags(1); + int nSpriteSortCnt = pm_spritesortcnt; + renderDrawMasks(); + pm_spritesortcnt = nSpriteSortCnt; + setPortalFlags(0); + processSpritesOnOtherSideOfPortal(cX, cY, gInterpolate); + renderDrawMasks(); + gView->pSprite->cstat = bakCstat; + +} + +// hack the portal planes with the sky flag for rendering. Only Polymost needs this hack. +void setPortalFlags(char mode) +{ + for (int i = mirrorcnt - 1; i >= 0; i--) + { + int nTile = 4080 + i; + if (TestBitString(gotpic, nTile)) + { + switch (mirror[i].type) + { + case 1: + if (mode) + sector[mirror[i].wallnum].ceilingstat |= 1; + else + sector[mirror[i].wallnum].ceilingstat &= ~1; + break; + case 2: + if (mode) + sector[mirror[i].wallnum].floorstat |= 1; + else + sector[mirror[i].wallnum].floorstat &= ~1; + break; + } + } + } +} + + +void DrawMirrors(int x, int y, int z, fixed_t a, fixed_t horiz, int smooth, int viewPlayer) +{ + for (int i = mirrorcnt - 1; i >= 0; i--) + { + int nTile = 4080 + i; + if (TestBitString(gotpic, nTile)) + { + ClearBitString(gotpic, nTile); + switch (mirror[i].type) + { + case 0: + { + int nWall = mirror[i].link; + int nSector = sectorofwall(nWall); + walltype* pWall = &wall[nWall]; + int nNextWall = pWall->nextwall; + int nNextSector = pWall->nextsector; + pWall->nextwall = mirrorwall[0]; + pWall->nextsector = mirrorsector; + wall[mirrorwall[0]].nextwall = nWall; + wall[mirrorwall[0]].nextsector = nSector; + wall[mirrorwall[0]].x = wall[pWall->point2].x; + wall[mirrorwall[0]].y = wall[pWall->point2].y; + wall[mirrorwall[1]].x = pWall->x; + wall[mirrorwall[1]].y = pWall->y; + wall[mirrorwall[2]].x = wall[mirrorwall[1]].x + (wall[mirrorwall[1]].x - wall[mirrorwall[0]].x) * 16; + wall[mirrorwall[2]].y = wall[mirrorwall[1]].y + (wall[mirrorwall[1]].y - wall[mirrorwall[0]].y) * 16; + wall[mirrorwall[3]].x = wall[mirrorwall[0]].x + (wall[mirrorwall[0]].x - wall[mirrorwall[1]].x) * 16; + wall[mirrorwall[3]].y = wall[mirrorwall[0]].y + (wall[mirrorwall[0]].y - wall[mirrorwall[1]].y) * 16; + sector[mirrorsector].floorz = sector[nSector].floorz; + sector[mirrorsector].ceilingz = sector[nSector].ceilingz; + int cx, cy, ca; + if (GetWallType(nWall) == kWallStack) + { + cx = x - (wall[pWall->hitag].x - wall[pWall->point2].x); + cy = y - (wall[pWall->hitag].y - wall[pWall->point2].y); + ca = a; + } + else + { + renderPrepareMirror(x, y, z, a, horiz, nWall, &cx, &cy, &ca); + } + int32_t didmirror = renderDrawRoomsQ16(cx, cy, z, ca, horiz, mirrorsector | MAXSECTORS); + viewProcessSprites(pm_tsprite, pm_spritesortcnt, cx, cy, z, FixedToInt(ca), smooth); + renderDrawMasks(); + if (GetWallType(nWall) != kWallStack) + renderCompleteMirror(); + if (wall[nWall].pal != 0 || wall[nWall].shade != 0) + TranslateMirrorColors(wall[nWall].shade, wall[nWall].pal); + pWall->nextwall = nNextWall; + pWall->nextsector = nNextSector; + return; + } + case 1: + { + r_rorphase = 1; + int nSector = mirror[i].link; + int bakCstat; + if (viewPlayer >= 0) + { + bakCstat = gPlayer[viewPlayer].pSprite->cstat; + if (gViewPos == 0) + { + gPlayer[viewPlayer].pSprite->cstat |= 32768; + } + else + { + gPlayer[viewPlayer].pSprite->cstat |= 514; + } + } + renderDrawRoomsQ16(x + mirror[i].dx, y + mirror[i].dy, z + mirror[i].dz, a, horiz, nSector | MAXSECTORS); + viewProcessSprites(pm_tsprite, pm_spritesortcnt, x + mirror[i].dx, y + mirror[i].dy, z + mirror[i].dz, FixedToInt(a), smooth); + short fstat = sector[nSector].floorstat; + sector[nSector].floorstat |= 1; + renderDrawMasks(); + sector[nSector].floorstat = fstat; + for (int i = 0; i < 16; i++) + ClearBitString(gotpic, 4080 + i); + if (viewPlayer >= 0) + { + gPlayer[viewPlayer].pSprite->cstat = bakCstat; + } + r_rorphase = 0; + return; + } + case 2: + { + r_rorphase = 1; + int nSector = mirror[i].link; + int bakCstat; + if (viewPlayer >= 0) + { + bakCstat = gPlayer[viewPlayer].pSprite->cstat; + if (gViewPos == 0) + { + gPlayer[viewPlayer].pSprite->cstat |= 32768; + } + else + { + gPlayer[viewPlayer].pSprite->cstat |= 514; + } + } + renderDrawRoomsQ16(x + mirror[i].dx, y + mirror[i].dy, z + mirror[i].dz, a, horiz, nSector | MAXSECTORS); + viewProcessSprites(pm_tsprite, pm_spritesortcnt, x + mirror[i].dx, y + mirror[i].dy, z + mirror[i].dz, FixedToInt(a), smooth); + short cstat = sector[nSector].ceilingstat; + sector[nSector].ceilingstat |= 1; + renderDrawMasks(); + sector[nSector].ceilingstat = cstat; + for (int i = 0; i < 16; i++) + ClearBitString(gotpic, 4080 + i); + if (viewPlayer >= 0) + { + gPlayer[viewPlayer].pSprite->cstat = bakCstat; + } + r_rorphase = 0; + return; + } + } + } + } +} + + + +END_BLD_NS diff --git a/source/games/blood/src/actor.cpp b/source/games/blood/src/actor.cpp index f78571942..7f09f4a7f 100644 --- a/source/games/blood/src/actor.cpp +++ b/source/games/blood/src/actor.cpp @@ -27,7 +27,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "build.h" #include "automap.h" -#include "mmulti.h" #include "savegamehelp.h" #include "blood.h" @@ -2638,10 +2637,8 @@ int actFloorBounceVector(int *x, int *y, int *z, int nSector, int a5) return mulscale16r(t8, t); } -void sub_2A620(int nSprite, int x, int y, int z, int nSector, int nDist, int a7, int a8, DAMAGE_TYPE a9, int a10, int a11, int a12, int a13) +void sub_2A620(int nSprite, int x, int y, int z, int nSector, int nDist, int a7, int a8, DAMAGE_TYPE a9, int a10, int a11, int , int ) { - UNREFERENCED_PARAMETER(a12); - UNREFERENCED_PARAMETER(a13); uint8_t va0[(kMaxSectors+7)>>3]; int nOwner = sprite[nSprite].owner; GetClosestSpriteSectors(nSector, x, y, nDist, va0); diff --git a/source/games/blood/src/ai.cpp b/source/games/blood/src/ai.cpp index 3bde01d66..347f94c34 100644 --- a/source/games/blood/src/ai.cpp +++ b/source/games/blood/src/ai.cpp @@ -25,7 +25,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "build.h" #include "savegamehelp.h" -#include "mmulti.h" #include "blood.h" diff --git a/source/games/blood/src/aibat.cpp b/source/games/blood/src/aibat.cpp index 6db80f812..878affe7d 100644 --- a/source/games/blood/src/aibat.cpp +++ b/source/games/blood/src/aibat.cpp @@ -25,7 +25,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "compat.h" #include "build.h" -#include "mmulti.h" #include "blood.h" diff --git a/source/games/blood/src/aibeast.cpp b/source/games/blood/src/aibeast.cpp index 30dd7d47b..857d01cfe 100644 --- a/source/games/blood/src/aibeast.cpp +++ b/source/games/blood/src/aibeast.cpp @@ -25,7 +25,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "compat.h" #include "build.h" -#include "mmulti.h" #include "blood.h" diff --git a/source/games/blood/src/aiboneel.cpp b/source/games/blood/src/aiboneel.cpp index 2fccc1674..0c56f7725 100644 --- a/source/games/blood/src/aiboneel.cpp +++ b/source/games/blood/src/aiboneel.cpp @@ -24,7 +24,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "ns.h" // Must come before everything else! #include "build.h" -#include "mmulti.h" #include "blood.h" diff --git a/source/games/blood/src/aiburn.cpp b/source/games/blood/src/aiburn.cpp index 15ad1528e..7470a20e0 100644 --- a/source/games/blood/src/aiburn.cpp +++ b/source/games/blood/src/aiburn.cpp @@ -25,7 +25,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "compat.h" #include "build.h" -#include "mmulti.h" #include "blood.h" diff --git a/source/games/blood/src/aicaleb.cpp b/source/games/blood/src/aicaleb.cpp index 9bf1c779f..7024c0b8f 100644 --- a/source/games/blood/src/aicaleb.cpp +++ b/source/games/blood/src/aicaleb.cpp @@ -25,7 +25,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "compat.h" #include "build.h" -#include "mmulti.h" #include "blood.h" diff --git a/source/games/blood/src/aicerber.cpp b/source/games/blood/src/aicerber.cpp index 7360f6b8c..414d267dc 100644 --- a/source/games/blood/src/aicerber.cpp +++ b/source/games/blood/src/aicerber.cpp @@ -25,7 +25,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "compat.h" #include "build.h" -#include "mmulti.h" #include "blood.h" diff --git a/source/games/blood/src/aicult.cpp b/source/games/blood/src/aicult.cpp index 36703635c..e1e8f7ab0 100644 --- a/source/games/blood/src/aicult.cpp +++ b/source/games/blood/src/aicult.cpp @@ -25,7 +25,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "compat.h" #include "build.h" -#include "mmulti.h" #include "blood.h" diff --git a/source/games/blood/src/aigarg.cpp b/source/games/blood/src/aigarg.cpp index b70bc1b6c..176af80b8 100644 --- a/source/games/blood/src/aigarg.cpp +++ b/source/games/blood/src/aigarg.cpp @@ -25,7 +25,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "compat.h" #include "build.h" -#include "mmulti.h" #include "blood.h" diff --git a/source/games/blood/src/aighost.cpp b/source/games/blood/src/aighost.cpp index 7b6d39541..d820cda3f 100644 --- a/source/games/blood/src/aighost.cpp +++ b/source/games/blood/src/aighost.cpp @@ -25,7 +25,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "compat.h" #include "build.h" -#include "mmulti.h" #include "blood.h" diff --git a/source/games/blood/src/aigilbst.cpp b/source/games/blood/src/aigilbst.cpp index 1de41c886..47b6267e9 100644 --- a/source/games/blood/src/aigilbst.cpp +++ b/source/games/blood/src/aigilbst.cpp @@ -25,7 +25,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "compat.h" #include "build.h" -#include "mmulti.h" #include "blood.h" diff --git a/source/games/blood/src/aihand.cpp b/source/games/blood/src/aihand.cpp index 389c6bfab..5f31be3ff 100644 --- a/source/games/blood/src/aihand.cpp +++ b/source/games/blood/src/aihand.cpp @@ -25,7 +25,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "compat.h" #include "build.h" -#include "mmulti.h" #include "blood.h" diff --git a/source/games/blood/src/aihound.cpp b/source/games/blood/src/aihound.cpp index 790a5180d..206871d6e 100644 --- a/source/games/blood/src/aihound.cpp +++ b/source/games/blood/src/aihound.cpp @@ -25,7 +25,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "compat.h" #include "build.h" -#include "mmulti.h" #include "blood.h" diff --git a/source/games/blood/src/aiinnoc.cpp b/source/games/blood/src/aiinnoc.cpp index b0595c500..09b83bdd8 100644 --- a/source/games/blood/src/aiinnoc.cpp +++ b/source/games/blood/src/aiinnoc.cpp @@ -25,7 +25,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "compat.h" #include "build.h" -#include "mmulti.h" #include "blood.h" diff --git a/source/games/blood/src/aipod.cpp b/source/games/blood/src/aipod.cpp index 822b45307..35c98a6c6 100644 --- a/source/games/blood/src/aipod.cpp +++ b/source/games/blood/src/aipod.cpp @@ -25,7 +25,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "compat.h" #include "build.h" -#include "mmulti.h" #include "blood.h" diff --git a/source/games/blood/src/airat.cpp b/source/games/blood/src/airat.cpp index 53198e62d..3cf5d93a8 100644 --- a/source/games/blood/src/airat.cpp +++ b/source/games/blood/src/airat.cpp @@ -25,7 +25,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "compat.h" #include "build.h" -#include "mmulti.h" #include "blood.h" diff --git a/source/games/blood/src/aispid.cpp b/source/games/blood/src/aispid.cpp index cd34b36e8..14517d58a 100644 --- a/source/games/blood/src/aispid.cpp +++ b/source/games/blood/src/aispid.cpp @@ -25,7 +25,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "compat.h" #include "build.h" -#include "mmulti.h" #include "blood.h" diff --git a/source/games/blood/src/aitchern.cpp b/source/games/blood/src/aitchern.cpp index 05cd294b9..17be3e2e5 100644 --- a/source/games/blood/src/aitchern.cpp +++ b/source/games/blood/src/aitchern.cpp @@ -25,7 +25,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "compat.h" #include "build.h" -#include "mmulti.h" #include "blood.h" diff --git a/source/games/blood/src/aiunicult.cpp b/source/games/blood/src/aiunicult.cpp index 85e6b9ea2..638a15e82 100644 --- a/source/games/blood/src/aiunicult.cpp +++ b/source/games/blood/src/aiunicult.cpp @@ -26,7 +26,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "compat.h" #include "build.h" -#include "mmulti.h" #include "raze_sound.h" #include "blood.h" diff --git a/source/games/blood/src/aizomba.cpp b/source/games/blood/src/aizomba.cpp index 442bdecfa..2351e1858 100644 --- a/source/games/blood/src/aizomba.cpp +++ b/source/games/blood/src/aizomba.cpp @@ -25,7 +25,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "compat.h" #include "build.h" -#include "mmulti.h" #include "blood.h" diff --git a/source/games/blood/src/aizombf.cpp b/source/games/blood/src/aizombf.cpp index ee74f601a..64b8fbd6d 100644 --- a/source/games/blood/src/aizombf.cpp +++ b/source/games/blood/src/aizombf.cpp @@ -25,7 +25,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "compat.h" #include "build.h" -#include "mmulti.h" #include "blood.h" diff --git a/source/games/blood/src/animatesprite.cpp b/source/games/blood/src/animatesprite.cpp index 918e24916..f96554cca 100644 --- a/source/games/blood/src/animatesprite.cpp +++ b/source/games/blood/src/animatesprite.cpp @@ -27,7 +27,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "compat.h" #include "build.h" -#include "mmulti.h" #include "v_font.h" #include "blood.h" @@ -38,6 +37,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "v_2ddrawer.h" #include "v_video.h" #include "v_font.h" +#include "hw_voxels.h" +#include "gamefuncs.h" +#include "glbackend/glbackend.h" BEGIN_BLD_NS @@ -66,7 +68,7 @@ static void RotateXZ(int *pX, int *, int *pZ, int ang) *pZ = dmulscale30r(oX,angSin,oZ,angCos); } -template tspritetype* viewInsertTSprite(int nSector, int nStatnum, T const * const pSprite) +template tspritetype* viewInsertTSprite(spritetype* tsprite, int& spritesortcnt, int nSector, int nStatnum, T const * const pSprite) { int nTSprite = spritesortcnt; tspritetype *pTSprite = &tsprite[nTSprite]; @@ -121,17 +123,17 @@ static const WEAPONICON gWeaponIcon[] = { }; -static tspritetype *viewAddEffect(int nTSprite, VIEW_EFFECT nViewEffect) +static tspritetype *viewAddEffect(spritetype* tsprite, int& spritesortcnt, int nTSprite, VIEW_EFFECT nViewEffect) { assert(nViewEffect >= 0 && nViewEffect < kViewEffectMax); auto pTSprite = &tsprite[nTSprite]; - if (gDetail < effectDetail[nViewEffect] || nTSprite >= maxspritesonscreen) return NULL; + if (gDetail < effectDetail[nViewEffect] || nTSprite >= MAXSPRITESONSCREEN) return NULL; switch (nViewEffect) { case VIEW_EFFECT_18: for (int i = 0; i < 16; i++) { - auto pNSprite = viewInsertTSprite(pTSprite->sectnum, 32767, pTSprite); + auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite); int ang = (PlayClock*2048)/120; int nRand1 = dword_172CE0[i][0]; int nRand2 = dword_172CE0[i][1]; @@ -154,7 +156,7 @@ static tspritetype *viewAddEffect(int nTSprite, VIEW_EFFECT nViewEffect) { int top, bottom; GetSpriteExtents(pTSprite, &top, &bottom); - auto pNSprite = viewInsertTSprite(pTSprite->sectnum, 32767, pTSprite); + auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite); pNSprite->shade = -128; pNSprite->pal = 0; pNSprite->z = top; @@ -167,7 +169,7 @@ static tspritetype *viewAddEffect(int nTSprite, VIEW_EFFECT nViewEffect) } case VIEW_EFFECT_15: { - auto pNSprite = viewInsertTSprite(pTSprite->sectnum, 32767, pTSprite); + auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite); pNSprite->z = pTSprite->z; pNSprite->cstat |= 2; pNSprite->shade = -128; @@ -178,7 +180,7 @@ static tspritetype *viewAddEffect(int nTSprite, VIEW_EFFECT nViewEffect) } case VIEW_EFFECT_14: { - auto pNSprite = viewInsertTSprite(pTSprite->sectnum, 32767, pTSprite); + auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite); pNSprite->shade = -128; pNSprite->pal = 0; pNSprite->xrepeat = pNSprite->yrepeat = 64; @@ -187,7 +189,7 @@ static tspritetype *viewAddEffect(int nTSprite, VIEW_EFFECT nViewEffect) } case VIEW_EFFECT_13: { - auto pNSprite = viewInsertTSprite(pTSprite->sectnum, 32767, pTSprite); + auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite); pNSprite->shade = 26; pNSprite->pal = 0; pNSprite->cstat |= 2; @@ -197,7 +199,7 @@ static tspritetype *viewAddEffect(int nTSprite, VIEW_EFFECT nViewEffect) } case VIEW_EFFECT_11: { - auto pNSprite = viewInsertTSprite(pTSprite->sectnum, 32767, pTSprite); + auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite); int top, bottom; GetSpriteExtents(pTSprite, &top, &bottom); pNSprite->shade = 26; @@ -219,10 +221,10 @@ static tspritetype *viewAddEffect(int nTSprite, VIEW_EFFECT nViewEffect) { nAng = (nAng+1024)&2047; } - for (int i = 0; i < 5 && spritesortcnt < maxspritesonscreen; i++) + for (int i = 0; i < 5 && spritesortcnt < MAXSPRITESONSCREEN; i++) { int nSector = pTSprite->sectnum; - auto pNSprite = viewInsertTSprite(nSector, 32767, NULL); + auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, nSector, 32767, NULL); int nLen = 128+(i<<7); int x = MulScale(nLen, Cos(nAng), 30); pNSprite->x = pTSprite->x + x; @@ -246,7 +248,7 @@ static tspritetype *viewAddEffect(int nTSprite, VIEW_EFFECT nViewEffect) } case VIEW_EFFECT_8: { - auto pNSprite = viewInsertTSprite(pTSprite->sectnum, 32767, pTSprite); + auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite); pNSprite->shade = -128; pNSprite->z = pTSprite->z; pNSprite->picnum = 908; @@ -256,7 +258,7 @@ static tspritetype *viewAddEffect(int nTSprite, VIEW_EFFECT nViewEffect) } case VIEW_EFFECT_6: { - auto pNSprite = viewInsertTSprite(pTSprite->sectnum, 32767, pTSprite); + auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite); int top, bottom; GetSpriteExtents(pTSprite, &top, &bottom); pNSprite->z = top; @@ -272,7 +274,7 @@ static tspritetype *viewAddEffect(int nTSprite, VIEW_EFFECT nViewEffect) } case VIEW_EFFECT_7: { - auto pNSprite = viewInsertTSprite(pTSprite->sectnum, 32767, pTSprite); + auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite); int top, bottom; GetSpriteExtents(pTSprite, &top, &bottom); pNSprite->z = bottom; @@ -288,7 +290,7 @@ static tspritetype *viewAddEffect(int nTSprite, VIEW_EFFECT nViewEffect) } case VIEW_EFFECT_4: { - auto pNSprite = viewInsertTSprite(pTSprite->sectnum, 32767, pTSprite); + auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite); int top, bottom; GetSpriteExtents(pTSprite, &top, &bottom); pNSprite->z = top; @@ -299,7 +301,7 @@ static tspritetype *viewAddEffect(int nTSprite, VIEW_EFFECT nViewEffect) } case VIEW_EFFECT_5: { - auto pNSprite = viewInsertTSprite(pTSprite->sectnum, 32767, pTSprite); + auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite); int top, bottom; GetSpriteExtents(pTSprite, &top, &bottom); pNSprite->z = bottom; @@ -312,7 +314,7 @@ static tspritetype *viewAddEffect(int nTSprite, VIEW_EFFECT nViewEffect) { if (r_shadows) { - auto pNSprite = viewInsertTSprite(pTSprite->sectnum, 32767, pTSprite); + auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite); pNSprite->z = getflorzofslope(pTSprite->sectnum, pNSprite->x, pNSprite->y); pNSprite->shade = 127; pNSprite->cstat |= 2; @@ -328,7 +330,7 @@ static tspritetype *viewAddEffect(int nTSprite, VIEW_EFFECT nViewEffect) } case VIEW_EFFECT_1: { - auto pNSprite = viewInsertTSprite(pTSprite->sectnum, 32767, pTSprite); + auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite); pNSprite->shade = -128; pNSprite->pal = 2; pNSprite->cstat |= 2; @@ -340,7 +342,7 @@ static tspritetype *viewAddEffect(int nTSprite, VIEW_EFFECT nViewEffect) } case VIEW_EFFECT_2: { - auto pNSprite = viewInsertTSprite(pTSprite->sectnum, 32767, pTSprite); + auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite); sectortype *pSector = §or[pTSprite->sectnum]; pNSprite->x = pTSprite->x; pNSprite->y = pTSprite->y; @@ -356,7 +358,7 @@ static tspritetype *viewAddEffect(int nTSprite, VIEW_EFFECT nViewEffect) } case VIEW_EFFECT_3: { - auto pNSprite = viewInsertTSprite(pTSprite->sectnum, 32767, pTSprite); + auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite); sectortype *pSector = §or[pTSprite->sectnum]; pNSprite->x = pTSprite->x; pNSprite->y = pTSprite->y; @@ -373,7 +375,7 @@ static tspritetype *viewAddEffect(int nTSprite, VIEW_EFFECT nViewEffect) } case VIEW_EFFECT_9: { - auto pNSprite = viewInsertTSprite(pTSprite->sectnum, 32767, pTSprite); + auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite); pNSprite->z = pTSprite->z; if (gDetail > 1) pNSprite->cstat |= 514; @@ -390,7 +392,7 @@ static tspritetype *viewAddEffect(int nTSprite, VIEW_EFFECT nViewEffect) WEAPONICON weaponIcon = gWeaponIcon[pPlayer->curWeapon]; const int nTile = weaponIcon.nTile; if (nTile < 0) break; - auto pNSprite = viewInsertTSprite(pTSprite->sectnum, 32767, pTSprite); + auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite); pNSprite->x = pTSprite->x; pNSprite->y = pTSprite->y; pNSprite->z = pTSprite->z-(32<<8); @@ -424,15 +426,15 @@ static void viewApplyDefaultPal(tspritetype *pTSprite, sectortype const *pSector XSECTOR const *pXSector = nXSector >= 0 ? &xsector[nXSector] : NULL; if (pXSector && pXSector->color && (VanillaMode() || pSector->floorpal != 0)) { - pTSprite->pal = pSector->floorpal; + copyfloorpal(pTSprite, pSector); } } -void viewProcessSprites(int32_t cX, int32_t cY, int32_t cZ, int32_t cA, int32_t smoothratio) +void viewProcessSprites(spritetype* tsprite, int& spritesortcnt, int32_t cX, int32_t cY, int32_t cZ, int32_t cA, int32_t smoothratio) { // shift before interpolating to increase precision. int myclock = (PlayClock<<3) + MulScale(4<<3, smoothratio, 16); - assert(spritesortcnt <= maxspritesonscreen); + assert(spritesortcnt <= MAXSPRITESONSCREEN); gCameraAng = cA; int nViewSprites = spritesortcnt; for (int nTSprite = spritesortcnt-1; nTSprite >= 0; nTSprite--) @@ -632,14 +634,14 @@ void viewProcessSprites(int32_t cX, int32_t cY, int32_t cZ, int32_t cA, int32_t pTSprite->xrepeat = pTSprite->yrepeat = 0; } } - if (spritesortcnt >= maxspritesonscreen) continue; + if (spritesortcnt >= MAXSPRITESONSCREEN) continue; if (pTXSprite && pTXSprite->burnTime > 0) { pTSprite->shade = ClipRange(pTSprite->shade-16-QRandom(8), -128, 127); } if (pTSprite->flags&256) { - viewAddEffect(nTSprite, VIEW_EFFECT_6); + viewAddEffect(tsprite, spritesortcnt, nTSprite, VIEW_EFFECT_6); } if (pTSprite->flags&1024) { @@ -655,7 +657,7 @@ void viewProcessSprites(int32_t cX, int32_t cY, int32_t cZ, int32_t cA, int32_t case kDecorationCandle: if (!pTXSprite || pTXSprite->state == 1) { pTSprite->shade = -128; - viewAddEffect(nTSprite, VIEW_EFFECT_11); + viewAddEffect(tsprite, spritesortcnt, nTSprite, VIEW_EFFECT_11); } else { pTSprite->shade = -8; } @@ -663,9 +665,9 @@ void viewProcessSprites(int32_t cX, int32_t cY, int32_t cZ, int32_t cA, int32_t case kDecorationTorch: if (!pTXSprite || pTXSprite->state == 1) { pTSprite->picnum++; - viewAddEffect(nTSprite, VIEW_EFFECT_4); + viewAddEffect(tsprite, spritesortcnt, nTSprite, VIEW_EFFECT_4); } else { - viewAddEffect(nTSprite, VIEW_EFFECT_6); + viewAddEffect(tsprite, spritesortcnt, nTSprite, VIEW_EFFECT_6); } break; default: @@ -678,13 +680,13 @@ void viewProcessSprites(int32_t cX, int32_t cY, int32_t cZ, int32_t cA, int32_t switch (pTSprite->type) { case kItemFlagABase: if (pTXSprite && pTXSprite->state > 0 && gGameOptions.nGameType == 3) { - auto pNTSprite = viewAddEffect(nTSprite, VIEW_EFFECT_17); + auto pNTSprite = viewAddEffect(tsprite, spritesortcnt, nTSprite, VIEW_EFFECT_17); if (pNTSprite) pNTSprite->pal = 10; } break; case kItemFlagBBase: if (pTXSprite && pTXSprite->state > 0 && gGameOptions.nGameType == 3) { - auto pNTSprite = viewAddEffect(nTSprite, VIEW_EFFECT_17); + auto pNTSprite = viewAddEffect(tsprite, spritesortcnt, nTSprite, VIEW_EFFECT_17); if (pNTSprite) pNTSprite->pal = 7; } break; @@ -712,10 +714,10 @@ void viewProcessSprites(int32_t cX, int32_t cY, int32_t cZ, int32_t cA, int32_t pTSprite->cstat |= 32; break; case kMissileTeslaRegular: - viewAddEffect(nTSprite, VIEW_EFFECT_15); + viewAddEffect(tsprite, spritesortcnt, nTSprite, VIEW_EFFECT_15); break; case kMissileButcherKnife: - viewAddEffect(nTSprite, VIEW_EFFECT_10); + viewAddEffect(tsprite, spritesortcnt, nTSprite, VIEW_EFFECT_10); break; case kMissileFlareRegular: case kMissileFlareAlt: @@ -727,18 +729,18 @@ void viewProcessSprites(int32_t cX, int32_t cY, int32_t cZ, int32_t cA, int32_t } } - viewAddEffect(nTSprite, VIEW_EFFECT_1); + viewAddEffect(tsprite, spritesortcnt, nTSprite, VIEW_EFFECT_1); if (pTSprite->type != kMissileFlareRegular) break; sectortype *pSector = §or[pTSprite->sectnum]; int zDiff = (pTSprite->z - pSector->ceilingz) >> 8; if ((pSector->ceilingstat&1) == 0 && zDiff < 64) { - viewAddEffect(nTSprite, VIEW_EFFECT_2); + viewAddEffect(tsprite, spritesortcnt, nTSprite, VIEW_EFFECT_2); } zDiff = (pSector->floorz - pTSprite->z) >> 8; if ((pSector->floorstat&1) == 0 && zDiff < 64) { - viewAddEffect(nTSprite, VIEW_EFFECT_3); + viewAddEffect(tsprite, spritesortcnt, nTSprite, VIEW_EFFECT_3); } break; } @@ -757,7 +759,7 @@ void viewProcessSprites(int32_t cX, int32_t cY, int32_t cZ, int32_t cA, int32_t } } - if (pXSector && pXSector->color) pTSprite->pal = pSector->floorpal; + if (pXSector && pXSector->color) copyfloorpal(pTSprite, pSector); if (powerupCheck(gView, kPwUpBeastVision) > 0) pTSprite->shade = -128; if (IsPlayerSprite(pTSprite)) { @@ -773,15 +775,15 @@ void viewProcessSprites(int32_t cX, int32_t cY, int32_t cZ, int32_t cA, int32_t } if (powerupCheck(pPlayer, kPwUpReflectShots)) { - viewAddEffect(nTSprite, VIEW_EFFECT_13); + viewAddEffect(tsprite, spritesortcnt, nTSprite, VIEW_EFFECT_13); } if (cl_showweapon && gGameOptions.nGameType > 0 && gView) { - viewAddEffect(nTSprite, VIEW_EFFECT_12); + viewAddEffect(tsprite, spritesortcnt, nTSprite, VIEW_EFFECT_12); } if (pPlayer->flashEffect && (gView != pPlayer || gViewPos != VIEWPOS_0)) { - auto pNTSprite = viewAddEffect(nTSprite, VIEW_EFFECT_14); + auto pNTSprite = viewAddEffect(tsprite, spritesortcnt, nTSprite, VIEW_EFFECT_14); if (pNTSprite) { POSTURE *pPosture = &pPlayer->pPosture[pPlayer->lifeMode][pPlayer->posture]; pNTSprite->x += MulScale(pPosture->zOffset, Cos(pTSprite->ang), 28); @@ -792,7 +794,7 @@ void viewProcessSprites(int32_t cX, int32_t cY, int32_t cZ, int32_t cA, int32_t if (pPlayer->hasFlag > 0 && gGameOptions.nGameType == 3) { if (pPlayer->hasFlag&1) { - auto pNTSprite = viewAddEffect(nTSprite, VIEW_EFFECT_16); + auto pNTSprite = viewAddEffect(tsprite, spritesortcnt, nTSprite, VIEW_EFFECT_16); if (pNTSprite) { pNTSprite->pal = 10; @@ -800,7 +802,7 @@ void viewProcessSprites(int32_t cX, int32_t cY, int32_t cZ, int32_t cA, int32_t } } if (pPlayer->hasFlag&2) { - auto pNTSprite = viewAddEffect(nTSprite, VIEW_EFFECT_16); + auto pNTSprite = viewAddEffect(tsprite, spritesortcnt, nTSprite, VIEW_EFFECT_16); if (pNTSprite) { pNTSprite->pal = 7; @@ -813,7 +815,7 @@ void viewProcessSprites(int32_t cX, int32_t cY, int32_t cZ, int32_t cA, int32_t if (pTSprite->owner != gView->pSprite->index || gViewPos != VIEWPOS_0) { if (getflorzofslope(pTSprite->sectnum, pTSprite->x, pTSprite->y) >= cZ) { - viewAddEffect(nTSprite, VIEW_EFFECT_0); + viewAddEffect(tsprite, spritesortcnt, nTSprite, VIEW_EFFECT_0); } } break; @@ -824,7 +826,7 @@ void viewProcessSprites(int32_t cX, int32_t cY, int32_t cZ, int32_t cA, int32_t if (pTXSprite->data1) { pTSprite->picnum = 772; if (pTXSprite->data2) - viewAddEffect(nTSprite, VIEW_EFFECT_9); + viewAddEffect(tsprite, spritesortcnt, nTSprite, VIEW_EFFECT_9); } } else if (pTXSprite->data1) pTSprite->picnum = 773; @@ -838,7 +840,7 @@ void viewProcessSprites(int32_t cX, int32_t cY, int32_t cZ, int32_t cA, int32_t if (pTSprite->type < kThingBase || pTSprite->type >= kThingMax || !gSpriteHit[nXSprite].florhit) { if ((pTSprite->flags & kPhysMove) && getflorzofslope(pTSprite->sectnum, pTSprite->x, pTSprite->y) >= cZ) - viewAddEffect(nTSprite, VIEW_EFFECT_0); + viewAddEffect(tsprite, spritesortcnt, nTSprite, VIEW_EFFECT_0); } } break; @@ -886,4 +888,29 @@ void viewProcessSprites(int32_t cX, int32_t cY, int32_t cZ, int32_t cA, int32_t } +void GameInterface::processSprites(spritetype* tsprite, int& spritesortcnt, int viewx, int viewy, int viewz, binangle viewang, double smoothRatio) +{ + viewProcessSprites(tsprite, spritesortcnt, viewx, viewy, viewz, viewang.asbuild(), int(smoothRatio)); +} + +int display_mirror; + +void GameInterface::EnterPortal(spritetype* viewer, int type) +{ + if (type == PORTAL_WALL_MIRROR) + { + display_mirror++; + if (viewer) viewer->cstat &= ~CSTAT_SPRITE_INVISIBLE; + } +} + +void GameInterface::LeavePortal(spritetype* viewer, int type) +{ + if (type == PORTAL_WALL_MIRROR) + { + display_mirror--; + if (viewer && display_mirror == 0 && !(viewer->cstat & CSTAT_SPRITE_TRANSLUCENT)) viewer->cstat |= CSTAT_SPRITE_INVISIBLE; + } +} + END_BLD_NS diff --git a/source/games/blood/src/blood.cpp b/source/games/blood/src/blood.cpp index 0c7d38cd2..f37c40693 100644 --- a/source/games/blood/src/blood.cpp +++ b/source/games/blood/src/blood.cpp @@ -24,7 +24,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "ns.h" // Must come before everything else! #include "build.h" -#include "mmulti.h" #include "compat.h" #include "g_input.h" #include "automap.h" @@ -49,6 +48,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "v_draw.h" #include "texturemanager.h" #include "statusbar.h" +#include "vm.h" BEGIN_BLD_NS @@ -79,7 +79,7 @@ void EndLevel(void) seqKillAll(); } -void StartLevel(MapRecord* level) +void StartLevel(MapRecord* level, bool newgame) { if (!level) return; gFrameCount = 0; @@ -96,14 +96,14 @@ void StartLevel(MapRecord* level) /////// } #if 0 - else if (gGameOptions.nGameType > 0 && !(gGameOptions.uGameFlags & GF_AdvanceLevel)) + else if (gGameOptions.nGameType > 0 && newgame) { // todo gBlueFlagDropped = false; gRedFlagDropped = false; } #endif - if (gGameOptions.uGameFlags & GF_AdvanceLevel) + if (!newgame) { for (int i = connecthead; i >= 0; i = connectpoint2[i]) { @@ -112,7 +112,6 @@ void StartLevel(MapRecord* level) } } memset(xsprite, 0, sizeof(xsprite)); - memset(sprite, 0, kMaxSprites * sizeof(spritetype)); //drawLoadingScreen(); dbLoadMap(currentLevel->fileName, (int*)&startpos.x, (int*)&startpos.y, (int*)&startpos.z, &startang, &startsectnum, nullptr); SECRET_SetMapName(currentLevel->DisplayName(), currentLevel->name); @@ -181,13 +180,13 @@ void StartLevel(MapRecord* level) evInit(); for (int i = connecthead; i >= 0; i = connectpoint2[i]) { - if (!(gGameOptions.uGameFlags & GF_AdvanceLevel)) + if (newgame) { playerInit(i, 0); } playerStart(i, 1); } - if (gGameOptions.uGameFlags & GF_AdvanceLevel) + if (!newgame) { for (int i = connecthead; i >= 0; i = connectpoint2[i]) { @@ -204,7 +203,6 @@ void StartLevel(MapRecord* level) pPlayer->nextWeapon = gPlayerTemp[i].nextWeapon; } } - gGameOptions.uGameFlags &= ~(GF_AdvanceLevel|GF_EndGame); PreloadCache(); InitMirrors(); trInit(); @@ -223,43 +221,24 @@ void StartLevel(MapRecord* level) } -void NewLevel(MapRecord *sng, int skill) +void NewLevel(MapRecord *sng, int skill, bool newgame) { - auto completion = [=](bool = false) - { - if (skill != -1) gGameOptions.nDifficulty = skill; - gSkill = gGameOptions.nDifficulty; - StartLevel(sng); - gameaction = ga_level; - }; - - bool startedCutscene = false; - if (!(sng->flags & MI_USERMAP)) - { - int episode = volfromlevelnum(sng->levelNumber); - int level = mapfromlevelnum(sng->levelNumber); - if (gEpisodeInfo[episode].cutALevel == level && gEpisodeInfo[episode].cutsceneAName[0]) - { - levelPlayIntroScene(episode, completion); - startedCutscene = true; - } - - } - if (!startedCutscene) completion(false); - + if (skill != -1) gGameOptions.nDifficulty = skill; + gSkill = gGameOptions.nDifficulty; + StartLevel(sng, newgame); + gameaction = ga_level; } void GameInterface::NewGame(MapRecord *sng, int skill, bool) { gGameOptions.uGameFlags = 0; cheatReset(); - NewLevel(sng, skill); + NewLevel(sng, skill, true); } void GameInterface::NextLevel(MapRecord *map, int skill) { - gGameOptions.uGameFlags = GF_AdvanceLevel; - NewLevel(map, skill); + NewLevel(map, skill, false); } void GameInterface::Ticker() @@ -304,7 +283,6 @@ void GameInterface::Ticker() viewCorrectPrediction(); ambProcess(); viewUpdateDelirium(); - viewUpdateShake(); gi->UpdateSounds(); if (gMe->hand == 1) { @@ -330,40 +308,12 @@ void GameInterface::Ticker() team_ticker[i] = 0; } - if ((gGameOptions.uGameFlags & GF_AdvanceLevel) != 0) + if (gGameOptions.uGameFlags & GF_AdvanceLevel) { + gGameOptions.uGameFlags &= ~GF_AdvanceLevel; seqKillAll(); - if (gGameOptions.uGameFlags & GF_EndGame) - { - STAT_Update(true); - if (gGameOptions.nGameType == 0) - { - auto completion = [](bool) { - gGameOptions.uGameFlags &= ~(GF_AdvanceLevel|GF_EndGame); - gameaction = ga_creditsmenu; - }; - - if (gGameOptions.uGameFlags & GF_PlayCutscene) - { - levelPlayEndScene(volfromlevelnum(currentLevel->levelNumber), completion); - } - else completion(false); - } - else - { - gGameOptions.uGameFlags &= ~(GF_AdvanceLevel|GF_EndGame); - } - } - else - { - STAT_Update(false); - EndLevel(); - Mus_Stop(); - // Fixme: Link maps, not episode/level pairs. - int ep = volfromlevelnum(currentLevel->levelNumber); - auto map = FindMapByLevelNum(levelnum(ep, gNextLevel)); - CompleteLevel(map); - } + STAT_Update(gNextLevel == nullptr); + CompleteLevel(gNextLevel); } r_NoInterpolate = false; } @@ -467,20 +417,18 @@ void GameInterface::app_init() HookReplaceFunctions(); - Printf(PRINT_NONOTIFY, "Initializing Build 3D engine\n"); - engineInit(); - Printf(PRINT_NONOTIFY, "Loading tiles\n"); - if (!tileInit(0, NULL)) + if (!tileInit()) I_FatalError("TILES###.ART files not found"); levelLoadDefaults(); LoadDefinitions(); + + //--------- SetTileNames(); C_InitConback(TexMan.CheckForTexture("BACKTILE", ETextureType::Any), true, 0.25); TileFiles.SetBackup(); - powerupInit(); Printf(PRINT_NONOTIFY, "Loading cosine table\n"); trigInit(); Printf(PRINT_NONOTIFY, "Initializing view subsystem\n"); @@ -490,15 +438,15 @@ void GameInterface::app_init() Printf(PRINT_NONOTIFY, "Initializing weapon animations\n"); WeaponInit(); + Printf(PRINT_NONOTIFY, "Initializing sound system\n"); + sndInit(); + myconnectindex = connecthead = 0; gNetPlayers = numplayers = 1; connectpoint2[0] = -1; gGameOptions.nGameType = 0; UpdateNetworkMenus(); - Printf(PRINT_NONOTIFY, "Initializing sound system\n"); - sndInit(); - gChoke.init(518, chokeCallback); UpdateDacs(0, true); @@ -523,17 +471,7 @@ static void gameInit() void GameInterface::Startup() { gameInit(); - if (userConfig.CommandMap.IsNotEmpty()) - { - } - else - { - if (!userConfig.nologo && gGameOptions.nGameType == 0) playlogos(); - else - { - gameaction = ga_mainmenu; - } - } + PlayLogos(ga_mainmenu, ga_mainmenu, true); } @@ -583,4 +521,48 @@ ReservedSpace GameInterface::GetReservedScreenSpace(int viewsize) return new GameInterface; } +enum +{ + kLoadScreenCRC = -2051908571, + kLoadScreenWideBackWidth = 256, + kLoadScreenWideSideWidth = 128, + +}; + + +DEFINE_ACTION_FUNCTION(_Blood, OriginalLoadScreen) +{ + static int bLoadScreenCrcMatch = -1; + if (bLoadScreenCrcMatch == -1) bLoadScreenCrcMatch = tileGetCRC32(kLoadScreen) == kLoadScreenCRC; + ACTION_RETURN_INT(bLoadScreenCrcMatch); +} + +DEFINE_ACTION_FUNCTION(_Blood, PlayIntroMusic) +{ + Mus_Play(nullptr, "PESTIS.MID", false); + return 0; +} + +DEFINE_ACTION_FUNCTION(_Blood, sndStartSample) +{ + PARAM_PROLOGUE; + PARAM_INT(id); + PARAM_INT(vol); + PARAM_INT(chan); + PARAM_BOOL(looped); + PARAM_INT(chanflags); + sndStartSample(id, vol, chan, looped, EChanFlags::FromInt(chanflags)); + return 0; +} + +DEFINE_ACTION_FUNCTION(_Blood, sndStartSampleNamed) +{ + PARAM_PROLOGUE; + PARAM_STRING(id); + PARAM_INT(vol); + PARAM_INT(chan); + sndStartSample(id, vol, chan); + return 0; +} + END_BLD_NS diff --git a/source/games/blood/src/blood.h b/source/games/blood/src/blood.h index 5c253f635..6202eca69 100644 --- a/source/games/blood/src/blood.h +++ b/source/games/blood/src/blood.h @@ -25,6 +25,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "build.h" #include "gamestruct.h" #include "mapinfo.h" +#include "d_net.h" #include "common_game.h" #include "fx.h" @@ -35,7 +36,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "ai.h" #include "aistate.h" #include "aiunicult.h" -#include "blood.h" #include "callback.h" #include "db.h" #include "endgame.h" @@ -78,11 +78,24 @@ extern int blood_globalflags; void QuitGame(void); void PreloadCache(void); -void StartLevel(MapRecord *gameOptions); void ProcessFrame(void); void ScanINIFiles(void); void EndLevel(); +struct MIRROR +{ + int type; + int link; + int dx; + int dy; + int dz; + int wallnum; +}; + +extern MIRROR mirror[16]; +extern int mirrorcnt, mirrorsector, mirrorwall[4]; + + inline bool DemoRecordStatus(void) { return false; @@ -108,8 +121,6 @@ struct GameInterface : ::GameInterface void MenuOpened() override; void MenuClosed() override; bool CanSave() override; - bool StartGame(FNewGameStartup& gs) override; - void QuitToTitle() override; FString GetCoordString() override; ReservedSpace GetReservedScreenSpace(int viewsize) override; void UpdateSounds() override; @@ -131,9 +142,12 @@ struct GameInterface : ::GameInterface void ToggleThirdPerson() override; void SwitchCoopView() override; void ToggleShowWeapon() override; - int chaseCamX(binangle ang) { return MulScale(-Cos(ang.asbuild()), 1280, 30); } - int chaseCamY(binangle ang) { return MulScale(-Sin(ang.asbuild()), 1280, 30); } - int chaseCamZ(fixedhoriz horiz) { return FixedToInt(MulScale(horiz.asq16(), 1280, 3)) - (16 << 8); } + int chaseCamX(binangle ang) override { return MulScale(-Cos(ang.asbuild()), 1280, 30); } + int chaseCamY(binangle ang) override { return MulScale(-Sin(ang.asbuild()), 1280, 30); } + int chaseCamZ(fixedhoriz horiz) override { return FixedToInt(MulScale(horiz.asq16(), 1280, 3)) - (16 << 8); } + void processSprites(spritetype* tsprite, int& spritesortcnt, int viewx, int viewy, int viewz, binangle viewang, double smoothRatio) override; + void EnterPortal(spritetype* viewer, int type) override; + void LeavePortal(spritetype* viewer, int type) override; GameStats getStats() override; }; diff --git a/source/games/blood/src/bloodactor.h b/source/games/blood/src/bloodactor.h index 544bc9c1f..afab01824 100644 --- a/source/games/blood/src/bloodactor.h +++ b/source/games/blood/src/bloodactor.h @@ -1,13 +1,5 @@ #pragma once -#include "build.h" -#include "common_game.h" -#include "db.h" -#include "ai.h" -#include "nnexts.h" -#include "seq.h" -#include "aiunicult.h" - BEGIN_BLD_NS extern int cumulDamage[kMaxXSprites]; diff --git a/source/games/blood/src/common_game.h b/source/games/blood/src/common_game.h index 2556e2336..a313364e7 100644 --- a/source/games/blood/src/common_game.h +++ b/source/games/blood/src/common_game.h @@ -527,36 +527,6 @@ inline int ClipRange(int a, int b, int c) return a; } -inline int interpolate(int a, int b, int c) -{ - return a+MulScale(b-a,c, 16); -} - -inline double finterpolate(double a, double b, double c) -{ - return a+MulScaleF(b-a,c, 16); -} - -inline int interpolateang(int a, int b, int c) -{ - return a+MulScale(((b-a+1024)&2047)-1024, c, 16); -} - -inline fixed_t interpolateangfix16(fixed_t a, fixed_t b, int c) -{ - return a+MulScale(((b-a+0x4000000)&0x7ffffff)-0x4000000, c, 16); -} - -inline binangle interpolateangbin(uint32_t a, uint32_t b, double c) -{ - return bamang(xs_CRoundToUInt(a + MulScaleF(b - a, c, 16))); -} - -inline lookangle interpolateanglook(int32_t a, int32_t b, double c) -{ - return bamlook(xs_CRoundToUInt(a + MulScaleF(b - a, c, 16))); -} - inline char Chance(int a1) { return wrand() < (a1>>1); diff --git a/source/games/blood/src/controls.cpp b/source/games/blood/src/controls.cpp index 9da34ba1e..41b5f27ee 100644 --- a/source/games/blood/src/controls.cpp +++ b/source/games/blood/src/controls.cpp @@ -24,7 +24,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "ns.h" // Must come before everything else! -#include "mmulti.h" #include "blood.h" #include "gamestate.h" #include "inputstate.h" @@ -58,8 +57,8 @@ void GameInterface::GetInput(InputPacket* packet, ControlInfo* const hidInput) // Perform unsynchronised angle/horizon if not dead. if (gView->pXSprite->health != 0) { - applylook(&pPlayer->angle, input.avel, &pPlayer->input.actions, scaleAdjust); - sethorizon(&pPlayer->horizon, input.horz, &pPlayer->input.actions, scaleAdjust); + pPlayer->angle.applyinput(input.avel, &pPlayer->input.actions, scaleAdjust); + pPlayer->horizon.applyinput(input.horz, &pPlayer->input.actions, scaleAdjust); doslopetilting(pPlayer, scaleAdjust); } diff --git a/source/games/blood/src/credits.cpp b/source/games/blood/src/credits.cpp deleted file mode 100644 index 90f9cbc9d..000000000 --- a/source/games/blood/src/credits.cpp +++ /dev/null @@ -1,147 +0,0 @@ -//------------------------------------------------------------------------- -/* -Copyright (C) 2010-2019 EDuke32 developers and contributors -Copyright (C) 2019 Nuke.YKT - -This file is part of NBlood. - -NBlood is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License version 2 -as published by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - -See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ -//------------------------------------------------------------------------- - -#include "ns.h" // Must come before everything else! - -#include "build.h" -#include "compat.h" -#include "SmackerDecoder.h" -#include "blood.h" -#include "animtexture.h" -#include "raze_sound.h" -#include "v_2ddrawer.h" -#include "screenjob.h" -#include "gamestate.h" -#include "razemenu.h" - -BEGIN_BLD_NS - - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -void playlogos() -{ - JobDesc jobs[6]; - int job = 0; - static AnimSound logosound[] = - { - { 1, -1 }, - { -1, -1 }, - { 1, -1 }, - { -1,-1 } - }; - - if (logosound[0].soundnum == -1) - { - logosound[0].soundnum = S_FindSound("logo.wav"); - logosound[2].soundnum = S_FindSound("gt.wav"); - } - - - if (!userConfig.nologo) - { - if (fileSystem.FindFile("logo.smk") != -1) - { - jobs[job++] = { PlayVideo("logo.smk", &logosound[0], 0) }; - } - else - { - jobs[job++] = { Create(1), []() { sndStartSample("THUNDER2", 128, -1); } }; - jobs[job++] = { Create(2050) }; - } - if (fileSystem.FindFile("gti.smk") != -1) - { - jobs[job++] = { PlayVideo("gti.smk", &logosound[2], 0) }; - } - else - { - jobs[job++] = { Create(1), []() { sndStartSample("THUNDER2", 128, -1); } }; - jobs[job++] = { Create(2052) }; - } - } - jobs[job++] = { Create(1), []() { sndPlaySpecialMusicOrNothing(MUS_INTRO); sndStartSample("THUNDER2", 128, -1); }}; - jobs[job++] = { Create(2518, DScreenJob::fadein) }; - - RunScreenJob(jobs, job, [](bool) { - Mus_Stop(); - gameaction = ga_mainmenu; - }, true, true); -} - -void playSmk(const char *smk, const char *wav, int wavid, CompletionFunc func) -{ - JobDesc jobs{}; - static AnimSound smksound[] = - { - { 1, -1 }, - { -1, -1 }, - }; - int id = S_FindSoundByResID(wavid); - if (id <= 0) - { - FString wavv = wav; - FixPathSeperator(wavv); - id = S_FindSound(wavv); - // Strip the drive letter and retry. - if (id <= 0 && wavv.Len() > 3 && wavv[1] == ':' && isalpha(wavv[0]) && wavv[2] == '/') - { - id = S_FindSound(wavv.GetChars() + 3); - } - } - FString smkk = smk; - FixPathSeperator(smkk); - smksound[0].soundnum = id; - jobs.job = PlayVideo(smkk, smksound, nullptr); - RunScreenJob(&jobs, 1, func); -} - -void levelPlayIntroScene(int nEpisode, CompletionFunc completion) -{ - Mus_Stop(); - sndKillAllSounds(); - sfxKillAllSounds(); - ambKillAll(); - seqKillAll(); - EPISODEINFO *pEpisode = &gEpisodeInfo[nEpisode]; - playSmk(pEpisode->cutsceneAName, pEpisode->cutsceneASound, pEpisode->at9028, completion); -} - -void levelPlayEndScene(int nEpisode, CompletionFunc completion) -{ - gGameOptions.uGameFlags &= ~GF_PlayCutscene; - Mus_Stop(); - sndKillAllSounds(); - sfxKillAllSounds(); - ambKillAll(); - seqKillAll(); - EPISODEINFO *pEpisode = &gEpisodeInfo[nEpisode]; - playSmk(pEpisode->cutsceneBName, pEpisode->cutsceneBSound, pEpisode->at902c, completion); -} - - - -END_BLD_NS diff --git a/source/games/blood/src/d_menu.cpp b/source/games/blood/src/d_menu.cpp index 8db9fc494..ec3818489 100644 --- a/source/games/blood/src/d_menu.cpp +++ b/source/games/blood/src/d_menu.cpp @@ -26,7 +26,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "build.h" #include "compat.h" -#include "mmulti.h" #include "c_bind.h" #include "razemenu.h" #include "gamestate.h" @@ -159,34 +158,11 @@ bool GameInterface::CanSave() return (gamestate == GS_LEVEL && gPlayer[myconnectindex].pXSprite->health != 0); } -bool GameInterface::StartGame(FNewGameStartup& gs) -{ - if (gs.Episode >= 1) - { - if (g_gameType & GAMEFLAG_SHAREWARE) - { - M_StartMessage(GStrings("BUYBLOOD"), 1, NAME_None); // unreachable because we do not support Blood SW versions yet. - return false; - } - } - - sfxKillAllSounds(); - auto map = FindMapByLevelNum(levelnum(gs.Episode, gs.Level)); - DeferedStartGame(map, gs.Skill); - return true; -} - FSavegameInfo GameInterface::GetSaveSig() { return { SAVESIG_BLD, MINSAVEVER_BLD, SAVEVER_BLD }; } -void GameInterface::QuitToTitle() -{ - Mus_Stop(); - gameaction = ga_mainmenu; -} - END_BLD_NS using namespace Blood; diff --git a/source/games/blood/src/db.cpp b/source/games/blood/src/db.cpp index 9b0d3b362..cb6a53c9c 100644 --- a/source/games/blood/src/db.cpp +++ b/source/games/blood/src/db.cpp @@ -31,6 +31,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "md4.h" #include "automap.h" #include "raze_sound.h" +#include "gamefuncs.h" +#include "hw_sections.h" +#include "sectorgeometry.h" #include "blood.h" @@ -200,6 +203,7 @@ int InsertSprite(int nSector, int nStat) Numsprites++; + sprite[nSprite].time = leveltimer++; return nSprite; } @@ -511,8 +515,12 @@ void dbLoadMap(const char *pPath, int *pX, int *pY, int *pZ, short *pAngle, shor gModernMap = false; #endif + memset(sector, 0, sizeof(*sector) * MAXSECTORS); + memset(wall, 0, sizeof(*wall) * MAXWALLS); + memset(sprite, 0, sizeof(*sector) * MAXSPRITES); + #ifdef USE_OPENGL - Polymost_prepare_loadboard(); + Polymost::Polymost_prepare_loadboard(); #endif FString mapname = pPath; @@ -654,6 +662,8 @@ void dbLoadMap(const char *pPath, int *pX, int *pY, int *pZ, short *pAngle, shor pSector->floorypan_ = load.floorypanning; pSector->visibility = load.visibility; qsector_filler[i] = load.fogpal; + pSector->dirty = 255; + pSector->exflags = 0; pSector->fogpal = 0; if (sector[i].extra > 0) @@ -1058,6 +1068,9 @@ void dbLoadMap(const char *pPath, int *pX, int *pY, int *pZ, short *pAngle, shor } } + setWallSectors(); + hw_BuildSections(); + sectorGeometry.SetSize(numsections); memcpy(wallbackup, wall, sizeof(wallbackup)); memcpy(sectorbackup, sector, sizeof(sectorbackup)); } diff --git a/source/games/blood/src/endgame.cpp b/source/games/blood/src/endgame.cpp index a50b2e298..452711f74 100644 --- a/source/games/blood/src/endgame.cpp +++ b/source/games/blood/src/endgame.cpp @@ -25,7 +25,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "build.h" #include "v_draw.h" -#include "mmulti.h" #include "statistics.h" #include "gstrings.h" #include "gamestate.h" @@ -36,152 +35,26 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. BEGIN_BLD_NS -enum -{ - kLoadScreenCRC = -2051908571, - kLoadScreenWideBackWidth = 256, - kLoadScreenWideSideWidth = 128, - -}; - -static int bLoadScreenCrcMatch = -1; - -static void drawTextScreenBackground(void) -{ - if (bLoadScreenCrcMatch == -1) bLoadScreenCrcMatch = tileGetCRC32(kLoadScreen) == kLoadScreenCRC; - - if (bLoadScreenCrcMatch) - { - if (ActiveRatio(twod->GetWidth(), twod->GetHeight()) < 1.34f) - { - DrawTexture(twod, tileGetTexture(kLoadScreen), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE); - } - else - { - int width = scale(twod->GetWidth(), 240, twod->GetHeight()); - int nCount = (width + kLoadScreenWideBackWidth - 1) / kLoadScreenWideBackWidth; - for (int i = 0; i < nCount; i++) - { - DrawTexture(twod, tileGetTexture(kLoadScreenWideBack), (i * kLoadScreenWideBackWidth), 0, - DTA_VirtualWidth, width, DTA_VirtualHeight, 200, DTA_KeepRatio, true, TAG_DONE); - } - DrawTexture(twod, tileGetTexture(kLoadScreenWideLeft), 0, 0, DTA_VirtualWidth, width, DTA_VirtualHeight, 200, DTA_KeepRatio, true, DTA_TopLeft, true, TAG_DONE); - DrawTexture(twod, tileGetTexture(kLoadScreenWideRight), width - tileWidth(kLoadScreenWideRight), 0, DTA_TopLeft, true, - DTA_VirtualWidth, width, DTA_VirtualHeight, 200, DTA_KeepRatio, true, TAG_DONE); - DrawTexture(twod, tileGetTexture(kLoadScreenWideMiddle), (width - tileWidth(kLoadScreenWideMiddle)) / 2, 0, DTA_TopLeft, true, - DTA_VirtualWidth, width, DTA_VirtualHeight, 200, DTA_KeepRatio, true, TAG_DONE); - } - } - else - { - DrawTexture(twod, tileGetTexture(kLoadScreen), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE); - } -} - -// One these screens get scriptified this should use the version in BloodMenuDelegate. -static void DrawCaption(const char* text) -{ - double scalex = 1.; // Expand the box if the text is longer - int width = BigFont->StringWidth(text); - int boxwidth = tileWidth(2038); - if (boxwidth - 10 < width) scalex = double(width) / (boxwidth - 10); - - DrawTexture(twod, tileGetTexture(2038, true), 160, 20, DTA_FullscreenScale, FSMode_Fit320x200Top, DTA_CenterOffsetRel, true, DTA_ScaleX, scalex, TAG_DONE); - DrawText(twod, BigFont, CR_UNDEFINED, 160 - width / 2, 20 - tileHeight(4193) / 2, text, DTA_FullscreenScale, FSMode_Fit320x200Top, TAG_DONE); -} - - -class DBloodSummaryScreen : public DSkippableScreenJob -{ - void DrawKills(void) - { - char pBuffer[40]; - if (gGameOptions.nGameType == 0) - { - viewDrawText(1, FStringf("%s:", GStrings("KILLS")), 75, 50, -128, 0, 0, 1); - mysnprintf(pBuffer, 40,"%2d", gKillMgr.Kills); - viewDrawText(1, pBuffer, 160, 50, -128, 0, 0, 1); - viewDrawText(1, GStrings("OF"), 190, 50, -128, 0, 0, 1); - mysnprintf(pBuffer, 40, "%2d", gKillMgr.TotalKills); - viewDrawText(1, pBuffer, 220, 50, -128, 0, 0, 1); - } - else - { - viewDrawText(3, "#", 85, 35, -128, 0, 0, 1); - viewDrawText(3, GStrings("NAME"), 100, 35, -128, 0, 0, 1); - viewDrawText(3, GStrings("FRAGS"), 210, 35, -128, 0, 0, 1); - int nStart = 0; - int nEnd = kMaxPlayers; - - for (int i = nStart; i < nEnd; i++) if (playeringame[i]) - { - mysnprintf(pBuffer, 40, "%-2d", i); - viewDrawText(3, pBuffer, 85, 50 + 8 * i, -128, 0, 0, 1); - mysnprintf(pBuffer, 40, "%s", PlayerName(i)); - viewDrawText(3, pBuffer, 100, 50 + 8 * i, -128, 0, 0, 1); - mysnprintf(pBuffer, 40, "%d", gPlayer[i].fragCount); - viewDrawText(3, pBuffer, 210, 50 + 8 * i, -128, 0, 0, 1); - } - } - } - - void DrawSecrets(void) - { - char pBuffer[40]; - viewDrawText(1, FStringf("%s:", GStrings("TXT_SECRETS")), 75, 70, -128, 0, 0, 1); - mysnprintf(pBuffer, 40, "%2d", gSecretMgr.Founds); - viewDrawText(1, pBuffer, 160, 70, -128, 0, 0, 1); - viewDrawText(1, GStrings("OF"), 190, 70, -128, 0, 0, 1); - mysnprintf(pBuffer, 40, "%2d", gSecretMgr.Total); - viewDrawText(1, pBuffer, 220, 70, -128, 0, 0, 1); - if (gSecretMgr.Super > 0) - viewDrawText(1, GStrings("TXT_SUPERSECRET"), 160, 100, -128, 2, 1, 1); - } - - - void Draw(double) override - { - drawTextScreenBackground(); - if (gGameOptions.nGameType == 0) - { - DrawCaption(GStrings("TXTB_LEVELSTATS")); - if (bPlayerCheated) - { - auto text = GStrings("TXTB_CHEATED"); - int font = 3; - if (!SmallFont2->CanPrint(text)) font = 0; - viewDrawText(font, text, 160, 32, -128, 0, 1, font == 3); - } - DrawKills(); - DrawSecrets(); - } - else - { - DrawCaption(GStrings("TXTB_FRAGSTATS")); - DrawKills(); - } - int myclock = ticks * 120 / GameTicRate; - if ((myclock & 32)) - { - auto text = GStrings("PRESSKEY"); - int font = 3; - if (!SmallFont2->CanPrint(text)) font = 0; - viewDrawText(font, text, 160, 134, -128, 0, 1, font == 3); - } - } -}; void GameInterface::LevelCompleted(MapRecord *map, int skill) { - JobDesc job = { Create() }; - sndStartSample(268, 128, -1, false, CHANF_UI); + EndLevel(); Mus_Stop(); - RunScreenJob(&job, 1, [=](bool) + + SummaryInfo info{}; + + info.kills = gKillMgr.Kills; + info.maxkills = gKillMgr.TotalKills; + info.secrets = gSecretMgr.Founds; + info.maxsecrets = gSecretMgr.Total; + info.time = gSecretMgr.Super; + info.endofgame = map == nullptr; + + ShowIntermission(currentLevel, map, &info, [=](bool) { soundEngine->StopAllChannels(); - gameaction = ga_nextlevel; + gameaction = map? ga_nextlevel : ga_creditsmenu; }); - } @@ -272,38 +145,4 @@ void SerializeGameStats(FSerializer& arc) CSecretMgr gSecretMgr; CKillMgr gKillMgr; -class DBloodLoadScreen : public DScreenJob -{ - const char* pzLoadingScreenText1; - MapRecord* rec; - -public: - DBloodLoadScreen(const char* caption, MapRecord* maprec) : DScreenJob(), rec(maprec) - { - if (gGameOptions.nGameType == 0) pzLoadingScreenText1 = GStrings("TXTB_LLEVEL"); - else pzLoadingScreenText1 = GStrings(FStringf("TXTB_NETGT%d", gGameOptions.nGameType)); - } - - void Draw(double) override - { - twod->ClearScreen(); - drawTextScreenBackground(); - DrawCaption(pzLoadingScreenText1); - viewDrawText(1, rec->DisplayName(), 160, 50, -128, 0, 1, 1); - - auto text = GStrings("TXTB_PLSWAIT"); - int font = 3; - if (!SmallFont2->CanPrint(text)) font = 0; - - viewDrawText(font, GStrings("TXTB_PLSWAIT"), 160, 134, -128, 0, 1, font == 3); - } -}; - -void loadscreen(const char *caption, MapRecord* rec, CompletionFunc func) -{ - JobDesc job = { Create(caption, rec) }; - RunScreenJob(&job, 1, func); -} - - END_BLD_NS diff --git a/source/games/blood/src/fx.cpp b/source/games/blood/src/fx.cpp index 074b71f03..78a05a3a5 100644 --- a/source/games/blood/src/fx.cpp +++ b/source/games/blood/src/fx.cpp @@ -261,9 +261,8 @@ void CFX::fxProcess(void) } } -void fxSpawnBlood(spritetype *pSprite, int a2) +void fxSpawnBlood(spritetype *pSprite, int ) { - UNREFERENCED_PARAMETER(a2); if (pSprite->sectnum < 0 || pSprite->sectnum >= numsectors) return; int nSector = pSprite->sectnum; @@ -282,9 +281,8 @@ void fxSpawnBlood(spritetype *pSprite, int a2) } } -void sub_746D4(spritetype *pSprite, int a2) +void sub_746D4(spritetype *pSprite, int ) { - UNREFERENCED_PARAMETER(a2); if (pSprite->sectnum < 0 || pSprite->sectnum >= numsectors) return; int nSector = pSprite->sectnum; diff --git a/source/games/blood/src/hudsprites.cpp b/source/games/blood/src/hudsprites.cpp index b7324cdd4..bb0f4a311 100644 --- a/source/games/blood/src/hudsprites.cpp +++ b/source/games/blood/src/hudsprites.cpp @@ -27,7 +27,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "compat.h" #include "build.h" -#include "mmulti.h" #include "v_font.h" #include "blood.h" @@ -97,14 +96,22 @@ void hudDraw(PLAYER *gView, int nSectnum, double bobx, double boby, double zDelt if (gViewPos == 0) { - double looking_arc = fabs(look_anghalf) / 4.5; + double looking_arc = gView->angle.looking_arc(smoothratio); double cX = 160 - look_anghalf; double cY = 220 + looking_arc; if (cl_weaponsway) { - cX += (bobx / 256.); - cY += (boby / 256.) + (zDelta / 128.); + if (cl_hudinterpolation) + { + cX += (bobx / 256.); + cY += (boby / 256.) + (zDelta / 128.); + } + else + { + cX += (int(bobx) >> 8); + cY += (int(boby) >> 8) + (int(zDelta) >> 7); + } } else { @@ -157,11 +164,8 @@ void hudDraw(PLAYER *gView, int nSectnum, double bobx, double boby, double zDelt { DoLensEffect(); viewingRange = viewingrange; - yxAspect = yxaspect; - renderSetAspect(65536, 54613); r otatesprite(IntToFixed(280), IntToFixed(35), 53248, 512, 4077, v10, v14, 512 + 6, gViewX0, gViewY0, gViewX1, gViewY1); r otatesprite(IntToFixed(280), IntToFixed(35), 53248, 0, 1683, v10, 0, 512 + 35, gViewX0, gViewY0, gViewX1, gViewY1); - renderSetAspect(viewingRange, yxAspect); } #endif } diff --git a/source/games/blood/src/levels.cpp b/source/games/blood/src/levels.cpp index 34f1c6d40..449f617ce 100644 --- a/source/games/blood/src/levels.cpp +++ b/source/games/blood/src/levels.cpp @@ -38,11 +38,8 @@ GAMEOPTIONS gSingleGameOptions = { 0, 2, 0, 0, 0, 0, 0, 0, 2, 3600, 1800, 1800, 7200 }; -EPISODEINFO gEpisodeInfo[kMaxEpisodes+1]; - int gSkill = 2; -int gEpisodeCount; -int gNextLevel; // fixme: let this contain a full level number. +MapRecord* gNextLevel; char BloodIniFile[BMAX_PATH] = "BLOOD.INI"; bool bINIOverride = false; @@ -94,22 +91,23 @@ void CheckKeyAbend(const char *pzSection, const char *pzKey) } -void levelLoadMapInfo(IniFile *pIni, MapRecord *pLevelInfo, const char *pzSection, int epinum, int mapnum) + +void levelLoadMapInfo(IniFile* pIni, MapRecord* pLevelInfo, const char* pzSection, int epinum, int mapnum, int* nextmap, int* nextsecret) { char buffer[16]; pLevelInfo->SetName(pIni->GetKeyString(pzSection, "Title", pLevelInfo->labelName)); - pLevelInfo->author = pIni->GetKeyString(pzSection, "Author", ""); - pLevelInfo->music = pIni->GetKeyString(pzSection, "Song", ""); DefaultExtension(pLevelInfo->music, ".mid"); + pLevelInfo->Author = pIni->GetKeyString(pzSection, "Author", ""); + pLevelInfo->music = pIni->GetKeyString(pzSection, "Song", ""); if (pLevelInfo->music.IsNotEmpty()) DefaultExtension(pLevelInfo->music, ".mid"); pLevelInfo->cdSongId = pIni->GetKeyInt(pzSection, "Track", -1); - pLevelInfo->nextLevel = pIni->GetKeyInt(pzSection, "EndingA", -1); - pLevelInfo->nextSecret = pIni->GetKeyInt(pzSection, "EndingB", -1); + *nextmap = pIni->GetKeyInt(pzSection, "EndingA", 0); + *nextsecret = pIni->GetKeyInt(pzSection, "EndingB", 0); pLevelInfo->fog = pIni->GetKeyInt(pzSection, "Fog", -0); pLevelInfo->weather = pIni->GetKeyInt(pzSection, "Weather", -0); for (int i = 0; i < kMaxMessages; i++) { - sprintf(buffer, "Message%d", i+1); - auto msg = pIni->GetKeyString(pzSection, buffer, ""); - pLevelInfo->AddMessage(i, msg); + sprintf(buffer, "Message%d", i + 1); + auto msg = pIni->GetKeyString(pzSection, buffer, ""); + pLevelInfo->AddMessage(i, msg); } } @@ -142,108 +140,102 @@ static const char* DefFile(void) return userConfig.DefaultCon.IsNotEmpty() ? userConfig.DefaultCon.GetChars() : "blood.ini"; } +static FString cleanPath(const char* pth) +{ + FString path = pth; + FixPathSeperator(path); + if (FileExists(path)) return path; + if (path.Len() > 3 && path[1] == ':' && isalpha(path[0]) && path[2] == '/') + { + return path.Mid(3); + } + return path; +} + void levelLoadDefaults(void) { char buffer[64]; char buffer2[16]; + + int cutALevel = 0; + levelInitINI(DefFile()); - memset(gEpisodeInfo, 0, sizeof(gEpisodeInfo)); - quoteMgr.InitializeQuote(MUS_INTRO, "PESTIS.MID"); int i; - for (i = 0; i < kMaxEpisodes; i++) + for (i = 1; i <= kMaxEpisodes; i++) { - sprintf(buffer, "Episode%d", i+1); + sprintf(buffer, "Episode%d", i); if (!BloodINI->SectionExists(buffer)) break; - EPISODEINFO *pEpisodeInfo = &gEpisodeInfo[i]; - auto ep_str = BloodINI->GetKeyString(buffer, "Title", buffer); - gVolumeNames[i] = ep_str; // only keep one table for the names. Todo: Consolidate this across games. - strncpy(pEpisodeInfo->cutsceneAName, BloodINI->GetKeyString(buffer, "CutSceneA", ""), BMAX_PATH); - pEpisodeInfo->at9028 = BloodINI->GetKeyInt(buffer, "CutWavA", -1); - if (pEpisodeInfo->at9028 == 0) - strncpy(pEpisodeInfo->cutsceneASound, BloodINI->GetKeyString(buffer, "CutWavA", ""), BMAX_PATH); - else - pEpisodeInfo->cutsceneASound[0] = 0; - strncpy(pEpisodeInfo->cutsceneBName, BloodINI->GetKeyString(buffer, "CutSceneB", ""), BMAX_PATH); - pEpisodeInfo->at902c = BloodINI->GetKeyInt(buffer, "CutWavB", -1); - if (pEpisodeInfo->at902c == 0) - strncpy(pEpisodeInfo->cutsceneBSound, BloodINI->GetKeyString(buffer, "CutWavB", ""), BMAX_PATH); - else - pEpisodeInfo->cutsceneBSound[0] = 0; + auto cluster = MustFindCluster(i); + auto volume = MustFindVolume(i); + CutsceneDef &csB = cluster->outro; + auto ep_str = BloodINI->GetKeyString(buffer, "Title", buffer); + cluster->name = volume->name = ep_str; + if (i > 1) volume->flags |= VF_SHAREWARELOCK; - pEpisodeInfo->bloodbath = BloodINI->GetKeyInt(buffer, "BloodBathOnly", 0); - pEpisodeInfo->cutALevel = BloodINI->GetKeyInt(buffer, "CutSceneALevel", 0); - if (pEpisodeInfo->cutALevel > 0) - pEpisodeInfo->cutALevel--; - int j; - for (j = 0; j < kMaxLevels; j++) + csB.video = cleanPath(BloodINI->GetKeyString(buffer, "CutSceneB", "")); + int soundint = BloodINI->GetKeyInt(buffer, "CutWavB", -1); + if (soundint > 0) csB.soundID = soundint + 0x40000000; + else csB.soundName = cleanPath(BloodINI->GetKeyString(buffer, "CutWavB", "")); + + //pEpisodeInfo->bloodbath = BloodINI->GetKeyInt(buffer, "BloodBathOnly", 0); + cutALevel = BloodINI->GetKeyInt(buffer, "CutSceneALevel", 0); + if (cutALevel < 1) cutALevel = 1; + + int nextmaps[kMaxLevels]{}, nextsecrets[kMaxLevels]{}; + for (int j = 1; j <= kMaxLevels; j++) { - sprintf(buffer2, "Map%d", j+1); + sprintf(buffer2, "Map%d", j); if (!BloodINI->KeyExists(buffer, buffer2)) break; auto pLevelInfo = AllocateMap(); const char *pMap = BloodINI->GetKeyString(buffer, buffer2, NULL); CheckSectionAbend(pMap); - pLevelInfo->levelNumber = levelnum(i, j); + SetLevelNum(pLevelInfo, makelevelnum(i, j)); + pLevelInfo->cluster = i; pLevelInfo->labelName = pMap; + if (j == 1) volume->startmap = pLevelInfo->labelName; pLevelInfo->fileName.Format("%s.map", pMap); - levelLoadMapInfo(BloodINI, pLevelInfo, pMap, i, j); + levelLoadMapInfo(BloodINI, pLevelInfo, pMap, i, j, &nextmaps[j - 1], &nextsecrets[j - 1]); + if (j == cutALevel) + { + CutsceneDef& csA = pLevelInfo->intro; + csA.video = cleanPath(BloodINI->GetKeyString(buffer, "CutSceneA", "")); + int soundint = BloodINI->GetKeyInt(buffer, "CutWavA", -1); + if (soundint > 0) csA.soundID = soundint + 0x40000000; + else csA.soundName = cleanPath(BloodINI->GetKeyString(buffer, "CutWavA", "")); + } + } + // Now resolve the level links + for (int j = 1; j <= kMaxLevels; j++) + { + auto map = FindMapByIndexOnly(i, j); + if (map) + { + if (nextmaps[j - 1] > 0) + { + auto nmap = FindMapByIndexOnly(i, nextmaps[j - 1]); + if (nmap) map->NextMap = nmap->labelName; + else map->NextMap = "-"; + } + else map->NextMap = "-"; + if (nextsecrets[j - 1] > 0) + { + auto nmap = FindMapByIndexOnly(i, nextsecrets[j - 1]); + if (nmap) map->NextSecret = nmap->labelName; + else map->NextSecret = "-"; + } + else map->NextSecret = "-"; + } } - pEpisodeInfo->nLevels = j; } - gEpisodeCount = i; } -void levelGetNextLevels(int *pnEndingA, int *pnEndingB) +void levelEndLevel(int secret) { - assert(pnEndingA != NULL && pnEndingB != NULL); - int nEndingA = currentLevel->nextLevel; - if (nEndingA >= 0) - nEndingA--; - int nEndingB = currentLevel->nextSecret; - if (nEndingB >= 0) - nEndingB--; - *pnEndingA = nEndingA; - *pnEndingB = nEndingB; -} - -void levelEndLevel(int arg) -{ - int nEndingA, nEndingB; - auto episode = volfromlevelnum(currentLevel->levelNumber); - EPISODEINFO *pEpisodeInfo = &gEpisodeInfo[episode]; gGameOptions.uGameFlags |= GF_AdvanceLevel; - levelGetNextLevels(&nEndingA, &nEndingB); - switch (arg) - { - case 0: - if (nEndingA == -1) - { - if (pEpisodeInfo->cutsceneBName[0]) - gGameOptions.uGameFlags |= GF_PlayCutscene; - gGameOptions.uGameFlags |= GF_EndGame; - } - else - gNextLevel = nEndingA; - break; - case 1: - if (nEndingB == -1) - { - if (episode + 1 < gEpisodeCount) - { - if (pEpisodeInfo->cutsceneBName[0]) - gGameOptions.uGameFlags |= GF_PlayCutscene; - gGameOptions.uGameFlags |= GF_EndGame; - } - else - { - gGameOptions.uGameFlags |= GF_AdvanceLevel; - } - } - else - gNextLevel = nEndingB; - break; - } + if (!secret) gNextLevel = FindNextMap(currentLevel); + else gNextLevel = FindNextSecretMap(currentLevel); } void levelTryPlayMusic() @@ -255,7 +247,7 @@ void levelTryPlayMusic() { buffer = currentLevel->music; if (Mus_Play(currentLevel->labelName, buffer, true)) return; - DefaultExtension(buffer, ".mid"); + if (buffer.IsNotEmpty()) DefaultExtension(buffer, ".mid"); } if (!Mus_Play(currentLevel->labelName, buffer, true)) { diff --git a/source/games/blood/src/levels.h b/source/games/blood/src/levels.h index 27d65964d..fdd0fc019 100644 --- a/source/games/blood/src/levels.h +++ b/source/games/blood/src/levels.h @@ -41,7 +41,6 @@ enum enum EGameFlag { GF_AdvanceLevel = 1, - GF_EndGame = 2, // 4 was for playing intro cutscenes but is no longer used. GF_PlayCutscene = 8, }; @@ -67,38 +66,16 @@ struct GAMEOPTIONS { #pragma pack(pop) -enum { - MUS_INTRO = 0, - MUS_LOADING = 1, -}; - -struct EPISODEINFO -{ - int nLevels; - unsigned int bloodbath : 1; - unsigned int cutALevel : 4; - char cutsceneAName[BMAX_PATH]; - char cutsceneBName[BMAX_PATH]; - int at9028; - int at902c; - char cutsceneASound[BMAX_PATH]; - char cutsceneBSound[BMAX_PATH]; -}; - -extern EPISODEINFO gEpisodeInfo[]; extern GAMEOPTIONS gSingleGameOptions; extern GAMEOPTIONS gGameOptions; extern int gSkill; extern char BloodIniFile[]; extern bool bINIOverride; -extern int gEpisodeCount; -extern int gNextLevel; +extern MapRecord* gNextLevel; extern bool gGameStarted; void levelInitINI(const char *pzIni); void levelOverrideINI(const char *pzIni); -void levelPlayIntroScene(int nEpisode, CompletionFunc completion); -void levelPlayEndScene(int nEpisode, CompletionFunc completion); void levelSetupSecret(int nCount); void levelTriggerSecret(int nSecret); void CheckSectionAbend(const char *pzSection); diff --git a/source/games/blood/src/loadsave.cpp b/source/games/blood/src/loadsave.cpp index db6c86062..d9da35f39 100644 --- a/source/games/blood/src/loadsave.cpp +++ b/source/games/blood/src/loadsave.cpp @@ -25,7 +25,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include #include "build.h" #include "compat.h" -#include "mmulti.h" #include "blood.h" #include "i_specialpaths.h" @@ -641,7 +640,6 @@ void SerializeState(FSerializer& arc) ("modern", gModernMap) #endif ("cheating", bPlayerCheated) - ("nextlevel", gNextLevel) ("skyhoriz", pSky->horizfrac) ("skyy", pSky->yoffs) ("scale", pSky->yscale) @@ -724,7 +722,7 @@ void GameInterface::SerializeGameState(FSerializer& arc) viewSetErrorMessage(""); Net_ClearFifo(); paused = 0; - Polymost_prepare_loadboard(); + Polymost::Polymost_prepare_loadboard(); Mus_ResumeSaved(); } } diff --git a/source/games/blood/src/messages.cpp b/source/games/blood/src/messages.cpp index 3fc2fd938..250dba55d 100644 --- a/source/games/blood/src/messages.cpp +++ b/source/games/blood/src/messages.cpp @@ -23,7 +23,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "ns.h" // Must come before everything else! #include "build.h" -#include "mmulti.h" #include "compat.h" #include "gamecontrol.h" @@ -267,10 +266,8 @@ static int parseArgs(char *pzArgs, int *nArg1, int *nArg2) { if (!nArg1 || !nArg2 || strlen(pzArgs) < 3) return -1; - *nArg1 = pzArgs[0] - '0' - 1; - *nArg2 = (pzArgs[1] - '0')*10+(pzArgs[2]-'0') - 1; - *nArg1 = ClipRange(*nArg1, 0, gEpisodeCount-1); - *nArg2 = ClipRange(*nArg2, 0, gEpisodeInfo[*nArg1].nLevels-1); + *nArg1 = pzArgs[0] - '0'; + *nArg2 = (pzArgs[1] - '0')*10+(pzArgs[2]-'0'); return 2; } @@ -423,7 +420,7 @@ static bool cheatMario(cheatseq_t* c) int nEpisode, nLevel; if (parseArgs((char*)c->Args, &nEpisode, &nLevel) == 2) { - auto map = FindMapByLevelNum(levelnum(nEpisode, nLevel)); + auto map = FindMapByIndex(nEpisode, nLevel); if (map) DeferedStartGame(map, -1); } return true; diff --git a/source/games/blood/src/mirrors.cpp b/source/games/blood/src/mirrors.cpp index 5bce8e6ca..72ad38070 100644 --- a/source/games/blood/src/mirrors.cpp +++ b/source/games/blood/src/mirrors.cpp @@ -23,23 +23,16 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "ns.h" // Must come before everything else! #include "build.h" -#include "compat.h" -#include "blood.h" +#include "automap.h" +#include "savegamehelp.h" + +#include "blood.h" +#include "render.h" BEGIN_BLD_NS int mirrorcnt, mirrorsector, mirrorwall[4]; -typedef struct -{ - int type; - int link; - int dx; - int dy; - int dz; - int wallnum; -} MIRROR; - MIRROR mirror[16]; void InitMirrors(void) @@ -49,6 +42,7 @@ void InitMirrors(void) mirrorcnt = 0; tileDelete(504); + portalClear(); for (int i = 0; i < 16; i++) { @@ -64,6 +58,7 @@ void InitMirrors(void) if (wall[i].extra > 0 && GetWallType(i) == kWallStack) { wall[i].overpicnum = nTile; + mirror[mirrorcnt].wallnum = i; mirror[mirrorcnt].type = 0; wall[i].cstat |= 32; @@ -84,8 +79,15 @@ void InitMirrors(void) } } if (j < 0) - I_Error("wall[%d] has no matching wall link! (data=%d)\n", i, tmp); - mirrorcnt++; + { + Printf(PRINT_HIGH, "wall[%d] has no matching wall link! (data=%d)\n", i, tmp); + } + else + { + mirrorcnt++; + wall[i].portalflags = PORTAL_WALL_VIEW; + wall[i].portalnum = j; + } } continue; } @@ -96,6 +98,7 @@ void InitMirrors(void) wall[i].picnum = nTile; mirror[mirrorcnt].type = 0; wall[i].cstat |= 32; + wall[i].portalflags = PORTAL_WALL_MIRROR; mirrorcnt++; continue; } @@ -121,6 +124,8 @@ void InitMirrors(void) mirror[mirrorcnt].wallnum = i; mirror[mirrorcnt].link = j; sector[i].floorpicnum = 4080+mirrorcnt; + sector[i].portalflags = PORTAL_SECTOR_FLOOR; + sector[i].portalnum = portalAdd(PORTAL_SECTOR_FLOOR, j, mirror[mirrorcnt].dx, mirror[mirrorcnt].dy, mirror[mirrorcnt].dz); mirrorcnt++; mirror[mirrorcnt].type = 1; mirror[mirrorcnt].dx = sprite[nLink].x-sprite[nLink2].x; @@ -129,10 +134,14 @@ void InitMirrors(void) mirror[mirrorcnt].wallnum = j; mirror[mirrorcnt].link = i; sector[j].ceilingpicnum = 4080+mirrorcnt; + sector[j].portalflags = PORTAL_SECTOR_CEILING; + sector[j].portalnum = portalAdd(PORTAL_SECTOR_CEILING, i, mirror[mirrorcnt].dx, mirror[mirrorcnt].dy, mirror[mirrorcnt].dz); mirrorcnt++; } } mirrorsector = numsectors; + mergePortals(); +#if 1 // The new backend won't need this shit anymore. for (int i = 0; i < 4; i++) { mirrorwall[i] = numwalls+i; @@ -148,270 +157,13 @@ void InitMirrors(void) sector[mirrorsector].floorpicnum = 504; sector[mirrorsector].wallptr = mirrorwall[0]; sector[mirrorsector].wallnum = 4; +#endif } void TranslateMirrorColors(int nShade, int nPalette) { } -void sub_5571C(char mode) -{ - for (int i = mirrorcnt-1; i >= 0; i--) - { - int nTile = 4080+i; - if (TestBitString(gotpic, nTile)) - { - switch (mirror[i].type) - { - case 1: - if (mode) - sector[mirror[i].wallnum].ceilingstat |= 1; - else - sector[mirror[i].wallnum].ceilingstat &= ~1; - break; - case 2: - if (mode) - sector[mirror[i].wallnum].floorstat |= 1; - else - sector[mirror[i].wallnum].floorstat &= ~1; - break; - } - } - } -} - -void sub_557C4(int x, int y, int interpolation) -{ - if (spritesortcnt == 0) return; - int nViewSprites = spritesortcnt-1; - for (int nTSprite = nViewSprites; nTSprite >= 0; nTSprite--) - { - tspritetype *pTSprite = &tsprite[nTSprite]; - pTSprite->xrepeat = pTSprite->yrepeat = 0; - } - for (int i = mirrorcnt-1; i >= 0; i--) - { - int nTile = 4080+i; - if (TestBitString(gotpic, nTile)) - { - if (mirror[i].type == 1 || mirror[i].type == 2) - { - int nSector = mirror[i].link; - int nSector2 = mirror[i].wallnum; - int nSprite; - SectIterator it(nSector); - while ((nSprite = it.NextIndex()) >= 0) - { - spritetype *pSprite = &sprite[nSprite]; - if (pSprite == gView->pSprite) - continue; - int top, bottom; - GetSpriteExtents(pSprite, &top, &bottom); - int zCeil, zFloor; - getzsofslope(nSector, pSprite->x, pSprite->y, &zCeil, &zFloor); - if (pSprite->statnum == kStatDude && (top < zCeil || bottom > zFloor)) - { - int j = i; - if (mirror[i].type == 2) - j++; - else - j--; - int dx = mirror[j].dx; - int dy = mirror[j].dy; - int dz = mirror[j].dz; - tspritetype *pTSprite = &tsprite[spritesortcnt]; - memset(pTSprite, 0, sizeof(tspritetype)); - pTSprite->type = pSprite->type; - pTSprite->index = pSprite->index; - pTSprite->sectnum = nSector2; - pTSprite->x = pSprite->x+dx; - pTSprite->y = pSprite->y+dy; - pTSprite->z = pSprite->z+dz; - pTSprite->ang = pSprite->ang; - pTSprite->picnum = pSprite->picnum; - pTSprite->shade = pSprite->shade; - pTSprite->pal = pSprite->pal; - pTSprite->xrepeat = pSprite->xrepeat; - pTSprite->yrepeat = pSprite->yrepeat; - pTSprite->xoffset = pSprite->xoffset; - pTSprite->yoffset = pSprite->yoffset; - pTSprite->cstat = pSprite->cstat; - pTSprite->statnum = kStatDecoration; - pTSprite->owner = pSprite->index; - pTSprite->extra = pSprite->extra; - pTSprite->flags = pSprite->hitag|0x200; - pTSprite->x = dx+interpolate(pSprite->ox, pSprite->x, interpolation); - pTSprite->y = dy+interpolate(pSprite->oy, pSprite->y, interpolation); - pTSprite->z = dz+interpolate(pSprite->oz, pSprite->z, interpolation); - pTSprite->ang = pSprite->interpolatedang(interpolation); - spritesortcnt++; - } - } - } - } - } - for (int nTSprite = spritesortcnt-1; nTSprite >= nViewSprites; nTSprite--) - { - tspritetype *pTSprite = &tsprite[nTSprite]; - int nAnim = 0; - switch (picanm[pTSprite->picnum].extra&7) - { - case 1: - { - int dX = x - pTSprite->x; - int dY = y - pTSprite->y; - RotateVector(&dX, &dY, 128 - pTSprite->ang); - nAnim = GetOctant(dX, dY); - if (nAnim <= 4) - { - pTSprite->cstat &= ~4; - } - else - { - nAnim = 8 - nAnim; - pTSprite->cstat |= 4; - } - break; - } - case 2: - { - int dX = x - pTSprite->x; - int dY = y - pTSprite->y; - RotateVector(&dX, &dY, 128 - pTSprite->ang); - nAnim = GetOctant(dX, dY); - break; - } - } - while (nAnim > 0) - { - pTSprite->picnum += picanm[pTSprite->picnum].num+1; - nAnim--; - } - } -} - -void DrawMirrors(int x, int y, int z, fixed_t a, fixed_t horiz, int smooth, int viewPlayer) -{ - for (int i = mirrorcnt - 1; i >= 0; i--) - { - int nTile = 4080+i; - if (TestBitString(gotpic, nTile)) - { - ClearBitString(gotpic, nTile); - switch (mirror[i].type) - { - case 0: - { - int nWall = mirror[i].link; - int nSector = sectorofwall(nWall); - walltype *pWall = &wall[nWall]; - int nNextWall = pWall->nextwall; - int nNextSector = pWall->nextsector; - pWall->nextwall = mirrorwall[0]; - pWall->nextsector = mirrorsector; - wall[mirrorwall[0]].nextwall = nWall; - wall[mirrorwall[0]].nextsector = nSector; - wall[mirrorwall[0]].x = wall[pWall->point2].x; - wall[mirrorwall[0]].y = wall[pWall->point2].y; - wall[mirrorwall[1]].x = pWall->x; - wall[mirrorwall[1]].y = pWall->y; - wall[mirrorwall[2]].x = wall[mirrorwall[1]].x+(wall[mirrorwall[1]].x-wall[mirrorwall[0]].x)*16; - wall[mirrorwall[2]].y = wall[mirrorwall[1]].y+(wall[mirrorwall[1]].y-wall[mirrorwall[0]].y)*16; - wall[mirrorwall[3]].x = wall[mirrorwall[0]].x+(wall[mirrorwall[0]].x-wall[mirrorwall[1]].x)*16; - wall[mirrorwall[3]].y = wall[mirrorwall[0]].y+(wall[mirrorwall[0]].y-wall[mirrorwall[1]].y)*16; - sector[mirrorsector].floorz = sector[nSector].floorz; - sector[mirrorsector].ceilingz = sector[nSector].ceilingz; - int cx, cy, ca; - if (GetWallType(nWall) == kWallStack) - { - cx = x - (wall[pWall->hitag].x-wall[pWall->point2].x); - cy = y - (wall[pWall->hitag].y-wall[pWall->point2].y); - ca = a; - } - else - { - renderPrepareMirror(x,y,z,a,horiz,nWall,&cx,&cy,&ca); - } - int32_t didmirror = renderDrawRoomsQ16(cx, cy, z, ca,horiz,mirrorsector|MAXSECTORS); - viewProcessSprites(cx,cy,z,FixedToInt(ca),smooth); - renderDrawMasks(); - if (GetWallType(nWall) != kWallStack) - renderCompleteMirror(); - if (wall[nWall].pal != 0 || wall[nWall].shade != 0) - TranslateMirrorColors(wall[nWall].shade, wall[nWall].pal); - pWall->nextwall = nNextWall; - pWall->nextsector = nNextSector; - return; - } - case 1: - { - r_rorphase = 1; - int nSector = mirror[i].link; - int bakCstat; - if (viewPlayer >= 0) - { - bakCstat = gPlayer[viewPlayer].pSprite->cstat; - if (gViewPos == 0) - { - gPlayer[viewPlayer].pSprite->cstat |= 32768; - } - else - { - gPlayer[viewPlayer].pSprite->cstat |= 514; - } - } - renderDrawRoomsQ16(x+mirror[i].dx, y+mirror[i].dy, z+mirror[i].dz, a, horiz, nSector|MAXSECTORS); - viewProcessSprites(x+mirror[i].dx, y+mirror[i].dy, z+mirror[i].dz, FixedToInt(a), smooth); - short fstat = sector[nSector].floorstat; - sector[nSector].floorstat |= 1; - renderDrawMasks(); - sector[nSector].floorstat = fstat; - for (int i = 0; i < 16; i++) - ClearBitString(gotpic, 4080+i); - if (viewPlayer >= 0) - { - gPlayer[viewPlayer].pSprite->cstat = bakCstat; - } - r_rorphase = 0; - return; - } - case 2: - { - r_rorphase = 1; - int nSector = mirror[i].link; - int bakCstat; - if (viewPlayer >= 0) - { - bakCstat = gPlayer[viewPlayer].pSprite->cstat; - if (gViewPos == 0) - { - gPlayer[viewPlayer].pSprite->cstat |= 32768; - } - else - { - gPlayer[viewPlayer].pSprite->cstat |= 514; - } - } - renderDrawRoomsQ16(x+mirror[i].dx, y+mirror[i].dy, z+mirror[i].dz, a, horiz, nSector|MAXSECTORS); - viewProcessSprites(x+mirror[i].dx, y+mirror[i].dy, z+mirror[i].dz, FixedToInt(a), smooth); - short cstat = sector[nSector].ceilingstat; - sector[nSector].ceilingstat |= 1; - renderDrawMasks(); - sector[nSector].ceilingstat = cstat; - for (int i = 0; i < 16; i++) - ClearBitString(gotpic, 4080+i); - if (viewPlayer >= 0) - { - gPlayer[viewPlayer].pSprite->cstat = bakCstat; - } - r_rorphase = 0; - return; - } - } - } - } -} - //--------------------------------------------------------------------------- // // diff --git a/source/games/blood/src/misc.h b/source/games/blood/src/misc.h index b10ac1616..7f4491f93 100644 --- a/source/games/blood/src/misc.h +++ b/source/games/blood/src/misc.h @@ -35,11 +35,10 @@ void FireInit(void); void FireProcess(void); void UpdateNetworkMenus(void); void InitMirrors(void); -void sub_5571C(char mode); -void sub_557C4(int x, int y, int interpolation); +void setPortalFlags(char mode); +void processSpritesOnOtherSideOfPortal(int x, int y, int interpolation); void DrawMirrors(int x, int y, int z, fixed_t a, fixed_t horiz, int smooth, int viewPlayer); int qanimateoffs(int a1, int a2); -int32_t qgetpalookup(int32_t a1, int32_t a2); void HookReplaceFunctions(); struct QAV; @@ -120,8 +119,7 @@ extern short voxelIndex[MAXTILES]; extern int nPrecacheCount; -int tileInit(char a1, const char *a2); -void tileProcessGLVoxels(void); +int tileInit(); void tilePrecacheTile(int nTile, int nType, int palette); char tileGetSurfType(int hit); diff --git a/source/games/blood/src/namelist.h b/source/games/blood/src/namelist.h index b81969d7b..89ed1a7b4 100644 --- a/source/games/blood/src/namelist.h +++ b/source/games/blood/src/namelist.h @@ -1,6 +1,9 @@ // names for everything that gets accessed by scripts. x(MENUBAR, 2038) x(BackTile, 253) +x(Monolithscreen, 2050) +x(GTIScreen, 2052) +x(TitleScreen, 2518) x(CrosshairTile, 2319) x(LoadScreen, 2049) diff --git a/source/games/blood/src/nnexts.cpp b/source/games/blood/src/nnexts.cpp index a708623e0..ecbd18e60 100644 --- a/source/games/blood/src/nnexts.cpp +++ b/source/games/blood/src/nnexts.cpp @@ -31,10 +31,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #ifdef NOONE_EXTENSIONS #include -#include "mmulti.h" #include "blood.h" #include "savegamehelp.h" -#include "bloodactor.h" BEGIN_BLD_NS @@ -2533,8 +2531,6 @@ void condError(XSPRITE* pXCond, const char* pzFormat, ...) { bool condCheckMixed(XSPRITE* pXCond, EVENT event, int cmpOp, bool PUSH) { - UNREFERENCED_PARAMETER(PUSH); - //int var = -1; int cond = pXCond->data1 - kCondMixedBase; int arg1 = pXCond->data2; int arg2 = pXCond->data3; int arg3 = pXCond->data4; @@ -2813,8 +2809,6 @@ bool condCheckSector(XSPRITE* pXCond, int cmpOp, bool PUSH) { bool condCheckWall(XSPRITE* pXCond, int cmpOp, bool PUSH) { - UNREFERENCED_PARAMETER(PUSH); - int var = -1; int cond = pXCond->data1 - kCondWallBase; int arg1 = pXCond->data2; int arg2 = pXCond->data3; //int arg3 = pXCond->data4; @@ -2930,8 +2924,6 @@ bool condCheckPlayer(XSPRITE* pXCond, int cmpOp, bool PUSH) { bool condCheckDude(XSPRITE* pXCond, int cmpOp, bool PUSH) { - UNREFERENCED_PARAMETER(cmpOp); - int var = -1; //PLAYER* pPlayer = NULL; int cond = pXCond->data1 - kCondDudeBase; int arg1 = pXCond->data2; int arg2 = pXCond->data3; //int arg3 = pXCond->data4; @@ -3027,7 +3019,6 @@ bool condCheckDude(XSPRITE* pXCond, int cmpOp, bool PUSH) { bool condCheckSprite(XSPRITE* pXCond, int cmpOp, bool PUSH) { - UNREFERENCED_PARAMETER(PUSH); auto actor = &bloodActors[pXCond->reference]; int var = -1; PLAYER* pPlayer = NULL; bool retn = false; int cond = pXCond->data1 - kCondSpriteBase; int arg1 = pXCond->data2; @@ -4094,8 +4085,7 @@ void seqTxSendCmdAll(XSPRITE* pXSource, int nIndex, COMMAND_ID cmd, bool modernS void useRandomTx(XSPRITE* pXSource, COMMAND_ID cmd, bool setState) { - UNREFERENCED_PARAMETER(cmd); - + spritetype* pSource = &sprite[pXSource->reference]; int tx = 0; int maxRetries = kMaxRandomizeRetries; @@ -5215,13 +5205,7 @@ void seqSpawnerOffSameTx(XSPRITE* pXSource) { void levelEndLevelCustom(int nLevel) { gGameOptions.uGameFlags |= GF_AdvanceLevel; - - if (nLevel >= 16 || nLevel < 0) { - gGameOptions.uGameFlags |= GF_EndGame; - return; - } - - gNextLevel = nLevel; + gNextLevel = FindMapByIndex(currentLevel->cluster, nLevel + 1); } void callbackUniMissileBurst(int nSprite) // 22 diff --git a/source/games/blood/src/nnexts.h b/source/games/blood/src/nnexts.h index bb678dccb..3a1ef8f85 100644 --- a/source/games/blood/src/nnexts.h +++ b/source/games/blood/src/nnexts.h @@ -355,10 +355,11 @@ XSPRITE* evrListRedirectors(int objType, int objXIndex, XSPRITE* pXRedir, int* t XSPRITE* evrIsRedirector(int nSprite); int listTx(XSPRITE* pXRedir, int tx); void seqSpawnerOffSameTx(XSPRITE* pXSource); -#endif //////////////////////////////////////////////////////////////////////// // This file provides modern features for mappers. // For full documentation please visit http://cruo.bloodgame.ru/xxsystem //////////////////////////////////////////////////////////////////////////////////// END_BLD_NS + +#endif diff --git a/source/games/blood/src/osdcmd.cpp b/source/games/blood/src/osdcmd.cpp index e64343488..fbc73d9de 100644 --- a/source/games/blood/src/osdcmd.cpp +++ b/source/games/blood/src/osdcmd.cpp @@ -25,7 +25,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "build.h" #include "compat.h" -#include "mmulti.h" #include "blood.h" #include "mapinfo.h" @@ -38,18 +37,18 @@ void GameInterface::WarpToCoords(int x, int y, int z, int ang, int horz) PLAYER *pPlayer = &gPlayer[myconnectindex]; VIEW* pView = &gPrevView[myconnectindex]; - pPlayer->pSprite->x = pView->at50 = gView->pSprite->x = x; - pPlayer->pSprite->y = pView->at54 = gView->pSprite->y = y; - pPlayer->zView = pView->at38 = gView->zView = z; + pPlayer->pSprite->x = pView->x = gView->pSprite->x = x; + pPlayer->pSprite->y = pView->y = gView->pSprite->y = y; + pPlayer->zView = pView->viewz = gView->zView = z; if (ang != INT_MIN) { - pPlayer->angle.oang = pPlayer->angle.ang = pView->at30 = gView->angle.ang = buildang(ang); + pPlayer->angle.oang = pPlayer->angle.ang = pView->angle = gView->angle.ang = buildang(ang); } if (horz != INT_MIN) { - pPlayer->horizon.ohoriz = pPlayer->horizon.horiz = pView->at24 = gView->horizon.horiz = buildhoriz(horz); + pPlayer->horizon.ohoriz = pPlayer->horizon.horiz = pView->horiz = gView->horizon.horiz = buildhoriz(horz); } } diff --git a/source/games/blood/src/player.cpp b/source/games/blood/src/player.cpp index 3fd2f6aae..9fb455f95 100644 --- a/source/games/blood/src/player.cpp +++ b/source/games/blood/src/player.cpp @@ -27,7 +27,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "automap.h" #include "compat.h" #include "build.h" -#include "mmulti.h" #include "blood.h" #include "gstrings.h" @@ -400,10 +399,6 @@ void powerupClear(PLAYER *pPlayer) } } -void powerupInit(void) -{ -} - int packItemToPowerup(int nPack) { int nPowerUp = -1; @@ -700,7 +695,7 @@ void playerStart(int nPlayer, int bNewLevel) pPlayer->restTime = 0; pPlayer->kickPower = 0; pPlayer->laughCount = 0; - pPlayer->angle.spin = buildlook(0); + pPlayer->angle.spin = 0; pPlayer->posture = 0; pPlayer->voodooTarget = -1; pPlayer->voodooTargets = 0; @@ -1300,7 +1295,7 @@ void doslopetilting(PLAYER* pPlayer, double const scaleAdjust = 1) auto* const pXSprite = pPlayer->pXSprite; int const florhit = gSpriteHit[pSprite->extra].florhit & 0xc000; char const va = pXSprite->height < 16 && (florhit == 0x4000 || florhit == 0) ? 1 : 0; - calcviewpitch(pSprite->pos.vec2, &pPlayer->horizon.horizoff, buildang(pSprite->ang), va, sector[pSprite->sectnum].floorstat & 2, pSprite->sectnum, scaleAdjust); + pPlayer->horizon.calcviewpitch(pSprite->pos.vec2, buildang(pSprite->ang), va, sector[pSprite->sectnum].floorstat & 2, pSprite->sectnum, scaleAdjust); } void ProcessInput(PLAYER *pPlayer) @@ -1422,7 +1417,7 @@ void ProcessInput(PLAYER *pPlayer) if (SyncInput()) { - applylook(&pPlayer->angle, pInput->avel, &pInput->actions); + pPlayer->angle.applyinput(pInput->avel, &pInput->actions); } // unconditionally update the player's sprite angle @@ -1549,7 +1544,7 @@ void ProcessInput(PLAYER *pPlayer) if (SyncInput()) { - sethorizon(&pPlayer->horizon, pInput->horz, &pInput->actions); + pPlayer->horizon.applyinput(pInput->horz, &pInput->actions); doslopetilting(pPlayer); } @@ -1640,14 +1635,14 @@ void playerProcess(PLAYER *pPlayer) } ProcessInput(pPlayer); int nSpeed = approxDist(xvel[nSprite], yvel[nSprite]); - pPlayer->zViewVel = interpolate(pPlayer->zViewVel, zvel[nSprite], 0x7000); + pPlayer->zViewVel = interpolatedvalue(pPlayer->zViewVel, zvel[nSprite], 0x7000); int dz = pPlayer->pSprite->z-pPosture->eyeAboveZ-pPlayer->zView; if (dz > 0) pPlayer->zViewVel += MulScale(dz<<8, 0xa000, 16); else pPlayer->zViewVel += MulScale(dz<<8, 0x1800, 16); pPlayer->zView += pPlayer->zViewVel>>8; - pPlayer->zWeaponVel = interpolate(pPlayer->zWeaponVel, zvel[nSprite], 0x5000); + pPlayer->zWeaponVel = interpolatedvalue(pPlayer->zWeaponVel, zvel[nSprite], 0x5000); dz = pPlayer->pSprite->z-pPosture->weaponAboveZ-pPlayer->zWeapon; if (dz > 0) pPlayer->zWeaponVel += MulScale(dz<<8, 0x8000, 16); diff --git a/source/games/blood/src/player.h b/source/games/blood/src/player.h index 53f4dc4a3..c5f72d49d 100644 --- a/source/games/blood/src/player.h +++ b/source/games/blood/src/player.h @@ -241,7 +241,6 @@ void powerupDeactivate(PLAYER *pPlayer, int nPowerUp); void powerupSetState(PLAYER *pPlayer, int nPowerUp, char bState); void powerupProcess(PLAYER *pPlayer); void powerupClear(PLAYER *pPlayer); -void powerupInit(void); int packItemToPowerup(int nPack); int powerupToPackItem(int nPowerUp); char packAddItem(PLAYER *pPlayer, unsigned int nPack); diff --git a/source/games/blood/src/prediction.cpp b/source/games/blood/src/prediction.cpp index 156d70588..69403dd43 100644 --- a/source/games/blood/src/prediction.cpp +++ b/source/games/blood/src/prediction.cpp @@ -27,7 +27,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "compat.h" #include "build.h" -#include "mmulti.h" #include "v_font.h" #include "blood.h" @@ -50,37 +49,37 @@ static VIEW predictFifo[256]; void viewInitializePrediction(void) { - predict.at30 = gMe->angle.ang; - predict.at24 = gMe->horizon.horiz; - predict.at28 = gMe->horizon.horizoff; + predict.angle = gMe->angle.ang; + predict.horiz = gMe->horizon.horiz; + predict.horizoff = gMe->horizon.horizoff; predict.at2c = gMe->slope; predict.at6f = gMe->cantJump; predict.at70 = gMe->isRunning; predict.at72 = gMe->isUnderwater; predict.at71 = !!(gMe->input.actions & SB_JUMP); - predict.at50 = gMe->pSprite->x; - predict.at54 = gMe->pSprite->y; - predict.at58 = gMe->pSprite->z; - predict.at68 = gMe->pSprite->sectnum; + predict.x = gMe->pSprite->x; + predict.y = gMe->pSprite->y; + predict.z = gMe->pSprite->z; + predict.sectnum = gMe->pSprite->sectnum; predict.at73 = gMe->pSprite->flags; - predict.at5c = xvel[gMe->pSprite->index]; - predict.at60 = yvel[gMe->pSprite->index]; - predict.at64 = zvel[gMe->pSprite->index]; - predict.at6a = gMe->pXSprite->height; + predict.xvel = xvel[gMe->pSprite->index]; + predict.yvel = yvel[gMe->pSprite->index]; + predict.zvel = zvel[gMe->pSprite->index]; + predict.floordist = gMe->pXSprite->height; predict.at48 = gMe->posture; - predict.at4c = gMe->angle.spin; + predict.spin = gMe->angle.spin; predict.at6e = !!(gMe->input.actions & SB_CENTERVIEW); memcpy(&predict.at75,&gSpriteHit[gMe->pSprite->extra],sizeof(SPRITEHIT)); predict.bobPhase = gMe->bobPhase; predict.Kills = gMe->bobAmp; - predict.at8 = gMe->bobHeight; - predict.atc = gMe->bobWidth; + predict.bobHeight = gMe->bobHeight; + predict.bobWidth = gMe->bobWidth; predict.at10 = gMe->swayPhase; predict.at14 = gMe->swayAmp; - predict.at18 = gMe->swayHeight; - predict.at1c = gMe->swayWidth; - predict.at34 = gMe->zWeapon-gMe->zView-(12<<8); - predict.at38 = gMe->zView; + predict.shakeBobY = gMe->swayHeight; + predict.shakeBobX = gMe->swayWidth; + predict.weaponZ = gMe->zWeapon-gMe->zView-(12<<8); + predict.viewz = gMe->zView; predict.at3c = gMe->zViewVel; predict.at40 = gMe->zWeapon; predict.at44 = gMe->zWeaponVel; @@ -101,8 +100,8 @@ void viewUpdatePrediction(InputPacket *pInput) static void sub_158B4(PLAYER *pPlayer) { - predict.at38 = predict.at58 - pPlayer->pPosture[pPlayer->lifeMode][predict.at48].eyeAboveZ; - predict.at40 = predict.at58 - pPlayer->pPosture[pPlayer->lifeMode][predict.at48].weaponAboveZ; + predict.viewz = predict.z - pPlayer->pPosture[pPlayer->lifeMode][predict.at48].eyeAboveZ; + predict.at40 = predict.z - pPlayer->pPosture[pPlayer->lifeMode][predict.at48].weaponAboveZ; } static void fakeProcessInput(PLAYER *pPlayer, InputPacket *pInput) @@ -113,8 +112,8 @@ static void fakeProcessInput(PLAYER *pPlayer, InputPacket *pInput) predict.at71 = !!(gMe->input.actions & SB_JUMP); if (predict.at48 == 1) { - int x = Cos(predict.at30.asbuild()); - int y = Sin(predict.at30.asbuild()); + int x = Cos(predict.angle.asbuild()); + int y = Sin(predict.angle.asbuild()); if (pInput->fvel) { int forward = pInput->fvel; @@ -122,24 +121,24 @@ static void fakeProcessInput(PLAYER *pPlayer, InputPacket *pInput) forward = MulScale(pPosture->frontAccel, forward, 8); else forward = MulScale(pPosture->backAccel, forward, 8); - predict.at5c += MulScale(forward, x, 30); - predict.at60 += MulScale(forward, y, 30); + predict.xvel += MulScale(forward, x, 30); + predict.yvel += MulScale(forward, y, 30); } if (pInput->svel) { int strafe = pInput->svel; strafe = MulScale(pPosture->sideAccel, strafe, 8); - predict.at5c += MulScale(strafe, y, 30); - predict.at60 -= MulScale(strafe, x, 30); + predict.xvel += MulScale(strafe, y, 30); + predict.yvel -= MulScale(strafe, x, 30); } } - else if (predict.at6a < 0x100) + else if (predict.floordist < 0x100) { int speed = 0x10000; - if (predict.at6a > 0) - speed -= DivScale(predict.at6a, 0x100, 16); - int x = Cos(predict.at30.asbuild()); - int y = Sin(predict.at30.asbuild()); + if (predict.floordist > 0) + speed -= DivScale(predict.floordist, 0x100, 16); + int x = Cos(predict.angle.asbuild()); + int y = Sin(predict.angle.asbuild()); if (pInput->fvel) { int forward = pInput->fvel; @@ -147,27 +146,27 @@ static void fakeProcessInput(PLAYER *pPlayer, InputPacket *pInput) forward = MulScale(pPosture->frontAccel, forward, 8); else forward = MulScale(pPosture->backAccel, forward, 8); - if (predict.at6a) + if (predict.floordist) forward = MulScale(forward, speed, 16); - predict.at5c += MulScale(forward, x, 30); - predict.at60 += MulScale(forward, y, 30); + predict.xvel += MulScale(forward, x, 30); + predict.yvel += MulScale(forward, y, 30); } if (pInput->svel) { int strafe = pInput->svel; strafe = MulScale(pPosture->sideAccel, strafe, 8); - if (predict.at6a) + if (predict.floordist) strafe = MulScale(strafe, speed, 16); - predict.at5c += MulScale(strafe, y, 30); - predict.at60 -= MulScale(strafe, x, 30); + predict.xvel += MulScale(strafe, y, 30); + predict.yvel -= MulScale(strafe, x, 30); } } if (pInput->avel) - predict.at30 = degang(pInput->avel); + predict.angle = degang(pInput->avel); if (pInput->actions & SB_TURNAROUND) - if (!predict.at4c.asbuild()) - predict.at4c = buildlook(-1024); - if (predict.at4c.asbuild() < 0) + if (!predict.spin) + predict.spin = -1024; + if (predict.spin < 0) { int speed; if (predict.at48 == 1) @@ -175,8 +174,8 @@ static void fakeProcessInput(PLAYER *pPlayer, InputPacket *pInput) else speed = 128; - predict.at4c = buildlook(min(predict.at4c.asbuild()+speed, 0)); - predict.at30 += buildang(speed); + predict.spin = min(int(predict.spin) + speed, 0); + predict.angle += buildang(speed); } if (!predict.at71) @@ -186,18 +185,18 @@ static void fakeProcessInput(PLAYER *pPlayer, InputPacket *pInput) { case 1: if (predict.at71) - predict.at64 -= pPosture->normalJumpZ;//0x5b05; + predict.zvel -= pPosture->normalJumpZ;//0x5b05; if (pInput->actions & SB_CROUCH) - predict.at64 += pPosture->normalJumpZ;//0x5b05; + predict.zvel += pPosture->normalJumpZ;//0x5b05; break; case 2: if (!(pInput->actions & SB_CROUCH)) predict.at48 = 0; break; default: - if (!predict.at6f && predict.at71 && predict.at6a == 0) { - if (packItemActive(pPlayer, 4)) predict.at64 = pPosture->pwupJumpZ;//-0x175555; - else predict.at64 = pPosture->normalJumpZ;//-0xbaaaa; + if (!predict.at6f && predict.at71 && predict.floordist == 0) { + if (packItemActive(pPlayer, 4)) predict.zvel = pPosture->pwupJumpZ;//-0x175555; + else predict.zvel = pPosture->normalJumpZ;//-0xbaaaa; predict.at6f = 1; } if (pInput->actions & SB_CROUCH) @@ -232,33 +231,33 @@ static void fakeProcessInput(PLAYER *pPlayer, InputPacket *pInput) predict.at24 = 0; #endif - int nSector = predict.at68; + int nSector = predict.sectnum; int florhit = predict.at75.florhit & 0xc000; char va; - if (predict.at6a < 16 && (florhit == 0x4000 || florhit == 0)) + if (predict.floordist < 16 && (florhit == 0x4000 || florhit == 0)) va = 1; else va = 0; if (va && (sector[nSector].floorstat&2) != 0) { - int z1 = getflorzofslope(nSector, predict.at50, predict.at54); - int x2 = predict.at50+MulScale(64, Cos(predict.at30.asbuild()), 30); - int y2 = predict.at54+MulScale(64, Sin(predict.at30.asbuild()), 30); + int z1 = getflorzofslope(nSector, predict.x, predict.y); + int x2 = predict.x+MulScale(64, Cos(predict.angle.asbuild()), 30); + int y2 = predict.y+MulScale(64, Sin(predict.angle.asbuild()), 30); short nSector2 = nSector; updatesector(x2, y2, &nSector2); if (nSector2 == nSector) { int z2 = getflorzofslope(nSector2, x2, y2); - predict.at28 = q16horiz(interpolate(predict.at28.asq16(), IntToFixed(z1 - z2) >> 3, 0x4000)); + predict.horizoff = q16horiz(interpolatedvalue(predict.horizoff.asq16(), IntToFixed(z1 - z2) >> 3, 0x4000)); } } else { - predict.at28 = q16horiz(interpolate(predict.at28.asq16(), 0, 0x4000)); - if (abs(predict.at28.asq16()) < 4) - predict.at28 = q16horiz(0); + predict.horizoff = q16horiz(interpolatedvalue(predict.horizoff.asq16(), 0, 0x4000)); + if (abs(predict.horizoff.asq16()) < 4) + predict.horizoff = q16horiz(0); } - predict.at2c = -predict.at24.asq16() >> 9; + predict.at2c = -predict.horiz.asq16() >> 9; } void fakePlayerProcess(PLAYER *pPlayer, InputPacket *pInput) @@ -270,41 +269,41 @@ void fakePlayerProcess(PLAYER *pPlayer, InputPacket *pInput) int top, bottom; GetSpriteExtents(pSprite, &top, &bottom); - top += predict.at58-pSprite->z; - bottom += predict.at58-pSprite->z; + top += predict.z-pSprite->z; + bottom += predict.z-pSprite->z; - int dzb = (bottom-predict.at58)/4; - int dzt = (predict.at58-top)/4; + int dzb = (bottom-predict.z)/4; + int dzt = (predict.z-top)/4; int dw = pSprite->clipdist<<2; - short nSector = predict.at68; + short nSector = predict.sectnum; if (!gNoClip) { - pushmove_old((int32_t*)&predict.at50, (int32_t*)&predict.at54, (int32_t*)&predict.at58, &predict.at68, dw, dzt, dzb, CLIPMASK0); - if (predict.at68 == -1) - predict.at68 = nSector; + pushmove_old((int32_t*)&predict.x, (int32_t*)&predict.y, (int32_t*)&predict.z, &predict.sectnum, dw, dzt, dzb, CLIPMASK0); + if (predict.sectnum == -1) + predict.sectnum = nSector; } fakeProcessInput(pPlayer, pInput); - int nSpeed = approxDist(predict.at5c, predict.at60); + int nSpeed = approxDist(predict.xvel, predict.yvel); - predict.at3c = interpolate(predict.at3c, predict.at64, 0x7000); - int dz = predict.at58-pPosture->eyeAboveZ-predict.at38; + predict.at3c = interpolatedvalue(predict.at3c, predict.zvel, 0x7000); + int dz = predict.z-pPosture->eyeAboveZ-predict.viewz; if (dz > 0) predict.at3c += MulScale(dz<<8, 0xa000, 16); else predict.at3c += MulScale(dz<<8, 0x1800, 16); - predict.at38 += predict.at3c>>8; + predict.viewz += predict.at3c>>8; - predict.at44 = interpolate(predict.at44, predict.at64, 0x5000); - dz = predict.at58-pPosture->weaponAboveZ-predict.at40; + predict.at44 = interpolatedvalue(predict.at44, predict.zvel, 0x5000); + dz = predict.z-pPosture->weaponAboveZ-predict.at40; if (dz > 0) predict.at44 += MulScale(dz<<8, 0x8000, 16); else predict.at44 += MulScale(dz<<8, 0xc00, 16); predict.at40 += predict.at44>>8; - predict.at34 = predict.at40 - predict.at38 - (12<<8); + predict.weaponZ = predict.at40 - predict.viewz - (12<<8); predict.bobPhase = ClipLow(predict.bobPhase-4, 0); @@ -313,10 +312,10 @@ void fakePlayerProcess(PLAYER *pPlayer, InputPacket *pInput) { predict.Kills = (predict.Kills+17)&2047; predict.at14 = (predict.at14+17)&2047; - predict.at8 = MulScale(10*pPosture->bobV,Sin(predict.Kills*2), 30); - predict.atc = MulScale(predict.bobPhase*pPosture->bobH,Sin(predict.Kills-256), 30); - predict.at18 = MulScale(predict.bobPhase*pPosture->swayV,Sin(predict.at14*2), 30); - predict.at1c = MulScale(predict.bobPhase*pPosture->swayH,Sin(predict.at14-0x155), 30); + predict.bobHeight = MulScale(10*pPosture->bobV,Sin(predict.Kills*2), 30); + predict.bobWidth = MulScale(predict.bobPhase*pPosture->bobH,Sin(predict.Kills-256), 30); + predict.shakeBobY = MulScale(predict.bobPhase*pPosture->swayV,Sin(predict.at14*2), 30); + predict.shakeBobX = MulScale(predict.bobPhase*pPosture->swayH,Sin(predict.at14-0x155), 30); } else { @@ -335,10 +334,10 @@ void fakePlayerProcess(PLAYER *pPlayer, InputPacket *pInput) predict.bobPhase = ClipHigh(predict.bobPhase + nSpeed, 30); } } - predict.at8 = MulScale(predict.bobPhase*pPosture->bobV,Sin(predict.Kills*2), 30); - predict.atc = MulScale(predict.bobPhase*pPosture->bobH,Sin(predict.Kills-256), 30); - predict.at18 = MulScale(predict.bobPhase*pPosture->swayV,Sin(predict.at14*2), 30); - predict.at1c = MulScale(predict.bobPhase*pPosture->swayH,Sin(predict.at14-0x155), 30); + predict.bobHeight = MulScale(predict.bobPhase*pPosture->bobV,Sin(predict.Kills*2), 30); + predict.bobWidth = MulScale(predict.bobPhase*pPosture->bobH,Sin(predict.Kills-256), 30); + predict.shakeBobY = MulScale(predict.bobPhase*pPosture->swayV,Sin(predict.at14*2), 30); + predict.shakeBobX = MulScale(predict.bobPhase*pPosture->swayH,Sin(predict.at14-0x155), 30); } if (!pXSprite->health) return; @@ -346,11 +345,11 @@ void fakePlayerProcess(PLAYER *pPlayer, InputPacket *pInput) if (predict.at48 == 1) { predict.at72 = 1; - int nSector = predict.at68; + int nSector = predict.sectnum; int nLink = gLowerLink[nSector]; if (nLink > 0 && (sprite[nLink].type == kMarkerLowGoo || sprite[nLink].type == kMarkerLowWater)) { - if (getceilzofslope(nSector, predict.at50, predict.at54) > predict.at38) + if (getceilzofslope(nSector, predict.x, predict.y) > predict.viewz) predict.at72 = 0; } } @@ -364,34 +363,34 @@ static void fakeMoveDude(spritetype *pSprite) pPlayer = &gPlayer[pSprite->type-kDudePlayer1]; assert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); GetSpriteExtents(pSprite, &top, &bottom); - top += predict.at58 - pSprite->z; - bottom += predict.at58 - pSprite->z; - int bz = (bottom-predict.at58)/4; - int tz = (predict.at58-top)/4; + top += predict.z - pSprite->z; + bottom += predict.z - pSprite->z; + int bz = (bottom-predict.z)/4; + int tz = (predict.z-top)/4; int wd = pSprite->clipdist*4; - int nSector = predict.at68; + int nSector = predict.sectnum; assert(nSector >= 0 && nSector < kMaxSectors); - if (predict.at5c || predict.at60) + if (predict.xvel || predict.yvel) { if (pPlayer && gNoClip) { - predict.at50 += predict.at5c>>12; - predict.at54 += predict.at60>>12; - if (!FindSector(predict.at50, predict.at54, &nSector)) - nSector = predict.at68; + predict.x += predict.xvel>>12; + predict.y += predict.yvel>>12; + if (!FindSector(predict.x, predict.y, &nSector)) + nSector = predict.sectnum; } else { short bakCstat = pSprite->cstat; pSprite->cstat &= ~257; - predict.at75.hit = ClipMove(&predict.at50, &predict.at54, &predict.at58, &nSector, predict.at5c >> 12, predict.at60 >> 12, wd, tz, bz, CLIPMASK0); + predict.at75.hit = ClipMove(&predict.x, &predict.y, &predict.z, &nSector, predict.xvel >> 12, predict.yvel >> 12, wd, tz, bz, CLIPMASK0); if (nSector == -1) - nSector = predict.at68; + nSector = predict.sectnum; if (sector[nSector].type >= kSectorPath && sector[nSector].type <= kSectorRotate) { short nSector2 = nSector; - pushmove_old((int32_t*)&predict.at50, (int32_t*)&predict.at54, (int32_t*)&predict.at58, &nSector2, wd, tz, bz, CLIPMASK0); + pushmove_old((int32_t*)&predict.x, (int32_t*)&predict.y, (int32_t*)&predict.z, &nSector2, wd, tz, bz, CLIPMASK0); if (nSector2 != -1) nSector = nSector2; } @@ -414,15 +413,15 @@ static void fakeMoveDude(spritetype *pSprite) // ??? } } - actWallBounceVector(&predict.at5c, &predict.at60, nHitWall, 0); + actWallBounceVector(&predict.xvel, &predict.yvel, nHitWall, 0); break; } } } - if (predict.at68 != nSector) + if (predict.sectnum != nSector) { assert(nSector >= 0 && nSector < kMaxSectors); - predict.at68 = nSector; + predict.sectnum = nSector; } char bUnderwater = 0; char bDepth = 0; @@ -444,16 +443,16 @@ static void fakeMoveDude(spritetype *pSprite) if (pPlayer) wd += 16; - if (predict.at64) - predict.at58 += predict.at64 >> 8; + if (predict.zvel) + predict.z += predict.zvel >> 8; static_assert(sizeof(tspritetype) == sizeof(spritetype)); tspritetype pSpriteBak; memcpy(&pSpriteBak, pSprite, sizeof(pSpriteBak)); // how dare you??? (Use a tspritetype here so that if the sprite storage gets refactored, this line gets flagged.) spritetype *pTempSprite = pSprite; - pTempSprite->x = predict.at50; - pTempSprite->y = predict.at54; - pTempSprite->z = predict.at58; - pTempSprite->sectnum = predict.at68; + pTempSprite->x = predict.x; + pTempSprite->y = predict.y; + pTempSprite->z = predict.z; + pTempSprite->sectnum = predict.sectnum; int ceilZ, ceilHit, floorZ, floorHit; GetZRange(pTempSprite, &ceilZ, &ceilHit, &floorZ, &floorHit, wd, CLIPMASK0); GetSpriteExtents(pTempSprite, &top, &bottom); @@ -464,7 +463,7 @@ static void fakeMoveDude(spritetype *pSprite) { if (bUnderwater) { - int cz = getceilzofslope(nSector, predict.at50, predict.at54); + int cz = getceilzofslope(nSector, predict.x, predict.y); if (cz > top) vc += ((bottom-cz)*-80099) / (bottom-top); else @@ -472,7 +471,7 @@ static void fakeMoveDude(spritetype *pSprite) } else { - int fz = getflorzofslope(nSector, predict.at50, predict.at54); + int fz = getflorzofslope(nSector, predict.x, predict.y); if (fz < bottom) vc += ((bottom-fz)*-80099) / (bottom-top); } @@ -486,8 +485,8 @@ static void fakeMoveDude(spritetype *pSprite) } if (vc) { - predict.at58 += ((vc*4)/2)>>8; - predict.at64 += vc; + predict.z += ((vc*4)/2)>>8; + predict.zvel += vc; } } GetSpriteExtents(pTempSprite, &top, &bottom); @@ -496,7 +495,7 @@ static void fakeMoveDude(spritetype *pSprite) int floorZ2 = floorZ; int floorHit2 = floorHit; GetZRange(pTempSprite, &ceilZ, &ceilHit, &floorZ, &floorHit, pSprite->clipdist<<2, CLIPMASK0, PARALLAXCLIP_CEILING|PARALLAXCLIP_FLOOR); - if (bottom <= floorZ && predict.at58-floorZ2 < bz) + if (bottom <= floorZ && predict.z-floorZ2 < bz) { floorZ = floorZ2; floorHit = floorHit2; @@ -505,21 +504,21 @@ static void fakeMoveDude(spritetype *pSprite) if (floorZ <= bottom) { predict.at75.florhit = floorHit; - predict.at58 += floorZ-bottom; - int var44 = predict.at64-velFloor[predict.at68]; + predict.z += floorZ-bottom; + int var44 = predict.zvel-velFloor[predict.sectnum]; if (var44 > 0) { - actFloorBounceVector(&predict.at5c, &predict.at60, &var44, predict.at68, 0); - predict.at64 = var44; - if (abs(predict.at64) < 0x10000) + actFloorBounceVector(&predict.xvel, &predict.yvel, &var44, predict.sectnum, 0); + predict.zvel = var44; + if (abs(predict.zvel) < 0x10000) { - predict.at64 = velFloor[predict.at68]; + predict.zvel = velFloor[predict.sectnum]; predict.at73 &= ~4; } else predict.at73 |= 4; } - else if (predict.at64 == 0) + else if (predict.zvel == 0) predict.at73 &= ~4; } else @@ -531,40 +530,40 @@ static void fakeMoveDude(spritetype *pSprite) if (top <= ceilZ) { predict.at75.ceilhit = ceilHit; - predict.at58 += ClipLow(ceilZ-top, 0); - if (predict.at64 <= 0 && (predict.at73&4)) - predict.at64 = MulScale(-predict.at64, 0x2000, 16); + predict.z += ClipLow(ceilZ-top, 0); + if (predict.zvel <= 0 && (predict.at73&4)) + predict.zvel = MulScale(-predict.zvel, 0x2000, 16); } else predict.at75.ceilhit = 0; GetSpriteExtents(pTempSprite, &top, &bottom); memcpy(pSprite, &pSpriteBak, sizeof(pSpriteBak)); - predict.at6a = ClipLow(floorZ-bottom, 0)>>8; - if (predict.at5c || predict.at60) + predict.floordist = ClipLow(floorZ-bottom, 0)>>8; + if (predict.xvel || predict.yvel) { if ((floorHit & 0xc000) == 0xc000) { int nHitSprite = floorHit & 0x3fff; if ((sprite[nHitSprite].cstat & 0x30) == 0) { - predict.at5c += MulScale(4, predict.at50 - sprite[nHitSprite].x, 2); - predict.at60 += MulScale(4, predict.at54 - sprite[nHitSprite].y, 2); + predict.xvel += MulScale(4, predict.x - sprite[nHitSprite].x, 2); + predict.yvel += MulScale(4, predict.y - sprite[nHitSprite].y, 2); return; } } int nXSector = sector[pSprite->sectnum].extra; if (nXSector > 0 && xsector[nXSector].Underwater) return; - if (predict.at6a >= 0x100) + if (predict.floordist >= 0x100) return; int nDrag = gDudeDrag; - if (predict.at6a > 0) - nDrag -= scale(gDudeDrag, predict.at6a, 0x100); - predict.at5c -= mulscale16r(predict.at5c, nDrag); - predict.at60 -= mulscale16r(predict.at60, nDrag); - if (approxDist(predict.at5c, predict.at60) < 0x1000) - predict.at5c = predict.at60 = 0; + if (predict.floordist > 0) + nDrag -= scale(gDudeDrag, predict.floordist, 0x100); + predict.xvel -= mulscale16r(predict.xvel, nDrag); + predict.yvel -= mulscale16r(predict.yvel, nDrag); + if (approxDist(predict.xvel, predict.yvel) < 0x1000) + predict.xvel = predict.yvel = 0; } } @@ -572,7 +571,7 @@ static void fakeActAirDrag(spritetype *, int num) { int xvec = 0; int yvec = 0; - int nSector = predict.at68; + int nSector = predict.sectnum; assert(nSector >= 0 && nSector < kMaxSectors); sectortype *pSector = §or[nSector]; int nXSector = pSector->extra; @@ -589,9 +588,9 @@ static void fakeActAirDrag(spritetype *, int num) yvec = MulScale(vel, Sin(pXSector->windAng), 30); } } - predict.at5c += MulScale(xvec-predict.at5c, num, 16); - predict.at60 += MulScale(yvec-predict.at60, num, 16); - predict.at64 -= MulScale(predict.at64, num, 16); + predict.xvel += MulScale(xvec-predict.xvel, num, 16); + predict.yvel += MulScale(yvec-predict.yvel, num, 16); + predict.zvel -= MulScale(predict.zvel, num, 16); } void fakeActProcessSprites(void) @@ -601,7 +600,7 @@ void fakeActProcessSprites(void) { int nXSprite = pSprite->extra; assert(nXSprite > 0 && nXSprite < kMaxXSprites); - int nSector = predict.at68; + int nSector = predict.sectnum; int nXSector = sector[nSector].extra; XSECTOR *pXSector = NULL; if (nXSector > 0) @@ -614,9 +613,9 @@ void fakeActProcessSprites(void) { int top, bottom; GetSpriteExtents(pSprite, &top, &bottom); - top += predict.at58 - pSprite->z; - bottom += predict.at58 - pSprite->z; - if (getflorzofslope(nSector, predict.at50, predict.at54) < bottom) + top += predict.z - pSprite->z; + bottom += predict.z - pSprite->z; + if (getflorzofslope(nSector, predict.x, predict.y) < bottom) { int angle = pXSector->panAngle; int speed = 0; @@ -628,8 +627,8 @@ void fakeActProcessSprites(void) } if (sector[nSector].floorstat&64) angle = (GetWallAngle(sector[nSector].wallptr)+512)&2047; - predict.at5c += MulScale(speed,Cos(angle), 30); - predict.at60 += MulScale(speed,Sin(angle), 30); + predict.xvel += MulScale(speed,Cos(angle), 30); + predict.yvel += MulScale(speed,Sin(angle), 30); } } if (pXSector && pXSector->Underwater) @@ -637,7 +636,7 @@ void fakeActProcessSprites(void) else fakeActAirDrag(pSprite, 128); - if ((predict.at73 & 4) != 0 || predict.at5c != 0 || predict.at60 != 0 || predict.at64 != 0 || velFloor[predict.at68] != 0 || velCeil[predict.at68] != 0) + if ((predict.at73 & 4) != 0 || predict.xvel != 0 || predict.yvel != 0 || predict.zvel != 0 || velFloor[predict.sectnum] != 0 || velCeil[predict.sectnum] != 0) { fakeMoveDude(pSprite); } diff --git a/source/games/blood/src/replace.cpp b/source/games/blood/src/replace.cpp index 5ab7c7e1c..9ef9e82e5 100644 --- a/source/games/blood/src/replace.cpp +++ b/source/games/blood/src/replace.cpp @@ -62,14 +62,6 @@ int qanimateoffs(int a1, int a2) return offset; } -int32_t qgetpalookup(int32_t a1, int32_t a2) -{ - if (gFogMode) - return ClipHigh(a1 >> 8, 15) * 16 + ClipRange(a2, 0, 15); - else - return ClipRange((a1 >> 8) + a2, 0, 63); -} - void qinitspritelists(); int32_t qinsertsprite(int16_t nSector, int16_t nStat); int32_t qdeletesprite(int16_t nSprite); @@ -79,7 +71,6 @@ int32_t qchangespritestat(int16_t nSprite, int16_t nStatus); void HookReplaceFunctions(void) { animateoffs_replace = qanimateoffs; - getpalookup_replace = qgetpalookup; initspritelists_replace = qinitspritelists; insertsprite_replace = qinsertsprite; deletesprite_replace = qdeletesprite; diff --git a/source/games/blood/src/sbar.cpp b/source/games/blood/src/sbar.cpp index 952e40ab8..4f9621f87 100644 --- a/source/games/blood/src/sbar.cpp +++ b/source/games/blood/src/sbar.cpp @@ -27,7 +27,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "compat.h" #include "build.h" -#include "mmulti.h" #include "v_font.h" #include "blood.h" @@ -130,7 +129,7 @@ private: int flags = align | ((nStat & RS_CENTERBOTTOM)? DI_ITEM_CENTER_BOTTOM : (nStat & RS_TOPLEFT)? DI_ITEM_LEFT_TOP : DI_ITEM_RELCENTER); double alpha = 1.; double scale = nScale / 65536.; - DrawGraphic(tileGetTexture(nTile, true), x, y, flags, alpha, -1, -1, scale, scale, shadeToLight(nShade), TRANSLATION(Translation_Remap, nPalette), style); + DrawGraphic(tileGetTexture(nTile, true), x, y, flags, alpha, -1, -1, scale, scale, STYLE_Translucent, shadeToLight(nShade), TRANSLATION(Translation_Remap, nPalette), style); } void DrawStatMaskedSprite(int nTile, double x, double y, int nShade = 0, int nPalette = 0, unsigned int nStat = 0, int nScale = 65536, int align = DI_SCREEN_AUTO) { @@ -197,7 +196,7 @@ private: int bx = scale(MulScale(w, nScale, 16), nMult, nDiv) + x; double scale = double(bx - x) / w; double sc = nScale / 65536.; - DrawGraphic(tileGetTexture(nTile, true), x, y, DI_ITEM_LEFT_TOP, 1., -1, -1, sc, sc, 0xffffffff, 0, STYLE_Translucent, scale); + DrawGraphic(tileGetTexture(nTile, true), x, y, DI_ITEM_LEFT_TOP, 1., -1, -1, sc, sc, STYLE_Translucent, 0xffffffff, 0, scale); } @@ -218,7 +217,7 @@ private: stats.font = SmallFont; stats.letterColor = CR_DARKRED; stats.standardColor = CR_DARKGRAY; - stats.time = Scale(gFrameCount, 1000, kTicsPerSec); + stats.time = gFrameCount / GameTicRate; if (automapMode == am_full) { @@ -248,6 +247,7 @@ private: stats.secrets = gSecretMgr.Founds; stats.supersecrets = gSecretMgr.Super; stats.maxsecrets = max(gSecretMgr.Founds, gSecretMgr.Total); // If we found more than there are, increase the total. Some levels have a bugged counter. + stats.time = Scale(PlayClock, 1000, 120); DBaseStatusBar::PrintLevelStats(stats); } diff --git a/source/games/blood/src/tile.cpp b/source/games/blood/src/tile.cpp index 0eff626e5..0c4be50a5 100644 --- a/source/games/blood/src/tile.cpp +++ b/source/games/blood/src/tile.cpp @@ -33,7 +33,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. BEGIN_BLD_NS -bool artLoaded = false; int nTileFiles = 0; int tileStart[256]; @@ -44,17 +43,8 @@ char surfType[kMaxTiles]; int8_t tileShade[kMaxTiles]; short voxelIndex[kMaxTiles]; -const char *pzBaseFileName = "TILES%03i.ART"; //"TILES%03i.ART"; - -int tileInit(char a1, const char *a2) +int tileInit() { - UNREFERENCED_PARAMETER(a1); - if (artLoaded) - return 1; - TileFiles.artLoadFiles(a2 ? a2 : pzBaseFileName); - for (int i = 0; i < kMaxTiles; i++) - voxelIndex[i] = 0; - auto hFile = fileSystem.OpenFileReader("SURFACE.DAT"); if (hFile.isOpen()) { @@ -77,36 +67,11 @@ int tileInit(char a1, const char *a2) for (int i = 0; i < kMaxTiles; i++) { if (voxelIndex[i] >= 0 && voxelIndex[i] < kMaxVoxels) - SetBitString((char*)voxreserve, voxelIndex[i]); + voxreserve.Set(voxelIndex[i]); } - - artLoaded = 1; - - #ifdef USE_OPENGL - PolymostProcessVoxels_Callback = tileProcessGLVoxels; - #endif - return 1; } -#ifdef USE_OPENGL -void tileProcessGLVoxels(void) -{ - static bool voxInit = false; - if (voxInit) - return; - voxInit = true; - for (int i = 0; i < kMaxVoxels; i++) - { - auto index = fileSystem.FindResource(i, "KVX"); - if (index >= 0) - { - voxmodels[i] = voxload(index); - } - } -} -#endif - char tileGetSurfType(int hit) { int n = hit & 0x3fff; diff --git a/source/games/blood/src/triggers.cpp b/source/games/blood/src/triggers.cpp index 8da50f7af..5bc3c10b0 100644 --- a/source/games/blood/src/triggers.cpp +++ b/source/games/blood/src/triggers.cpp @@ -26,7 +26,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "build.h" #include "compat.h" -#include "mmulti.h" #include "blood.h" #include "d_net.h" @@ -794,6 +793,7 @@ void PathSound(int nSector, int nSound) void DragPoint(int nWall, int x, int y) { + sector[wall[nWall].sector].dirty = 255; viewInterpolateWall(nWall, &wall[nWall]); wall[nWall].x = x; wall[nWall].y = y; @@ -805,6 +805,7 @@ void DragPoint(int nWall, int x, int y) if (wall[vb].nextwall >= 0) { vb = wall[wall[vb].nextwall].point2; + sector[wall[vb].sector].dirty = 255; viewInterpolateWall(vb, &wall[vb]); wall[vb].x = x; wall[vb].y = y; @@ -817,6 +818,7 @@ void DragPoint(int nWall, int x, int y) if (wall[lastwall(vb)].nextwall >= 0) { vb = wall[lastwall(vb)].nextwall; + sector[wall[vb].sector].dirty = 255; viewInterpolateWall(vb, &wall[vb]); wall[vb].x = x; wall[vb].y = y; @@ -836,14 +838,14 @@ void TranslateSector(int nSector, int a2, int a3, int a4, int a5, int a6, int a7 int x, y; int nXSector = sector[nSector].extra; XSECTOR *pXSector = &xsector[nXSector]; - int v20 = interpolate(a6, a9, a2); - int vc = interpolate(a6, a9, a3); + int v20 = interpolatedvalue(a6, a9, a2); + int vc = interpolatedvalue(a6, a9, a3); int v28 = vc - v20; - int v24 = interpolate(a7, a10, a2); - int v8 = interpolate(a7, a10, a3); + int v24 = interpolatedvalue(a7, a10, a2); + int v8 = interpolatedvalue(a7, a10, a3); int v2c = v8 - v24; - int v44 = interpolate(a8, a11, a2); - int vbp = interpolate(a8, a11, a3); + int v44 = interpolatedvalue(a8, a11, a2); + int vbp = interpolatedvalue(a8, a11, a3); int v14 = vbp - v44; int nWall = sector[nSector].wallptr; if (a12) diff --git a/source/games/blood/src/view.cpp b/source/games/blood/src/view.cpp index 28cc22f62..13c661af0 100644 --- a/source/games/blood/src/view.cpp +++ b/source/games/blood/src/view.cpp @@ -27,7 +27,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "compat.h" #include "build.h" -#include "mmulti.h" #include "v_font.h" #include "blood.h" @@ -43,7 +42,10 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "gamefuncs.h" #include "v_draw.h" #include "precache.h" +#include "render.h" + +EXTERN_CVAR(Bool, testnewrenderer) BEGIN_BLD_NS FixedBitArray gInterpolateSprite; @@ -85,18 +87,18 @@ void viewBackupView(int nPlayer) { PLAYER *pPlayer = &gPlayer[nPlayer]; VIEW *pView = &gPrevView[nPlayer]; - pView->at30 = pPlayer->angle.ang; - pView->at50 = pPlayer->pSprite->x; - pView->at54 = pPlayer->pSprite->y; - pView->at38 = pPlayer->zView; - pView->at34 = pPlayer->zWeapon-pPlayer->zView-0xc00; - pView->at24 = pPlayer->horizon.horiz; - pView->at28 = pPlayer->horizon.horizoff; + pView->angle = pPlayer->angle.ang; + pView->x = pPlayer->pSprite->x; + pView->y = pPlayer->pSprite->y; + pView->viewz = pPlayer->zView; + pView->weaponZ = pPlayer->zWeapon-pPlayer->zView-0xc00; + pView->horiz = pPlayer->horizon.horiz; + pView->horizoff = pPlayer->horizon.horizoff; pView->at2c = pPlayer->slope; - pView->at8 = pPlayer->bobHeight; - pView->atc = pPlayer->bobWidth; - pView->at18 = pPlayer->swayHeight; - pView->at1c = pPlayer->swayWidth; + pView->bobHeight = pPlayer->bobHeight; + pView->bobWidth = pPlayer->bobWidth; + pView->shakeBobY = pPlayer->swayHeight; + pView->shakeBobX = pPlayer->swayWidth; pView->look_ang = pPlayer->angle.look_ang; pView->rotscrnang = pPlayer->angle.rotscrnang; pPlayer->angle.backup(); @@ -107,9 +109,9 @@ void viewCorrectViewOffsets(int nPlayer, vec3_t const *oldpos) { PLAYER *pPlayer = &gPlayer[nPlayer]; VIEW *pView = &gPrevView[nPlayer]; - pView->at50 += pPlayer->pSprite->x-oldpos->x; - pView->at54 += pPlayer->pSprite->y-oldpos->y; - pView->at38 += pPlayer->pSprite->z-oldpos->z; + pView->x += pPlayer->pSprite->x-oldpos->x; + pView->y += pPlayer->pSprite->y-oldpos->y; + pView->viewz += pPlayer->pSprite->z-oldpos->z; } void viewDrawText(int nFont, const char *pString, int x, int y, int nShade, int nPalette, int position, char shadow, unsigned int nStat, uint8_t alpha) @@ -388,17 +390,15 @@ void viewUpdateDelirium(void) } } -int shakeHoriz, shakeAngle, shakeX, shakeY, shakeZ, shakeBobX, shakeBobY; - -void viewUpdateShake(void) +void viewUpdateShake(int& cX, int& cY, int& cZ, binangle& cA, fixedhoriz& cH, double& pshakeX, double& pshakeY) { - shakeHoriz = 0; - shakeAngle = 0; - shakeX = 0; - shakeY = 0; - shakeZ = 0; - shakeBobX = 0; - shakeBobY = 0; + int shakeHoriz = 0; + int shakeAngle = 0; + int shakeX = 0; + int shakeY = 0; + int shakeZ = 0; + int shakeBobX = 0; + int shakeBobY = 0; if (gView->flickerEffect) { int nValue = ClipHigh(gView->flickerEffect * 8, 2000); @@ -421,6 +421,14 @@ void viewUpdateShake(void) shakeBobX += QRandom2(nValue); shakeBobY += QRandom2(nValue); } + cH += buildhoriz(shakeHoriz); + cA += buildang(shakeAngle); + cX += shakeX; + cY += shakeY; + cZ += shakeZ; + pshakeX += shakeBobX; + pshakeY += shakeBobY; + } @@ -437,15 +445,192 @@ static void DrawMap(spritetype* pSprite) tm = 1; } VIEW* pView = &gPrevView[gViewIndex]; - int x = interpolate(pView->at50, pSprite->x, gInterpolate); - int y = interpolate(pView->at54, pSprite->y, gInterpolate); + int x = interpolatedvalue(pView->x, pSprite->x, gInterpolate); + int y = interpolatedvalue(pView->y, pSprite->y, gInterpolate); int ang = (!SyncInput() ? gView->angle.sum() : gView->angle.interpolatedsum(gInterpolate)).asbuild(); DrawOverheadMap(x, y, ang, gInterpolate); if (tm) setViewport(hud_size); } +void SetupView(int &cX, int& cY, int& cZ, binangle& cA, fixedhoriz& cH, int& nSectnum, double& zDelta, double& shakeX, double& shakeY, binangle& rotscrnang) +{ + int bobWidth, bobHeight; + + nSectnum = gView->pSprite->sectnum; + if (numplayers > 1 && gView == gMe && gPrediction && gMe->pXSprite->health > 0) + { + nSectnum = predict.sectnum; + cX = interpolatedvalue(predictOld.x, predict.x, gInterpolate); + cY = interpolatedvalue(predictOld.y, predict.y, gInterpolate); + cZ = interpolatedvalue(predictOld.viewz, predict.viewz, gInterpolate); + zDelta = interpolatedvaluef(predictOld.weaponZ, predict.weaponZ, gInterpolate); + bobWidth = interpolatedvalue(predictOld.bobWidth, predict.bobWidth, gInterpolate); + bobHeight = interpolatedvalue(predictOld.bobHeight, predict.bobHeight, gInterpolate); + shakeX = interpolatedvaluef(predictOld.shakeBobX, predict.shakeBobX, gInterpolate); + shakeY = interpolatedvaluef(predictOld.shakeBobY, predict.shakeBobY, gInterpolate); + if (!SyncInput()) + { + cA = bamang(predict.angle.asbam() + predict.look_ang.asbam()); + cH = predict.horiz + predict.horizoff; + rotscrnang = predict.rotscrnang; + } + else + { + auto oang = predictOld.angle + predictOld.look_ang; + auto ang = predict.angle + predict.look_ang; + cA = interpolatedangle(oang, ang, gInterpolate); + + fixed_t ohoriz = (predictOld.horiz + predictOld.horizoff).asq16(); + fixed_t horiz = (predict.horiz + predict.horizoff).asq16(); + cH = q16horiz(interpolatedvalue(ohoriz, horiz, gInterpolate)); + + rotscrnang = interpolatedangle(predictOld.rotscrnang, predict.rotscrnang, gInterpolate); + } + } + else + { + VIEW* pView = &gPrevView[gViewIndex]; + cX = interpolatedvalue(pView->x, gView->pSprite->x, gInterpolate); + cY = interpolatedvalue(pView->y, gView->pSprite->y, gInterpolate); + cZ = interpolatedvalue(pView->viewz, gView->zView, gInterpolate); + zDelta = interpolatedvaluef(pView->weaponZ, gView->zWeapon - gView->zView - (12 << 8), gInterpolate); + bobWidth = interpolatedvalue(pView->bobWidth, gView->bobWidth, gInterpolate); + bobHeight = interpolatedvalue(pView->bobHeight, gView->bobHeight, gInterpolate); + shakeX = interpolatedvaluef(pView->shakeBobX, gView->swayWidth, gInterpolate); + shakeY = interpolatedvaluef(pView->shakeBobY, gView->swayHeight, gInterpolate); + + if (!SyncInput()) + { + cA = gView->angle.sum(); + cH = gView->horizon.sum(); + rotscrnang = gView->angle.rotscrnang; + } + else + { + cA = gView->angle.interpolatedsum(gInterpolate); + cH = gView->horizon.interpolatedsum(gInterpolate); + rotscrnang = gView->angle.interpolatedrotscrn(gInterpolate); + } + } + + viewUpdateShake(cX, cY, cZ, cA, cH, shakeX, shakeY); + cH += buildhoriz(MulScale(0x40000000 - Cos(gView->tiltEffect << 2), 30, 30)); + if (gViewPos == 0) + { + if (cl_viewhbob) + { + cX -= MulScale(bobWidth, Sin(cA.asbuild()), 30) >> 4; + cY += MulScale(bobWidth, Cos(cA.asbuild()), 30) >> 4; + } + if (cl_viewvbob) + { + cZ += bobHeight; + } + cZ += xs_CRoundToInt(cH.asq16() / 6553.6); + cameradist = -1; + cameraclock = PlayClock + MulScale(4, (int)gInterpolate, 16); + } + else + { + calcChaseCamPos((int*)&cX, (int*)&cY, (int*)&cZ, gView->pSprite, (short*)&nSectnum, cA, cH, gInterpolate); + } + CheckLink((int*)&cX, (int*)&cY, (int*)&cZ, &nSectnum); +} + +void renderCrystalBall() +{ +#if 0 + // needs to be redone for pure hardware rendering when MP is working again. + int tmp = (PlayClock / 240) % (gNetPlayers - 1); + int i = connecthead; + while (1) + { + if (i == gViewIndex) + i = connectpoint2[i]; + if (tmp == 0) + break; + i = connectpoint2[i]; + tmp--; + } + PLAYER* pOther = &gPlayer[i]; + //othercameraclock = PlayClock + MulScale(4, (int)gInterpolate, 16);; + if (!tileData(4079)) + { + TileFiles.tileCreate(4079, 128, 128); + } + //renderSetTarget(4079, 128, 128); + renderSetAspect(65536, 78643); + int vd8 = pOther->pSprite->x; + int vd4 = pOther->pSprite->y; + int vd0 = pOther->zView; + int vcc = pOther->pSprite->sectnum; + int v50 = pOther->pSprite->ang; + int v54 = 0; + if (pOther->flickerEffect) + { + int nValue = ClipHigh(pOther->flickerEffect * 8, 2000); + v54 += QRandom2(nValue >> 8); + v50 += QRandom2(nValue >> 8); + vd8 += QRandom2(nValue >> 4); + vd4 += QRandom2(nValue >> 4); + vd0 += QRandom2(nValue); + } + if (pOther->quakeEffect) + { + int nValue = ClipHigh(pOther->quakeEffect * 8, 2000); + v54 += QRandom2(nValue >> 8); + v50 += QRandom2(nValue >> 8); + vd8 += QRandom2(nValue >> 4); + vd4 += QRandom2(nValue >> 4); + vd0 += QRandom2(nValue); + } + CalcOtherPosition(pOther->pSprite, &vd8, &vd4, &vd0, &vcc, v50, 0, (int)gInterpolate); + CheckLink(&vd8, &vd4, &vd0, &vcc); + uint8_t v14 = 0; + if (IsUnderwaterSector(vcc)) + { + v14 = 10; + } + memcpy(bakMirrorGotpic, gotpic + 510, 2); + memcpy(gotpic + 510, otherMirrorGotpic, 2); + g_visibility = (int32_t)(ClipLow(gVisibility - 32 * pOther->visibility, 0)); + int vc4, vc8; + getzsofslope(vcc, vd8, vd4, &vc8, &vc4); + if (vd0 >= vc4) + { + vd0 = vc4 - (gUpperLink[vcc] >= 0 ? 0 : (8 << 8)); + } + if (vd0 <= vc8) + { + vd0 = vc8 + (gLowerLink[vcc] >= 0 ? 0 : (8 << 8)); + } + v54 = ClipRange(v54, -200, 200); +RORHACKOTHER: + int ror_status[16]; + for (int i = 0; i < 16; i++) + ror_status[i] = TestBitString(gotpic, 4080 + i); + DrawMirrors(vd8, vd4, vd0, IntToFixed(v50), IntToFixed(v54), gInterpolate, -1); + drawrooms(vd8, vd4, vd0, v50, v54, vcc); + bool do_ror_hack = false; + for (int i = 0; i < 16; i++) + if (ror_status[i] != TestBitString(gotpic, 4080 + i)) + do_ror_hack = true; + if (do_ror_hack) + { + spritesortcnt = 0; + goto RORHACKOTHER; + } + memcpy(otherMirrorGotpic, gotpic + 510, 2); + memcpy(gotpic + 510, bakMirrorGotpic, 2); + viewProcessSprites(vd8, vd4, vd0, v50, gInterpolate); + renderDrawMasks(); + renderRestoreTarget(); +#endif +} + +void render3DViewPolymost(int nSectnum, int cX, int cY, int cZ, binangle cA, fixedhoriz cH); void viewDrawScreen(bool sceneonly) { @@ -489,208 +674,30 @@ void viewDrawScreen(bool sceneonly) UpdateDacs(basepal); UpdateBlend(); - int yxAspect = yxaspect; - int viewingRange = viewingrange; - videoSetCorrectedAspect(); - - int v1 = xs_CRoundToInt(double(viewingrange) * tan(r_fov * (pi::pi() / 360.))); - - renderSetAspect(v1, yxaspect); - - int cX, cY, cZ, v74, v8c; - lookangle rotscrnang; + int cX, cY, cZ; binangle cA; fixedhoriz cH; - double zDelta, v4c, v48; - int nSectnum = gView->pSprite->sectnum; - if (numplayers > 1 && gView == gMe && gPrediction && gMe->pXSprite->health > 0) - { - nSectnum = predict.at68; - cX = interpolate(predictOld.at50, predict.at50, gInterpolate); - cY = interpolate(predictOld.at54, predict.at54, gInterpolate); - cZ = interpolate(predictOld.at38, predict.at38, gInterpolate); - zDelta = finterpolate(predictOld.at34, predict.at34, gInterpolate); - v74 = interpolate(predictOld.atc, predict.atc, gInterpolate); - v8c = interpolate(predictOld.at8, predict.at8, gInterpolate); - v4c = finterpolate(predictOld.at1c, predict.at1c, gInterpolate); - v48 = finterpolate(predictOld.at18, predict.at18, gInterpolate); + int nSectnum; + double zDelta; + double shakeX, shakeY; + binangle rotscrnang; + SetupView(cX, cY, cZ, cA, cH, nSectnum, zDelta, shakeX, shakeY, rotscrnang); - if (!SyncInput()) - { - cA = bamang(predict.at30.asbam() + predict.look_ang.asbam()); - cH = predict.at24 + predict.at28; - rotscrnang = predict.rotscrnang; - } - else - { - uint32_t oang = predictOld.at30.asbam() + predictOld.look_ang.asbam(); - uint32_t ang = predict.at30.asbam() + predict.look_ang.asbam(); - cA = interpolateangbin(oang, ang, gInterpolate); - - fixed_t ohoriz = (predictOld.at24 + predictOld.at28).asq16(); - fixed_t horiz = (predict.at24 + predict.at28).asq16(); - cH = q16horiz(interpolate(ohoriz, horiz, gInterpolate)); - - rotscrnang = interpolateanglook(predictOld.rotscrnang.asbam(), predict.rotscrnang.asbam(), gInterpolate); - } - } - else - { - VIEW* pView = &gPrevView[gViewIndex]; - cX = interpolate(pView->at50, gView->pSprite->x, gInterpolate); - cY = interpolate(pView->at54, gView->pSprite->y, gInterpolate); - cZ = interpolate(pView->at38, gView->zView, gInterpolate); - zDelta = finterpolate(pView->at34, gView->zWeapon - gView->zView - (12 << 8), gInterpolate); - v74 = interpolate(pView->atc, gView->bobWidth, gInterpolate); - v8c = interpolate(pView->at8, gView->bobHeight, gInterpolate); - v4c = finterpolate(pView->at1c, gView->swayWidth, gInterpolate); - v48 = finterpolate(pView->at18, gView->swayHeight, gInterpolate); - - if (!SyncInput()) - { - cA = gView->angle.sum(); - cH = gView->horizon.sum(); - rotscrnang = gView->angle.rotscrnang; - } - else - { - cA = gView->angle.interpolatedsum(gInterpolate); - cH = gView->horizon.interpolatedsum(gInterpolate); - rotscrnang = gView->angle.interpolatedrotscrn(gInterpolate); - } - } - - viewUpdateShake(); - cH += buildhoriz(shakeHoriz); - cA += buildang(shakeAngle); - cX += shakeX; - cY += shakeY; - cZ += shakeZ; - v4c += shakeBobX; - v48 += shakeBobY; - cH += buildhoriz(MulScale(0x40000000 - Cos(gView->tiltEffect << 2), 30, 30)); - if (gViewPos == 0) - { - if (cl_viewhbob) - { - cX -= MulScale(v74, Sin(cA.asbuild()), 30) >> 4; - cY += MulScale(v74, Cos(cA.asbuild()), 30) >> 4; - } - if (cl_viewvbob) - { - cZ += v8c; - } - cZ += xs_CRoundToInt(cH.asq16() / 6553.6); - cameradist = -1; - cameraclock = PlayClock + MulScale(4, (int)gInterpolate, 16); - } - else - { - calcChaseCamPos((int*)&cX, (int*)&cY, (int*)&cZ, gView->pSprite, (short*)&nSectnum, cA, cH, gInterpolate); - } - CheckLink((int*)&cX, (int*)&cY, (int*)&cZ, &nSectnum); - int v78 = interpolateang(gScreenTiltO, gScreenTilt, gInterpolate); + int tilt = interpolatedangle(gScreenTiltO, gScreenTilt, gInterpolate); uint8_t v14 = 0; uint8_t v10 = 0; bool bDelirium = powerupCheck(gView, kPwUpDeliriumShroom) > 0; static bool bDeliriumOld = false; //int tiltcs, tiltdim; - uint8_t v4 = powerupCheck(gView, kPwUpCrystalBall) > 0; -#ifdef USE_OPENGL - renderSetRollAngle(rotscrnang.asbuildf()); -#endif - if (v78 || bDelirium) + uint8_t otherview = powerupCheck(gView, kPwUpCrystalBall) > 0; + if (tilt || bDelirium) { - renderSetRollAngle(v78); + rotscrnang = buildang(tilt); } - else if (v4 && gNetPlayers > 1) + else if (otherview && gNetPlayers > 1) { -#if 0 // needs to be redone for pure hardware rendering. - int tmp = (PlayClock / 240) % (gNetPlayers - 1); - int i = connecthead; - while (1) - { - if (i == gViewIndex) - i = connectpoint2[i]; - if (tmp == 0) - break; - i = connectpoint2[i]; - tmp--; - } - PLAYER* pOther = &gPlayer[i]; - //othercameraclock = PlayClock + MulScale(4, (int)gInterpolate, 16);; - if (!tileData(4079)) - { - TileFiles.tileCreate(4079, 128, 128); - } - r enderSetTarget(4079, 128, 128); - renderSetAspect(65536, 78643); - int vd8 = pOther->pSprite->x; - int vd4 = pOther->pSprite->y; - int vd0 = pOther->zView; - int vcc = pOther->pSprite->sectnum; - int v50 = pOther->pSprite->ang; - int v54 = 0; - if (pOther->flickerEffect) - { - int nValue = ClipHigh(pOther->flickerEffect * 8, 2000); - v54 += QRandom2(nValue >> 8); - v50 += QRandom2(nValue >> 8); - vd8 += QRandom2(nValue >> 4); - vd4 += QRandom2(nValue >> 4); - vd0 += QRandom2(nValue); - } - if (pOther->quakeEffect) - { - int nValue = ClipHigh(pOther->quakeEffect * 8, 2000); - v54 += QRandom2(nValue >> 8); - v50 += QRandom2(nValue >> 8); - vd8 += QRandom2(nValue >> 4); - vd4 += QRandom2(nValue >> 4); - vd0 += QRandom2(nValue); - } - CalcOtherPosition(pOther->pSprite, &vd8, &vd4, &vd0, &vcc, v50, 0, (int)gInterpolate); - CheckLink(&vd8, &vd4, &vd0, &vcc); - if (IsUnderwaterSector(vcc)) - { - v14 = 10; - } - memcpy(bakMirrorGotpic, gotpic + 510, 2); - memcpy(gotpic + 510, otherMirrorGotpic, 2); - g_visibility = (int32_t)(ClipLow(gVisibility - 32 * pOther->visibility, 0)); - int vc4, vc8; - getzsofslope(vcc, vd8, vd4, &vc8, &vc4); - if (vd0 >= vc4) - { - vd0 = vc4 - (gUpperLink[vcc] >= 0 ? 0 : (8 << 8)); - } - if (vd0 <= vc8) - { - vd0 = vc8 + (gLowerLink[vcc] >= 0 ? 0 : (8 << 8)); - } - v54 = ClipRange(v54, -200, 200); - RORHACKOTHER: - int ror_status[16]; - for (int i = 0; i < 16; i++) - ror_status[i] = TestBitString(gotpic, 4080 + i); - yax_preparedrawrooms(); - DrawMirrors(vd8, vd4, vd0, IntToFixed(v50), IntToFixed(v54), gInterpolate, -1); - drawrooms(vd8, vd4, vd0, v50, v54, vcc); - yax_drawrooms(viewProcessSprites, vcc, 0, gInterpolate); - bool do_ror_hack = false; - for (int i = 0; i < 16; i++) - if (ror_status[i] != TestBitString(gotpic, 4080 + i)) - do_ror_hack = true; - if (do_ror_hack) - { - spritesortcnt = 0; - goto RORHACKOTHER; - } - memcpy(otherMirrorGotpic, gotpic+510, 2); - memcpy(gotpic+510, bakMirrorGotpic, 2); - viewProcessSprites(vd8, vd4, vd0, v50, gInterpolate); - renderDrawMasks(); - renderRestoreTarget(); +#if 0 + renderCrystalBall(); #endif } else @@ -704,7 +711,7 @@ void viewDrawScreen(bool sceneonly) deliriumTurn = 0; deliriumPitch = 0; } - int unk = 0; + int brightness = 0; int nSprite; StatIterator it(kStatExplosion); @@ -714,9 +721,9 @@ void viewDrawScreen(bool sceneonly) int nXSprite = pSprite->extra; assert(nXSprite > 0 && nXSprite < kMaxXSprites); XSPRITE* pXSprite = &xsprite[nXSprite]; - if (TestBitString(gotsector, pSprite->sectnum)) + if (gotsector[pSprite->sectnum]) { - unk += pXSprite->data3 * 32; + brightness += pXSprite->data3 * 32; } } it.Reset(kStatProjectile); @@ -728,84 +735,52 @@ void viewDrawScreen(bool sceneonly) case kMissileTeslaAlt: case kMissileFlareAlt: case kMissileTeslaRegular: - if (TestBitString(gotsector, pSprite->sectnum)) unk += 256; + if (gotsector[pSprite->sectnum]) brightness += 256; break; } } - g_visibility = (int32_t)(ClipLow(gVisibility - 32 * gView->visibility - unk, 0)); - cA += q16ang(interpolateangfix16(IntToFixed(deliriumTurnO), IntToFixed(deliriumTurn), gInterpolate)); - int vfc, vf8; - getzsofslope(nSectnum, cX, cY, &vfc, &vf8); - if (cZ >= vf8) + g_visibility = (int32_t)(ClipLow(gVisibility - 32 * gView->visibility - brightness, 0)); + cA += interpolatedangle(buildang(deliriumTurnO), buildang(deliriumTurn), gInterpolate); + + int ceilingZ, floorZ; + getzsofslope(nSectnum, cX, cY, &ceilingZ, &floorZ); + if (cZ >= floorZ) { - cZ = vf8 - (gUpperLink[nSectnum] >= 0 ? 0 : (8 << 8)); + cZ = floorZ - (gUpperLink[nSectnum] >= 0 ? 0 : (8 << 8)); } - if (cZ <= vfc) + if (cZ <= ceilingZ) { - cZ = vfc + (gLowerLink[nSectnum] >= 0 ? 0 : (8 << 8)); + cZ = ceilingZ + (gLowerLink[nSectnum] >= 0 ? 0 : (8 << 8)); } cH = q16horiz(ClipRange(cH.asq16(), gi->playerHorizMin(), gi->playerHorizMax())); - RORHACK: - int ror_status[16]; - for (int i = 0; i < 16; i++) - ror_status[i] = TestBitString(gotpic, 4080 + i); - fixed_t deliriumPitchI = interpolate(IntToFixed(deliriumPitchO), IntToFixed(deliriumPitch), gInterpolate); - DrawMirrors(cX, cY, cZ, cA.asq16(), cH.asq16() + deliriumPitchI, gInterpolate, gViewIndex); - int bakCstat = gView->pSprite->cstat; - if (gViewPos == 0) - { - gView->pSprite->cstat |= 32768; - } - else - { - gView->pSprite->cstat |= 514; - } - renderDrawRoomsQ16(cX, cY, cZ, cA.asq16(), cH.asq16() + deliriumPitchI, nSectnum); - viewProcessSprites(cX, cY, cZ, cA.asbuild(), gInterpolate); - bool do_ror_hack = false; - for (int i = 0; i < 16; i++) - if (ror_status[i] != TestBitString(gotpic, 4080 + i)) - do_ror_hack = true; - if (do_ror_hack) - { - gView->pSprite->cstat = bakCstat; - spritesortcnt = 0; - goto RORHACK; - } - sub_5571C(1); - int nSpriteSortCnt = spritesortcnt; - renderDrawMasks(); - spritesortcnt = nSpriteSortCnt; - sub_5571C(0); - sub_557C4(cX, cY, gInterpolate); - renderDrawMasks(); - gView->pSprite->cstat = bakCstat; - - if ((v78 || bDelirium) && !sceneonly) + if ((tilt || bDelirium) && !sceneonly) { if (gDeliriumBlur) { - // todo: Implement using modern techniques instead of relying on deprecated old stuff that isn't well supported anymore. - /* names broken up so that searching for GL keywords won't find them anymore - if (!bDeliriumOld) - { - g lAccum(GL_LOAD, 1.f); - } - else - { - const float fBlur = pow(1.f/3.f, 30.f/g_frameRate); - g lAccum(GL _MULT, fBlur); - g lAccum(GL _ACCUM, 1.f-fBlur); - g lAccum(GL _RETURN, 1.f); - } - */ + // todo: Set up a blurring postprocessing shader. + //const float fBlur = pow(1.f/3.f, 30.f/g_frameRate); + //g lAccum(GL _MULT, fBlur); + //g lAccum(GL _ACCUM, 1.f-fBlur); + //g lAccum(GL _RETURN, 1.f); } } + if (testnewrenderer) + { + fixedhoriz deliriumPitchI = q16horiz(interpolatedvalue(IntToFixed(deliriumPitchO), IntToFixed(deliriumPitch), gInterpolate)); + int bakCstat = gView->pSprite->cstat; + gView->pSprite->cstat |= (gViewPos == 0) ? CSTAT_SPRITE_INVISIBLE : CSTAT_SPRITE_TRANSLUCENT | CSTAT_SPRITE_TRANSLUCENT_INVERT; + render_drawrooms(gView->pSprite, { cX, cY, cZ }, nSectnum, cA, cH + deliriumPitchI, rotscrnang, gInterpolate); + gView->pSprite->cstat = bakCstat; + } + else + { + renderSetRollAngle(rotscrnang.asbuildf()); + render3DViewPolymost(nSectnum, cX, cY, cZ, cA, cH); + } bDeliriumOld = bDelirium && gDeliriumBlur; - renderSetAspect(viewingRange, yxAspect); int nClipDist = gView->pSprite->clipdist << 2; int ve8, vec, vf0, vf4; GetZRange(gView->pSprite, &vf4, &vf0, &vec, &ve8, nClipDist, 0); @@ -830,7 +805,7 @@ void viewDrawScreen(bool sceneonly) } } #endif - hudDraw(gView, nSectnum, v4c, v48, zDelta, basepal, gInterpolate); + hudDraw(gView, nSectnum, shakeX, shakeY, zDelta, basepal, gInterpolate); } UpdateDacs(0, true); // keep the view palette active only for the actual 3D view and its overlays. if (automapMode != am_off) @@ -889,8 +864,6 @@ bool GameInterface::DrawAutomapPlayer(int x, int y, int z, int a, double const s // [MR]: Confirm that this is correct as math doesn't match the variable names. int nCos = z * -bsin(a); int nSin = z * -bcos(a); - int nCos2 = MulScale(nCos, yxaspect, 16); - int nSin2 = MulScale(nSin, yxaspect, 16); int nPSprite = gView->pSprite->index; for (int i = connecthead; i >= 0; i = connectpoint2[i]) @@ -907,7 +880,7 @@ bool GameInterface::DrawAutomapPlayer(int x, int y, int z, int a, double const s GetZRange(pSprite, &ceilZ, &ceilHit, &floorZ, &floorHit, (pSprite->clipdist << 2) + 16, CLIPMASK0, PARALLAXCLIP_CEILING | PARALLAXCLIP_FLOOR); int nTop, nBottom; GetSpriteExtents(pSprite, &nTop, &nBottom); - int nScale = MulScale((pSprite->yrepeat + ((floorZ - nBottom) >> 8)) * z, yxaspect, 16); + int nScale = (pSprite->yrepeat + ((floorZ - nBottom) >> 8)) * z; nScale = ClipRange(nScale, 8000, 65536 << 1); // Players on automap double x = xdim / 2. + x1 / double(1 << 12); diff --git a/source/games/blood/src/view.h b/source/games/blood/src/view.h index ebc93783e..9b1d1399b 100644 --- a/source/games/blood/src/view.h +++ b/source/games/blood/src/view.h @@ -34,31 +34,31 @@ BEGIN_BLD_NS struct VIEW { int bobPhase; int Kills; - int at8; // bob height - int atc; // bob width + int bobHeight; // bob height + int bobWidth; // bob width int at10; int at14; - int at18; // bob sway y - int at1c; // bob sway x - fixedhoriz at24; // horiz - fixedhoriz at28; // horizoff + int shakeBobY; // bob sway y + int shakeBobX; // bob sway x + fixedhoriz horiz; // horiz + fixedhoriz horizoff; // horizoff int at2c; - binangle at30; // angle - int at34; // weapon z - int at38; // view z + binangle angle; // angle + int weaponZ; // weapon z + int viewz; // view z int at3c; int at40; int at44; int at48; // posture - lookangle at4c; // spin - int at50; // x - int at54; // y - int at58; // z - int at5c; //xvel - int at60; //yvel - int at64; //zvel - short at68; // sectnum - unsigned int at6a; // floordist + double spin; // spin + int x; // x + int y; // y + int z; // z + int xvel; //xvel + int yvel; //yvel + int zvel; //zvel + short sectnum; // sectnum + unsigned int floordist; // floordist char at6e; // look center char at6f; char at70; // run @@ -66,8 +66,8 @@ struct VIEW { char at72; // underwater short at73; // sprite flags SPRITEHIT at75; - lookangle look_ang; - lookangle rotscrnang; + binangle look_ang; + binangle rotscrnang; }; extern VIEW gPrevView[kMaxPlayers]; @@ -144,7 +144,7 @@ void viewDrawText(int nFont, const char *pString, int x, int y, int nShade, int void InitStatusBar(void); void UpdateStatusBar(); void viewInit(void); -void viewProcessSprites(int32_t cX, int32_t cY, int32_t cZ, int32_t cA, int32_t smooth); +void viewProcessSprites(spritetype* tsprite, int& spritesortcnt, int32_t cX, int32_t cY, int32_t cZ, int32_t cA, int32_t smooth); void viewSetMessage(const char *pMessage, const int pal = 0, const MESSAGE_PRIORITY priority = MESSAGE_PRIORITY_NORMAL); @@ -153,7 +153,6 @@ void DoLensEffect(void); void UpdateDacs(int nPalette, bool bNoTint = false); void viewDrawScreen(bool sceneonly = false); void viewUpdateDelirium(void); -void viewUpdateShake(void); void viewSetSystemMessage(const char* pMessage, ...); inline void viewInterpolateSector(int nSector, sectortype *pSector) diff --git a/source/games/blood/src/weapon.cpp b/source/games/blood/src/weapon.cpp index 86cffff6a..f180a3092 100644 --- a/source/games/blood/src/weapon.cpp +++ b/source/games/blood/src/weapon.cpp @@ -27,7 +27,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include #include "compat.h" #include "build.h" -#include "mmulti.h" #include "blood.h" @@ -466,9 +465,9 @@ void UpdateAimVector(PLAYER * pPlayer) aim2 = aim; RotateVector((int*)&aim2.dx, (int*)&aim2.dy, -pPSprite->ang); aim2.dz -= pPlayer->slope; - pPlayer->relAim.dx = interpolate(pPlayer->relAim.dx, aim2.dx, pWeaponTrack->aimSpeedHorz); - pPlayer->relAim.dy = interpolate(pPlayer->relAim.dy, aim2.dy, pWeaponTrack->aimSpeedHorz); - pPlayer->relAim.dz = interpolate(pPlayer->relAim.dz, aim2.dz, pWeaponTrack->aimSpeedVert); + pPlayer->relAim.dx = interpolatedvalue(pPlayer->relAim.dx, aim2.dx, pWeaponTrack->aimSpeedHorz); + pPlayer->relAim.dy = interpolatedvalue(pPlayer->relAim.dy, aim2.dy, pWeaponTrack->aimSpeedHorz); + pPlayer->relAim.dz = interpolatedvalue(pPlayer->relAim.dz, aim2.dz, pWeaponTrack->aimSpeedVert); pPlayer->aim = pPlayer->relAim; RotateVector((int*)&pPlayer->aim.dx, (int*)&pPlayer->aim.dy, pPSprite->ang); pPlayer->aim.dz += pPlayer->slope; @@ -1548,9 +1547,8 @@ void AltFireVoodoo(int nTrigger, PLAYER *pPlayer) } } -void DropVoodoo(int nTrigger, PLAYER *pPlayer) +void DropVoodoo(int , PLAYER *pPlayer) { - UNREFERENCED_PARAMETER(nTrigger); sfxPlay3DSound(pPlayer->pSprite, 455, 2, 0); spritetype *pSprite = playerFireThing(pPlayer, 0, -4730, kThingVoodooHead, 0xccccc); if (pSprite) @@ -1610,9 +1608,8 @@ void FireTesla(int nTrigger, PLAYER *pPlayer) } } -void AltFireTesla(int nTrigger, PLAYER *pPlayer) +void AltFireTesla(int , PLAYER *pPlayer) { - UNREFERENCED_PARAMETER(nTrigger); spritetype *pSprite = pPlayer->pSprite; playerFireMissile(pPlayer, 0, pPlayer->aim.dx, pPlayer->aim.dy, pPlayer->aim.dz, kMissileTeslaAlt); UseAmmo(pPlayer, pPlayer->weaponAmmo, 35); @@ -1640,9 +1637,8 @@ void FireNapalm(int nTrigger, PLAYER *pPlayer) pPlayer->flashEffect = 1; } -void FireNapalm2(int nTrigger, PLAYER *pPlayer) +void FireNapalm2(int , PLAYER *pPlayer) { - UNREFERENCED_PARAMETER(nTrigger); spritetype *pSprite = pPlayer->pSprite; playerFireMissile(pPlayer, -120, pPlayer->aim.dx, pPlayer->aim.dy, pPlayer->aim.dz, kMissileFireballNapam); playerFireMissile(pPlayer, 120, pPlayer->aim.dx, pPlayer->aim.dy, pPlayer->aim.dz, kMissileFireballNapam); @@ -1651,9 +1647,8 @@ void FireNapalm2(int nTrigger, PLAYER *pPlayer) pPlayer->flashEffect = 1; } -void AltFireNapalm(int nTrigger, PLAYER *pPlayer) +void AltFireNapalm(int , PLAYER *pPlayer) { - UNREFERENCED_PARAMETER(nTrigger); int nSpeed = MulScale(0x8000, 0x177777, 16)+0x66666; spritetype *pMissile = playerFireThing(pPlayer, 0, -4730, kThingNapalmBall, nSpeed); if (pMissile) @@ -1691,9 +1686,8 @@ void FireLifeLeech(int nTrigger, PLAYER *pPlayer) pPlayer->visibility = ClipHigh(pPlayer->visibility+5, 50); } -void AltFireLifeLeech(int nTrigger, PLAYER *pPlayer) +void AltFireLifeLeech(int , PLAYER *pPlayer) { - UNREFERENCED_PARAMETER(nTrigger); sfxPlay3DSound(pPlayer->pSprite, 455, 2, 0); spritetype *pMissile = playerFireThing(pPlayer, 0, -4730, kThingDroppedLifeLeech, 0x19999); if (pMissile) @@ -1726,9 +1720,8 @@ void AltFireLifeLeech(int nTrigger, PLAYER *pPlayer) } } -void FireBeast(int nTrigger, PLAYER * pPlayer) +void FireBeast(int , PLAYER * pPlayer) { - UNREFERENCED_PARAMETER(nTrigger); int r1 = Random2(2000); int r2 = Random2(2000); int r3 = Random2(2000); diff --git a/source/games/duke/src/2d_d.cpp b/source/games/duke/src/2d_d.cpp index c26c7ad14..e8f3561f6 100644 --- a/source/games/duke/src/2d_d.cpp +++ b/source/games/duke/src/2d_d.cpp @@ -41,6 +41,7 @@ Modifications for JonoF's port by Jonathon Fowler (jf@jonof.id.au) #include "mapinfo.h" #include "c_dispatch.h" #include "gamestate.h" +#include "gamefuncs.h" BEGIN_DUKE_NS @@ -132,1041 +133,4 @@ void InitFonts_d() } -//========================================================================== -// -// wrappers around DrawText to allow easier reuse of the old code. -// The vertical displacements are to have the same positioning as with the original code. -// -//========================================================================== - -static void BigText(double x, double y, const char* text, double alpha = 1.) -{ - auto width = BigFont->StringWidth(text); - DrawText(twod, BigFont, CR_UNTRANSLATED, x - width / 2, y - 12, text, DTA_FullscreenScale, FSMode_Fit320x200, DTA_Alpha, alpha, TAG_DONE); -} - -static void GameText(double x, double y, const char* t, int shade, int align = -1, int trans = 0) -{ - if (align != -1) - x -= SmallFont->StringWidth(t) * (align == 0 ? 0.5 : 1); - DrawText(twod, SmallFont, CR_UNDEFINED, x, y + 2, t, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TranslationIndex, TRANSLATION(Translation_Remap, trans), DTA_Color, shadeToLight(shade), TAG_DONE); -} - -static void MiniText(double x, double y, const char* t, int shade, int align = -1, int trans = 0) -{ - if (align != -1) - x -= SmallFont2->StringWidth(t) * (align == 0 ? 0.5 : 1); - DrawText(twod, SmallFont2, CR_UNDEFINED, x, y, t, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TranslationIndex, TRANSLATION(Translation_Remap, trans), DTA_Color, shadeToLight(shade), TAG_DONE); -} - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -class DDRealmsScreen : public DSkippableScreenJob -{ -public: - DDRealmsScreen() : DSkippableScreenJob(fadein | fadeout) {} - - void OnTick() override - { - if (ticks >= 7 * GameTicRate) state = finished; - } - - void Draw(double smoothratio) override - { - const auto tex = tileGetTexture(DREALMS, true); - int translation = tex->GetTexture()->GetImage()->UseGamePalette() ? TRANSLATION(Translation_BasePalettes, DREALMSPAL) : 0; - - twod->ClearScreen(); - DrawTexture(twod, tex, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_TranslationIndex, translation, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE); - } -}; - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -class DTitleScreen : public DSkippableScreenJob -{ - int soundanm = 0; - -public: - DTitleScreen() : DSkippableScreenJob(fadein | fadeout) - { - } - - void OnTick() override - { - int clock = ticks * 120 / GameTicRate; - if (soundanm == 0 && clock >= 120 && clock < 120 + 60) - { - soundanm = 1; - S_PlaySound(PIPEBOMB_EXPLODE, CHAN_AUTO, CHANF_UI); - } - if (soundanm == 1 && clock > 220 && clock < (220 + 30)) - { - soundanm = 2; - S_PlaySound(PIPEBOMB_EXPLODE, CHAN_AUTO, CHANF_UI); - } - if (soundanm == 2 && clock >= 280 && clock < 395) - { - soundanm = 3; - if (isPlutoPak()) S_PlaySound(FLY_BY, CHAN_AUTO, CHANF_UI); - } - else if (soundanm == 3 && clock >= 395) - { - soundanm = 4; - if (isPlutoPak()) S_PlaySound(PIPEBOMB_EXPLODE, CHAN_AUTO, CHANF_UI); - } - - if (clock > (860 + 120)) - { - state = finished; - } - } - - void Draw(double smoothratio) override - { - twod->ClearScreen(); - int clock = (ticks + smoothratio) * 120 / GameTicRate; - - twod->ClearScreen(); - - // Only translate if the image depends on the global palette. - auto tex = tileGetTexture(BETASCREEN, true); - int translation = tex->GetTexture()->GetImage()->UseGamePalette() ? TRANSLATION(Translation_BasePalettes, TITLEPAL) : 0; - DrawTexture(twod, tex, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_TranslationIndex, translation, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE); - - - double scale = clamp(clock - 120, 0, 60) / 64.; - if (scale > 0.) - { - tex = tileGetTexture(DUKENUKEM, true); - translation = tex->GetTexture()->GetImage()->UseGamePalette() ? TRANSLATION(Translation_BasePalettes, TITLEPAL) : 0; - - DrawTexture(twod, tileGetTexture(DUKENUKEM, true), 160, 104, DTA_FullscreenScale, FSMode_Fit320x200, - DTA_CenterOffsetRel, true, DTA_TranslationIndex, translation, DTA_ScaleX, scale, DTA_ScaleY, scale, TAG_DONE); - } - - scale = clamp(clock - 220, 0, 30) / 32.; - if (scale > 0.) - { - tex = tileGetTexture(THREEDEE, true); - translation = tex->GetTexture()->GetImage()->UseGamePalette() ? TRANSLATION(Translation_BasePalettes, TITLEPAL) : 0; - - DrawTexture(twod, tileGetTexture(THREEDEE, true), 160, 129, DTA_FullscreenScale, FSMode_Fit320x200, - DTA_CenterOffsetRel, true, DTA_TranslationIndex, translation, DTA_ScaleX, scale, DTA_ScaleY, scale, TAG_DONE); - } - - if (isPlutoPak()) - { - scale = (410 - clamp(clock, 280, 395)) / 16.; - if (scale > 0. && clock > 280) - { - tex = tileGetTexture(PLUTOPAKSPRITE + 1, true); - translation = tex->GetTexture()->GetImage()->UseGamePalette() ? TRANSLATION(Translation_BasePalettes, TITLEPAL) : 0; - - DrawTexture(twod, tileGetTexture(PLUTOPAKSPRITE + 1, true), 160, 151, DTA_FullscreenScale, FSMode_Fit320x200, - DTA_CenterOffsetRel, true, DTA_TranslationIndex, translation, DTA_ScaleX, scale, DTA_ScaleY, scale, TAG_DONE); - } - } - } -}; - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -void Logo_d(const CompletionFunc &completion) -{ - Mus_Stop(); - FX_StopAllSounds(); // JBF 20031228 - - static const AnimSound logosound[] = - { - { 1, FLY_BY+1 }, - { 19, PIPEBOMB_EXPLODE+1 }, - { -1, -1 } - }; - static const int logoframetimes[] = { 9, 9, 9 }; - - JobDesc jobs[3]; - int job = 0; - if (!userConfig.nologo) - { - if (!isShareware()) jobs[job++] = { PlayVideo("logo.anm", logosound, logoframetimes), []() { S_PlaySpecialMusic(MUS_INTRO); } }; - else jobs[job++] = { Create(1), []() { S_PlaySpecialMusic(MUS_INTRO); } }; - if (!isNam()) jobs[job++] = { Create(), nullptr }; - } - else S_PlaySpecialMusic(MUS_INTRO); - jobs[job++] = { Create(), []() { S_PlaySound(NITEVISION_ONOFF, CHAN_AUTO, CHANF_UI); } }; - RunScreenJob(jobs, job, completion, true, true); -} - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -class DEpisode1End1 : public DSkippableScreenJob -{ - int bonuscnt = 0; - int bossani = -1; - int breatheani = -1; - bool breathebg = false; - - static inline const int breathe[] = - { - 0, 30,VICTORY1 + 1,176,59, - 30, 60,VICTORY1 + 2,176,59, - 60, 90,VICTORY1 + 1,176,59, - 90, 120,0 ,176,59 - }; - - static inline const int bossmove[] = - { - 0, 120,VICTORY1 + 3,86,59, - 220, 260,VICTORY1 + 4,86,59, - 260, 290,VICTORY1 + 5,86,59, - 290, 320,VICTORY1 + 6,86,59, - 320, 350,VICTORY1 + 7,86,59, - 350, 380,VICTORY1 + 8,86,59, - 350, 380,VICTORY1 + 8,86,59, - }; - -public: - DEpisode1End1() : DSkippableScreenJob(fadein | fadeout) {} - - void OnTick() - { - int currentclock = ticks * 120 / GameTicRate; - - bossani = -1; - breathebg = false; - breatheani = -1; - - // boss - if (currentclock > 390 && currentclock < 780) - { - for (int t = 0; t < 35; t += 5) if (bossmove[t + 2] && (currentclock % 390) > bossmove[t] && (currentclock % 390) <= bossmove[t + 1]) - { - if (t == 10 && bonuscnt == 1) - { - S_PlaySound(SHOTGUN_FIRE, CHAN_AUTO, CHANF_UI); - S_PlaySound(SQUISHED, CHAN_AUTO, CHANF_UI); - bonuscnt++; - } - bossani = t; - } - } - - // Breathe - if (currentclock < 450 || currentclock >= 750) - { - if (currentclock >= 750) - { - breathebg = true; - if (currentclock >= 750 && bonuscnt == 2) - { - S_PlaySound(DUKETALKTOBOSS, CHAN_AUTO, CHANF_UI); - bonuscnt++; - } - } - for (int t = 0; t < 20; t += 5) - if (breathe[t + 2] && (currentclock % 120) > breathe[t] && (currentclock % 120) <= breathe[t + 1]) - { - if (t == 5 && bonuscnt == 0) - { - S_PlaySound(BOSSTALKTODUKE, CHAN_AUTO, CHANF_UI); - bonuscnt++; - } - breatheani = t; - } - } - - } - - void Draw(double) override - { - auto translation = TRANSLATION(Translation_BasePalettes, ENDINGPAL); - - twod->ClearScreen(); - DrawTexture(twod, tileGetTexture(VICTORY1, true), 0, 50, DTA_FullscreenScale, FSMode_Fit320x200, - DTA_TranslationIndex, translation, DTA_LegacyRenderStyle, STYLE_Normal, DTA_TopLeft, true, TAG_DONE); - - if (bossani != -1) - { - DrawTexture(twod, tileGetTexture(bossmove[bossani + 2], true), bossmove[bossani + 3], bossmove[bossani + 4], DTA_FullscreenScale, FSMode_Fit320x200, - DTA_TranslationIndex, translation, DTA_TopLeft, true, TAG_DONE); - } - - if (breathebg) - { - DrawTexture(twod, tileGetTexture(VICTORY1 + 8, true), 86, 59, DTA_FullscreenScale, FSMode_Fit320x200, - DTA_TranslationIndex, translation, DTA_TopLeft, true, TAG_DONE); - } - - if (breatheani != -1) - { - DrawTexture(twod, tileGetTexture(breathe[breatheani + 2], true), breathe[breatheani + 3], breathe[breatheani + 4], DTA_FullscreenScale, FSMode_Fit320x200, - DTA_TranslationIndex, translation, DTA_TopLeft, true, TAG_DONE); - } - } -}; - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -class DEpisode3End : public DImageScreen -{ - int sound = 0; - int64_t waittime = 0; - -public: - - FGameTexture* getTexture() - { - auto texid = TexMan.CheckForTexture("radlogo.anm", ETextureType::Any, FTextureManager::TEXMAN_TryAny | FTextureManager::TEXMAN_ForceLookup); - if (texid.isValid()) return TexMan.GetGameTexture(texid); - else return TexMan.GameByIndex(0); - } - -public: - DEpisode3End() : DImageScreen(getTexture(), fadein|fadeout, 0x7fffffff) - { - } - - void Skipped() override - { - FX_StopAllSounds(); - } - - void OnTick() override - { - switch (sound) - { - case 0: - S_PlaySound(ENDSEQVOL3SND5, CHAN_AUTO, CHANF_UI); - sound++; - break; - - case 1: - if (!S_CheckSoundPlaying(ENDSEQVOL3SND5)) - { - S_PlaySound(ENDSEQVOL3SND6, CHAN_AUTO, CHANF_UI); - sound++; - } - break; - - case 2: - if (!S_CheckSoundPlaying(ENDSEQVOL3SND6)) - { - S_PlaySound(ENDSEQVOL3SND7, CHAN_AUTO, CHANF_UI); - sound++; - } - break; - - case 3: - if (!S_CheckSoundPlaying(ENDSEQVOL3SND7)) - { - S_PlaySound(ENDSEQVOL3SND8, CHAN_AUTO, CHANF_UI); - sound++; - } - break; - - case 4: - if (!S_CheckSoundPlaying(ENDSEQVOL3SND8)) - { - S_PlaySound(ENDSEQVOL3SND9, CHAN_AUTO, CHANF_UI); - sound++; - } - break; - - case 5: - if (!S_CheckSoundPlaying(ENDSEQVOL3SND9)) - { - sound++; - waittime = ticks + GameTicRate * (SoundEnabled() ? 1 : 5); // if sound is off this wouldn't wait without a longer delay here. - } - break; - - case 6: - if (isPlutoPak()) - { - if (ticks > waittime) state = finished; - } - break; - - default: - break; - } - if (state != running) FX_StopAllSounds(); - } -}; - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -class DEpisode4Text : public DSkippableScreenJob -{ -public: - DEpisode4Text() : DSkippableScreenJob(fadein | fadeout) {} - - void Draw(double) override - { - twod->ClearScreen(); - BigText(160, 60, GStrings("Thanks to all our")); - BigText(160, 60 + 16, GStrings("fans for giving")); - BigText(160, 60 + 16 + 16, GStrings("us big heads.")); - BigText(160, 70 + 16 + 16 + 16, GStrings("Look for a Duke Nukem 3D")); - BigText(160, 70 + 16 + 16 + 16 + 16, GStrings("sequel soon.")); - } -}; - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -class DEpisode5End : public DImageScreen -{ - int sound = 0; - -public: - DEpisode5End() : DImageScreen(FIREFLYGROWEFFECT, fadein|fadeout) - { - } - - void OnTick() override - { - switch (sound) - { - case 0: - sound++; - break; - - case 1: - S_PlaySound(E5L7_DUKE_QUIT_YOU, CHAN_AUTO, CHANF_UI); - sound++; - break; - - default: - break; - } - } -}; - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -static void bonussequence_d(int num, JobDesc *jobs, int &job) -{ - static const AnimSound cineov2sound[] = - { - { 1, WIND_AMBIENCE+1 }, - { 26, ENDSEQVOL2SND1+1 }, - { 36, ENDSEQVOL2SND2+1 }, - { 54, THUD+1 }, - { 62, ENDSEQVOL2SND3+1 }, - { 75, ENDSEQVOL2SND4 + 1 }, - { 81, ENDSEQVOL2SND5 + 1 }, - { 115, ENDSEQVOL2SND6 + 1 }, - { 124, ENDSEQVOL2SND7 + 1 }, - { -1, -1 } - }; - - static const AnimSound cineov3sound[] = - { - { 1, WIND_REPEAT + 1 }, - { 98, DUKE_GRUNT + 1 }, - { 102, THUD + 1 }, - { 102, SQUISHED + 1 }, - { 124, ENDSEQVOL3SND3 + 1 }, - { 134, ENDSEQVOL3SND2 + 1 }, - { 158, PIPEBOMB_EXPLODE + 1 }, - { -1,-1 } - }; - - static const AnimSound dukedcsound[] = - { - { 144, ENDSEQVOL3SND3 + 1 }, - { -1,-1 } - }; - - static const AnimSound vol4e1[] = - { - { 3, DUKE_UNDERWATER+1 }, - { 35, VOL4ENDSND1+1 }, - { -1,-1 } - }; - - static const AnimSound vol4e2[] = - { - { 11, DUKE_UNDERWATER+1 }, - { 20, VOL4ENDSND1+1 }, - { 39, VOL4ENDSND2+1 }, - { 50, -1 }, - { -1,-1 } - }; - - static const AnimSound vol4e3[] = - { - { 1, BOSS4_DEADSPEECH+1 }, - { 40, VOL4ENDSND1+1 }, - { 40, DUKE_UNDERWATER+1 }, - { 50, BIGBANG+1 }, - { -1,-1 } - }; - - - static const int framespeed_10[] = { 10, 10, 10 }; - static const int framespeed_14[] = { 14, 14, 14 }; - static const int framespeed_18[] = { 18, 18, 18 }; - - switch (num) - { - case 0: - jobs[job++] = { Create(), nullptr }; - jobs[job++] = { Create(E1ENDSCREEN, DScreenJob::fadein|DScreenJob::fadeout, 0x7fffffff), []() { Mus_Stop(); } }; - break; - - case 1: - Mus_Stop(); - jobs[job++] = { PlayVideo("cineov2.anm", cineov2sound, framespeed_18), []() { S_PlaySound(PIPEBOMB_EXPLODE, CHAN_AUTO, CHANF_UI); } }; - jobs[job++] = { Create(E2ENDSCREEN, DScreenJob::fadein | DScreenJob::fadeout, 0x7fffffff), []() { FX_StopAllSounds(); } }; - break; - - case 2: - Mus_Stop(); - if (g_gameType & GAMEFLAG_DUKEDC) - { - jobs[job++] = { PlayVideo("radlogo.anm", dukedcsound, framespeed_10), nullptr }; - } - else - { - jobs[job++] = { PlayVideo("cineov3.anm", cineov3sound, framespeed_10), nullptr }; - jobs[job++] = { Create(200), []() { FX_StopAllSounds(); } }; - jobs[job++] = { Create(), []() { if (!isPlutoPak()) S_PlaySound(ENDSEQVOL3SND4, CHAN_AUTO, CHANF_UI); } }; - if (!isPlutoPak()) jobs[job++] = { Create(TexMan.GetGameTextureByName("DUKETEAM.ANM", false, FTextureManager::TEXMAN_ForceLookup), - DScreenJob::fadein | DScreenJob::fadeout, 0x7fffffff), []() { FX_StopAllSounds(); } }; - } - break; - - case 3: - Mus_Stop(); - jobs[job++] = { PlayVideo("vol4e1.anm", vol4e1, framespeed_10), nullptr }; - jobs[job++] = { PlayVideo("vol4e2.anm", vol4e2, framespeed_10), nullptr }; - jobs[job++] = { PlayVideo("vol4e3.anm", vol4e3, framespeed_10), []() { S_PlaySound(ENDSEQVOL3SND4, CHAN_AUTO, CHANF_UI); } }; - jobs[job++] = { Create(), nullptr }; - jobs[job++] = { Create(TexMan.GetGameTextureByName("DUKETEAM.ANM", false, FTextureManager::TEXMAN_ForceLookup), - DScreenJob::fadein | DScreenJob::fadeout, 0x7fffffff), []() { FX_StopAllSounds(); } }; - break; - - case 4: - Mus_Stop(); - jobs[job++] = { Create(), []() { FX_StopAllSounds(); } }; - break; - } -} - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -void showtwoscreens(const CompletionFunc& completion) -{ - JobDesc jobs[2]; - int job = 0; - - jobs[job++] = { Create(3291), nullptr }; - jobs[job++] = { Create(3290), nullptr }; - RunScreenJob(jobs, job, completion); -} - -void doorders(const CompletionFunc& completion) -{ - JobDesc jobs[4]; - int job = 0; - - for (int i = 0; i < 4; i++) - jobs[job++] = { Create(ORDERING + i), nullptr }; - RunScreenJob(jobs, job, completion); -} - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -class DDukeMultiplayerBonusScreen : public DSkippableScreenJob -{ - int playerswhenstarted; - -public: - DDukeMultiplayerBonusScreen(int pws) : DSkippableScreenJob(fadein|fadeout) - { - playerswhenstarted = pws; - } - - void Start() override - { - S_PlayBonusMusic(); - } - - void Draw(double smoothratio) override - { - char tempbuf[32]; - int currentclock = int((ticks + smoothratio) * 120 / GameTicRate); - twod->ClearScreen(); - DrawTexture(twod, tileGetTexture(MENUSCREEN), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_Color, 0xff808080, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE); - DrawTexture(twod, tileGetTexture(INGAMEDUKETHREEDEE, true), 160, 34, DTA_FullscreenScale, FSMode_Fit320x200, DTA_CenterOffsetRel, true, TAG_DONE); - if (isPlutoPak()) - DrawTexture(twod, tileGetTexture(PLUTOPAKSPRITE+2, true), 260, 36, DTA_FullscreenScale, FSMode_Fit320x200, DTA_CenterOffsetRel, true, TAG_DONE); - - GameText(160, 58 + 2, GStrings("Multiplayer Totals"), 0, 0); - GameText(160, 58 + 10, currentLevel->DisplayName(), 0, 0); - GameText(160, 165, GStrings("Presskey"), 8 - int(sin(currentclock / 10.) * 8), 0); - - int t = 0; - - MiniText(38, 80, GStrings("Name"), 0, -1, 8); - MiniText(269+20, 80, GStrings("Kills"), 0, 1, 8); - - for (int i = 0; i < playerswhenstarted; i++) - { - mysnprintf(tempbuf, 32, "%-4d", i + 1); - MiniText(92 + (i * 23), 80, tempbuf, 0, -1, 3); - } - - for (int i = 0; i < playerswhenstarted; i++) - { - int xfragtotal = 0; - mysnprintf(tempbuf, 32, "%d", i + 1); - - MiniText(30, 90 + t, tempbuf, 0); - MiniText(38, 90 + t, PlayerName(i), 0, -1, ps[i].palookup); - - for (int y = 0; y < playerswhenstarted; y++) - { - int frag = ps[i].frags[y]; - if (i == y) - { - mysnprintf(tempbuf, 32, "%-4d", ps[y].fraggedself); - MiniText(92 + (y * 23), 90 + t, tempbuf, 0, -1, 2); - xfragtotal -= ps[y].fraggedself; - } - else - { - mysnprintf(tempbuf, 32, "%-4d", frag); - MiniText(92 + (y * 23), 90 + t, tempbuf, 0); - xfragtotal += frag; - } - /* - if (myconnectindex == connecthead) - { - mysnprintf(tempbuf, 32, "stats %ld killed %ld %ld\n", i + 1, y + 1, frag); - sendscore(tempbuf); - } - */ - } - - mysnprintf(tempbuf, 32, "%-4d", xfragtotal); - MiniText(101 + (8 * 23), 90 + t, tempbuf, 0, -1, 2); - - t += 7; - } - - for (int y = 0; y < playerswhenstarted; y++) - { - int yfragtotal = 0; - for (int i = 0; i < playerswhenstarted; i++) - { - if (i == y) - yfragtotal += ps[i].fraggedself; - int frag = ps[i].frags[y]; - yfragtotal += frag; - } - mysnprintf(tempbuf, 32, "%-4d", yfragtotal); - MiniText(92 + (y * 23), 96 + (8 * 7), tempbuf, 0, -1, 2); - } - - MiniText(45, 96 + (8 * 7), GStrings("Deaths"), 0, -1, 8); - } -}; - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -class DDukeLevelSummaryScreen : public DScreenJob -{ - const char* lastmapname; - int gfx_offset; - int speech = -1; - int displaystate = 0; - int dukeAnimStart; - - enum - { - printTimeText = 1, - printTimeVal = 2, - printKillsText = 4, - printKillsVal = 8, - printSecretsText = 16, - printSecretsVal = 32, - printStatsAll = 63, - dukeAnim = 64, - dukeWait = 128, - - }; - -public: - DDukeLevelSummaryScreen() : DScreenJob(fadein | fadeout) - { - int vol = volfromlevelnum(currentLevel->levelNumber); - gfx_offset = BONUSSCREEN + ((vol == 1) ? 5 : 0); - lastmapname = currentLevel->DisplayName(); - } - - void FormatTime(int time, char* tempbuf) - { - mysnprintf(tempbuf, 32, "%02d:%02d", (time / (26 * 60)) % 60, (time / 26) % 60); - } - - bool OnEvent(event_t* ev) override - { - if (ev->type == EV_KeyDown && !specialKeyEvent(ev)) - { - if ((displaystate & printStatsAll) != printStatsAll) - { - S_PlaySound(PIPEBOMB_EXPLODE, CHAN_AUTO, CHANF_UI); - displaystate = printStatsAll; - } - else if (!(displaystate & dukeAnim)) - { - displaystate |= dukeAnim; - dukeAnimStart = ticks; - S_PlaySound(SHOTGUN_COCK, CHAN_AUTO, CHANF_UI); - static const uint16_t speeches[] = { BONUS_SPEECH1, BONUS_SPEECH2, BONUS_SPEECH3, BONUS_SPEECH4 }; - speech = speeches[(rand() & 3)]; - S_PlaySound(speech, CHAN_AUTO, CHANF_UI, 1); - } - return true; - } - return false; - } - - void Start() override - { - S_PlayBonusMusic(); - } - - void OnTick() override - { - if ((displaystate & printStatsAll) != printStatsAll) - { - if (ticks == 15 * 3) - { - displaystate |= printTimeText; - } - else if (ticks == 15 * 4) - { - displaystate |= printTimeVal; - S_PlaySound(PIPEBOMB_EXPLODE, CHAN_AUTO, CHANF_UI); - } - else if (ticks == 15 * 6) - { - displaystate |= printKillsText; - S_PlaySound(FLY_BY, CHAN_AUTO, CHANF_UI); - } - else if (ticks == 15 * 7) - { - displaystate |= printKillsVal; - S_PlaySound(PIPEBOMB_EXPLODE, CHAN_AUTO, CHANF_UI); - } - else if (ticks == 15 * 9) - { - displaystate |= printSecretsText; - } - else if (ticks == 15 * 10) - { - displaystate |= printSecretsVal; - S_PlaySound(PIPEBOMB_EXPLODE, CHAN_AUTO, CHANF_UI); - } - } - if (displaystate & dukeAnim) - { - if (ticks >= dukeAnimStart + 60) - { - displaystate ^= dukeAnim | dukeWait; - } - } - if (displaystate & dukeWait) - { - if (speech <= 0 || !S_CheckSoundPlaying(speech)) - state = finished; - } - } - - void PrintTime() - { - char tempbuf[32]; - GameText(10, 59 + 9, GStrings("TXT_YourTime"), 0); - GameText(10, 69 + 9, GStrings("TXT_ParTime"), 0); - if (!isNamWW2GI()) - GameText(10, 79 + 9, GStrings("TXT_3DRTIME"), 0); - - if (displaystate & printTimeVal) - { - FormatTime(ps[myconnectindex].player_par, tempbuf); - GameText((320 >> 2) + 71, 59 + 9, tempbuf, 0); - - FormatTime(currentLevel->parTime, tempbuf); - GameText((320 >> 2) + 71, 69 + 9, tempbuf, 0); - - if (!isNamWW2GI()) - { - FormatTime(currentLevel->designerTime, tempbuf); - GameText((320 >> 2) + 71, 79 + 9, tempbuf, 0); - } - } - } - - void PrintKills() - { - char tempbuf[32]; - GameText(10, 94 + 9, GStrings("TXT_EnemiesKilled"), 0); - GameText(10, 104 + 9, GStrings("TXT_EnemiesLeft"), 0); - - if (displaystate & printKillsVal) - { - mysnprintf(tempbuf, 32, "%-3d", ps[myconnectindex].actors_killed); - GameText((320 >> 2) + 70, 94 + 9, tempbuf, 0); - - if (ud.player_skill > 3) - { - mysnprintf(tempbuf, 32, "%s", GStrings("TXT_N_A")); - } - else - { - if ((ps[myconnectindex].max_actors_killed - ps[myconnectindex].actors_killed) < 0) - mysnprintf(tempbuf, 32, "%-3d", 0); - else mysnprintf(tempbuf, 32, "%-3d", ps[myconnectindex].max_actors_killed - ps[myconnectindex].actors_killed); - - } - GameText((320 >> 2) + 70, 104 + 9, tempbuf, 0); - } - } - - void PrintSecrets() - { - char tempbuf[32]; - GameText(10, 119 + 9, GStrings("TXT_SECFND"), 0); - GameText(10, 129 + 9, GStrings("TXT_SECMISS"), 0); - - if (displaystate & printSecretsVal) - { - mysnprintf(tempbuf, 32, "%-3d", ps[myconnectindex].secret_rooms); - GameText((320 >> 2) + 70, 119 + 9, tempbuf, 0); - if (ps[myconnectindex].secret_rooms > 0) - sprintf(tempbuf, "%-3d", (100 * ps[myconnectindex].secret_rooms / ps[myconnectindex].max_secret_rooms)); - mysnprintf(tempbuf, 32, "%-3d", ps[myconnectindex].max_secret_rooms - ps[myconnectindex].secret_rooms); - GameText((320 >> 2) + 70, 129 + 9, tempbuf, 0); - } - } - - void Draw(double) override - { - twod->ClearScreen(); - DrawTexture(twod, tileGetTexture(gfx_offset, true), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE); - - GameText(160, 190, GStrings("PRESSKEY"), 8 - int(sin(ticks * 12 / GameTicRate) * 8), 0); - - if (displaystate & printTimeText) - { - PrintTime(); - } - if (displaystate & printKillsText) - { - PrintKills(); - } - if (displaystate & printSecretsText) - { - PrintSecrets(); - } - - if (displaystate & dukeAnim) - { - switch (((ticks - dukeAnimStart) >> 2) % 15) - { - case 0: - case 1: - case 4: - case 5: - DrawTexture(twod, tileGetTexture(gfx_offset + 3), 199, 31, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TopLeft, true, TAG_DONE); - break; - case 2: - case 3: - DrawTexture(twod, tileGetTexture(gfx_offset + 4), 199, 31, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TopLeft, true, TAG_DONE); - break; - } - } - else if (!(displaystate & dukeWait)) - { - switch((ticks >> 3) & 3) - { - case 1: - case 3: - DrawTexture(twod, tileGetTexture(gfx_offset + 1), 199, 31, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TopLeft, true, TAG_DONE); - break; - case 2: - DrawTexture(twod, tileGetTexture(gfx_offset + 2), 199, 31, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TopLeft, true, TAG_DONE); - break; - } - } - - if (lastmapname) BigText(160, 20 - 6, lastmapname); - BigText(160, 36 - 6, GStrings("Completed")); - } - -}; - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -void dobonus_d(int bonusonly, const CompletionFunc& completion) -{ - JobDesc jobs[20]; - int job = 0; - - FX_StopAllSounds(); - - if (bonusonly < 0 && numplayers < 2 && ud.from_bonus == 0) - { - bonussequence_d(volfromlevelnum(currentLevel->levelNumber), jobs, job); - } - else - Mus_Stop(); - - if (playerswhenstarted > 1 && ud.coop != 1) - { - jobs[job++] = { Create(playerswhenstarted) }; - } - else if (bonusonly <= 0 && ud.multimode <= 1) - { - jobs[job++] = { Create() }; - } - if (job) - { - RunScreenJob(jobs, job, completion); - } - else if (completion) completion(false); -} - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -void e4intro(const CompletionFunc& completion) -{ - JobDesc jobs[5]; - int job = 0; - - static const AnimSound vol42a[] = - { - { 1, INTRO4_B + 1 }, - { 12, SHORT_CIRCUIT + 1 }, - { 18, INTRO4_5 + 1 }, - { 34, SHORT_CIRCUIT + 1 }, - { -1,-1 } - }; - - static const AnimSound vol41a[] = - { - { 1, INTRO4_1 + 1 }, - { 7, INTRO4_3 + 1 }, - { 12, INTRO4_2 + 1 }, - { 26, INTRO4_4 + 1 }, - { -1,-1 } - }; - - static const AnimSound vol43a[] = - { - { 10, INTRO4_6 + 1 }, - { -1,-1 } - }; - - static const int framespeed_10[] = { 10, 10, 10 }; - static const int framespeed_14[] = { 14, 14, 14 }; - - S_PlaySpecialMusic(MUS_BRIEFING); - jobs[job++] = { PlayVideo("vol41a.anm", vol41a, framespeed_10), nullptr }; - jobs[job++] = { PlayVideo("vol42a.anm", vol42a, framespeed_14), nullptr, true }; - jobs[job++] = { PlayVideo("vol43a.anm", vol43a, framespeed_10), nullptr, true }; - RunScreenJob(jobs, job, completion); -} - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -class DDukeLoadScreen : public DScreenJob -{ - MapRecord* rec; - -public: - DDukeLoadScreen(MapRecord *maprec) : DScreenJob(0), rec(maprec) {} - - void Draw(double) override - { - twod->ClearScreen(); - DrawTexture(twod, tileGetTexture(LOADSCREEN), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE); - - BigText(160, 90, (rec->flags & MI_USERMAP)? GStrings("TXT_LOADUM") : GStrings("TXT_LOADING")); - BigText(160, 114, rec->DisplayName()); - } -}; - -void loadscreen_d(MapRecord *rec, CompletionFunc func) -{ - JobDesc job = { Create(rec) }; - RunScreenJob(&job, 1, func); -} - -void PrintPaused_d() -{ - BigText(160, 100, GStrings("Game Paused")); -} - - END_DUKE_NS diff --git a/source/games/duke/src/2d_r.cpp b/source/games/duke/src/2d_r.cpp index c59c28bb6..3b84c900b 100644 --- a/source/games/duke/src/2d_r.cpp +++ b/source/games/duke/src/2d_r.cpp @@ -39,6 +39,7 @@ Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms #include "texturemanager.h" #include "c_dispatch.h" #include "gamestate.h" +#include "gamefuncs.h" BEGIN_DUKE_NS @@ -123,549 +124,5 @@ void InitFonts_r() } -//========================================================================== -// -// wrappers around DrawText to allow easier reuse of the old code. -// The vertical displacements are to have the same positioning as with the original code. -// -//========================================================================== - -static void BigText(double x, double y, const char* text, int align, double alpha = 1.) -{ - //x *= 2.2; y *= 2.64; - if (align != -1) - x -= BigFont->StringWidth(text) * (align == 0 ? 0.2 : 0.4); - auto width = BigFont->StringWidth(text); - DrawText(twod, BigFont, CR_UNTRANSLATED, x, y - 12, text, DTA_FullscreenScale, FSMode_Fit320x200, DTA_ScaleX, 0.4, DTA_ScaleY, 0.4, DTA_Alpha, alpha, TAG_DONE); -} - -static void GameText(double x, double y, const char* t, int shade, int align = -1, int trans = 0) -{ - x *= 2; y *= 2; - if (align != -1) - x -= SmallFont->StringWidth(t) * (align == 0 ? 0.5 : 1); - DrawText(twod, SmallFont, CR_UNDEFINED, x, y + 2, t, DTA_FullscreenScale, FSMode_Fit640x400, DTA_TranslationIndex, TRANSLATION(Translation_Remap, trans), DTA_Color, shadeToLight(shade), TAG_DONE); -} - -static void MiniText(double x, double y, const char* t, int shade, int align = -1, int trans = 0) -{ - x *= 2; y *= 2; - if (align != -1) - x -= SmallFont2->StringWidth(t) * (align == 0 ? 0.5 : 1); - DrawText(twod, SmallFont2, CR_UNDEFINED, x, y, t, DTA_FullscreenScale, FSMode_Fit640x400, DTA_TranslationIndex, TRANSLATION(Translation_Remap, trans), DTA_Color, shadeToLight(shade), TAG_DONE); -} - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -void Logo_r(const CompletionFunc& completion) -{ - Mus_Stop(); - FX_StopAllSounds(); // JBF 20031228 - - static const AnimSound introsound[] = - { - { 1, 29+1 }, - { -1, -1 } - }; - - static const AnimSound rednecksound[] = - { - { 1, 478+1 }, - { -1, -1 } - }; - - static const AnimSound xatrixsound[] = - { - { 1, 479+1 }, - { -1, -1 } - }; - - static const int framespeed[] = { 9, 9, 9 }; // same for all 3 anims - - JobDesc jobs[3]; - int job = 0; - - if (userConfig.nologo) - { - completion(false); - return; - } - else if (!isRRRA()) - { - jobs[job++] = { PlayVideo("rr_intro.anm", introsound, framespeed), nullptr }; - jobs[job++] = { PlayVideo("redneck.anm", rednecksound, framespeed), nullptr }; - jobs[job++] = { PlayVideo("xatlogo.anm", xatrixsound, framespeed), nullptr }; - } - else - { - jobs[job++] = { PlayVideo("redint.mve"), nullptr }; - } - RunScreenJob(jobs, job, completion, true, true); -} - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -static void bonussequence_r(int num, JobDesc* jobs, int& job) -{ - static const AnimSound turdmov[] = - { - { 1, 82 + 1 }, - { -1, -1 } - }; - - static const AnimSound rr_outro[] = - { - { 1, 35 + 1 }, - { -1, -1 } - }; - - static const int framespeed[] = { 9, 9, 9 }; // same for all 3 anims - - Mus_Stop(); - FX_StopAllSounds(); - - switch (num) - { - case 0: - jobs[job++] = { PlayVideo("turdmov.anm", turdmov, framespeed), nullptr }; - jobs[job++] = { Create(TENSCREEN), nullptr }; - break; - - case 1: - jobs[job++] = { PlayVideo("rr_outro.anm", rr_outro, framespeed), nullptr }; - jobs[job++] = { Create(TENSCREEN), nullptr }; - break; - - default: - break; - } -} - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -class DRRMultiplayerBonusScreen : public DScreenJob -{ - int playerswhenstarted; - -public: - DRRMultiplayerBonusScreen(int pws) : DScreenJob(fadein | fadeout) - { - playerswhenstarted = pws; - } - - void Start() override - { - S_PlayBonusMusic(); - } - - - void Draw(double) override - { - char tempbuf[32]; - twod->ClearScreen(); - DrawTexture(twod, tileGetTexture(MENUSCREEN), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_Color, 0xff808080, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE); - double scale = 0.36; - DrawTexture(twod, tileGetTexture(INGAMEDUKETHREEDEE, true), 160, 34, DTA_FullscreenScale, FSMode_Fit320x200, - DTA_CenterOffsetRel, true, DTA_ScaleX, scale, DTA_ScaleY, 0.36, TAG_DONE); - - GameText(160, 58, GStrings("Multiplayer Totals"), 0, 0); - GameText(160, 58 + 10, currentLevel->DisplayName(), 0, 0); - GameText(160, 165, GStrings("Presskey"), 0, 0); - - int t = 0; - - MiniText(38, 80, GStrings("Name"), 0); - MiniText(269 + 20, 80, GStrings("Kills"), 0, 1); - - for (int i = 0; i < playerswhenstarted; i++) - { - mysnprintf(tempbuf, 32, "%-4d", i + 1); - MiniText(92 + (i * 23), 80, tempbuf, 0); - } - - for (int i = 0; i < playerswhenstarted; i++) - { - int xfragtotal = 0; - mysnprintf(tempbuf, 32, "%d", i + 1); - - MiniText(30, 90 + t, tempbuf, 0); - MiniText(38, 90 + t, PlayerName(i), 0, -1, ps[i].palookup); - - for (int y = 0; y < playerswhenstarted; y++) - { - int frag = ps[i].frags[y]; - if (i == y) - { - mysnprintf(tempbuf, 32, "%-4d", ps[y].fraggedself); - MiniText(92 + (y * 23), 90 + t, tempbuf, 0); - xfragtotal -= ps[y].fraggedself; - } - else - { - mysnprintf(tempbuf, 32, "%-4d", frag); - MiniText(92 + (y * 23), 90 + t, tempbuf, 0); - xfragtotal += frag; - } - /* - if (myconnectindex == connecthead) - { - mysnprintf(tempbuf, 32, "stats %ld killed %ld %ld\n", i + 1, y + 1, frag); - sendscore(tempbuf); - } - */ - } - - mysnprintf(tempbuf, 32, "%-4d", xfragtotal); - MiniText(101 + (8 * 23), 90 + t, tempbuf, 0); - - t += 7; - } - - for (int y = 0; y < playerswhenstarted; y++) - { - int yfragtotal = 0; - for (int i = 0; i < playerswhenstarted; i++) - { - if (i == y) - yfragtotal += ps[i].fraggedself; - int frag = ps[i].frags[y]; - yfragtotal += frag; - } - mysnprintf(tempbuf, 32, "%-4d", yfragtotal); - MiniText(92 + (y * 23), 96 + (8 * 7), tempbuf, 0); - } - - MiniText(45, 96 + (8 * 7), GStrings("Deaths"), 0); - } -}; - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -class DRRLevelSummaryScreen : public DScreenJob -{ - const char* lastmapname; - int gfx_offset; - int displaystate = 0; - int speech = -1; - int exitSoundStart; - - enum - { - printTimeText = 1, - printTimeVal = 2, - printKillsText = 4, - printKillsVal = 8, - printSecretsText = 16, - printSecretsVal = 32, - printStatsAll = 63, - exitSound = 64, - exitWait = 128, - - }; - -public: - DRRLevelSummaryScreen(bool dofadeout = true) : DScreenJob(dofadeout? (fadein | fadeout) : fadein) - { - if (currentLevel->flags & MI_USERMAP) - gfx_offset = BONUSPIC01; - else if (!isRRRA()) - gfx_offset = BONUSPIC01 + clamp((currentLevel->levelNumber / 1000) * 7 + (currentLevel->levelNumber % 1000), 0, 13); - else - gfx_offset = LEVELMAP01 + clamp((currentLevel->levelNumber / 1000) * 7 + (currentLevel->levelNumber % 1000), 0, 13); - - - lastmapname = currentLevel->DisplayName(); - } - - void FormatTime(int time, char* tempbuf) - { - mysnprintf(tempbuf, 32, "%02d:%02d", (time / (26 * 60)) % 60, (time / 26) % 60); - } - - bool OnEvent(event_t* ev) override - { - if (ev->type == EV_KeyDown && !specialKeyEvent(ev)) - { - if ((displaystate & printStatsAll) != printStatsAll) - { - S_PlaySound(404, CHAN_AUTO, CHANF_UI); - displaystate = printStatsAll; - } - else if (!(displaystate & exitSound)) - { - displaystate |= exitSound; - exitSoundStart = ticks; - S_PlaySound(425, CHAN_AUTO, CHANF_UI); - speech = BONUS_SPEECH1 + (rand() & 3); - S_PlaySound(speech, CHAN_AUTO, CHANF_UI); - } - return true; - } - return false; - } - - void Start() override - { - S_PlayBonusMusic(); - } - - void OnTick() override - { - if ((displaystate & printStatsAll) != printStatsAll) - { - if (ticks == 15 * 3) - { - displaystate |= printTimeText; - } - else if (ticks == 15 * 4) - { - displaystate |= printTimeVal; - S_PlaySound(404, CHAN_AUTO, CHANF_UI); - } - else if (ticks == 15 * 6) - { - displaystate |= printKillsText; - } - else if (ticks == 15 * 7) - { - displaystate |= printKillsVal; - S_PlaySound(404, CHAN_AUTO, CHANF_UI); - } - else if (ticks == 15 * 9) - { - displaystate |= printSecretsText; - } - else if (ticks == 15 * 10) - { - displaystate |= printSecretsVal; - S_PlaySound(404, CHAN_AUTO, CHANF_UI); - } - } - if (displaystate & exitSound) - { - if (ticks >= exitSoundStart + 60) - { - displaystate ^= exitSound | exitWait; - } - } - if (displaystate & exitWait) - { - if (speech <= 0 || !S_CheckSoundPlaying(speech)) - state = finished; - } - } - - void PrintTime() - { - char tempbuf[32]; - BigText(30, 48, GStrings("TXT_YerTime"), -1); - BigText(30, 64, GStrings("TXT_ParTime"), -1); - BigText(30, 80, GStrings("TXT_XTRTIME"), -1); - - if (displaystate & printTimeVal) - { - FormatTime(ps[myconnectindex].player_par, tempbuf); - BigText(191, 48, tempbuf, -1); - - FormatTime(currentLevel->parTime, tempbuf); - BigText(191, 64, tempbuf, -1); - - FormatTime(currentLevel->designerTime, tempbuf); - BigText(191, 80, tempbuf, -1); - } - } - - void PrintKills() - { - char tempbuf[32]; - BigText(30, 112, GStrings("TXT_VarmintsKilled"), -1); - BigText(30, 128, GStrings("TXT_VarmintsLeft"), -1); - - if (displaystate & printKillsVal) - { - mysnprintf(tempbuf, 32, "%-3d", ps[myconnectindex].actors_killed); - BigText(231, 112, tempbuf, -1); - if (ud.player_skill > 3) - { - mysnprintf(tempbuf, 32, "%s", GStrings("TXT_N_A")); - BigText(231, 128, tempbuf, -1); - } - else - { - if ((ps[myconnectindex].max_actors_killed - ps[myconnectindex].actors_killed) < 0) - mysnprintf(tempbuf, 32, "%-3d", 0); - else mysnprintf(tempbuf, 32, "%-3d", ps[myconnectindex].max_actors_killed - ps[myconnectindex].actors_killed); - BigText(231, 128, tempbuf, -1); - } - } - } - - void PrintSecrets() - { - char tempbuf[32]; - BigText(30, 144, GStrings("TXT_SECFND"), -1); - BigText(30, 160, GStrings("TXT_SECMISS"), -1); - - if (displaystate & printSecretsVal) - { - mysnprintf(tempbuf, 32, "%-3d", ps[myconnectindex].secret_rooms); - BigText(231, 144, tempbuf, -1); - if (ps[myconnectindex].secret_rooms > 0) - sprintf(tempbuf, "%-3d", (100 * ps[myconnectindex].secret_rooms / ps[myconnectindex].max_secret_rooms)); - mysnprintf(tempbuf, 32, "%-3d", ps[myconnectindex].max_secret_rooms - ps[myconnectindex].secret_rooms); - BigText(231, 160, tempbuf, -1); - } - } - - void Draw(double) override - { - twod->ClearScreen(); - DrawTexture(twod, tileGetTexture(gfx_offset, true), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE); - - if (lastmapname) BigText(80, 16, lastmapname, -1); - BigText(15, 192, GStrings("PRESSKEY"), -1); - - if (displaystate & printTimeText) - { - PrintTime(); - } - if (displaystate & printKillsText) - { - PrintKills(); - } - if (displaystate & printSecretsText) - { - PrintSecrets(); - } - } - -}; - - -class DRRRAEndOfGame : public DSkippableScreenJob -{ -public: - DRRRAEndOfGame() : DSkippableScreenJob(fadein|fadeout) - { - } - - void Skipped() override - { - S_StopSound(35); - } - - void Start() override - { - S_PlaySound(35, CHAN_AUTO, CHANF_UI); - } - - void OnTick() override - { - if (!S_CheckSoundPlaying(-1, 35) && ticks > 15 * GameTicRate) state = finished; // make sure it stays, even if sound is off. - } - void Draw(double) override - { - auto tex = tileGetTexture(ENDGAME + ((ticks >> 2) & 1)); - DrawTexture(twod, tex, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE); - } -}; - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -void dobonus_r(int bonusonly, const CompletionFunc& completion) -{ - JobDesc jobs[20]; - int job = 0; - - FX_StopAllSounds(); - Mus_Stop(); - - if (bonusonly < 0 && !isRRRA() && numplayers < 2 && ud.from_bonus == 0) - { - int vol = volfromlevelnum(currentLevel->levelNumber); - bonussequence_r(vol, jobs, job); - } - - if (playerswhenstarted > 1 && ud.coop != 1) - { - jobs[job++] = { Create(playerswhenstarted) }; - } - else if (bonusonly <= 0 && ud.multimode <= 1) - { - if (isRRRA() && !(currentLevel->flags & MI_USERMAP) && currentLevel->levelNumber < 106) // fixme: The logic here is awful. Shift more control to the map records. - { - jobs[job++] = { Create(true) }; - int levnum = clamp((currentLevel->levelNumber / 100) * 7 + (currentLevel->levelNumber % 100), 0, 13); - char fn[20]; - mysnprintf(fn, 20, "lvl%d.anm", levnum + 1); - static const int framespeed[] = { 20, 20, 7200 }; // wait for one minute on the final frame so that the video doesn't stop before the user notices. - jobs[job++] = { PlayVideo(fn, nullptr, framespeed) }; - if (bonusonly < 0 && currentLevel->levelNumber > 100) - { - jobs[job++] = { Create() }; - } - } - else jobs[job++] = { Create(false) }; - } - if (job) - RunScreenJob(jobs, job, completion); - else if (completion) completion(false); -} - - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -class DRRLoadScreen : public DScreenJob -{ - MapRecord* rec; - -public: - DRRLoadScreen(MapRecord* maprec) : DScreenJob(0), rec(maprec) {} - - void Draw(double) override - { - DrawTexture(twod, tileGetTexture(LOADSCREEN), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE); - - int y = isRRRA()? 140 : 90; - BigText(160, y, (rec->flags & MI_USERMAP) ? GStrings("TXT_ENTRUM") : GStrings("TXT_ENTERIN"), 0); - BigText(160, y+24, rec->DisplayName(), 0); - } -}; - -void loadscreen_r(MapRecord* rec, CompletionFunc func) -{ - JobDesc job = { Create(rec) }; - RunScreenJob(&job, 1, func); -} - -void PrintPaused_r() -{ - BigText(160, 100, GStrings("Game Paused"), 0); -} - END_DUKE_NS diff --git a/source/games/duke/src/_polymost.cpp b/source/games/duke/src/_polymost.cpp new file mode 100644 index 000000000..bb3d544ad --- /dev/null +++ b/source/games/duke/src/_polymost.cpp @@ -0,0 +1,279 @@ +BEGIN_DUKE_NS + +extern int tempsectorz[MAXSECTORS]; +extern int tempsectorpicnum[MAXSECTORS]; + + +void SE40_Draw(int tag, spritetype *spr, int x, int y, int z, binangle a, fixedhoriz h, int smoothratio) +{ + int i, j = 0, k = 0; + int ok = 0, fofmode = 0; + int offx, offy; + spritetype* floor1, *floor2 = nullptr; + + if (spr->ang != 512) return; + + i = FOF; //Effect TILE + tileDelete(FOF); + if (!(gotpic[i >> 3] & (1 << (i & 7)))) return; + gotpic[i >> 3] &= ~(1 << (i & 7)); + + floor1 = spr; + + if (spr->lotag == tag + 2) fofmode = tag + 0; + if (spr->lotag == tag + 3) fofmode = tag + 1; + if (spr->lotag == tag + 4) fofmode = tag + 0; + if (spr->lotag == tag + 5) fofmode = tag + 1; + + ok++; + + DukeStatIterator it(STAT_RAROR); + while (auto act = it.Next()) + { + auto spr = act->s; + if ( + spr->picnum == SECTOREFFECTOR && + spr->lotag == fofmode && + spr->hitag == floor1->hitag + ) + { + floor1 = spr; + fofmode = spr->lotag; + ok++; + break; + } + } + // if(ok==1) { Message("no floor1",RED); return; } + + if (fofmode == tag + 0) k = tag + 1; else k = tag + 0; + + it.Reset(STAT_RAROR); + while (auto act = it.Next()) + { + auto spr = act->s; + if ( + spr->picnum == SECTOREFFECTOR && + spr->lotag == k && + spr->hitag == floor1->hitag + ) + { + floor2 = spr; + ok++; + break; + } + } + + // if(ok==2) { Message("no floor2",RED); return; } + + it.Reset(STAT_RAROR); + while (auto act = it.Next()) + { + auto spr = act->s; + if (spr->picnum == SECTOREFFECTOR && + spr->lotag == k + 2 && + spr->hitag == floor1->hitag + ) + { + if (k == tag + 0) + { + tempsectorz[spr->sectnum] = sector[spr->sectnum].floorz; + sector[spr->sectnum].floorz += (((z - sector[spr->sectnum].floorz) / 32768) + 1) * 32768; + tempsectorpicnum[spr->sectnum] = sector[spr->sectnum].floorpicnum; + sector[spr->sectnum].floorpicnum = 13; + } + if (k == tag + 1) + { + tempsectorz[spr->sectnum] = sector[spr->sectnum].ceilingz; + sector[spr->sectnum].ceilingz += (((z - sector[spr->sectnum].ceilingz) / 32768) - 1) * 32768; + tempsectorpicnum[spr->sectnum] = sector[spr->sectnum].ceilingpicnum; + sector[spr->sectnum].ceilingpicnum = 13; + } + } + } + + offx = x - floor1->x; + offy = y - floor1->y; + + renderDrawRoomsQ16(floor2->x + offx, floor2->y + offy, z, a.asq16(), h.asq16(), floor2->sectnum); + fi.animatesprites(pm_tsprite, pm_spritesortcnt, offx + floor2->x, offy + floor2->y, a.asbuild(), smoothratio); + renderDrawMasks(); + + it.Reset(STAT_RAROR); + while (auto act = it.Next()) + { + auto spr = act->s; + if (spr->picnum == 1 && + spr->lotag == k + 2 && + spr->hitag == floor1->hitag + ) + { + if (k == tag + 0) + { + sector[spr->sectnum].floorz = tempsectorz[spr->sectnum]; + sector[spr->sectnum].floorpicnum = tempsectorpicnum[spr->sectnum]; + } + if (k == tag + 1) + { + sector[spr->sectnum].ceilingz = tempsectorz[spr->sectnum]; + sector[spr->sectnum].ceilingpicnum = tempsectorpicnum[spr->sectnum]; + } + }// end if + }// end for + +} // end SE40 + + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +void se40code(int x, int y, int z, binangle a, fixedhoriz h, int smoothratio) +{ + int tag; + if (!isRR()) tag = 40; + else if (isRRRA()) tag = 150; + else return; + + DukeStatIterator it(STAT_RAROR); + while (auto act = it.Next()) + { + switch (act->s->lotag - tag + 40) + { + // case 40: + // case 41: + // SE40_Draw(i,x,y,a,smoothratio); + // break; + case 42: + case 43: + case 44: + case 45: + if (ps[screenpeek].cursectnum == act->s->sectnum) + SE40_Draw(tag, act->s, x, y, z, a, h, smoothratio); + break; + } + } +} + + +//--------------------------------------------------------------------------- +// +// split out so it can also be applied to camera views +// +//--------------------------------------------------------------------------- + +void renderMirror(int cposx, int cposy, int cposz, binangle cang, fixedhoriz choriz, int smoothratio) +{ + if ((gotpic[TILE_MIRROR >> 3] & (1 << (TILE_MIRROR & 7))) > 0) + { + int dst = 0x7fffffff, i = 0; + for (int k = 0; k < mirrorcnt; k++) + { + int j = abs(wall[mirrorwall[k]].x - cposx) + abs(wall[mirrorwall[k]].y - cposy); + if (j < dst) dst = j, i = k; + } + + if (wall[mirrorwall[i]].overpicnum == TILE_MIRROR) + { + int tposx, tposy; + fixed_t tang; + + renderPrepareMirror(cposx, cposy, cposz, cang.asq16(), choriz.asq16(), mirrorwall[i], &tposx, &tposy, &tang); + + int j = g_visibility; + g_visibility = (j >> 1) + (j >> 2); + + renderDrawRoomsQ16(tposx, tposy, cposz, tang, choriz.asq16(), mirrorsector[i] + MAXSECTORS); + + display_mirror = 1; + fi.animatesprites(pm_tsprite, pm_spritesortcnt, tposx, tposy, tang, smoothratio); + display_mirror = 0; + + renderDrawMasks(); + renderCompleteMirror(); //Reverse screen x-wise in this function + g_visibility = j; + } + gotpic[TILE_MIRROR >> 3] &= ~(1 << (TILE_MIRROR & 7)); + } +} + +//--------------------------------------------------------------------------- +// +// used by RR to inject some external geometry into a scene. +// +//--------------------------------------------------------------------------- + +static void geometryEffect(int cposx, int cposy, int cposz, binangle cang, fixedhoriz choriz, int sect, int smoothratio) +{ + short gs, tgsect, geosect, geoid = 0; + renderDrawRoomsQ16(cposx, cposy, cposz, cang.asq16(), choriz.asq16(), sect); + fi.animatesprites(pm_tsprite, pm_spritesortcnt, cposx, cposy, cang.asbuild(), smoothratio); + renderDrawMasks(); + for (gs = 0; gs < geocnt; gs++) + { + tgsect = geosector[gs]; + + DukeSectIterator it(tgsect); + while (auto act = it.Next()) + { + changespritesect(act, geosectorwarp[gs]); + setsprite(act, act->s->x -= geox[gs], act->s->y -= geoy[gs], act->s->z); + } + if (geosector[gs] == sect) + { + geosect = geosectorwarp[gs]; + geoid = gs; + } + } + cposx -= geox[geoid]; + cposy -= geoy[geoid]; + renderDrawRoomsQ16(cposx, cposy, cposz, cang.asq16(), choriz.asq16(), sect); + cposx += geox[geoid]; + cposy += geoy[geoid]; + for (gs = 0; gs < geocnt; gs++) + { + tgsect = geosectorwarp[gs]; + DukeSectIterator it(tgsect); + while (auto act = it.Next()) + { + changespritesect(act, geosector[gs]); + setsprite(act, act->s->x += geox[gs], act->s->y += geoy[gs], act->s->z); + } + } + fi.animatesprites(pm_tsprite, pm_spritesortcnt, cposx, cposy, cang.asbuild(), smoothratio); + renderDrawMasks(); + for (gs = 0; gs < geocnt; gs++) + { + tgsect = geosector[gs]; + DukeSectIterator it(tgsect); + while (auto act = it.Next()) + { + changespritesect(act, geosectorwarp2[gs]); + setsprite(act, act->s->x -= geox2[gs], act->s->y -= geoy2[gs], act->s->z); + } + if (geosector[gs] == sect) + { + geosect = geosectorwarp2[gs]; + geoid = gs; + } + } + cposx -= geox2[geoid]; + cposy -= geoy2[geoid]; + renderDrawRoomsQ16(cposx, cposy, cposz, cang.asq16(), choriz.asq16(), sect); + cposx += geox2[geoid]; + cposy += geoy2[geoid]; + for (gs = 0; gs < geocnt; gs++) + { + tgsect = geosectorwarp2[gs]; + DukeSectIterator it(tgsect); + while (auto act = it.Next()) + { + changespritesect(act, geosector[gs]); + setsprite(act, act->s->x += geox2[gs], act->s->y += geoy2[gs], act->s->z); + } + } + fi.animatesprites(pm_tsprite, pm_spritesortcnt, cposx, cposy, cang.asbuild(), smoothratio); + renderDrawMasks(); +} +END_DUKE_NS diff --git a/source/games/duke/src/actors.cpp b/source/games/duke/src/actors.cpp index d24915c25..89ba3c3fa 100644 --- a/source/games/duke/src/actors.cpp +++ b/source/games/duke/src/actors.cpp @@ -464,7 +464,7 @@ void moveplayers(void) if (p->actorsqu != nullptr) { - p->angle.addadjustment(getincanglebam(p->angle.ang, bvectangbam(p->actorsqu->s->x - p->posx, p->actorsqu->s->y - p->posy)) >> 2); + p->angle.addadjustment(getincanglebam(p->angle.ang, bvectangbam(p->actorsqu->s->x - p->posx, p->actorsqu->s->y - p->posy)).signedbuild() >> 2); } if (spri->extra > 0) @@ -487,7 +487,7 @@ void moveplayers(void) if (p->wackedbyactor != nullptr && p->wackedbyactor->s->statnum < MAXSTATUS) { - p->angle.addadjustment(getincanglebam(p->angle.ang, bvectangbam(p->wackedbyactor->s->x - p->posx, p->wackedbyactor->s->y - p->posy)) >> 1); + p->angle.addadjustment(getincanglebam(p->angle.ang, bvectangbam(p->wackedbyactor->s->x - p->posx, p->wackedbyactor->s->y - p->posy)).signedbuild() >> 1); } } spri->ang = p->angle.ang.asbuild(); diff --git a/source/games/duke/src/actors_d.cpp b/source/games/duke/src/actors_d.cpp index 10ad5dee0..0962311d8 100644 --- a/source/games/duke/src/actors_d.cpp +++ b/source/games/duke/src/actors_d.cpp @@ -3697,7 +3697,7 @@ void moveeffectors_d(void) //STATNUM 3 { static int16_t list1[] = { BLOODPOOL, PUKE, FOOTPRINTS, FOOTPRINTS2, FOOTPRINTS3, FOOTPRINTS4, BULLETHOLE, BLOODSPLAT1, BLOODSPLAT2, BLOODSPLAT3, BLOODSPLAT4, -1 }; static int16_t list2[] = { BOLT1, BOLT1 + 1,BOLT1 + 2, BOLT1 + 3, SIDEBOLT1, SIDEBOLT1 + 1, SIDEBOLT1 + 2, SIDEBOLT1 + 3, -1 }; - handle_se24(act, list1, list2, false, TRIPBOMB, LASERLINE, CRANE, 2); + handle_se24(act, list1, list2, true, TRIPBOMB, LASERLINE, CRANE, 2); break; } case SE_35: diff --git a/source/games/duke/src/actors_r.cpp b/source/games/duke/src/actors_r.cpp index 8403bb23c..76fcc7170 100644 --- a/source/games/duke/src/actors_r.cpp +++ b/source/games/duke/src/actors_r.cpp @@ -29,7 +29,6 @@ Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms #include "ns.h" #include "global.h" #include "names_r.h" -#include "mmulti.h" #include "mapinfo.h" #include "dukeactor.h" diff --git a/source/games/duke/src/animatesprites_d.cpp b/source/games/duke/src/animatesprites_d.cpp index cdb2a047f..e014bc8be 100644 --- a/source/games/duke/src/animatesprites_d.cpp +++ b/source/games/duke/src/animatesprites_d.cpp @@ -43,7 +43,7 @@ EXTERN_CVAR(Bool, wt_commentary) BEGIN_DUKE_NS -void animatesprites_d(int x, int y, int a, int smoothratio) +void animatesprites_d(spritetype* tsprite, int& spritesortcnt, int x, int y, int a, int smoothratio) { int i, j, k, p; short sect; @@ -177,8 +177,8 @@ void animatesprites_d(int x, int y, int a, int smoothratio) { t->x -= MulScale(MaxSmoothRatio - smoothratio, ps[s->yvel].posx - ps[s->yvel].oposx, 16); t->y -= MulScale(MaxSmoothRatio - smoothratio, ps[s->yvel].posy - ps[s->yvel].oposy, 16); - t->z = ps[s->yvel].oposz + MulScale(smoothratio, ps[s->yvel].posz - ps[s->yvel].oposz, 16); - t->z += (40 << 8); + t->z = interpolatedvalue(ps[s->yvel].oposz, ps[s->yvel].posz, smoothratio); + t->z += PHEIGHT_DUKE; } else if (s->picnum != CRANEPOLE) { @@ -328,10 +328,10 @@ void animatesprites_d(int x, int y, int a, int smoothratio) t->cstat |= 2; if (screenpeek == myconnectindex && numplayers >= 2) { - t->x = omyx + MulScale((int)(myx - omyx), smoothratio, 16); - t->y = omyy + MulScale((int)(myy - omyy), smoothratio, 16); - t->z = omyz + MulScale((int)(myz - omyz), smoothratio, 16) + (40 << 8); - t->ang = myang.asbuild() + MulScale((((myang.asbuild() + 1024 - myang.asbuild()) & 2047) - 1024), smoothratio, 16); + t->x = interpolatedvalue(omyx, myx, smoothratio); + t->y = interpolatedvalue(omyy, myy, smoothratio); + t->z = interpolatedvalue(omyz, myz, smoothratio) + PHEIGHT_DUKE; + t->ang = interpolatedangle(omyang, myang, smoothratio).asbuild(); t->sectnum = mycursectnum; } } @@ -438,7 +438,7 @@ void animatesprites_d(int x, int y, int a, int smoothratio) PALONLY: if (sector[sect].floorpal) - t->pal = sector[sect].floorpal; + copyfloorpal(t, §or[sect]); if (!h->GetOwner()) continue; @@ -484,7 +484,7 @@ void animatesprites_d(int x, int y, int a, int smoothratio) t->shade -= 6; if (sector[sect].floorpal) - t->pal = sector[sect].floorpal; + copyfloorpal(t, §or[sect]); break; case WATERBUBBLE: @@ -496,7 +496,7 @@ void animatesprites_d(int x, int y, int a, int smoothratio) default: if (sector[sect].floorpal) - t->pal = sector[sect].floorpal; + copyfloorpal(t, §or[sect]); break; } diff --git a/source/games/duke/src/animatesprites_r.cpp b/source/games/duke/src/animatesprites_r.cpp index 3a96c4c8c..301cb7151 100644 --- a/source/games/duke/src/animatesprites_r.cpp +++ b/source/games/duke/src/animatesprites_r.cpp @@ -36,7 +36,7 @@ Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms BEGIN_DUKE_NS -void animatesprites_r(int x, int y, int a, int smoothratio) +void animatesprites_r(spritetype* tsprite, int& spritesortcnt, int x, int y, int a, int smoothratio) { int i, j, k, p; short sect; @@ -163,8 +163,8 @@ void animatesprites_r(int x, int y, int a, int smoothratio) { t->x -= MulScale(MaxSmoothRatio - smoothratio, ps[s->yvel].posx - ps[s->yvel].oposx, 16); t->y -= MulScale(MaxSmoothRatio - smoothratio, ps[s->yvel].posy - ps[s->yvel].oposy, 16); - t->z = ps[s->yvel].oposz + MulScale(smoothratio, ps[s->yvel].posz - ps[s->yvel].oposz, 16); - t->z += (40 << 8); + t->z = interpolatedvalue(ps[s->yvel].oposz, ps[s->yvel].posz, smoothratio); + t->z += PHEIGHT_RR; s->xrepeat = 24; s->yrepeat = 17; } @@ -374,10 +374,10 @@ void animatesprites_r(int x, int y, int a, int smoothratio) t->cstat |= 2; if (screenpeek == myconnectindex && numplayers >= 2) { - t->x = omyx + MulScale((int)(myx - omyx), smoothratio, 16); - t->y = omyy + MulScale((int)(myy - omyy), smoothratio, 16); - t->z = omyz + MulScale((int)(myz - omyz), smoothratio, 16) + (40 << 8); - t->ang = omyang.asbuild() + MulScale((((myang.asbuild() + 1024 - omyang.asbuild()) & 2047) - 1024), smoothratio, 16); + t->x = interpolatedvalue(omyx, myx, smoothratio); + t->y = interpolatedvalue(omyy, myy, smoothratio); + t->z = interpolatedvalue(omyz, myz, smoothratio) + PHEIGHT_RR; + t->ang = interpolatedangle(omyang, myang, smoothratio).asbuild(); t->sectnum = mycursectnum; } } @@ -487,7 +487,7 @@ void animatesprites_r(int x, int y, int a, int smoothratio) PALONLY: if (sector[sect].floorpal) - t->pal = sector[sect].floorpal; + copyfloorpal(t, §or[sect]); if (!h->GetOwner()) continue; @@ -619,7 +619,7 @@ void animatesprites_r(int x, int y, int a, int smoothratio) else t->picnum += h->temp_data[0]; if (sector[sect].floorpal) - t->pal = sector[sect].floorpal; + copyfloorpal(t, §or[sect]); break; case WATERBUBBLE: @@ -632,7 +632,7 @@ void animatesprites_r(int x, int y, int a, int smoothratio) default_case: if (sector[sect].floorpal) - t->pal = sector[sect].floorpal; + copyfloorpal(t, §or[sect]); break; } diff --git a/source/games/duke/src/cheats.cpp b/source/games/duke/src/cheats.cpp index 6c7f67bc3..c76f8c96e 100644 --- a/source/games/duke/src/cheats.cpp +++ b/source/games/duke/src/cheats.cpp @@ -292,11 +292,11 @@ static bool cheatItems(int player) static bool cheatLevel(cheatseq_t *s) { int volnume,levnume; - volnume = s->Args[0] - '0' - 1; - levnume = (s->Args[1] - '0')*10+(s->Args[2]-'0') - 1; + volnume = s->Args[0] - '0'; + levnume = (s->Args[1] - '0')*10+(s->Args[2]-'0'); // Instead of hard coded range checks on volume and level, let's just check if the level is defined. - auto map = FindMapByLevelNum(levelnum(volnume, levnume)); + auto map = FindMapByIndex(volnume, levnume); if (map) { ChangeLevel(map, -1); diff --git a/source/games/duke/src/d_menu.cpp b/source/games/duke/src/d_menu.cpp index 879597e58..ea3ea9a23 100644 --- a/source/games/duke/src/d_menu.cpp +++ b/source/games/duke/src/d_menu.cpp @@ -97,19 +97,8 @@ bool GameInterface::CanSave() bool GameInterface::StartGame(FNewGameStartup& gs) { - if (gs.Episode >= 1) - { - if (g_gameType & GAMEFLAG_SHAREWARE) - { - M_StartMessage(GStrings("BUYDUKE"), 1, NAME_None); - return false; - } - } - int32_t skillsound = PISTOL_BODYHIT; - soundEngine->StopAllChannels(); - static const short sounds_d[] = { JIBBED_ACTOR6, BONUS_SPEECH1, DUKE_GETWEAPON2, JIBBED_ACTOR5, JIBBED_ACTOR5 }; static const short sounds_r[] = { 427, 428, 196, 195, 197 }; if (gs.Skill >=0 && gs.Skill <= 5) skillsound = isRR()? sounds_r[gs.Skill] : sounds_d[gs.Skill]; @@ -126,15 +115,7 @@ bool GameInterface::StartGame(FNewGameStartup& gs) } Net_ClearFifo(); } - - auto map = FindMapByLevelNum(levelnum(gs.Episode, gs.Level)); - if (map) - { - DeferedStartGame(map, gs.Skill); - return true; - } - return false; - + return true; } FSavegameInfo GameInterface::GetSaveSig() @@ -155,10 +136,4 @@ void GameInterface::DrawPlayerSprite(const DVector2& origin, bool onteam) DrawTexture(twod, tex, x, y, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TranslationIndex, color, DTA_ScaleX, scale, DTA_ScaleY, scale, TAG_DONE); } -void GameInterface::QuitToTitle() -{ - gameaction = ga_startup; -} - - END_DUKE_NS diff --git a/source/games/duke/src/dispatch.cpp b/source/games/duke/src/dispatch.cpp index b19a80e46..39df23122 100644 --- a/source/games/duke/src/dispatch.cpp +++ b/source/games/duke/src/dispatch.cpp @@ -98,19 +98,15 @@ void processinput_d(int snum); void processinput_r(int snum); void displayweapon_d(int snum, double smoothratio); void displayweapon_r(int snum, double smoothratio); -void displaymasks_d(int snum, double smoothratio); -void displaymasks_r(int snum, double smoothratio); +void displaymasks_d(int snum, int p, double smoothratio); +void displaymasks_r(int snum, int p, double smoothratio); void think_d(); void think_r(); -void animatesprites_d(int x, int y, int a, int smoothratio); -void animatesprites_r(int x, int y, int a, int smoothratio); +void animatesprites_d(spritetype* tsprite, int& spritesortcnt, int x, int y, int a, int smoothratio); +void animatesprites_r(spritetype* tsprite, int& spritesortcnt, int x, int y, int a, int smoothratio); -void Logo_d(const CompletionFunc&); -void Logo_r(const CompletionFunc&); void InitFonts_d(); void InitFonts_r(); -void PrintPaused_d(); -void PrintPaused_r(); Dispatcher fi; @@ -120,9 +116,7 @@ void SetDispatcher() if (!isRR()) { fi = { - Logo_d, InitFonts_d, - PrintPaused_d, think_d, initactorflags_d, @@ -167,9 +161,7 @@ void SetDispatcher() else { fi = { - Logo_r, InitFonts_r, - PrintPaused_r, think_r, initactorflags_r, @@ -235,8 +227,6 @@ int TILE_STATIC; int TILE_BOTTOMSTATUSBAR; int TILE_THREEDEE; int TILE_INGAMEDUKETHREEDEE; -int TILE_PLUTOPAKSPRITE; -int TILE_MENUBAR; int TILE_ATOMICHEALTH; int TILE_FLOORSLIME; int TILE_JIBS6; diff --git a/source/games/duke/src/duke3d.h b/source/games/duke/src/duke3d.h index 8628373d7..da153613a 100644 --- a/source/games/duke/src/duke3d.h +++ b/source/games/duke/src/duke3d.h @@ -40,7 +40,6 @@ struct GameInterface : public ::GameInterface FSavegameInfo GetSaveSig() override; double SmallFontScale() override { return isRR() ? 0.5 : 1.; } void SerializeGameState(FSerializer& arc) override; - void QuitToTitle() override; FString GetCoordString() override; void ExitFromMenu() override; ReservedSpace GetReservedScreenSpace(int viewsize) override; @@ -65,15 +64,18 @@ struct GameInterface : public ::GameInterface int chaseCamX(binangle ang) { return -ang.bcos(-4); } int chaseCamY(binangle ang) { return -ang.bsin(-4); } int chaseCamZ(fixedhoriz horiz) { return horiz.asq16() >> 9; } + void processSprites(spritetype* tsprite, int& spritesortcnt, int viewx, int viewy, int viewz, binangle viewang, double smoothRatio) override; + void UpdateCameras(double smoothratio) override; + void EnterPortal(spritetype* viewer, int type) override; + void LeavePortal(spritetype* viewer, int type) override; + bool GetGeoEffect(GeoEffect* eff, int viewsector) override; }; struct Dispatcher { // global stuff - void (*ShowLogo)(const CompletionFunc& completion); void (*InitFonts)(); - void (*PrintPaused)(); // sectors_?.cpp void (*think)(); @@ -113,9 +115,9 @@ struct Dispatcher void (*checkweapons)(struct player_struct* p); void (*processinput)(int snum); void (*displayweapon)(int snum, double smoothratio); - void (*displaymasks)(int snum, double smoothratio); + void (*displaymasks)(int snum, int p, double smoothratio); - void (*animatesprites)(int x, int y, int a, int smoothratio); + void (*animatesprites)(spritetype* tsprite, int& spritesortcnt, int x, int y, int a, int smoothratio); }; diff --git a/source/games/duke/src/flags_d.cpp b/source/games/duke/src/flags_d.cpp index 28b50b056..4a7029751 100644 --- a/source/games/duke/src/flags_d.cpp +++ b/source/games/duke/src/flags_d.cpp @@ -261,10 +261,6 @@ void initactorflags_d() TILE_CAMLIGHT = CAMLIGHT; TILE_STATIC = STATIC; TILE_BOTTOMSTATUSBAR = isWorldTour()? WIDESCREENSTATUSBAR : BOTTOMSTATUSBAR; - TILE_THREEDEE = THREEDEE; - TILE_INGAMEDUKETHREEDEE = INGAMEDUKETHREEDEE; - TILE_PLUTOPAKSPRITE = PLUTOPAKSPRITE; - TILE_MENUBAR = MENUBAR; TILE_ATOMICHEALTH = ATOMICHEALTH; TILE_FLOORSLIME = FLOORSLIME; TILE_JIBS6 = JIBS6; diff --git a/source/games/duke/src/flags_r.cpp b/source/games/duke/src/flags_r.cpp index e609c925f..04ef28b6e 100644 --- a/source/games/duke/src/flags_r.cpp +++ b/source/games/duke/src/flags_r.cpp @@ -235,10 +235,6 @@ void initactorflags_r() TILE_CAMLIGHT = CAMLIGHT; TILE_STATIC = STATIC; TILE_BOTTOMSTATUSBAR = BOTTOMSTATUSBAR; - TILE_THREEDEE = THREEDEE; - TILE_INGAMEDUKETHREEDEE = INGAMEDUKETHREEDEE; - TILE_PLUTOPAKSPRITE = PLUTOPAKSPRITE; - TILE_MENUBAR = MENUBAR; TILE_ATOMICHEALTH = ATOMICHEALTH; TILE_FLOORSLIME = FLOORSLIME; TILE_JIBS6 = JIBS6; diff --git a/source/games/duke/src/funct.h b/source/games/duke/src/funct.h index 1785fabce..64b9199f6 100644 --- a/source/games/duke/src/funct.h +++ b/source/games/duke/src/funct.h @@ -162,9 +162,6 @@ int hits(DDukeActor* snum); DDukeActor* LocateTheLocator(int n, int sectnum); void clearcamera(player_struct* ps); -void showtwoscreens(const CompletionFunc& func); -void doorders(const CompletionFunc& func); - void LoadActor(DDukeActor* i, int p, int x); void execute(DDukeActor* s, int p, int d); void makeitfall(DDukeActor* s); @@ -211,8 +208,6 @@ void OffBoat(player_struct *pl); void cameratext(DDukeActor* i); void dobonus(int bonusonly, const CompletionFunc& completion); -void dobonus_d(int bonusonly, const CompletionFunc& completion); -void dobonus_r(int bonusonly, const CompletionFunc& completion); void drawoverlays(double smoothratio); void drawbackground(void); @@ -227,7 +222,6 @@ void e4intro(const CompletionFunc& completion); void exitlevel(MapRecord *next); void enterlevel(MapRecord* mi, int gm); void donewgame(MapRecord* map, int sk); -void startnewgame(MapRecord* map, int skill); int playercolor2lookup(int color); void PlayerColorChanged(void); bool movementBlocked(player_struct *p); diff --git a/source/games/duke/src/game.cpp b/source/games/duke/src/game.cpp index da5c038cf..9dc6d5d45 100644 --- a/source/games/duke/src/game.cpp +++ b/source/games/duke/src/game.cpp @@ -229,9 +229,6 @@ static void setupbackdrop() static void initTiles() { - if (TileFiles.artLoadFiles("tiles%03i.art") < 0) - I_FatalError("Failed loading art."); - tileDelete(TILE_MIRROR); skiptile = TILE_W_FORCEFIELD + 1; @@ -309,13 +306,10 @@ void GameInterface::app_init() OnEvent(EVENT_INIT); - if (engineInit()) - G_FatalEngineError(); - - setupbackdrop(); //Net_SendClientInfo(); initTiles(); + setupbackdrop(); genspriteremaps(); SetupGameButtons(); InitCheats(); diff --git a/source/games/duke/src/game_misc.cpp b/source/games/duke/src/game_misc.cpp index a864c378c..89e32c0af 100644 --- a/source/games/duke/src/game_misc.cpp +++ b/source/games/duke/src/game_misc.cpp @@ -92,13 +92,12 @@ static void endthegame(bool) void GameInterface::ExitFromMenu() { +#if 0 + // do we really need this scoreboard stuff here? auto runbonus = [=](auto completion) { // MP scoreboard - if (playerswhenstarted > 1 && !ud.coop) - { - dobonus(1, completion); - } + if (playerswhenstarted > 1 && !ud.coop) ShowScoreboard(playerswhenstarted); else completion(false); }; @@ -106,11 +105,16 @@ void GameInterface::ExitFromMenu() { // shareware and TEN screens if (isShareware() && !isRR()) - showtwoscreens(completion); + StartCutscene("DukeCutscenes.BuildSharewareExit", 0, completion); else completion(false); }; runbonus([=](bool aborted) { runtwoscreens(endthegame); }); +#else + if (isShareware() && !isRR()) + StartCutscene("DukeCutscenes.BuildSharewareExit", 0, endthegame); + else endthegame(false); +#endif } //--------------------------------------------------------------------------- @@ -208,7 +212,7 @@ void V_AddBlend (float r, float g, float b, float a, float v_blend[4]) //--------------------------------------------------------------------------- // -// 'rest' in this case means everything not part of the 3D scene and its weapon sprite. +// draws everything not part of the 3D scene and its weapon sprite. // //--------------------------------------------------------------------------- @@ -251,7 +255,7 @@ void drawoverlays(double smoothratio) { fi.displayweapon(screenpeek, smoothratio); if (pp->over_shoulder_on == 0) - fi.displaymasks(screenpeek, smoothratio); + fi.displaymasks(screenpeek, pp->GetActor()->s->pal == 1 ? 1 : sector[pp->cursectnum].floorpal, smoothratio); } if (!isRR()) moveclouds(smoothratio); @@ -265,15 +269,15 @@ void drawoverlays(double smoothratio) { if (screenpeek == myconnectindex && numplayers > 1) { - cposx = omyx + MulScale(myx - omyx, smoothratio, 16); - cposy = omyy + MulScale(myy - omyy, smoothratio, 16); - cang = !SyncInput() ? myang.asbuild() : omyang.asbuild() + MulScale(((myang.asbuild() + 1024 - omyang.asbuild()) & 2047) - 1024, smoothratio, 16); + cposx = interpolatedvalue(omyx, myx, smoothratio); + cposy = interpolatedvalue(omyy, myy, smoothratio); + cang = (!SyncInput() ? myang : interpolatedangle(omyang, myang, smoothratio)).asbuild(); } else { - cposx = pp->oposx + MulScale(pp->posx - pp->oposx, smoothratio, 16); - cposy = pp->oposy + MulScale(pp->posy - pp->oposy, smoothratio, 16); - cang = !SyncInput() ? pp->angle.ang.asbuild() : pp->angle.oang.asbuild() + MulScale(((pp->angle.ang.asbuild() + 1024 - pp->angle.oang.asbuild()) & 2047) - 1024, smoothratio, 16); + cposx = interpolatedvalue(pp->oposx, pp->posx, smoothratio); + cposy = interpolatedvalue(pp->oposy, pp->posy, smoothratio); + cang = (!SyncInput() ? pp->angle.ang : interpolatedangle(pp->angle.oang, pp->angle.ang, smoothratio)).asbuild(); } } else @@ -297,7 +301,13 @@ void drawoverlays(double smoothratio) } if (paused == 2) - fi.PrintPaused(); + { + double x = 160, y = 100; + double scale = isRR() ? 0.4 : 1.; + const char* text = GStrings("Game Paused"); + x -= BigFont->StringWidth(text) * 0.5 * scale; + DrawText(twod, BigFont, CR_UNTRANSLATED, x, y - 12, text, DTA_FullscreenScale, FSMode_Fit320x200, DTA_ScaleX, scale, DTA_ScaleY, scale, TAG_DONE); + } } @@ -342,18 +352,6 @@ void cameratext(DDukeActor *cam) // //--------------------------------------------------------------------------- -void dobonus(int bonusonly, const CompletionFunc& completion) -{ - if (isRR()) dobonus_r(bonusonly, completion); - else dobonus_d(bonusonly, completion); -} - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - int startrts(int lumpNum, int localPlayer) { if (SoundEnabled() && @@ -398,15 +396,13 @@ bool GameInterface::DrawAutomapPlayer(int cposx, int cposy, int czoom, int cang, int i, j, k, l, x1, y1, x2, y2, x3, y3, x4, y4, ox, oy, xoff, yoff; int dax, day, cosang, sinang, xspan, yspan, sprx, spry; int xrepeat, yrepeat, tilenum, daang; - int xvect, yvect, xvect2, yvect2; + int xvect, yvect; int p; PalEntry col; spritetype* spr; xvect = -bsin(cang) * czoom; yvect = -bcos(cang) * czoom; - xvect2 = MulScale(xvect, yxaspect, 16); - yvect2 = MulScale(yvect, yxaspect, 16); //Draw sprites auto pactor = ps[screenpeek].GetActor(); @@ -434,15 +430,15 @@ bool GameInterface::DrawAutomapPlayer(int cposx, int cposy, int czoom, int cang, ox = sprx - cposx; oy = spry - cposy; x1 = DMulScale(ox, xvect, -oy, yvect, 16); - y1 = DMulScale(oy, xvect2, ox, yvect2, 16); + y1 = DMulScale(oy, xvect, ox, yvect, 16); ox = bcos(spr->ang, -7); oy = bsin(spr->ang, -7); x2 = DMulScale(ox, xvect, -oy, yvect, 16); y2 = DMulScale(oy, xvect, ox, yvect, 16); - x3 = MulScale(x2, yxaspect, 16); - y3 = MulScale(y2, yxaspect, 16); + x3 = x2; + y3 = y2; drawlinergb(x1 - x2 + (xdim << 11), y1 - y3 + (ydim << 11), x1 + x2 + (xdim << 11), y1 + y3 + (ydim << 11), col); @@ -474,12 +470,12 @@ bool GameInterface::DrawAutomapPlayer(int cposx, int cposy, int czoom, int cang, ox = x1 - cposx; oy = y1 - cposy; x1 = DMulScale(ox, xvect, -oy, yvect, 16); - y1 = DMulScale(oy, xvect2, ox, yvect2, 16); + y1 = DMulScale(oy, xvect, ox, yvect, 16); ox = x2 - cposx; oy = y2 - cposy; x2 = DMulScale(ox, xvect, -oy, yvect, 16); - y2 = DMulScale(oy, xvect2, ox, yvect2, 16); + y2 = DMulScale(oy, xvect, ox, yvect, 16); drawlinergb(x1 + (xdim << 11), y1 + (ydim << 11), x2 + (xdim << 11), y2 + (ydim << 11), col); @@ -520,22 +516,22 @@ bool GameInterface::DrawAutomapPlayer(int cposx, int cposy, int czoom, int cang, ox = x1 - cposx; oy = y1 - cposy; x1 = DMulScale(ox, xvect, -oy, yvect, 16); - y1 = DMulScale(oy, xvect2, ox, yvect2, 16); + y1 = DMulScale(oy, xvect, ox, yvect, 16); ox = x2 - cposx; oy = y2 - cposy; x2 = DMulScale(ox, xvect, -oy, yvect, 16); - y2 = DMulScale(oy, xvect2, ox, yvect2, 16); + y2 = DMulScale(oy, xvect, ox, yvect, 16); ox = x3 - cposx; oy = y3 - cposy; x3 = DMulScale(ox, xvect, -oy, yvect, 16); - y3 = DMulScale(oy, xvect2, ox, yvect2, 16); + y3 = DMulScale(oy, xvect, ox, yvect, 16); ox = x4 - cposx; oy = y4 - cposy; x4 = DMulScale(ox, xvect, -oy, yvect, 16); - y4 = DMulScale(oy, xvect2, ox, yvect2, 16); + y4 = DMulScale(oy, xvect, ox, yvect, 16); drawlinergb(x1 + (xdim << 11), y1 + (ydim << 11), x2 + (xdim << 11), y2 + (ydim << 11), col); @@ -570,7 +566,7 @@ bool GameInterface::DrawAutomapPlayer(int cposx, int cposy, int czoom, int cang, i = TILE_APLAYERTOP; j = abs(pp.truefz - pp.posz) >> 8; - j = MulScale(czoom * (pspr->yrepeat + j), yxaspect, 16); + j = czoom * (pspr->yrepeat + j); if (j < 22000) j = 22000; else if (j > (65536 << 1)) j = (65536 << 1); diff --git a/source/games/duke/src/gamedef.cpp b/source/games/duke/src/gamedef.cpp index 830e83496..76859c7d1 100644 --- a/source/games/duke/src/gamedef.cpp +++ b/source/games/duke/src/gamedef.cpp @@ -48,11 +48,12 @@ Modifications for JonoF's port by Jonathon Fowler (jf@jonof.id.au) #include "conlabeldef.h" #include "gi.h" +extern TArray> mapList; + BEGIN_DUKE_NS enum { VERSIONCHECK = 41 }; - //--------------------------------------------------------------------------- // // definitions needed by the parser. @@ -88,7 +89,8 @@ public: struct TempMusic { - int levnum; + int volnum; + int levlnum; FString music; }; @@ -1016,7 +1018,8 @@ int ConCompiler::parsecommand() if (k >= 0) { tempMusic.Reserve(1); - tempMusic.Last().levnum = levelnum(k, i); + tempMusic.Last().volnum = k + 1; + tempMusic.Last().levlnum = i + 1; tempMusic.Last().music = parsebuffer.Data(); } else @@ -1644,6 +1647,7 @@ int ConCompiler::parsecommand() return 0; case concmd_definevolumename: + { popscriptvalue(); transnum(LABEL_DEFINE); j = popscriptvalue(); @@ -1658,8 +1662,13 @@ int ConCompiler::parsecommand() textptr++, i++; } parsebuffer.Push(0); - gVolumeNames[j] = FStringTable::MakeMacro(parsebuffer.Data(), i); + // We need both a volume and a cluster for this new episode. + auto vol = MustFindVolume(j); + auto clust = MustFindCluster(j + 1); + vol->name = clust->name = FStringTable::MakeMacro(parsebuffer.Data(), i); + if (j > 0) vol->flags |= VF_SHAREWARELOCK; return 0; + } case concmd_defineskillname: popscriptvalue(); transnum(LABEL_DEFINE); @@ -1695,25 +1704,31 @@ int ConCompiler::parsecommand() textptr++, i++; } parsebuffer.Push(0); - auto levnum = levelnum(j, k); - auto map = FindMapByLevelNum(levnum); + auto map = FindMapByIndexOnly(j + 1, k + 1); if (!map) map = AllocateMap(); map->SetFileName(parsebuffer.Data()); + if (k == 0) + { + auto vol = MustFindVolume(j); + vol->startmap = map->labelName; + } while (*textptr == ' ' || *textptr == '\t') textptr++; map->parTime = - (((*(textptr + 0) - '0') * 10 + (*(textptr + 1) - '0')) * 26 * 60) + - (((*(textptr + 3) - '0') * 10 + (*(textptr + 4) - '0')) * 26); + (((*(textptr + 0) - '0') * 10 + (*(textptr + 1) - '0')) * 60) + + (((*(textptr + 3) - '0') * 10 + (*(textptr + 4) - '0'))); textptr += 5; while (*textptr == ' ' || *textptr == '\t') textptr++; map->designerTime = - (((*(textptr + 0) - '0') * 10 + (*(textptr + 1) - '0')) * 26 * 60) + - (((*(textptr + 3) - '0') * 10 + (*(textptr + 4) - '0')) * 26); + (((*(textptr + 0) - '0') * 10 + (*(textptr + 1) - '0')) * 60) + + (((*(textptr + 3) - '0') * 10 + (*(textptr + 4) - '0'))); - map->levelNumber = levnum; + SetLevelNum(map, makelevelnum(j + 1, k + 1)); + + map->cluster = j + 1; textptr += 5; while (*textptr == ' ' || *textptr == '\t') textptr++; @@ -3132,7 +3147,7 @@ void ConCompiler::setmusic() { for (auto& tm : tempMusic) { - auto map = FindMapByLevelNum(tm.levnum); + auto map = FindMapByIndexOnly(tm.volnum, tm.levlnum); if (map) map->music = tm.music; } tempMusic.Clear(); @@ -3176,6 +3191,12 @@ void loadcons() ScriptCode.Push(0); ConCompiler comp; + + if (fileSystem.FileExists("engine/engine.con")) + { + comp.compilecon("engine/engine.con"); + } + comp.compilecon(ConFile()); //Tokenize if (userConfig.AddCons) for (FString& m : *userConfig.AddCons.get()) @@ -3206,45 +3227,38 @@ void loadcons() InitGameVarPointers(); ResetSystemDefaults(); S_WorldTourMappingsForOldSounds(); // create a sound mapping for World Tour. + S_CacheAllSounds(); + comp.setmusic(); + + // RR must link the last map of E1 to the first map of E2. + if (isRR()) for (auto& map : mapList) + { + if (map->cluster == 1) + { + if (!FindMapByLevelNum(map->levelNumber + 1)) + { + auto nextmap = FindMapByIndexOnly(map->cluster + 1, 1); + if (nextmap) + { + map->NextMap = nextmap->labelName; + map->flags |= LEVEL_FORCENOEOG; + } + } + } + } + if (isWorldTour()) { + // fix broken secret exit in WT's super secret map. + // This cannot be done from an RMAPINFO definition because the conditions are too specific and must not override custom maps. int num = fileSystem.CheckNumForName("e1l7.map"); int file = fileSystem.GetFileContainer(num); if (file <= fileSystem.GetMaxIwadNum()) { auto maprec = FindMapByName("e1l7"); - if (maprec) maprec->nextLevel = levelnum(0, 4); + if (maprec) maprec->NextMap = "e1l5"; } } - else if (isRRRA()) - { - // RRRA goes directly to the second episode after E1L7 to continue the game. - int num = fileSystem.CheckNumForName("e1l7.map"); - int file = fileSystem.GetFileContainer(num); - if (file <= fileSystem.GetMaxIwadNum()) - { - auto maprec = FindMapByName("e1l7"); - if (maprec) maprec->nextLevel = levelnum(1, 0); - } - } - else if (isRR()) - { - // RR does not define its final level and crudely hacked it into the progression. This puts it into the E2L8 slot so that the game can naturally progress there. - auto maprec1 = FindMapByLevelNum(levelnum(1, 6)); - auto maprec2 = FindMapByLevelNum(levelnum(1, 7)); - auto maprec3 = FindMapByName("endgame"); - int num3 = fileSystem.FindFile("endgame.map"); - if (maprec1 && !maprec2 && !maprec3 && num3 >= 0) - { - auto maprec = AllocateMap(); - maprec->designerTime = 0; - maprec->parTime = 0; - maprec->SetFileName("endgame.map"); - maprec->SetName("$TXT_CLOSEENCOUNTERS"); - maprec->levelNumber = levelnum(1, 7); - } - } - comp.setmusic(); } END_DUKE_NS diff --git a/source/games/duke/src/gameexec.cpp b/source/games/duke/src/gameexec.cpp index 4dd7c7181..1a9b7d754 100644 --- a/source/games/duke/src/gameexec.cpp +++ b/source/games/duke/src/gameexec.cpp @@ -130,11 +130,6 @@ static void DoUserDef(bool bSet, int lVar1, int lLabelID, int lVar2, DDukeActor* if (!bSet) SetGameVarID(lVar2, cl_showweapon, sActor, sPlayer); break; - case USERDEFS_FROM_BONUS: - if (bSet) ud.from_bonus = lValue; - else SetGameVarID(lVar2, ud.from_bonus, sActor, sPlayer); - break; - case USERDEFS_CAMERASPRITE: if (bSet) ud.cameraactor = ScriptIndexToActor(lValue); else SetGameVarID(lVar2, ActorToScriptIndex(ud.cameraactor), sActor, sPlayer); @@ -239,14 +234,6 @@ static void DoUserDef(bool bSet, int lVar1, int lLabelID, int lVar2, DDukeActor* else SetGameVarID(lVar2, ud.player_skill, sActor, sPlayer); break; - case USERDEFS_LEVEL_NUMBER: - if (!bSet) SetGameVarID(lVar2, mapfromlevelnum(currentLevel->levelNumber), sActor, sPlayer); - break; - - case USERDEFS_VOLUME_NUMBER: - if (!bSet) SetGameVarID(lVar2, volfromlevelnum(currentLevel->levelNumber), sActor, sPlayer); - break; - case USERDEFS_MARKER: if (bSet) ud.marker = lValue; else SetGameVarID(lVar2, ud.marker, sActor, sPlayer); @@ -484,7 +471,7 @@ void DoPlayer(bool bSet, int lVar1, int lLabelID, int lVar2, DDukeActor* sActor, break; case PLAYER_LOOK_ANG: - if (bSet) ps[iPlayer].angle.look_ang = buildlook(lValue); + if (bSet) ps[iPlayer].angle.look_ang = buildang(lValue); else SetGameVarID(lVar2, ps[iPlayer].angle.look_ang.asbuild(), sActor, sPlayer); break; @@ -645,8 +632,8 @@ void DoPlayer(bool bSet, int lVar1, int lLabelID, int lVar2, DDukeActor* sActor, break; case PLAYER_ONE_EIGHTY_COUNT: - if (bSet) ps[iPlayer].angle.spin = buildlook(lValue); - else SetGameVarID(lVar2, ps[iPlayer].angle.spin.asbuild(), sActor, sPlayer); + if (bSet) ps[iPlayer].angle.spin = lValue; + else SetGameVarID(lVar2, ps[iPlayer].angle.spin, sActor, sPlayer); break; case PLAYER_CHEAT_PHASE: @@ -705,7 +692,7 @@ void DoPlayer(bool bSet, int lVar1, int lLabelID, int lVar2, DDukeActor* sActor, break; case PLAYER_ROTSCRNANG: - if (bSet) ps[iPlayer].angle.rotscrnang = buildlook(lValue); + if (bSet) ps[iPlayer].angle.rotscrnang = buildang(lValue); else SetGameVarID(lVar2, ps[iPlayer].angle.rotscrnang.asbuild(), sActor, sPlayer); break; @@ -984,7 +971,7 @@ void DoWall(char bSet, int lVar1, int lLabelID, int lVar2, DDukeActor* sActor, s else SetGameVarID(lVar2, wall[iWall].overpicnum, sActor, sPlayer); break; case WALL_SHADE: - if (bSet) wall[iWall].x = lValue; + if (bSet) wall[iWall].shade = lValue; else SetGameVarID(lVar2, wall[iWall].shade, sActor, sPlayer); break; case WALL_PAL: @@ -1016,8 +1003,8 @@ void DoWall(char bSet, int lVar1, int lLabelID, int lVar2, DDukeActor* sActor, s else SetGameVarID(lVar2, wall[iWall].hitag, sActor, sPlayer); break; case WALL_EXTRA: - if (bSet) wall[iWall].x = lValue; - else SetGameVarID(lVar2, wall[iWall].x, sActor, sPlayer); + if (bSet) wall[iWall].extra = lValue; + else SetGameVarID(lVar2, wall[iWall].extra, sActor, sPlayer); break; default: break; @@ -2268,7 +2255,7 @@ int ParseState::parse(void) ps[g_p].weapreccnt = 0; ps[g_p].ftq = 0; ps[g_p].posxv = ps[g_p].posyv = 0; - if (!isRR()) ps[g_p].angle.orotscrnang = ps[g_p].angle.rotscrnang = buildlook(0); + if (!isRR()) ps[g_p].angle.orotscrnang = ps[g_p].angle.rotscrnang = buildang(0); ps[g_p].falling_counter = 0; @@ -3455,7 +3442,7 @@ int ParseState::parse(void) insptr++; // skip command volnume = GetGameVarID(*insptr++, g_ac, g_p); levnume = GetGameVarID(*insptr++, g_ac, g_p); - auto level = FindMapByLevelNum(levelnum(volnume - 1, levnume - 1)); + auto level = FindMapByIndex(volnume, levnume); if (level != nullptr) ChangeLevel(level, -1); break; @@ -3572,7 +3559,7 @@ int ParseState::parse(void) { insptr++; int music_select = *insptr++; - auto level = FindMapByLevelNum(levelnum(currentLevel->levelNumber, music_select)); + auto level = FindMapByIndex(currentLevel->cluster, music_select+1); // this was 0-based in EDuke 2.0... if (level) S_PlayLevelMusic(level); break; } diff --git a/source/games/duke/src/gameloop.cpp b/source/games/duke/src/gameloop.cpp index 3b5791477..492dddf95 100644 --- a/source/games/duke/src/gameloop.cpp +++ b/source/games/duke/src/gameloop.cpp @@ -113,29 +113,8 @@ void GameInterface::Ticker() void GameInterface::Startup() { - ps[myconnectindex].ftq = 0; - - if (userConfig.CommandMap.IsNotEmpty()) - { - auto maprecord = FindMapByName(userConfig.CommandMap); - userConfig.CommandMap = ""; - if (maprecord) - { - ud.m_respawn_monsters = ud.player_skill == 4; - - for (int i = 0; i != -1; i = connectpoint2[i]) - { - resetweapons(i); - resetinventory(i); - } - startnewgame(maprecord, /*userConfig.skill*/2); - } - } - else - { - if (!userConfig.nologo) fi.ShowLogo([](bool) { gameaction = ga_mainmenunostopsound; }); - else gameaction = ga_mainmenunostopsound; - } + ps[myconnectindex].ftq = 0; + PlayLogos(ga_mainmenunostopsound, ga_mainmenunostopsound, false); } //--------------------------------------------------------------------------- @@ -160,20 +139,9 @@ void GameInterface::Render() // // //--------------------------------------------------------------------------- -void loadscreen_d(MapRecord* rec, CompletionFunc func); -void loadscreen_r(MapRecord* rec, CompletionFunc func); void GameInterface::NextLevel(MapRecord* map, int skill) { -#if 0 - // Loading is so fast on modern system so that this only serves as an irritant, not an asset. - auto loadscreen = isRR() ? loadscreen_r : loadscreen_d; - loadscreen_d(map, [=](bool) - { - enterlevel(map, 0); - gameaction = ga_level; - }); -#endif enterlevel(map, 0); } @@ -183,23 +151,6 @@ void GameInterface::NextLevel(MapRecord* map, int skill) // //--------------------------------------------------------------------------- -void GameInterface::NewGame(MapRecord* map, int skill, bool) -{ - // Hmm... What about the other players? - ps[0].last_extra = gs.max_player_health; - resetweapons(0); - resetinventory(0); - if (skill != -1) skill = skill + 1; - - startnewgame(map, skill); -} - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - void GameInterface::LevelCompleted(MapRecord* map, int skill) { exitlevel(map); diff --git a/source/games/duke/src/gamevar.cpp b/source/games/duke/src/gamevar.cpp index 839092c33..c48830490 100644 --- a/source/games/duke/src/gamevar.cpp +++ b/source/games/duke/src/gamevar.cpp @@ -37,7 +37,6 @@ source as it is released. #include "serializer.h" #include "names.h" #include "build.h" -#include "mmulti.h" #include "gamevar.h" #include "mapinfo.h" @@ -555,9 +554,9 @@ void InitGameVarPointers(void) // //--------------------------------------------------------------------------- -// These are deliberately not stored in accessible variables anymore -int getmap() { return mapfromlevelnum(currentLevel->levelNumber); } -int getvol() { return volfromlevelnum(currentLevel->levelNumber); } +// These are deliberately not stored in accessible variables anymore. Use is deprecated. +int getmap() { return currentLevel->levelNumber; } +int getvol() { return currentLevel->cluster; } void AddSystemVars() { diff --git a/source/games/duke/src/global.h b/source/games/duke/src/global.h index d492d754e..9e101fb4f 100644 --- a/source/games/duke/src/global.h +++ b/source/games/duke/src/global.h @@ -3,7 +3,6 @@ #include "build.h" #include "compat.h" #include "duke3d.h" -#include "mmulti.h" #include "quotemgr.h" #include "sounds.h" #include "constants.h" diff --git a/source/games/duke/src/hudweapon_d.cpp b/source/games/duke/src/hudweapon_d.cpp index 3b3ebe5b9..8f7058736 100644 --- a/source/games/duke/src/hudweapon_d.cpp +++ b/source/games/duke/src/hudweapon_d.cpp @@ -39,9 +39,9 @@ source as it is released. BEGIN_DUKE_NS -double getavel(int snum) +inline static double getavel(int snum) { - return FixedToFloat(PlayerInputAngVel(screenpeek)); + return PlayerInputAngVel(snum) * (2048. / 360.); } //--------------------------------------------------------------------------- @@ -61,22 +61,22 @@ inline static void hud_drawpal(double x, double y, int tilenum, int shade, int o // //--------------------------------------------------------------------------- -void displayloogie(short snum) +void displayloogie(player_struct* p) { int i, a, y, z; double x; - if (ps[snum].loogcnt == 0) return; + if (p->loogcnt == 0) return; - y = (ps[snum].loogcnt << 2); - for (i = 0; i < ps[snum].numloogs; i++) + y = (p->loogcnt << 2); + for (i = 0; i < p->numloogs; i++) { - a = abs(bsinf((ps[snum].loogcnt + i) << 5, -5)); - z = 4096 + ((ps[snum].loogcnt + i) << 9); - x = -getavel(snum) + bsinf((ps[snum].loogcnt + i) << 6, -10); + a = abs(bsinf((p->loogcnt + i) << 5, -5)); + z = 4096 + ((p->loogcnt + i) << 9); + x = -getavel(p->i) + bsinf((p->loogcnt + i) << 6, -10); hud_drawsprite( - (ps[snum].loogiex[i] + x), (200 + ps[snum].loogiey[i] - y), z - (i << 8), 256 - a, + (p->loogiex[i] + x), (200 + p->loogiey[i] - y), z - (i << 8), 256 - a, LOOGIE, 0, 0, 2); } } @@ -87,18 +87,16 @@ void displayloogie(short snum) // //--------------------------------------------------------------------------- -int animatefist(int gs, int snum, double look_anghalf) +int animatefist(int gs, player_struct* p, double look_anghalf, double looking_arc, double plravel, int fistpal) { - short fisti, fistpal; + short fisti; int fistzoom; - double looking_arc, fistz; + double fistz; - fisti = ps[snum].fist_incs; + fisti = p->fist_incs; if (fisti > 32) fisti = 32; if (fisti <= 0) return 0; - looking_arc = fabs(look_anghalf) / 4.5; - fistzoom = 65536L - bcosf(fisti << 6, 2); if (fistzoom > 90612L) fistzoom = 90612L; @@ -106,13 +104,8 @@ int animatefist(int gs, int snum, double look_anghalf) fistzoom = 40290; fistz = 194 + bsinf((6 + fisti) << 7, -9); - if (ps[snum].GetActor()->s->pal == 1) - fistpal = 1; - else - fistpal = sector[ps[snum].cursectnum].floorpal; - hud_drawsprite( - (-fisti + 222 + (getavel(snum) / 16.)), + (-fisti + 222 + plravel), (looking_arc + fistz), fistzoom, 0, FIST, gs, fistpal, 2); @@ -125,28 +118,15 @@ int animatefist(int gs, int snum, double look_anghalf) // //--------------------------------------------------------------------------- -int animateknee(int gs, int snum, double hard_landing, double look_anghalf, double horiz16th) +int animateknee(int gs, player_struct* p, double look_anghalf, double looking_arc, double horiz16th, double plravel, int pal) { + if (p->knee_incs > 11 || p->knee_incs == 0 || p->GetActor()->s->extra <= 0) return 0; + static const short knee_y[] = { 0,-8,-16,-32,-64,-84,-108,-108,-108,-72,-32,-8 }; - short pal; - double looking_arc; - if (ps[snum].knee_incs > 11 || ps[snum].knee_incs == 0 || ps[snum].GetActor()->s->extra <= 0) return 0; + looking_arc += knee_y[p->knee_incs]; - looking_arc = knee_y[ps[snum].knee_incs] + (fabs(look_anghalf) / 4.5); - - looking_arc -= hard_landing * 8.; - - if (ps[snum].GetActor()->s->pal == 1) - pal = 1; - else - { - pal = sector[ps[snum].cursectnum].floorpal; - if (pal == 0) - pal = ps[snum].palookup; - } - - hud_drawpal(105 + (getavel(snum) / 16.) - look_anghalf + (knee_y[ps[snum].knee_incs] >> 2), looking_arc + 280 - horiz16th, KNEE, gs, 4, pal); + hud_drawpal(105 + plravel - look_anghalf + (knee_y[p->knee_incs] >> 2), looking_arc + 280 - horiz16th, KNEE, gs, 4, pal); return 1; } @@ -157,24 +137,13 @@ int animateknee(int gs, int snum, double hard_landing, double look_anghalf, doub // //--------------------------------------------------------------------------- -int animateknuckles(int gs, int snum, double hard_landing, double look_anghalf, double horiz16th) +int animateknuckles(int gs, player_struct* p, double look_anghalf, double looking_arc, double horiz16th, double plravel, int pal) { + if (isWW2GI() || p->over_shoulder_on != 0 || p->knuckle_incs == 0 || p->GetActor()->s->extra <= 0) return 0; + static const short knuckle_frames[] = { 0,1,2,2,3,3,3,2,2,1,0 }; - short pal; - double looking_arc; - if (isWW2GI() || ps[snum].over_shoulder_on != 0 || ps[snum].knuckle_incs == 0 || ps[snum].GetActor()->s->extra <= 0) return 0; - - looking_arc = fabs(look_anghalf) / 4.5; - - looking_arc -= hard_landing * 8.; - - if (ps[snum].GetActor()->s->pal == 1) - pal = 1; - else - pal = sector[ps[snum].cursectnum].floorpal; - - hud_drawpal(160 + (getavel(snum) / 16.) - look_anghalf, looking_arc + 180 - horiz16th, CRACKKNUCKLES + knuckle_frames[ps[snum].knuckle_incs >> 1], gs, 4, pal); + hud_drawpal(160 + plravel - look_anghalf, looking_arc + 180 - horiz16th, CRACKKNUCKLES + knuckle_frames[p->knuckle_incs >> 1], gs, 4, pal); return 1; } @@ -186,15 +155,8 @@ int animateknuckles(int gs, int snum, double hard_landing, double look_anghalf, // //--------------------------------------------------------------------------- -void displaymasks_d(int snum, double) +void displaymasks_d(int snum, int p, double) { - int p; - - if (ps[snum].GetActor()->s->pal == 1) - p = 1; - else - p = sector[ps[snum].cursectnum].floorpal; - if (ps[snum].scuba_on) { int y = 200 - tileHeight(SCUBAMASK); @@ -209,24 +171,14 @@ void displaymasks_d(int snum, double) // //--------------------------------------------------------------------------- -static int animatetip(int gs, int snum, double hard_landing, double look_anghalf, double horiz16th) +static int animatetip(int gs, player_struct* p, double look_anghalf, double looking_arc, double horiz16th, double plravel, int pal) { - int p; - double looking_arc; + if (p->tipincs == 0) return 0; + static const short tip_y[] = { 0,-8,-16,-32,-64,-84,-108,-108,-108,-108,-108,-108,-108,-108,-108,-108,-96,-72,-64,-32,-16 }; - if (ps[snum].tipincs == 0) return 0; - - looking_arc = fabs(look_anghalf) / 4.5; - looking_arc -= hard_landing * 8.; - - if (ps[snum].GetActor()->s->pal == 1) - p = 1; - else - p = sector[ps[snum].cursectnum].floorpal; - - hud_drawpal(170 + (getavel(snum) / 16.) - look_anghalf, - (tip_y[ps[snum].tipincs] >> 1) + looking_arc + 240 - horiz16th, TIP + ((26 - ps[snum].tipincs) >> 4), gs, 0, p); + hud_drawpal(170 + plravel - look_anghalf, + (tip_y[p->tipincs] >> 1) + looking_arc + 240 - horiz16th, TIP + ((26 - p->tipincs) >> 4), gs, 0, pal); return 1; } @@ -237,25 +189,23 @@ static int animatetip(int gs, int snum, double hard_landing, double look_anghalf // //--------------------------------------------------------------------------- -int animateaccess(int gs,int snum,double hard_landing,double look_anghalf,double horiz16th) +int animateaccess(int gs, player_struct* p, double look_anghalf, double looking_arc, double horiz16th, double plravel) { + if(p->access_incs == 0 || p->GetActor()->s->extra <= 0) return 0; + static const short access_y[] = {0,-8,-16,-32,-64,-84,-108,-108,-108,-108,-108,-108,-108,-108,-108,-108,-96,-72,-64,-32,-16}; - double looking_arc; - char p; - if(ps[snum].access_incs == 0 || ps[snum].GetActor()->s->extra <= 0) return 0; + looking_arc += access_y[p->access_incs]; - looking_arc = access_y[ps[snum].access_incs] + (fabs(look_anghalf) / 4.5); - looking_arc -= hard_landing * 8.; + int pal; + if (p->access_spritenum != nullptr) + pal = p->access_spritenum->s->pal; + else pal = 0; - if(ps[snum].access_spritenum != nullptr) - p = ps[snum].access_spritenum->s->pal; - else p = 0; - - if((ps[snum].access_incs-3) > 0 && (ps[snum].access_incs-3)>>3) - hud_drawpal(170+(getavel(snum)/16.)-look_anghalf+(access_y[ps[snum].access_incs]>>2),looking_arc+266-horiz16th,HANDHOLDINGLASER+(ps[snum].access_incs>>3),gs,0,p); + if((p->access_incs-3) > 0 && (p->access_incs-3)>>3) + hud_drawpal(170 + plravel - look_anghalf + (access_y[p->access_incs] >> 2), looking_arc + 266 - horiz16th, HANDHOLDINGLASER + (p->access_incs >> 3), gs, 0, pal); else - hud_drawpal(170+(getavel(snum)/16.)-look_anghalf+(access_y[ps[snum].access_incs]>>2),looking_arc+266-horiz16th,HANDHOLDINGACCESS,gs,4,p); + hud_drawpal(170 + plravel - look_anghalf + (access_y[p->access_incs] >> 2), looking_arc + 266 - horiz16th, HANDHOLDINGACCESS, gs, 4, pal); return 1; } @@ -270,8 +220,8 @@ void displayweapon_d(int snum, double smoothratio) { int cw; int i, j; - int o,pal; - double weapon_sway, weapon_xoffset, gun_pos, looking_arc, kickback_pic, random_club_frame, hard_landing, look_anghalf, horiz16th; + int o, pal; + double weapon_sway, weapon_xoffset, gun_pos, looking_arc, kickback_pic, random_club_frame, hard_landing, look_anghalf, horiz16th, plravel; signed char shade; struct player_struct *p; @@ -281,38 +231,52 @@ void displayweapon_d(int snum, double smoothratio) o = 0; - horiz16th = get16thOfHoriz(snum, SyncInput(), smoothratio); + if (cl_hudinterpolation) + { + weapon_sway = interpolatedvaluef(p->oweapon_sway, p->weapon_sway, smoothratio); + kickback_pic = interpolatedvaluef(p->okickback_pic, p->kickback_pic, smoothratio); + random_club_frame = interpolatedvaluef(p->orandom_club_frame, p->random_club_frame, smoothratio); + hard_landing = interpolatedvaluef(p->ohard_landing, p->hard_landing, smoothratio); + gun_pos = 80 - interpolatedvaluef(p->oweapon_pos * p->oweapon_pos, p->weapon_pos * p->weapon_pos, smoothratio); + } + else + { + weapon_sway = p->weapon_sway; + kickback_pic = p->kickback_pic; + random_club_frame = p->random_club_frame; + hard_landing = p->hard_landing; + gun_pos = 80 - (p->weapon_pos * p->weapon_pos); + } + + plravel = getavel(snum) * (1. / 16.); + horiz16th = p->horizon.horizsumfrac(smoothratio); look_anghalf = p->angle.look_anghalf(smoothratio); - looking_arc = fabs(look_anghalf) / 4.5; - weapon_sway = p->oweapon_sway + MulScaleF(p->weapon_sway - p->oweapon_sway, smoothratio, 16); - kickback_pic = p->okickback_pic + MulScaleF(*kb - p->okickback_pic, smoothratio, 16); - random_club_frame = p->orandom_club_frame + MulScaleF(p->random_club_frame - p->orandom_club_frame, smoothratio, 16); - hard_landing = p->ohard_landing + MulScaleF(p->hard_landing - p->ohard_landing, smoothratio, 16); + looking_arc = p->angle.looking_arc(smoothratio); + hard_landing *= 8.; + + gun_pos -= fabs(p->GetActor()->s->xrepeat < 32 ? bsinf(weapon_sway * 4., -9) : bsinf(weapon_sway * 0.5, -10)); + gun_pos -= hard_landing; + + weapon_xoffset = (160)-90; + weapon_xoffset -= bcosf(weapon_sway * 0.5) * (1. / 1536.); + weapon_xoffset -= 58 + p->weapon_ang; shade = p->GetActor()->s->shade; if(shade > 24) shade = 24; + pal = p->GetActor()->s->pal == 1 ? 1 : sector[p->cursectnum].floorpal; + if (pal == 0) + pal = p->palookup; + + auto adjusted_arc = looking_arc - hard_landing; bool playerVars = p->newOwner != nullptr || ud.cameraactor != nullptr || p->over_shoulder_on > 0 || (p->GetActor()->s->pal != 1 && p->GetActor()->s->extra <= 0); - bool playerAnims = animatefist(shade,snum,look_anghalf) || animateknuckles(shade,snum,hard_landing,look_anghalf,horiz16th) || - animatetip(shade,snum,hard_landing,look_anghalf,horiz16th) || animateaccess(shade,snum,hard_landing,look_anghalf,horiz16th); + bool playerAnims = animatefist(shade, p, look_anghalf, looking_arc, plravel, pal) || animateknuckles(shade, p, look_anghalf, adjusted_arc, horiz16th, plravel, pal) || + animatetip(shade, p, look_anghalf, adjusted_arc, horiz16th, plravel, pal) || animateaccess(shade, p, look_anghalf, adjusted_arc, horiz16th, plravel); if(playerVars || playerAnims) return; - animateknee(shade,snum,hard_landing,look_anghalf,horiz16th); - - int opos = p->oweapon_pos * p->oweapon_pos; - int npos = p->weapon_pos * p->weapon_pos; - gun_pos = 80 - (opos + MulScaleF(npos - opos, smoothratio, 16)); - - weapon_xoffset = (160)-90; - weapon_xoffset -= bcosf(weapon_sway * 0.5) * (1. / 1536.); - weapon_xoffset -= 58 + p->weapon_ang; - if( p->GetActor()->s->xrepeat < 32 ) - gun_pos -= fabs(bsinf(weapon_sway * 4., -9)); - else gun_pos -= fabs(bsinf(weapon_sway * 0.5, -10)); - - gun_pos -= hard_landing * 8.; + animateknee(shade, p, look_anghalf, adjusted_arc, horiz16th, plravel, pal); if (isWW2GI()) { @@ -339,16 +303,6 @@ void displayweapon_d(int snum, double smoothratio) j = 14-p->quick_kick; if (j != 14 || p->last_quick_kick) { - if (p->GetActor()->s->pal == 1) - pal = 1; - else - { - pal = sector[p->cursectnum].floorpal; - if (pal == 0) - pal = p->palookup; - } - - if (j < 5 || j > 9) { hud_drawpal(weapon_xoffset + 80 - look_anghalf, looking_arc + 250 - gun_pos, KNEE, shade, o | 4, pal); @@ -393,15 +347,6 @@ void displayweapon_d(int snum, double smoothratio) { if (*kb > 0) { - if (p->GetActor()->s->pal == 1) - pal = 1; - else - { - pal = sector[p->cursectnum].floorpal; - if (pal == 0) - pal = p->palookup; - } - if (*kb < 5 || *kb > 9) { hud_drawpal(weapon_xoffset + 220 - look_anghalf, @@ -423,12 +368,6 @@ void displayweapon_d(int snum, double smoothratio) auto displaytripbomb = [&]() { - - if (p->GetActor()->s->pal == 1) - pal = 1; - else - pal = sector[p->cursectnum].floorpal; - weapon_xoffset += 8; gun_pos -= 10; @@ -457,10 +396,6 @@ void displayweapon_d(int snum, double smoothratio) pin = ((gs.displayflags & DUKE3D_NO_WIDESCREEN_PINNING)) ? 0 : RS_ALIGN_R; auto rpgpic = RPGGUN; - if (p->GetActor()->s->pal == 1) - pal = 1; - else pal = sector[p->cursectnum].floorpal; - weapon_xoffset -= bsinf(768 + (kickback_pic * 128.), -11); gun_pos += bsinf(768 + (kickback_pic * 128.), -11); @@ -505,12 +440,6 @@ void displayweapon_d(int snum, double smoothratio) auto displayshotgun_ww = [&]() { - if (p->GetActor()->s->pal == 1) - pal = 1; - else - pal = sector[p->cursectnum].floorpal; - - if (*kb > 0) { gun_pos -= bsinf(kickback_pic * 128., -12); @@ -563,12 +492,6 @@ void displayweapon_d(int snum, double smoothratio) auto displayshotgun = [&]() { - if (p->GetActor()->s->pal == 1) - pal = 1; - else - pal = sector[p->cursectnum].floorpal; - - weapon_xoffset -= 8; switch(*kb) @@ -639,11 +562,6 @@ void displayweapon_d(int snum, double smoothratio) auto displaychaingun_ww = [&]() { - if (p->GetActor()->s->pal == 1) - pal = 1; - else - pal = sector[p->cursectnum].floorpal; - if (*kb > 0) gun_pos -= bsinf(kickback_pic * 128., -12); @@ -745,11 +663,6 @@ void displayweapon_d(int snum, double smoothratio) auto displaychaingun = [&] { - if (p->GetActor()->s->pal == 1) - pal = 1; - else - pal = sector[p->cursectnum].floorpal; - if (*kb > 0) gun_pos -= bsinf(kickback_pic * 128., -12); @@ -789,11 +702,6 @@ void displayweapon_d(int snum, double smoothratio) auto displaypistol = [&]() { - if (p->GetActor()->s->pal == 1) - pal = 1; - else - pal = sector[p->cursectnum].floorpal; - if (*kb < 5) { short kb_frames[] = { 0,1,2,0,0 }; @@ -852,11 +760,6 @@ void displayweapon_d(int snum, double smoothratio) auto displayhandbomb = [&]() { - if (p->GetActor()->s->pal == 1) - pal = 1; - else - pal = sector[p->cursectnum].floorpal; - if (*kb) { static const uint8_t throw_frames[] @@ -912,10 +815,6 @@ void displayweapon_d(int snum, double smoothratio) auto displayhandremote = [&]() { signed char remote_frames[] = { 0,1,1,2,1,1,0,0,0,0,0 }; - if (p->GetActor()->s->pal == 1) - pal = 1; - else - pal = sector[p->cursectnum].floorpal; weapon_xoffset = -48; @@ -933,11 +832,6 @@ void displayweapon_d(int snum, double smoothratio) auto displaydevastator_ww = [&] { - if (p->GetActor()->s->pal == 1) - pal = 1; - else - pal = sector[p->cursectnum].floorpal; - if (*kb) { if (*kb < aplWeaponTotalTime[p->curr_weapon][snum]) @@ -994,11 +888,6 @@ void displayweapon_d(int snum, double smoothratio) auto displaydevastator = [&] { - if (p->GetActor()->s->pal == 1) - pal = 1; - else - pal = sector[p->cursectnum].floorpal; - if (*kb) { char cycloidy[] = { 0,4,12,24,12,4,0 }; @@ -1034,11 +923,6 @@ void displayweapon_d(int snum, double smoothratio) pin = (isWW2GI() || (gs.displayflags & DUKE3D_NO_WIDESCREEN_PINNING)) ? 0 : RS_ALIGN_R; auto pic = FREEZE; - if (p->GetActor()->s->pal == 1) - pal = 1; - else - pal = sector[p->cursectnum].floorpal; - if (*kb) { char cat_frames[] = { 0,0,1,1,2,2 }; @@ -1065,14 +949,11 @@ void displayweapon_d(int snum, double smoothratio) { weapon_xoffset += 28; looking_arc += 18; - if (p->GetActor()->s->pal == 1) - pal = 1; - else - pal = sector[p->cursectnum].floorpal; + if (*kb == 0) { // the 'at rest' display - if (ps[snum].ammo_amount[cw] <= 0) //p->last_weapon >= 0) + if (p->ammo_amount[cw] <= 0) //p->last_weapon >= 0) { hud_drawpal(weapon_xoffset + 184 - look_anghalf, looking_arc + 240 - gun_pos, SHRINKER + 3 + (*kb & 3), -32, @@ -1158,10 +1039,7 @@ void displayweapon_d(int snum, double smoothratio) { weapon_xoffset += 28; looking_arc += 18; - if (p->GetActor()->s->pal == 1) - pal = 1; - else - pal = sector[p->cursectnum].floorpal; + if (*kb == 0) { { @@ -1234,10 +1112,7 @@ void displayweapon_d(int snum, double smoothratio) auto shrinker = /*isWorldTour() ? SHRINKERWIDE :*/ SHRINKER; weapon_xoffset += 28; looking_arc += 18; - if (p->GetActor()->s->pal == 1) - pal = 1; - else - pal = sector[p->cursectnum].floorpal; + if (*kb == 0) { if (cw == GROW_WEAPON) @@ -1299,16 +1174,6 @@ void displayweapon_d(int snum, double smoothratio) auto displayflamethrower = [&]() { - if (p->GetActor()->s->pal == 1) - pal = 1; - else - { - if (p->cursectnum < 0) - pal = 0; - else - pal = sector[p->cursectnum].floorpal; - } - if (*kb < 1 || sector[p->cursectnum].lotag == 2) { hud_drawpal(weapon_xoffset + 210 - look_anghalf, looking_arc + 261 - gun_pos, FLAMETHROWER, shade, o, pal); @@ -1396,7 +1261,7 @@ void displayweapon_d(int snum, double smoothratio) } } - displayloogie(snum); + displayloogie(p); } diff --git a/source/games/duke/src/hudweapon_r.cpp b/source/games/duke/src/hudweapon_r.cpp index 8ed45057a..d04c09b18 100644 --- a/source/games/duke/src/hudweapon_r.cpp +++ b/source/games/duke/src/hudweapon_r.cpp @@ -65,15 +65,8 @@ inline static void rd3myospal(double x, double y, int tilenum, int shade, int or // //--------------------------------------------------------------------------- -void displaymasks_r(int snum, double smoothratio) +void displaymasks_r(int snum, int p, double smoothratio) { - short p; - - if (ps[snum].GetActor()->s->pal == 1) - p = 1; - else - p = sector[ps[snum].cursectnum].floorpal; - if (ps[snum].scuba_on) { //int pin = 0; @@ -116,7 +109,7 @@ void displayweapon_r(int snum, double smoothratio) { int cw; int i, j; - double weapon_sway, weapon_xoffset, gun_pos, looking_arc, look_anghalf, TiltStatus; + double weapon_sway, weapon_xoffset, gun_pos, looking_arc, look_anghalf, hard_landing, TiltStatus; char o,pal; signed char shade; @@ -125,10 +118,31 @@ void displayweapon_r(int snum, double smoothratio) o = 0; + if (cl_hudinterpolation) + { + weapon_sway = interpolatedvaluef(p->oweapon_sway, p->weapon_sway, smoothratio); + hard_landing = interpolatedvaluef(p->ohard_landing, p->hard_landing, smoothratio); + gun_pos = 80 - interpolatedvaluef(p->oweapon_pos * p->oweapon_pos, p->weapon_pos * p->weapon_pos, smoothratio); + TiltStatus = !SyncInput() ? p->TiltStatus : interpolatedvaluef(p->oTiltStatus, p->TiltStatus, smoothratio); + } + else + { + weapon_sway = p->weapon_sway; + hard_landing = p->hard_landing; + gun_pos = 80 - (p->weapon_pos * p->weapon_pos); + TiltStatus = p->TiltStatus; + } + look_anghalf = p->angle.look_anghalf(smoothratio); - looking_arc = fabs(look_anghalf) / 4.5; - weapon_sway = p->oweapon_sway + MulScaleF((p->weapon_sway - p->oweapon_sway), smoothratio, 16); - TiltStatus = !SyncInput() ? p->TiltStatus : p->oTiltStatus + MulScaleF((p->TiltStatus - p->oTiltStatus), smoothratio, 16); + looking_arc = p->angle.looking_arc(smoothratio); + hard_landing *= 8.; + + gun_pos -= fabs(p->GetActor()->s->xrepeat < 8 ? bsinf(weapon_sway * 4., -9) : bsinf(weapon_sway * 0.5, -10)); + gun_pos -= hard_landing; + + weapon_xoffset = (160)-90; + weapon_xoffset -= bcosf(weapon_sway * 0.5) * (1. / 1536.); + weapon_xoffset -= 58 + p->weapon_ang; if (shadedsector[p->cursectnum] == 1) shade = 16; @@ -136,22 +150,11 @@ void displayweapon_r(int snum, double smoothratio) shade = p->GetActor()->s->shade; if(shade > 24) shade = 24; + pal = p->GetActor()->s->pal == 1 ? 1 : pal = sector[p->cursectnum].floorpal; + if(p->newOwner != nullptr || ud.cameraactor != nullptr || p->over_shoulder_on > 0 || (p->GetActor()->s->pal != 1 && p->GetActor()->s->extra <= 0)) return; - int opos = p->oweapon_pos * p->oweapon_pos; - int npos = p->weapon_pos * p->weapon_pos; - gun_pos = 80 - (opos + MulScaleF(npos - opos, smoothratio, 16)); - - weapon_xoffset = (160)-90; - weapon_xoffset -= bcosf(weapon_sway * 0.5) * (1. / 1536.); - weapon_xoffset -= 58 + p->weapon_ang; - if( p->GetActor()->s->xrepeat < 8 ) - gun_pos -= fabs(bsinf(weapon_sway * 4., -9)); - else gun_pos -= fabs(bsinf(weapon_sway * 0.5, -10)); - - gun_pos -= (p->ohard_landing + MulScaleF(p->hard_landing - p->ohard_landing, smoothratio, 16)) * 8.; - if(p->last_weapon >= 0) cw = p->last_weapon; else cw = p->curr_weapon; @@ -214,10 +217,6 @@ void displayweapon_r(int snum, double smoothratio) else temp_kb = MOTOHIT; } - if (p->GetActor()->s->pal == 1) - pal = 1; - else - pal = sector[p->cursectnum].floorpal; ShowMotorcycle(160-look_anghalf, 174, temp_kb, shade, 0, pal, TiltStatus*5); return; @@ -278,11 +277,6 @@ void displayweapon_r(int snum, double smoothratio) temp_kb = BOATHIT; } - if (p->GetActor()->s->pal == 1) - pal = 1; - else - pal = sector[p->cursectnum].floorpal; - if (p->NotOnWater) temp3 = 170; else @@ -315,11 +309,6 @@ void displayweapon_r(int snum, double smoothratio) { int pin = 0; - if (p->GetActor()->s->pal == 1) - pal = 1; - else - pal = sector[p->cursectnum].floorpal; - //--------------------------------------------------------------------------- // @@ -500,11 +489,6 @@ void displayweapon_r(int snum, double smoothratio) { weapon_xoffset -= 8; - if (p->GetActor()->s->pal == 1) - pal = 1; - else - pal = sector[p->cursectnum].floorpal; - { double x; short y; diff --git a/source/games/duke/src/inlines.h b/source/games/duke/src/inlines.h index 4482602df..59bc1c057 100644 --- a/source/games/duke/src/inlines.h +++ b/source/games/duke/src/inlines.h @@ -164,7 +164,7 @@ inline float PlayerInputAngVel(int pl) return ps[pl].sync.avel; } -inline float PlayerHorizon(int pl) +inline float GetPlayerHorizon(int pl) { return ps[pl].sync.horz; } @@ -187,16 +187,10 @@ inline bool playrunning() return (paused == 0 || (paused == 1 && (ud.recstat == 2 || ud.multimode > 1))); } -// the weapon display code uses this. -inline double get16thOfHoriz(int const snum, bool const interpolate, double const smoothratio) -{ - return (!interpolate ? ps[snum].horizon.sum() : ps[snum].horizon.interpolatedsum(smoothratio)).asq16() * (0.0625 / FRACUNIT); -} - inline void doslopetilting(player_struct* p, double const scaleAdjust = 1) { bool const canslopetilt = p->on_ground && sector[p->cursectnum].lotag != ST_2_UNDERWATER && (sector[p->cursectnum].floorstat & 2); - calcviewpitch(p->pos.vec2, &p->horizon.horizoff, p->angle.ang, p->aim_mode == 0, canslopetilt, p->cursectnum, scaleAdjust); + p->horizon.calcviewpitch(p->pos.vec2, p->angle.ang, p->aim_mode == 0, canslopetilt, p->cursectnum, scaleAdjust); } diff --git a/source/games/duke/src/input.cpp b/source/games/duke/src/input.cpp index 96569a2f7..913312268 100644 --- a/source/games/duke/src/input.cpp +++ b/source/games/duke/src/input.cpp @@ -478,7 +478,7 @@ void hud_input(int plnum) } } - if (PlayerInput(plnum, SB_TURNAROUND) && p->angle.spin.asbam() == 0 && p->on_crane == nullptr) + if (PlayerInput(plnum, SB_TURNAROUND) && p->angle.spin == 0 && p->on_crane == nullptr) { SetGameVarID(g_iReturnVarID, 0, nullptr, plnum); OnEvent(EVENT_TURNAROUND, plnum, nullptr, -1); @@ -834,9 +834,9 @@ void GameInterface::GetInput(InputPacket* packet, ControlInfo* const hidInput) { // Do these in the same order as the old code. doslopetilting(p, scaleAdjust); - applylook(&p->angle, p->adjustavel(input.avel), &p->sync.actions, scaleAdjust); + p->angle.applyinput(p->adjustavel(input.avel), &p->sync.actions, scaleAdjust); p->apply_seasick(scaleAdjust); - sethorizon(&p->horizon, input.horz, &p->sync.actions, scaleAdjust); + p->horizon.applyinput(input.horz, &p->sync.actions, scaleAdjust); } p->angle.processhelpers(scaleAdjust); diff --git a/source/games/duke/src/namelist_d.h b/source/games/duke/src/namelist_d.h index e098e67b4..d05332000 100644 --- a/source/games/duke/src/namelist_d.h +++ b/source/games/duke/src/namelist_d.h @@ -517,6 +517,7 @@ x(THREEDEE, 2498) x(INGAMEDUKETHREEDEE, 2499) x(TENSCREEN, 2500) x(PLUTOPAKSPRITE, 2501) +x(TITLEPLUTOPAKSPRITE, 2502) x(MENUPLUTOPAKSPRITE, 2503) x(CREDITPAGE1, 2504) x(CREDITPAGE2, 2505) @@ -587,11 +588,33 @@ x(RESPAWNMARKERRED, 3190) x(RESPAWNMARKERYELLOW, 3200) x(RESPAWNMARKERGREEN, 3210) x(BONUSSCREEN, 3240) +x(BONUSSCREEN_O1, 3241) +x(BONUSSCREEN_O2, 3242) +x(BONUSSCREEN_O3, 3243) +x(BONUSSCREEN_O4, 3244) +x(BONUSSCREEN2, 3245) +x(BONUSSCREEN2_O1, 3246) +x(BONUSSCREEN2_O2, 3247) +x(BONUSSCREEN2_O3, 3248) +x(BONUSSCREEN2_O4, 3249) x(VIEWBORDER, 3250) x(VICTORY1, 3260) +x(VICTORY2, 3261) +x(VICTORY3, 3262) +x(VICTORY4, 3263) +x(VICTORY5, 3264) +x(VICTORY6, 3265) +x(VICTORY7, 3266) +x(VICTORY8, 3267) +x(VICTORY9, 3268) x(ORDERING, 3270) +x(ORDERING1, 3271) +x(ORDERING2, 3272) +x(ORDERING3, 3273) x(TEXTSTORY, 3280) x(LOADSCREEN, 3281) +x(SWEXIT2, 3290) +x(SWEXIT1, 3291) x(E1ENDSCREEN, 3292) x(E2ENDSCREEN, 3293) x(BORNTOBEWILDSCREEN, 3370) diff --git a/source/games/duke/src/namelist_r.h b/source/games/duke/src/namelist_r.h index 9429bf35c..46696dd5e 100644 --- a/source/games/duke/src/namelist_r.h +++ b/source/games/duke/src/namelist_r.h @@ -1241,6 +1241,7 @@ y(RRTILE8640, 8640) y(RRTILE8651, 8651) y(RRTILE8660, 8660) x(ENDGAME, 8677) +x(ENDGAME2, 8678) y(RRTILE8679, 8679) y(RRTILE8680, 8680) y(RRTILE8681, 8681) diff --git a/source/games/duke/src/names.h b/source/games/duke/src/names.h index 2318c9bd1..e29e19ba6 100644 --- a/source/games/duke/src/names.h +++ b/source/games/duke/src/names.h @@ -22,10 +22,6 @@ extern int TILE_CAMCORNER; extern int TILE_CAMLIGHT; extern int TILE_STATIC; extern int TILE_BOTTOMSTATUSBAR; -extern int TILE_THREEDEE; -extern int TILE_INGAMEDUKETHREEDEE; -extern int TILE_PLUTOPAKSPRITE; -extern int TILE_MENUBAR; extern int TILE_ATOMICHEALTH; extern int TILE_FLOORSLIME; extern int TILE_JIBS6; diff --git a/source/games/duke/src/player.cpp b/source/games/duke/src/player.cpp index 5f175a3c1..bcbc0b057 100644 --- a/source/games/duke/src/player.cpp +++ b/source/games/duke/src/player.cpp @@ -121,7 +121,7 @@ void forceplayerangle(int snum) p->horizon.addadjustment(64); p->sync.actions |= SB_CENTERVIEW; - p->angle.rotscrnang = p->angle.look_ang = buildlook(n >> 1); + p->angle.rotscrnang = p->angle.look_ang = buildang(n >> 1); } //--------------------------------------------------------------------------- @@ -174,7 +174,7 @@ int hits(DDukeActor* actor) int zoff; DDukeActor* d; - if (sp->picnum == TILE_APLAYER) zoff = (40 << 8); + if (sp->picnum == TILE_APLAYER) zoff = isRR() ? PHEIGHT_RR : PHEIGHT_DUKE; else zoff = 0; hitscan(sp->x, sp->y, sp->z - zoff, sp->sectnum, bcos(sp->ang), bsin(sp->ang), 0, §, &hw, &d, &sx, &sy, &sz, CLIPMASK1); @@ -630,7 +630,7 @@ void playerisdead(int snum, int psectlotag, int fz, int cz) pushmove(&p->posx, &p->posy, &p->posz, &p->cursectnum, 128L, (4 << 8), (20 << 8), CLIPMASK0); if (fz > cz + (16 << 8) && s->pal != 1) - p->angle.rotscrnang = buildlook(p->dead_flag + ((fz + p->posz) >> 7)); + p->angle.rotscrnang = buildang(p->dead_flag + ((fz + p->posz) >> 7)); p->on_warping_sector = 0; @@ -741,16 +741,16 @@ void player_struct::apply_seasick(double factor) if (SeaSick < 250) { if (SeaSick >= 180) - angle.rotscrnang += bamlook(xs_CRoundToUInt(24 * factor * BAMUNIT)); + angle.rotscrnang += buildfang(24 * factor); else if (SeaSick >= 130) - angle.rotscrnang -= bamlook(xs_CRoundToUInt(24 * factor * BAMUNIT)); + angle.rotscrnang -= buildfang(24 * factor); else if (SeaSick >= 70) - angle.rotscrnang += bamlook(xs_CRoundToUInt(24 * factor * BAMUNIT)); + angle.rotscrnang += buildfang(24 * factor); else if (SeaSick >= 20) - angle.rotscrnang -= bamlook(xs_CRoundToUInt(24 * factor * BAMUNIT)); + angle.rotscrnang -= buildfang(24 * factor); } if (SeaSick < 250) - angle.look_ang = bamlook(xs_CRoundToUInt(((krand() & 255) - 128) * factor * BAMUNIT)); + angle.look_ang = buildfang(((krand() & 255) - 128) * factor); } } diff --git a/source/games/duke/src/player_d.cpp b/source/games/duke/src/player_d.cpp index 6f3873836..856703d6d 100644 --- a/source/games/duke/src/player_d.cpp +++ b/source/games/duke/src/player_d.cpp @@ -2708,7 +2708,6 @@ void processinput_d(int snum) int j, k, doubvel, fz, cz, truefdist; Collision chz, clz; bool shrunk; - ESyncBits actions; short psect, psectlotag; struct player_struct* p; spritetype* s; @@ -2720,7 +2719,7 @@ void processinput_d(int snum) p->horizon.resetadjustment(); p->angle.resetadjustment(); - actions = PlayerInputBits(snum, SB_ALL); + ESyncBits& actions = p->sync.actions; auto sb_fvel = PlayerInputForwardVel(snum); auto sb_svel = PlayerInputSideVel(snum); @@ -2885,7 +2884,7 @@ void processinput_d(int snum) // may still be needed later for demo recording sb_avel = p->adjustavel(sb_avel); - applylook(&p->angle, sb_avel, &p->sync.actions); + p->angle.applyinput(sb_avel, &actions); } if (p->spritebridge == 0) @@ -3118,7 +3117,7 @@ HORIZONLY: if (SyncInput()) { - sethorizon(&p->horizon, PlayerHorizon(snum), &p->sync.actions); + p->horizon.applyinput(GetPlayerHorizon(snum), &actions); } p->checkhardlanding(); diff --git a/source/games/duke/src/player_r.cpp b/source/games/duke/src/player_r.cpp index 7726b3525..0c2befe90 100644 --- a/source/games/duke/src/player_r.cpp +++ b/source/games/duke/src/player_r.cpp @@ -1464,26 +1464,8 @@ int doincrements_r(struct player_struct* p) { if (!wupass) { - short snd; + int snd = currentLevel->rr_startsound ? currentLevel->rr_startsound : 391; wupass = 1; - switch (currentLevel->levelNumber) - { - default: snd = 391; break; - case levelnum(0, 0): snd = isRRRA() ? 63 : 391; break; - case levelnum(0, 1): snd = 64; break; - case levelnum(0, 2): snd = 77; break; - case levelnum(0, 3): snd = 80; break; - case levelnum(0, 4): snd = 102; break; - case levelnum(0, 5): snd = 103; break; - case levelnum(0, 6): snd = 104; break; - case levelnum(1, 0): snd = 105; break; - case levelnum(1, 1): snd = 176; break; - case levelnum(1, 2): snd = 177; break; - case levelnum(1, 3): snd = 198; break; - case levelnum(1, 4): snd = 230; break; - case levelnum(1, 5): snd = 255; break; - case levelnum(1, 6): snd = 283; break; - } S_PlayActorSound(snd, pact); } else if (PlayClock > 1024) @@ -1773,7 +1755,7 @@ static void onMotorcycle(int snum, ESyncBits &actions) if (p->MotoSpeed >= 20 && p->on_ground == 1 && (p->vehTurnLeft || p->vehTurnRight)) { velAdjustment = p->vehTurnLeft ? -10 : 10; - auto angAdjustment = buildlook(velAdjustment < 0 ? 350 : -350); + auto angAdjustment = (velAdjustment < 0 ? 350 : -350) << BAMBITS; if (p->moto_on_mud || p->moto_on_oil || !p->NotOnWater) { @@ -1811,7 +1793,7 @@ static void onMotorcycle(int snum, ESyncBits &actions) p->posxv += currSpeed * bcos(velAdjustment * -51 + p->angle.ang.asbuild(), 4); p->posyv += currSpeed * bsin(velAdjustment * -51 + p->angle.ang.asbuild(), 4); - p->angle.addadjustment(getincanglebam(p->angle.ang, p->angle.ang - angAdjustment)); + p->angle.addadjustment(getincanglebam(p->angle.ang, p->angle.ang - bamang(angAdjustment))); } else if (p->MotoSpeed >= 20 && p->on_ground == 1 && (p->moto_on_mud || p->moto_on_oil)) { @@ -2040,7 +2022,7 @@ static void onBoat(int snum, ESyncBits &actions) { int currSpeed = p->MotoSpeed * 4.; short velAdjustment = p->vehTurnLeft ? -10 : 10; - auto angAdjustment = buildlook(velAdjustment < 0 ? 350 : -350); + auto angAdjustment = (velAdjustment < 0 ? 350 : -350) << BAMBITS; if (p->moto_do_bump) { @@ -2055,7 +2037,7 @@ static void onBoat(int snum, ESyncBits &actions) p->posxv += currSpeed * bcos(velAdjustment * -51 + p->angle.ang.asbuild(), 4); p->posyv += currSpeed * bsin(velAdjustment * -51 + p->angle.ang.asbuild(), 4); - p->angle.addadjustment(getincanglebam(p->angle.ang, p->angle.ang - angAdjustment)); + p->angle.addadjustment(getincanglebam(p->angle.ang, p->angle.ang - bamang(angAdjustment))); } if (p->NotOnWater && p->MotoSpeed > 50) p->MotoSpeed -= (p->MotoSpeed / 2.); @@ -3353,7 +3335,6 @@ void processinput_r(int snum) int i, k, doubvel, fz, cz, truefdist; Collision chz, clz; char shrunk; - ESyncBits actions; short psect, psectlotag; auto p = &ps[snum]; @@ -3363,7 +3344,7 @@ void processinput_r(int snum) p->horizon.resetadjustment(); p->angle.resetadjustment(); - actions = PlayerInputBits(snum, SB_ALL); + ESyncBits& actions = p->sync.actions; auto sb_fvel = PlayerInputForwardVel(snum); auto sb_svel = PlayerInputSideVel(snum); @@ -3400,8 +3381,7 @@ void processinput_r(int snum) psectlotag = 2; } } - else if (psectlotag == 7777) - if (currentLevel->levelNumber == levelnum(1, 6)) + else if (psectlotag == 7777 && (currentLevel->gameflags & LEVEL_RR_HULKSPAWN)) lastlevel = 1; if (psectlotag == 848 && sector[psect].floorpicnum == WATERTILE2) @@ -3639,7 +3619,7 @@ void processinput_r(int snum) // may still be needed later for demo recording sb_avel = p->adjustavel(sb_avel); - applylook(&p->angle, sb_avel, &p->sync.actions); + p->angle.applyinput(sb_avel, &actions); } if (p->spritebridge == 0) @@ -4001,7 +3981,7 @@ HORIZONLY: if (SyncInput()) { - sethorizon(&p->horizon, PlayerHorizon(snum), &p->sync.actions); + p->horizon.applyinput(GetPlayerHorizon(snum), &actions); } p->checkhardlanding(); diff --git a/source/games/duke/src/premap.cpp b/source/games/duke/src/premap.cpp index c81577d27..af1e757fd 100644 --- a/source/games/duke/src/premap.cpp +++ b/source/games/duke/src/premap.cpp @@ -36,6 +36,8 @@ Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms #include "automap.h" #include "dukeactor.h" #include "interpolate.h" +#include "precache.h" +#include "render.h" BEGIN_DUKE_NS @@ -140,8 +142,8 @@ void resetplayerstats(int snum) p->jetpack_on = 0; p->holoduke_on = nullptr; - p->angle.olook_ang = p->angle.look_ang = buildlook(512 - ((currentLevel->levelNumber & 1) << 10)); - p->angle.orotscrnang = p->angle.rotscrnang = buildlook(0); + p->angle.olook_ang = p->angle.look_ang = buildang(512 - (((~currentLevel->levelNumber) & 1) << 10)); + p->angle.orotscrnang = p->angle.rotscrnang = buildang(0); p->newOwner =nullptr; p->jumping_counter = 0; @@ -152,7 +154,7 @@ void resetplayerstats(int snum) p->fric.x = 0; p->fric.y = 0; p->somethingonplayer =nullptr; - p->angle.spin = bamlook(0); + p->angle.spin = 0; p->on_crane = nullptr; @@ -658,7 +660,6 @@ void prelevel_common(int g) p->SlotWin = 0; enemysizecheat = 0; p->MamaEnd = 0; - mamaspawn_count = 15; banjosound = 0; RRRA_ExitedLevel = 0; @@ -671,7 +672,7 @@ void prelevel_common(int g) WindDir = 0; fakebubba_spawn = 0; RRRA_ExitedLevel = 0; - mamaspawn_count = 15; + mamaspawn_count = currentLevel->rr_mamaspawn; BellTime = 0; BellSprite = nullptr; @@ -753,7 +754,6 @@ void donewgame(MapRecord* map, int sk) auto p = &ps[0]; show_shareware = 26 * 34; - //ud.nextLevel = map; ud.player_skill = sk; ud.secretlevel = 0; ud.from_bonus = 0; @@ -812,31 +812,122 @@ void donewgame(MapRecord* map, int sk) } } -template -void newgame(MapRecord* map, int sk, func completion) +//--------------------------------------------------------------------------- +// +// the setup here is very, very sloppy, because mappings are not 1:1. +// Each portal can have multiple sectors, and even extends to unmarked +// neighboring sectors if they got the portal tile as floor or ceiling +// +//--------------------------------------------------------------------------- + +static void SpawnPortals() { - auto completion1 = [=](bool res) + for (int i = 0; i < numwalls; i++) { - if (!isRR() && map->levelNumber == levelnum(3, 0) && (ud.multimode < 2)) + if (wall[i].overpicnum == TILE_MIRROR) wall[i].portalflags |= PORTAL_WALL_MIRROR; + } + + portalClear(); + int tag; + if (!isRR()) tag = 40; + else if (isRRRA()) tag = 150; + else return; + + TArray processedTags; + DukeStatIterator it(STAT_RAROR); + while (auto act = it.Next()) + { + auto spr = act->s; + if (spr->picnum == SECTOREFFECTOR && spr->lotag == tag) { - e4intro([=](bool) { donewgame(map, sk); completion(res); }); + if (processedTags.Find(spr->hitag) == processedTags.Size()) + { + DukeStatIterator it2(STAT_RAROR); + while (auto act2 = it2.Next()) + { + auto spr2 = act2->s; + if (spr2->picnum == SECTOREFFECTOR && spr2->lotag == tag + 1 && spr2->hitag == spr->hitag) + { + if (processedTags.Find(spr->hitag) == processedTags.Size()) + { + int s1 = spr->sectnum, s2 = spr2->sectnum; + sector[s1].portalflags = PORTAL_SECTOR_FLOOR; + sector[s2].portalflags = PORTAL_SECTOR_CEILING; + sector[s1].portalnum = portalAdd(PORTAL_SECTOR_FLOOR, s2, spr2->x - spr->x, spr2->y - spr->y, spr->hitag); + sector[s2].portalnum = portalAdd(PORTAL_SECTOR_CEILING, s1, spr->x - spr2->x, spr->y - spr2->y, spr->hitag); + processedTags.Push(spr->hitag); + } + else + { + for (auto& p : allPortals) + { + if (p.type == PORTAL_SECTOR_FLOOR && p.dz == spr->hitag) + { + p.targets.Push(spr2->sectnum); + } + } + } + } + } + } + else + { + for (auto& p : allPortals) + { + if (p.type == PORTAL_SECTOR_CEILING && p.dz == spr->hitag) + { + p.targets.Push(spr->sectnum); + } + } + } } - else + } + // Unfortunately the above still isn't enough. We got to do one more check to add stuff to the portals. + // There is one map where a sector neighboring a portal is not marked as part of the portal itself. + for (int i = 0; i < numsectors; i++) + { + if (sector[i].floorpicnum == FOF && sector[i].portalflags != PORTAL_SECTOR_FLOOR) { - donewgame(map, sk); - completion(res); + for (auto& pt : allPortals) + { + if (pt.type == PORTAL_SECTOR_CEILING) + { + for (auto& t : pt.targets) + { + if (findwallbetweensectors(i, t) >= 0) + { + sector[i].portalflags = PORTAL_SECTOR_FLOOR; + sector[i].portalnum = uint8_t(1 ^ (&pt - allPortals.Data())); + pt.targets.Push(i); + goto nexti; + } + } + } + } } - }; - - if (ud.m_recstat != 2 && ud.last_level >= 0 && ud.multimode > 1 && ud.coop != 1) - dobonus(1, completion1); - -#if 0 // this is one lousy hack job that's hopefully not needed anymore. - else if (isRR() && !isRRRA() && map->levelNumber == levelnum(0, 6)) - dobonus(0, completion1); -#endif - - else completion1(false); + else if (sector[i].ceilingpicnum == FOF && sector[i].portalflags != PORTAL_SECTOR_CEILING) + { + for (auto& pt : allPortals) + { + if (pt.type == PORTAL_SECTOR_FLOOR) + { + for (auto& t : pt.targets) + { + if (findwallbetweensectors(i, t) >= 0) + { + sector[i].portalflags = PORTAL_SECTOR_CEILING; + sector[i].portalnum = uint8_t(1 ^ (&pt - allPortals.Data())); + pt.targets.Push(i); + goto nexti; + } + } + } + } + } + nexti:; + } + for (auto& p : allPortals) p.dz = 0; + mergePortals(); } //--------------------------------------------------------------------------- @@ -859,12 +950,6 @@ static int LoadTheMap(MapRecord *mi, struct player_struct *p, int gamemode) SECRET_SetMapName(mi->DisplayName(), mi->name); STAT_NewLevel(mi->fileName); - if (isRR() && !isRRRA() && mi->levelNumber == levelnum(1, 1)) - { - for (int i = PISTOL_WEAPON; i < MAX_WEAPONS; i++) - ps[0].ammo_amount[i] = 0; - ps[0].gotweapon.Clear(KNEE_WEAPON); - } p->angle.ang = buildang(lbang); memset(gotpic, 0, sizeof(gotpic)); @@ -872,15 +957,7 @@ static int LoadTheMap(MapRecord *mi, struct player_struct *p, int gamemode) if (isRR()) prelevel_r(gamemode); else prelevel_d(gamemode); - if (isRRRA() && mi->levelNumber == levelnum(2, 0)) - { - for (int i = PISTOL_WEAPON; i < MAX_WEAPONS; i++) - ps[0].ammo_amount[i] = 0; - ps[0].gotweapon.Clear(KNEE_WEAPON); - ps[0].gotweapon.Set(SLINGBLADE_WEAPON); - ps[0].ammo_amount[SLINGBLADE_WEAPON] = 1; - ps[0].curr_weapon = SLINGBLADE_WEAPON; - } + SpawnPortals(); allignwarpelevators(); resetpspritevars(gamemode); @@ -926,7 +1003,6 @@ void enterlevel(MapRecord *mi, int gamemode) OnEvent(EVENT_ENTERLEVEL); // Stop all sounds - S_ResumeSound(false); FX_StopAllSounds(); FX_SetReverb(0); @@ -943,27 +1019,32 @@ void enterlevel(MapRecord *mi, int gamemode) S_PlayLevelMusic(mi); } - if (isShareware() && mi->levelNumber == 0 && ud.recstat != 2) FTA(QUOTE_F1HELP, &ps[myconnectindex]); - for (int i = connecthead; i >= 0; i = connectpoint2[i]) { + bool clearweapon = !!(currentLevel->flags & LEVEL_CLEARWEAPONS); int pn = sector[ps[i].GetActor()->s->sectnum].floorpicnum; if (pn == TILE_HURTRAIL || pn == TILE_FLOORSLIME || pn == TILE_FLOORPLASMA) { - resetweapons(i); resetinventory(i); + clearweapon = true; + } + if (clearweapon) + { + resetweapons(i); ps[i].gotweapon.Clear(PISTOL_WEAPON); ps[i].ammo_amount[PISTOL_WEAPON] = 0; ps[i].curr_weapon = KNEE_WEAPON; + ps[i].kickback_pic = 0; ps[i].okickback_pic = ps[i].kickback_pic = 0; } + if (currentLevel->flags & LEVEL_CLEARINVENTORY) resetinventory(i); } resetmys(); everyothertime = 0; global_random = 0; - ud.last_level = currentLevel->levelNumber; + ud.last_level = 1; ps[myconnectindex].over_shoulder_on = 0; clearfrags(); resettimevars(); // Here we go @@ -984,9 +1065,19 @@ void enterlevel(MapRecord *mi, int gamemode) // //--------------------------------------------------------------------------- -void startnewgame(MapRecord* map, int skill) +void GameInterface::NewGame(MapRecord* map, int skill, bool) { + for (int i = 0; i != -1; i = connectpoint2[i]) + { + resetweapons(i); + resetinventory(i); + } + + ps[0].last_extra = gs.max_player_health; + + if (skill == -1) skill = ud.player_skill; + else skill++; ud.player_skill = skill; ud.m_respawn_monsters = (skill == 4); ud.m_monsters_off = ud.monsters_off = 0; @@ -994,13 +1085,13 @@ void startnewgame(MapRecord* map, int skill) ud.m_respawn_inventory = 0; ud.multimode = 1; - newgame(map, skill, [=](bool) - { + donewgame(map, skill); enterlevel(map, 0); + if (isShareware() && ud.recstat != 2) FTA(QUOTE_F1HELP, &ps[myconnectindex]); + PlayerColorChanged(); inputState.ClearAllInput(); gameaction = ga_level; - }); } //--------------------------------------------------------------------------- @@ -1011,25 +1102,27 @@ void startnewgame(MapRecord* map, int skill) bool setnextmap(bool checksecretexit) { - MapRecord* map = nullptr;; - int from_bonus = 0; + MapRecord* map = nullptr; + MapRecord* from_bonus = nullptr; - if (ud.eog) + if (ud.eog && !(currentLevel->flags & LEVEL_FORCENOEOG)) { } else if (checksecretexit && ud.from_bonus == 0) { if (ud.secretlevel > 0) { - int newlevnum = levelnum(volfromlevelnum(currentLevel->levelNumber), ud.secretlevel-1); - map = FindMapByLevelNum(newlevnum); + // allow overriding the secret exit destination to make episode compilation easier with maps containing secret exits. + if (currentLevel->flags & LEVEL_SECRETEXITOVERRIDE) map = FindNextSecretMap(currentLevel); + if (!map) map = FindMapByIndex(currentLevel->cluster, ud.secretlevel); + if (map) { - from_bonus = currentLevel->levelNumber + 1; + from_bonus = FindNextMap(currentLevel); } } } - else if (ud.from_bonus && currentLevel->nextLevel == -1) // if the current level has an explicit link, use that instead of ud.from_bonus. + else if (ud.from_bonus && currentLevel->NextMap.IsEmpty()) // if the current level has an explicit link, use that instead of ud.from_bonus. { map = FindMapByLevelNum(ud.from_bonus); } @@ -1048,7 +1141,7 @@ bool setnextmap(bool checksecretexit) { I_Error("Trying to open non-existent %s", map->fileName.GetChars()); } - ud.from_bonus = from_bonus; + ud.from_bonus = from_bonus? from_bonus->levelNumber : 0; } CompleteLevel(map); return false; @@ -1060,30 +1153,33 @@ bool setnextmap(bool checksecretexit) // //--------------------------------------------------------------------------- -void exitlevel(MapRecord *nextlevel) +void exitlevel(MapRecord* nextlevel) { bool endofgame = nextlevel == nullptr; STAT_Update(endofgame); StopCommentary(); - dobonus(endofgame? -1 : 0, [=](bool) - { + SummaryInfo info{}; + info.kills = ps[0].actors_killed; + info.maxkills = ps[0].max_actors_killed; + info.secrets = ps[0].secret_rooms; + info.maxsecrets = ps[0].max_secret_rooms; + info.time = ps[0].player_par / GameTicRate; + info.endofgame = endofgame; + Mus_Stop(); + + if (playerswhenstarted > 1 && ud.coop != 1) + { + // MP scoreboard + ShowScoreboard(playerswhenstarted, [=](bool) + { // Clear potentially loaded per-map ART only after the bonus screens. artClearMapArt(); gameaction = ga_level; ud.eog = false; if (endofgame) { - if (ud.multimode < 2) - { - if (isShareware()) - doorders([](bool) { gameaction = ga_startup; }); - else gameaction = ga_startup; - return; - } - else - { auto nextlevel = FindMapByLevelNum(0); if (!nextlevel) { @@ -1092,11 +1188,22 @@ void exitlevel(MapRecord *nextlevel) } else gameaction = ga_nextlevel; } - } - else + else gameaction = ga_nextlevel; }); + } + else if (ud.multimode <= 1) + { + // SP cutscene + summary + ShowIntermission(currentLevel, nextlevel, &info, [=](bool) + { + // Clear potentially loaded per-map ART only after the bonus screens. + artClearMapArt(); + ud.eog = false; + gameaction = endofgame? ga_startup : ga_nextlevel; + }); + } } diff --git a/source/games/duke/src/premap_d.cpp b/source/games/duke/src/premap_d.cpp index c484017cd..2d1c9f61a 100644 --- a/source/games/duke/src/premap_d.cpp +++ b/source/games/duke/src/premap_d.cpp @@ -232,6 +232,7 @@ static void cachegoodsprites(void) void cacheit_d(void) { + if (!r_precache) return; int i; cachegoodsprites(); diff --git a/source/games/duke/src/premap_r.cpp b/source/games/duke/src/premap_r.cpp index a7aa8d365..cb86ffc62 100644 --- a/source/games/duke/src/premap_r.cpp +++ b/source/games/duke/src/premap_r.cpp @@ -366,37 +366,6 @@ static void cachegoodsprites(void) for (i = SMALLSMOKE; i < (SMALLSMOKE + 4); i++) tloadtile(i); - if (isRRRA() && currentLevel->levelNumber == levelnum(0, 4)) - { - tloadtile(RRTILE2577); - } - if (!isRRRA() && currentLevel->levelNumber == levelnum(1, 2)) - { - tloadtile(RRTILE3190); - tloadtile(RRTILE3191); - tloadtile(RRTILE3192); - tloadtile(RRTILE3144); - tloadtile(RRTILE3139); - tloadtile(RRTILE3132); - tloadtile(RRTILE3120); - tloadtile(RRTILE3121); - tloadtile(RRTILE3122); - tloadtile(RRTILE3123); - tloadtile(RRTILE3124); - } - if (lastlevel) - { - i = isRRRA() ? UFO1_RRRA : UFO1_RR; - tloadtile(i); - i = UFO2; - tloadtile(i); - i = UFO3; - tloadtile(i); - i = UFO4; - tloadtile(i); - i = UFO5; - tloadtile(i); - } } //--------------------------------------------------------------------------- @@ -407,6 +376,7 @@ static void cachegoodsprites(void) void cacheit_r(void) { + if (!r_precache) return; int i; cachegoodsprites(); @@ -461,11 +431,11 @@ void prelevel_r(int g) prelevel_common(g); p = &ps[screenpeek]; + if (currentLevel->gameflags & LEVEL_RR_CLEARMOONSHINE) + ps[myconnectindex].steroids_amount = 0; if (isRRRA()) { - if (currentLevel->levelNumber == levelnum(1, 4)) - ps[myconnectindex].steroids_amount = 0; for (j = 0; j < MAXSPRITES; j++) { diff --git a/source/games/duke/src/render.cpp b/source/games/duke/src/render.cpp index 294f409cc..71561d80f 100644 --- a/source/games/duke/src/render.cpp +++ b/source/games/duke/src/render.cpp @@ -33,8 +33,16 @@ Modifications for JonoF's port by Jonathon Fowler (jf@jonof.id.au) #include "automap.h" #include "dukeactor.h" #include "interpolate.h" +#include "render.h" #include "glbackend/glbackend.h" +#include "_polymost.cpp" + +// temporary hack to pass along RRRA's global fog. Needs to be done better later. +extern PalEntry GlobalMapFog; +extern float GlobalFogDensity; + +EXTERN_CVAR(Bool, testnewrenderer) BEGIN_DUKE_NS @@ -56,201 +64,26 @@ BEGIN_DUKE_NS // //--------------------------------------------------------------------------- -static int tempsectorz[MAXSECTORS]; -static int tempsectorpicnum[MAXSECTORS]; +/*static*/ int tempsectorz[MAXSECTORS]; +/*static*/ int tempsectorpicnum[MAXSECTORS]; //short tempcursectnum; -void SE40_Draw(int tag, spritetype *spr, int x, int y, int z, binangle a, fixedhoriz h, int smoothratio) +void renderView(spritetype* playersprite, int sectnum, int x, int y, int z, binangle a, fixedhoriz h, binangle rotscrnang, int smoothratio) { - int i, j = 0, k = 0; - int ok = 0, fofmode = 0; - int offx, offy; - spritetype* floor1, *floor2 = nullptr; - - if (spr->ang != 512) return; - - i = FOF; //Effect TILE - tileDelete(FOF); - if (!(gotpic[i >> 3] & (1 << (i & 7)))) return; - gotpic[i >> 3] &= ~(1 << (i & 7)); - - floor1 = spr; - - if (spr->lotag == tag + 2) fofmode = tag + 0; - if (spr->lotag == tag + 3) fofmode = tag + 1; - if (spr->lotag == tag + 4) fofmode = tag + 0; - if (spr->lotag == tag + 5) fofmode = tag + 1; - - ok++; - - DukeStatIterator it(STAT_RAROR); - while (auto act = it.Next()) + if (!testnewrenderer) { - auto spr = act->s; - if ( - spr->picnum == SECTOREFFECTOR && - spr->lotag == fofmode && - spr->hitag == floor1->hitag - ) - { - floor1 = spr; - fofmode = spr->lotag; - ok++; - break; - } + // do screen rotation. + renderSetRollAngle(rotscrnang.asbuildf()); + + se40code(x, y, z, a, h, smoothratio); + renderMirror(x, y, z, a, h, smoothratio); + renderDrawRoomsQ16(x, y, z, a.asq16(), h.asq16(), sectnum); + fi.animatesprites(pm_tsprite, pm_spritesortcnt, x, y, a.asbuild(), smoothratio); + renderDrawMasks(); } - // if(ok==1) { Message("no floor1",RED); return; } - - if (fofmode == tag + 0) k = tag + 1; else k = tag + 0; - - it.Reset(STAT_RAROR); - while (auto act = it.Next()) + else { - auto spr = act->s; - if ( - spr->picnum == SECTOREFFECTOR && - spr->lotag == k && - spr->hitag == floor1->hitag - ) - { - floor2 = spr; - ok++; - break; - } - } - - // if(ok==2) { Message("no floor2",RED); return; } - - it.Reset(STAT_RAROR); - while (auto act = it.Next()) - { - auto spr = act->s; - if (spr->picnum == SECTOREFFECTOR && - spr->lotag == k + 2 && - spr->hitag == floor1->hitag - ) - { - if (k == tag + 0) - { - tempsectorz[spr->sectnum] = sector[spr->sectnum].floorz; - sector[spr->sectnum].floorz += (((z - sector[spr->sectnum].floorz) / 32768) + 1) * 32768; - tempsectorpicnum[spr->sectnum] = sector[spr->sectnum].floorpicnum; - sector[spr->sectnum].floorpicnum = 13; - } - if (k == tag + 1) - { - tempsectorz[spr->sectnum] = sector[spr->sectnum].ceilingz; - sector[spr->sectnum].ceilingz += (((z - sector[spr->sectnum].ceilingz) / 32768) - 1) * 32768; - tempsectorpicnum[spr->sectnum] = sector[spr->sectnum].ceilingpicnum; - sector[spr->sectnum].ceilingpicnum = 13; - } - } - } - - offx = x - floor1->x; - offy = y - floor1->y; - - renderDrawRoomsQ16(floor2->x + offx, floor2->y + offy, z, a.asq16(), h.asq16(), floor2->sectnum); - fi.animatesprites(offx + floor2->x, offy + floor2->y, a.asbuild(), smoothratio); - renderDrawMasks(); - - it.Reset(STAT_RAROR); - while (auto act = it.Next()) - { - auto spr = act->s; - if (spr->picnum == 1 && - spr->lotag == k + 2 && - spr->hitag == floor1->hitag - ) - { - if (k == tag + 0) - { - sector[spr->sectnum].floorz = tempsectorz[spr->sectnum]; - sector[spr->sectnum].floorpicnum = tempsectorpicnum[spr->sectnum]; - } - if (k == tag + 1) - { - sector[spr->sectnum].ceilingz = tempsectorz[spr->sectnum]; - sector[spr->sectnum].ceilingpicnum = tempsectorpicnum[spr->sectnum]; - } - }// end if - }// end for - -} // end SE40 - - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -void se40code(int x, int y, int z, binangle a, fixedhoriz h, int smoothratio) -{ - int tag; - if (!isRR()) tag = 40; - else if (isRRRA()) tag = 150; - else return; - - DukeStatIterator it(STAT_RAROR); - while (auto act = it.Next()) - { - switch (act->s->lotag - tag + 40) - { - // case 40: - // case 41: - // SE40_Draw(i,x,y,a,smoothratio); - // break; - case 42: - case 43: - case 44: - case 45: - if (ps[screenpeek].cursectnum == act->s->sectnum) - SE40_Draw(tag, act->s, x, y, z, a, h, smoothratio); - break; - } - } -} - - -//--------------------------------------------------------------------------- -// -// split out so it can also be applied to camera views -// -//--------------------------------------------------------------------------- - -void renderMirror(int cposx, int cposy, int cposz, binangle cang, fixedhoriz choriz, int smoothratio) -{ - if ((gotpic[TILE_MIRROR >> 3] & (1 << (TILE_MIRROR & 7))) > 0) - { - int dst = 0x7fffffff, i = 0; - for (int k = 0; k < mirrorcnt; k++) - { - int j = abs(wall[mirrorwall[k]].x - cposx) + abs(wall[mirrorwall[k]].y - cposy); - if (j < dst) dst = j, i = k; - } - - if (wall[mirrorwall[i]].overpicnum == TILE_MIRROR) - { - int tposx, tposy; - fixed_t tang; - - renderPrepareMirror(cposx, cposy, cposz, cang.asq16(), choriz.asq16(), mirrorwall[i], &tposx, &tposy, &tang); - - int j = g_visibility; - g_visibility = (j >> 1) + (j >> 2); - - renderDrawRoomsQ16(tposx, tposy, cposz, tang, choriz.asq16(), mirrorsector[i] + MAXSECTORS); - - display_mirror = 1; - fi.animatesprites(tposx, tposy, tang, smoothratio); - display_mirror = 0; - - renderDrawMasks(); - renderCompleteMirror(); //Reverse screen x-wise in this function - g_visibility = j; - } - gotpic[TILE_MIRROR >> 3] &= ~(1 << (TILE_MIRROR & 7)); + render_drawrooms(playersprite, { x, y, z }, sectnum, a, h, rotscrnang, smoothratio); } } @@ -260,7 +93,7 @@ void renderMirror(int cposx, int cposy, int cposz, binangle cang, fixedhoriz cho // //--------------------------------------------------------------------------- -void animatecamsprite(double smoothratio) +void GameInterface::UpdateCameras(double smoothratio) { const int VIEWSCREEN_ACTIVE_DISTANCE = 8192; @@ -284,17 +117,51 @@ void animatecamsprite(double smoothratio) { auto camera = camsprite->GetOwner()->s; auto ang = buildang(camera->interpolatedang(smoothratio)); - // Note: no ROR or camera here for now - the current setup has no means to detect these things before rendering the scene itself. - renderDrawRoomsQ16(camera->x, camera->y, camera->z, ang.asq16(), IntToFixed(camera->shade), camera->sectnum); // why 'shade'...? display_mirror = 1; // should really be 'display external view'. - fi.animatesprites(camera->x, camera->y, ang.asbuild(), smoothratio); + if (!testnewrenderer) + { + // Note: no ROR or camera here - Polymost has no means to detect these things before rendering the scene itself. + renderDrawRoomsQ16(camera->x, camera->y, camera->z, ang.asq16(), IntToFixed(camera->shade), camera->sectnum); // why 'shade'...? + fi.animatesprites(pm_tsprite, pm_spritesortcnt, camera->x, camera->y, ang.asbuild(), smoothratio); + renderDrawMasks(); + } + else + { + render_camtex(camera, camera->pos, camera->sectnum, ang, buildhoriz(camera->shade), buildang(0), tex, rect, smoothratio); + } display_mirror = 0; - renderDrawMasks(); }); renderRestoreTarget(); } } +void GameInterface::EnterPortal(spritetype* viewer, int type) +{ + if (type == PORTAL_WALL_MIRROR) display_mirror++; +} + +void GameInterface::LeavePortal(spritetype* viewer, int type) +{ + if (type == PORTAL_WALL_MIRROR) display_mirror--; +} + +bool GameInterface::GetGeoEffect(GeoEffect* eff, int viewsector) +{ + if (isRR() && sector[viewsector].lotag == 848) + { + eff->geocnt = geocnt; + eff->geosector = geosector; + eff->geosectorwarp = geosectorwarp; + eff->geosectorwarp2 = geosectorwarp2; + eff->geox = geox; + eff->geoy = geoy; + eff->geox2 = geox2; + eff->geoy2 = geoy2; + return true; + } + return false; +} + //--------------------------------------------------------------------------- // // RRRA's drug distortion effect @@ -380,84 +247,6 @@ static int getdrugmode(player_struct *p, int oyrepeat) } } -//--------------------------------------------------------------------------- -// -// used by RR to inject some external geometry into a scene. -// -//--------------------------------------------------------------------------- - -static void geometryEffect(int cposx, int cposy, int cposz, binangle cang, fixedhoriz choriz, int sect, int smoothratio) -{ - short gs, tgsect, geosect, geoid = 0; - renderDrawRoomsQ16(cposx, cposy, cposz, cang.asq16(), choriz.asq16(), sect); - fi.animatesprites(cposx, cposy, cang.asbuild(), smoothratio); - renderDrawMasks(); - for (gs = 0; gs < geocnt; gs++) - { - tgsect = geosector[gs]; - - DukeSectIterator it(tgsect); - while (auto act = it.Next()) - { - changespritesect(act, geosectorwarp[gs]); - setsprite(act, act->s->x -= geox[gs], act->s->y -= geoy[gs], act->s->z); - } - if (geosector[gs] == sect) - { - geosect = geosectorwarp[gs]; - geoid = gs; - } - } - cposx -= geox[geoid]; - cposy -= geoy[geoid]; - renderDrawRoomsQ16(cposx, cposy, cposz, cang.asq16(), choriz.asq16(), sect); - cposx += geox[geoid]; - cposy += geoy[geoid]; - for (gs = 0; gs < geocnt; gs++) - { - tgsect = geosectorwarp[gs]; - DukeSectIterator it(tgsect); - while (auto act = it.Next()) - { - changespritesect(act, geosector[gs]); - setsprite(act, act->s->x += geox[gs], act->s->y += geoy[gs], act->s->z); - } - } - fi.animatesprites(cposx, cposy, cang.asbuild(), smoothratio); - renderDrawMasks(); - for (gs = 0; gs < geocnt; gs++) - { - tgsect = geosector[gs]; - DukeSectIterator it(tgsect); - while (auto act = it.Next()) - { - changespritesect(act, geosectorwarp2[gs]); - setsprite(act, act->s->x -= geox2[gs], act->s->y -= geoy2[gs], act->s->z); - } - if (geosector[gs] == sect) - { - geosect = geosectorwarp2[gs]; - geoid = gs; - } - } - cposx -= geox2[geoid]; - cposy -= geoy2[geoid]; - renderDrawRoomsQ16(cposx, cposy, cposz, cang.asq16(), choriz.asq16(), sect); - cposx += geox2[geoid]; - cposy += geoy2[geoid]; - for (gs = 0; gs < geocnt; gs++) - { - tgsect = geosectorwarp2[gs]; - DukeSectIterator it(tgsect); - while (auto act = it.Next()) - { - changespritesect(act, geosector[gs]); - setsprite(act, act->s->x += geox2[gs], act->s->y += geoy2[gs], act->s->z); - } - } - fi.animatesprites(cposx, cposy, cang.asbuild(), smoothratio); - renderDrawMasks(); -} //--------------------------------------------------------------------------- // // @@ -468,8 +257,7 @@ void displayrooms(int snum, double smoothratio) { int cposx, cposy, cposz, fz, cz; short sect; - binangle cang; - lookangle rotscrnang; + binangle cang, rotscrnang; fixedhoriz choriz; struct player_struct* p; int tiltcs = 0; // JBF 20030807 @@ -493,11 +281,13 @@ void displayrooms(int snum, double smoothratio) sect = p->cursectnum; if (sect < 0 || sect >= MAXSECTORS) return; + GlobalMapFog = fogactive ? 0x999999 : 0; + GlobalFogDensity = fogactive ? 350.f : 0.f; GLInterface.SetMapFog(fogactive != 0); DoInterpolations(smoothratio / 65536.); setgamepalette(BASEPAL); - animatecamsprite(smoothratio); + if (!testnewrenderer) gi->UpdateCameras(smoothratio); // Only Polymost does this here. The new renderer calls this internally. if (ud.cameraactor) { @@ -506,14 +296,10 @@ void displayrooms(int snum, double smoothratio) if (s->yvel < 0) s->yvel = -100; else if (s->yvel > 199) s->yvel = 300; - cang = buildfang(ud.cameraactor->tempang + MulScaleF(((s->ang + 1024 - ud.cameraactor->tempang) & 2047) - 1024, smoothratio, 16)); + cang = buildang(interpolatedangle(ud.cameraactor->tempang, s->ang, smoothratio)); auto bh = buildhoriz(s->yvel); - se40code(s->x, s->y, s->z, cang, bh, smoothratio); - renderMirror(s->x, s->y, s->z, cang, bh, smoothratio); - renderDrawRoomsQ16(s->x, s->y, s->z - (4 << 8), cang.asq16(), bh.asq16(), s->sectnum); - fi.animatesprites(s->x, s->y, cang.asbuild(), smoothratio); - renderDrawMasks(); + renderView(s, s->sectnum, s->x, s->y, s->z - (4 << 8), cang, bh, buildang(0), smoothratio); } else { @@ -531,15 +317,13 @@ void displayrooms(int snum, double smoothratio) if ((snum == myconnectindex) && (numplayers > 1)) { - cposx = omyx + xs_CRoundToInt(MulScaleF(myx - omyx, smoothratio, 16)); - cposy = omyy + xs_CRoundToInt(MulScaleF(myy - omyy, smoothratio, 16)); - cposz = omyz + xs_CRoundToInt(MulScaleF(myz - omyz, smoothratio, 16)); + cposx = interpolatedvalue(omyx, myx, smoothratio); + cposy = interpolatedvalue(omyy, myy, smoothratio); + cposz = interpolatedvalue(omyz, myz, smoothratio); if (SyncInput()) { - fixed_t ohorz = (omyhoriz + omyhorizoff).asq16(); - fixed_t horz = (myhoriz + myhorizoff).asq16(); - choriz = q16horiz(ohorz + xs_CRoundToInt(MulScaleF(horz - ohorz, smoothratio, 16))); - cang = bamang(xs_CRoundToUInt(omyang.asbam() + MulScaleF((myang - omyang).asbam(), smoothratio, 16))); + choriz = q16horiz(interpolatedvalue((omyhoriz + omyhorizoff).asq16(), (myhoriz + myhorizoff).asq16(), smoothratio)); + cang = interpolatedangle(omyang, myang, smoothratio); } else { @@ -550,9 +334,9 @@ void displayrooms(int snum, double smoothratio) } else { - cposx = p->oposx + xs_CRoundToInt(MulScaleF(p->posx - p->oposx, smoothratio, 16)); - cposy = p->oposy + xs_CRoundToInt(MulScaleF(p->posy - p->oposy, smoothratio, 16)); - cposz = p->oposz + xs_CRoundToInt(MulScaleF(p->posz - p->oposz, smoothratio, 16)); + cposx = interpolatedvalue(p->oposx, p->posx, smoothratio); + cposy = interpolatedvalue(p->oposy, p->posy, smoothratio); + cposz = interpolatedvalue(p->oposz, p->posz, smoothratio);; if (SyncInput()) { // Original code for when the values are passed through the sync struct @@ -567,6 +351,7 @@ void displayrooms(int snum, double smoothratio) } } + spritetype* viewer; if (p->newOwner != nullptr) { auto spr = p->newOwner->s; @@ -576,12 +361,14 @@ void displayrooms(int snum, double smoothratio) cposy = spr->pos.y; cposz = spr->pos.z; sect = spr->sectnum; - rotscrnang = buildlook(0); + rotscrnang = buildang(0); smoothratio = MaxSmoothRatio; + viewer = spr; } else if (p->over_shoulder_on == 0) { - if (cl_viewbob) cposz += p->opyoff + xs_CRoundToInt(MulScaleF(p->pyoff - p->opyoff, smoothratio, 16)); + if (cl_viewbob) cposz += interpolatedvalue(p->opyoff, p->pyoff, smoothratio); + viewer = p->GetActor()->s; } else { @@ -592,11 +379,9 @@ void displayrooms(int snum, double smoothratio) cposz += isRR() ? 3840 : 3072; calcChaseCamPos(&cposx, &cposy, &cposz, p->GetActor()->s, §, cang, choriz, smoothratio); } + viewer = p->GetActor()->s; } - // do screen rotation. - renderSetRollAngle(rotscrnang.asbuildf()); - cz = p->GetActor()->ceilingz; fz = p->GetActor()->floorz; @@ -623,17 +408,14 @@ void displayrooms(int snum, double smoothratio) choriz = clamp(choriz, q16horiz(gi->playerHorizMin()), q16horiz(gi->playerHorizMax())); - if (isRR() && sector[sect].lotag == 848) + if (isRR() && sector[sect].lotag == 848 && !testnewrenderer) { + renderSetRollAngle(rotscrnang.asbuildf()); geometryEffect(cposx, cposy, cposz, cang, choriz, sect, smoothratio); } else { - se40code(cposx, cposy, cposz, cang, choriz, smoothratio); - renderMirror(cposx, cposy, cposz, cang, choriz, smoothratio); - renderDrawRoomsQ16(cposx, cposy, cposz, cang.asq16(), choriz.asq16(), sect); - fi.animatesprites(cposx, cposy, cang.asbuild(), smoothratio); - renderDrawMasks(); + renderView(viewer, sect, cposx, cposy, cposz, cang, choriz, rotscrnang, smoothratio); } } //GLInterface.SetMapFog(false); @@ -656,6 +438,10 @@ bool GameInterface::GenerateSavePic() return true; } +void GameInterface::processSprites(spritetype* tsprite, int& spritesortcnt, int viewx, int viewy, int viewz, binangle viewang, double smoothRatio) +{ + fi.animatesprites(tsprite, spritesortcnt, viewx, viewy, viewang.asbuild(), int(smoothRatio)); +} END_DUKE_NS diff --git a/source/games/duke/src/sbar_d.cpp b/source/games/duke/src/sbar_d.cpp index ff1d620a7..e55fdea14 100644 --- a/source/games/duke/src/sbar_d.cpp +++ b/source/games/duke/src/sbar_d.cpp @@ -243,9 +243,9 @@ public: // // keys // - if (p->got_access & 1) DrawGraphic(tileGetTexture(ACCESSCARD), -12, -23.5, DI_ITEM_RIGHT, 1, -1, -1, 0.5, 0.5, 0xffffffff, TRANSLATION(Translation_Remap, 0)); - if (p->got_access & 4) DrawGraphic(tileGetTexture(ACCESSCARD), -7 , -21.5, DI_ITEM_RIGHT, 1, -1, -1, 0.5, 0.5, 0xffffffff, TRANSLATION(Translation_Remap, 23)); - if (p->got_access & 2) DrawGraphic(tileGetTexture(ACCESSCARD), -2 , -19.5, DI_ITEM_RIGHT, 1, -1, -1, 0.5, 0.5, 0xffffffff, TRANSLATION(Translation_Remap, 21)); + if (p->got_access & 1) DrawGraphic(tileGetTexture(ACCESSCARD), -12, -23.5, DI_ITEM_RIGHT, 1, -1, -1, 0.5, 0.5, STYLE_Translucent, 0xffffffff, TRANSLATION(Translation_Remap, 0)); + if (p->got_access & 4) DrawGraphic(tileGetTexture(ACCESSCARD), -7 , -21.5, DI_ITEM_RIGHT, 1, -1, -1, 0.5, 0.5, STYLE_Translucent, 0xffffffff, TRANSLATION(Translation_Remap, 23)); + if (p->got_access & 2) DrawGraphic(tileGetTexture(ACCESSCARD), -2 , -19.5, DI_ITEM_RIGHT, 1, -1, -1, 0.5, 0.5, STYLE_Translucent, 0xffffffff, TRANSLATION(Translation_Remap, 21)); } @@ -355,15 +355,15 @@ public: format.Format("%3d/%d", num1, num2); } y--; - DrawGraphic(tileGetTexture(THREEBYFIVE + index), x - 7, y, DI_ITEM_LEFT|DI_ITEM_VCENTER, 1, 0, 0, 1, 1, LightForShade(shade - 10), TRANSLATION(Translation_Remap, 7)); + DrawGraphic(tileGetTexture(THREEBYFIVE + index), x - 7, y, DI_ITEM_LEFT|DI_ITEM_VCENTER, 1, 0, 0, 1, 1, STYLE_Translucent, LightForShade(shade - 10), TRANSLATION(Translation_Remap, 7)); auto pe = LightForShade(shade); - DrawGraphic(tileGetTexture(THREEBYFIVE + 10), x - 3, y, DI_ITEM_LEFT | DI_ITEM_VCENTER, 1, 0, 0, 1, 1, pe); + DrawGraphic(tileGetTexture(THREEBYFIVE + 10), x - 3, y, DI_ITEM_LEFT | DI_ITEM_VCENTER, 1, 0, 0, 1, 1, STYLE_Translucent, pe); for (size_t i = 0; i < format.Len(); i++) { if (format[i] != ' ') { char c = format[i] == '/' ? 11 : format[i] - '0'; - DrawGraphic(tileGetTexture(THREEBYFIVE + c), x + 4 * i + (parsedDivisor ? 1 : 0), y, DI_ITEM_LEFT | DI_ITEM_VCENTER, 1, 0, 0, 1, 1, pe); + DrawGraphic(tileGetTexture(THREEBYFIVE + c), x + 4 * i + (parsedDivisor ? 1 : 0), y, DI_ITEM_LEFT | DI_ITEM_VCENTER, 1, 0, 0, 1, 1, STYLE_Translucent, pe); } if (format[i] == '/') { @@ -431,9 +431,9 @@ public: else { auto key = tileGetTexture(ACCESS_ICON); - if (p->got_access & 4) DrawGraphic(key, 275.5, top + 16, DI_ITEM_OFFSETS, 1, -1, -1, 1, 1, 0xffffffff, TRANSLATION(Translation_Remap, 23)); - if (p->got_access & 2) DrawGraphic(key, 288.5, top + 16, DI_ITEM_OFFSETS, 1, -1, -1, 1, 1, 0xffffffff, TRANSLATION(Translation_Remap, 21)); - if (p->got_access & 1) DrawGraphic(key, 282, top + 23, DI_ITEM_OFFSETS, 1, -1, -1, 1, 1, 0xffffffff, TRANSLATION(Translation_Remap, 0)); + if (p->got_access & 4) DrawGraphic(key, 275.5, top + 16, DI_ITEM_OFFSETS, 1, -1, -1, 1, 1, STYLE_Translucent, 0xffffffff, TRANSLATION(Translation_Remap, 23)); + if (p->got_access & 2) DrawGraphic(key, 288.5, top + 16, DI_ITEM_OFFSETS, 1, -1, -1, 1, 1, STYLE_Translucent, 0xffffffff, TRANSLATION(Translation_Remap, 21)); + if (p->got_access & 1) DrawGraphic(key, 282, top + 23, DI_ITEM_OFFSETS, 1, -1, -1, 1, 1, STYLE_Translucent, 0xffffffff, TRANSLATION(Translation_Remap, 0)); } DrawWeaponAmounts(p, 96, top + 15.5); diff --git a/source/games/duke/src/sbar_r.cpp b/source/games/duke/src/sbar_r.cpp index f2d8374b0..5de101c20 100644 --- a/source/games/duke/src/sbar_r.cpp +++ b/source/games/duke/src/sbar_r.cpp @@ -212,9 +212,9 @@ public: // // keys // - if (p->keys[1]) DrawGraphic(tileGetTexture(ACCESSCARD), -28.5, -32 , DI_ITEM_BOTTOM, 1, -1, -1, scale, scale, 0xffffffff, TRANSLATION(Translation_Remap, 0)); - if (p->keys[3]) DrawGraphic(tileGetTexture(ACCESSCARD), -21.25, -28.375, DI_ITEM_BOTTOM, 1, -1, -1, scale, scale, 0xffffffff, TRANSLATION(Translation_Remap, 23)); - if (p->keys[2]) DrawGraphic(tileGetTexture(ACCESSCARD), -14, -24.75 , DI_ITEM_BOTTOM, 1, -1, -1, scale, scale, 0xffffffff, TRANSLATION(Translation_Remap, 21)); + if (p->keys[1]) DrawGraphic(tileGetTexture(ACCESSCARD), -28.5, -32 , DI_ITEM_BOTTOM, 1, -1, -1, scale, scale, STYLE_Translucent, 0xffffffff, TRANSLATION(Translation_Remap, 0)); + if (p->keys[3]) DrawGraphic(tileGetTexture(ACCESSCARD), -21.25, -28.375, DI_ITEM_BOTTOM, 1, -1, -1, scale, scale, STYLE_Translucent, 0xffffffff, TRANSLATION(Translation_Remap, 23)); + if (p->keys[2]) DrawGraphic(tileGetTexture(ACCESSCARD), -14, -24.75 , DI_ITEM_BOTTOM, 1, -1, -1, scale, scale, STYLE_Translucent, 0xffffffff, TRANSLATION(Translation_Remap, 21)); } @@ -374,9 +374,9 @@ public: else { auto key = tileGetTexture(ACCESS_ICON); - if (p->keys[3]) DrawGraphic(key, 138, top + 13, DI_ITEM_OFFSETS, 1, -1, -1, scale, scale, 0xffffffff, TRANSLATION(Translation_Remap, 23)); - if (p->keys[2]) DrawGraphic(key, 152, top + 13, DI_ITEM_OFFSETS, 1, -1, -1, scale, scale, 0xffffffff, TRANSLATION(Translation_Remap, 21)); - if (p->keys[1]) DrawGraphic(key, 145, top + 21, DI_ITEM_OFFSETS, 1, -1, -1, scale, scale, 0xffffffff, TRANSLATION(Translation_Remap, 0)); + if (p->keys[3]) DrawGraphic(key, 138, top + 13, DI_ITEM_OFFSETS, 1, -1, -1, scale, scale, STYLE_Translucent, 0xffffffff, TRANSLATION(Translation_Remap, 23)); + if (p->keys[2]) DrawGraphic(key, 152, top + 13, DI_ITEM_OFFSETS, 1, -1, -1, scale, scale, STYLE_Translucent, 0xffffffff, TRANSLATION(Translation_Remap, 21)); + if (p->keys[1]) DrawGraphic(key, 145, top + 21, DI_ITEM_OFFSETS, 1, -1, -1, scale, scale, STYLE_Translucent, 0xffffffff, TRANSLATION(Translation_Remap, 0)); } int num = (p->GetActor()->s->pal == 1 && p->last_extra < 2) ? 1 : p->last_extra; diff --git a/source/games/duke/src/sectors.cpp b/source/games/duke/src/sectors.cpp index 1574ac3eb..8cc7d4919 100644 --- a/source/games/duke/src/sectors.cpp +++ b/source/games/duke/src/sectors.cpp @@ -1271,8 +1271,18 @@ void moveclouds(double smoothratio) cloudy += ps[screenpeek].angle.ang.fsin() * 0.5f; for (int i = 0; i < numclouds; i++) { - sector[clouds[i]].setceilingxpan(cloudx); - sector[clouds[i]].setceilingypan(cloudy); + if (!testnewrenderer) + { + sector[clouds[i]].setceilingxpan(cloudx); + sector[clouds[i]].setceilingypan(cloudy); + } + else + { + // no clamping here! + sector[clouds[i]].ceilingxpan_ = cloudx; + sector[clouds[i]].ceilingypan_ = cloudy; + } + sector[clouds[i]].exflags |= SECTOREX_CLOUDSCROLL; } } } diff --git a/source/games/duke/src/sectors_d.cpp b/source/games/duke/src/sectors_d.cpp index 737d76d80..5b4948123 100644 --- a/source/games/duke/src/sectors_d.cpp +++ b/source/games/duke/src/sectors_d.cpp @@ -660,6 +660,7 @@ void checkhitwall_d(DDukeActor* spr, int dawallnum, int x, int y, int z, int atw lotsofglass(spr, dawallnum, 70); wal->cstat &= ~16; wal->overpicnum = MIRRORBROKE; + wal->portalflags = 0; S_PlayActorSound(GLASS_HEAVYBREAK, spr); return; } diff --git a/source/games/duke/src/sectors_r.cpp b/source/games/duke/src/sectors_r.cpp index ac31c329b..4b89feab1 100644 --- a/source/games/duke/src/sectors_r.cpp +++ b/source/games/duke/src/sectors_r.cpp @@ -998,6 +998,7 @@ void checkhitwall_r(DDukeActor* spr, int dawallnum, int x, int y, int z, int atw lotsofglass(spr, dawallnum, 70); wal->cstat &= ~16; wal->overpicnum = MIRRORBROKE; + wal->portalflags = 0; S_PlayActorSound(GLASS_HEAVYBREAK, spr); return; } diff --git a/source/games/duke/src/sounds.cpp b/source/games/duke/src/sounds.cpp index c9214b65b..9617b033e 100644 --- a/source/games/duke/src/sounds.cpp +++ b/source/games/duke/src/sounds.cpp @@ -46,6 +46,7 @@ source as it is released. #include "gamestate.h" #include "names_d.h" #include "i_music.h" +#include "vm.h" CVAR(Bool, wt_forcemidi, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // quick hack to disable the oggs, which are of lower quality than playing the MIDIs with a good synth and sound font. CVAR(Bool, wt_forcevoc, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // The same for sound effects. The re-recordings are rather poor and disliked @@ -872,4 +873,48 @@ bool StartCommentary(int tag, DDukeActor* actor) return false; } + +DEFINE_ACTION_FUNCTION_NATIVE(_Duke, PlaySpecialMusic, S_PlaySpecialMusic) +{ + PARAM_PROLOGUE; + PARAM_INT(song); + S_PlaySpecialMusic(song); + return 0; +} + +static int PlaySound(int num, int chan, int flags, double vol) +{ + return S_PlaySound(num, chan, EChanFlags::FromInt(flags), float(vol)); +} + +DEFINE_ACTION_FUNCTION_NATIVE(_Duke, PlaySound, PlaySound) +{ + PARAM_PROLOGUE; + PARAM_INT(snd); + PARAM_INT(chan); + PARAM_INT(flags); + PARAM_FLOAT(vol); + ACTION_RETURN_INT(PlaySound(snd, chan, flags, vol)); +} +static void StopSound(int num) +{ + S_StopSound(num); +} + + +DEFINE_ACTION_FUNCTION_NATIVE(_Duke, StopSound, StopSound) +{ + PARAM_PROLOGUE; + PARAM_INT(snd); + StopSound(snd); + return 0; +} + +DEFINE_ACTION_FUNCTION_NATIVE(_Duke, CheckSoundPlaying, S_CheckSoundPlaying) +{ + PARAM_PROLOGUE; + PARAM_INT(snd); + ACTION_RETURN_INT(S_CheckSoundPlaying(snd)); +} + END_DUKE_NS diff --git a/source/games/duke/src/sounds.h b/source/games/duke/src/sounds.h index 415af4406..c0c894cf1 100644 --- a/source/games/duke/src/sounds.h +++ b/source/games/duke/src/sounds.h @@ -48,7 +48,6 @@ void S_MenuSound(void); void S_StopSound(int sndNum, DDukeActor* spr = nullptr, int flags = -1); int S_CheckSoundPlaying(int soundNum); -inline int S_CheckSoundPlaying(int sprnum, int soundNum) { return S_CheckSoundPlaying(soundNum); } int S_CheckActorSoundPlaying(DDukeActor* spriteNum, int soundNum, int channel = 0); int S_CheckAnyActorSoundPlaying(DDukeActor* spriteNum); @@ -64,7 +63,6 @@ inline bool S_IsSoundValid(int num) void S_PlayRRMusic(int newTrack = -1); void S_PlayBonusMusic(); void S_PlayLevelMusic(MapRecord* mi); -void S_PlaySpecialMusic(unsigned int); void S_ContinueLevelMusic(void); // Placeholders. diff --git a/source/games/duke/src/spawn.cpp b/source/games/duke/src/spawn.cpp index 85ec40f64..b087cba1b 100644 --- a/source/games/duke/src/spawn.cpp +++ b/source/games/duke/src/spawn.cpp @@ -306,7 +306,7 @@ void spawntransporter(DDukeActor *actj, DDukeActor* acti, bool beam) { sp->xrepeat = 31; sp->yrepeat = 1; - sp->z = sector[spj->sectnum].floorz - (40 << 8); + sp->z = sector[spj->sectnum].floorz - isRR() ? PHEIGHT_RR : PHEIGHT_DUKE; } else { diff --git a/source/games/duke/src/spawn_d.cpp b/source/games/duke/src/spawn_d.cpp index c3852b313..7b06e1ab0 100644 --- a/source/games/duke/src/spawn_d.cpp +++ b/source/games/duke/src/spawn_d.cpp @@ -210,7 +210,7 @@ int spawn_d(int j, int pn) case TONGUE: if(j >= 0) sp->ang = spj->ang; - sp->z -= 38<<8; + sp->z -= PHEIGHT_DUKE; sp->zvel = 256-(krand()&511); sp->xvel = 64-(krand()&127); changespritestat(i,4); diff --git a/source/games/duke/src/spawn_r.cpp b/source/games/duke/src/spawn_r.cpp index 2187967b2..d63d6cfc0 100644 --- a/source/games/duke/src/spawn_r.cpp +++ b/source/games/duke/src/spawn_r.cpp @@ -293,7 +293,7 @@ int spawn_r(int j, int pn) case TONGUE: if(j >= 0) sp->ang = spj->ang; - sp->z -= 38<<8; + sp->z -= PHEIGHT_RR; sp->zvel = 256-(krand()&511); sp->xvel = 64-(krand()&127); changespritestat(i,4); diff --git a/source/games/duke/src/types.h b/source/games/duke/src/types.h index 33d948929..9dbbbc151 100644 --- a/source/games/duke/src/types.h +++ b/source/games/duke/src/types.h @@ -143,7 +143,6 @@ struct user_defs int m_respawn_items, m_respawn_monsters, m_respawn_inventory, m_recstat, m_monsters_off; int m_ffire, ffire, m_player_skill, multimode; int player_skill, marker; - //MapRecord* nextLevel; DDukeActor* cameraactor; diff --git a/source/games/exhumed/src/2d.cpp b/source/games/exhumed/src/2d.cpp index 2acd24315..c6035b582 100644 --- a/source/games/exhumed/src/2d.cpp +++ b/source/games/exhumed/src/2d.cpp @@ -38,7 +38,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "v_draw.h" #include "m_random.h" #include "gstrings.h" +#include "gamefuncs.h" #include "c_bind.h" +#include "vm.h" #include @@ -46,8 +48,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. BEGIN_PS_NS -int selectedlevelnew; - //--------------------------------------------------------------------------- // // @@ -372,471 +372,26 @@ void menu_DoPlasma() DrawRel(kTile3512 + ((dword_9AB5F + 2) & 3), 270, 150); } -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- -class DLobotomyScreen : public DImageScreen +DEFINE_ACTION_FUNCTION(_Exhumed, DrawPlasma) { -public: - DLobotomyScreen(FGameTexture *tex, int fade) : DImageScreen(tex, fade) - {} - - void Skipped() override - { - StopLocalSound(); - } - - void Start() override - { - PlayLocalSound(StaticSound[kSoundJonLaugh2], 7000, false, CHANF_UI); - } - - void OnTick() override - { - - DImageScreen::OnTick(); - if (state == finished) StopLocalSound(); - } -}; - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -static const short skullDurations[] = { 6, 25, 43, 50, 68, 78, 101, 111, 134, 158, 173, 230, 600 }; - -class DMainTitle : public DSkippableScreenJob -{ - const char* a; - const char* b; - int state = 0; - int duration; - int var_4 = 0; - int esi = 130; - int nCount = 0; - int start; - - -public: - DMainTitle() : DSkippableScreenJob(fadein) - { - a = GStrings("TXT_EX_COPYRIGHT1"); - b = GStrings("TXT_EX_COPYRIGHT2"); - duration = skullDurations[0]; - } - - void Start() override - { - PlayLocalSound(StaticSound[59], 0, true, CHANF_UI); - playCDtrack(19, true); - } - - void OnTick() override - { - int ticker = ticks * 120 / GameTicRate; - if (ticks > 1 && state == 0 && !soundEngine->IsSourcePlayingSomething(SOURCE_None, nullptr, CHAN_AUTO, -1)) - { - if (time(0) & 0xF) // cheap-ass random... - PlayGameOverSound(); - else - PlayLocalSound(StaticSound[61], 0, false, CHANF_UI); - state = 1; - start = ticker; - } - if (state == 1) - { - if (ticker > duration) - { - nCount++; - if (nCount > 12) - { - state = finished; - return; - } - duration = start + skullDurations[nCount]; - var_4 = var_4 == 0; - } - } - } - - void Draw(double) override - { - twod->ClearScreen(); - menu_DoPlasma(); - - DrawRel(kSkullHead, 160, 100); - if (state == 0) - { - DrawRel(kSkullJaw, 161, 130); - } - else - { - int nStringWidth = SmallFont->StringWidth(a); - DrawText(twod, SmallFont, CR_UNTRANSLATED, 160 - nStringWidth / 2, 200 - 24, a, DTA_FullscreenScale, FSMode_Fit320x200, TAG_DONE); - nStringWidth = SmallFont->StringWidth(b); - DrawText(twod, SmallFont, CR_UNTRANSLATED, 160 - nStringWidth / 2, 200 - 16, b, DTA_FullscreenScale, FSMode_Fit320x200, TAG_DONE); - - - short nTile = kSkullJaw; - - if (var_4) - { - if (esi >= 135) nTile = kTile3583; - else esi += 5; - } - else if (esi <= 130) esi = 130; - else esi -= 2; - - int y; - - if (nTile == kTile3583) - { - y = 131; - } - else - { - y = esi; - if (y > 135) y = 135; - } - - DrawRel(nTile, 161, y); - } - } -}; - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -DScreenJob *PlayMovie(const char* fileName); - -void DoTitle(CompletionFunc completion) -{ - JobDesc jobs[5]; - int job = 0; - - jobs[job++] = { Create(tileGetTexture(PublisherLogo()), DScreenJob::fadein | DScreenJob::fadeout) }; - jobs[job++] = { Create(tileGetTexture(seq_GetSeqPicnum(kSeqScreens, 0, 0)), DScreenJob::fadein | DScreenJob::fadeout) }; - jobs[job++] = { PlayMovie("book.mov") }; - jobs[job++] = { Create() }; - - RunScreenJob(jobs, job, completion, true, true); - + return 0; } //--------------------------------------------------------------------------- // -// pre-level map display +// text overlay (native version still needed for Ramses texts. // //--------------------------------------------------------------------------- - static const int8_t MapLevelOffsets[] = { 0, 50, 10, 20, 0, 45, -20, 20, 5, 0, -10, 10, 30, -20, 0, 20, 0, 0, 0, 0 }; - - struct TILEFRAMEDEF - { - short nTile; - short xOffs; - short yOffs; - }; - - // 22 bytes - struct MapNamePlaque - { - short xPos; - short yPos; - TILEFRAMEDEF tiles[2]; - TILEFRAMEDEF text; - }; - - static const MapNamePlaque mapNamePlaques[] = { - { 100, 170, kTile3376, 0, 0, kTile3377, 0, 0, kTile3411, 18, 6 }, - { 230, 10, kTile3378, 0, 0, kTile3379, 0, 0, kTile3414, 18, 6 }, // DENDUR (level 2) - { 180, 125, kTile3380, 0, 0, kTile3381, 0, 0, kTile3417, 18, 6 }, // Kalabash - { 10, 95, kTile3382, 0, 0, kTile3383, 0, 0, kTile3420, 18, 6 }, - { 210, 160, kTile3384, 0, 0, kTile3385, 0, 0, kTile3423, 18, 6 }, - { 10, 110, kTile3371, 0, 0, kTile3386, 0, 0, kTile3426, 18, 6 }, - { 10, 50, kTile3387, 0, 0, kTile3388, 0, 0, kTile3429, 18, 6 }, - { 140, 0, kTile3389, 0, 0, kTile3390, 0, 0, kTile3432, 18, 6 }, - { 30, 20, kTile3391, 0, 0, kTile3392, 0, 0, kTile3435, 18, 6 }, - { 200, 150, kTile3409, 0, 0, kTile3410, 0, 0, kTile3418, 20, 4 }, - { 145, 170, kTile3393, 0, 0, kTile3394, 0, 0, kTile3438, 18, 6 }, - { 80, 80, kTile3395, 0, 0, kTile3396, 0, 0, kTile3441, 18, 6 }, - { 15, 0, kTile3397, 0, 0, kTile3398, 0, 0, kTile3444, 18, 5 }, - { 220, 35, kTile3399, 0, 0, kTile3400, 0, 0, kTile3447, 18, 6 }, - { 190, 40, kTile3401, 0, 0, kTile3402, 0, 0, kTile3450, 18, 6 }, - { 20, 130, kTile3403, 0, 0, kTile3404, 0, 0, kTile3453, 19, 6 }, - { 220, 160, kTile3405, 0, 0, kTile3406, 0, 0, kTile3456, 18, 6 }, - { 20, 10, kTile3407, 0, 0, kTile3408, 0, 0, kTile3459, 18, 6 }, - { 200, 10, kTile3412, 0, 0, kTile3413, 0, 0, kTile3419, 18, 5 }, - { 20, 10, kTile3415, 0, 0, kTile3416, 0, 0, kTile3421, 19, 4 } - }; - - // 3 different types of fire, each with 4 frames - static const TILEFRAMEDEF FireTiles[3][4] = { - {{ kTile3484,0,3 },{ kTile3485,0,0 },{ kTile3486,0,3 },{ kTile3487,0,0 }}, - {{ kTile3488,1,0 },{ kTile3489,1,0 },{ kTile3490,0,1 },{ kTile3491,1,1 }}, - {{ kTile3492,1,2 },{ kTile3493,1,0 },{ kTile3494,1,2 },{ kTile3495,1,0 }} - }; - - struct Fire - { - short nFireType; - short xPos; - short yPos; - }; - - // 20 bytes - struct MapFire - { - short nFires; - Fire fires[3]; - }; - - /* - level 1 - 3 fires - level 2 - 3 fires - level 3 - 1 fire - - */ - - static const MapFire MapLevelFires[] = { - 3, {{0, 107, 95}, {1, 58, 140}, {2, 28, 38}}, - 3, {{2, 240, 0}, {0, 237, 32}, {1, 200, 30}}, - 2, {{2, 250, 57}, {0, 250, 43}, {2, 200, 70}}, - 2, {{1, 82, 59}, {2, 84, 16}, {0, 10, 95}}, - 2, {{2, 237, 50}, {1, 215, 42}, {1, 210, 50}}, - 3, {{0, 40, 7}, {1, 75, 6}, {2, 100, 10}}, - 3, {{0, 58, 61}, {1, 85, 80}, {2, 111, 63}}, - 3, {{0, 260, 65}, {1, 228, 0}, {2, 259, 15}}, - 2, {{0, 81, 38}, {2, 58, 38}, {2, 30, 20}}, - 3, {{0, 259, 49}, {1, 248, 76}, {2, 290, 65}}, - 3, {{2, 227, 66}, {0, 224, 98}, {1, 277, 30}}, - 2, {{0, 100, 10}, {2, 48, 76}, {2, 80, 80}}, - 3, {{0, 17, 2}, {1, 29, 49}, {2, 53, 28}}, - 3, {{0, 266, 42}, {1, 283, 99}, {2, 243, 108}}, - 2, {{0, 238, 19}, {2, 240, 92}, {2, 190, 40}}, - 2, {{0, 27, 0}, {1, 70, 40}, {0, 20, 130}}, - 3, {{0, 275, 65}, {1, 235, 8}, {2, 274, 6}}, - 3, {{0, 75, 45}, {1, 152, 105}, {2, 24, 68}}, - 3, {{0, 290, 25}, {1, 225, 63}, {2, 260, 110}}, - 0, {{1, 20, 10}, {1, 20, 10}, {1, 20, 10}} - }; - -class DMapScreen : public DScreenJob +void TextOverlay::Create(const FString& text, int pal) { - int i; - int x = 0; - int delta = 0; - int nIdleSeconds = 0; - - int curYPos, destYPos; - int nLevel, nLevelNew, nLevelBest; - -public: - DMapScreen(int nLevel_, int nLevelNew_, int nLevelBest_) : DScreenJob(fadein|fadeout), nLevel(nLevel_), nLevelNew(nLevelNew_), nLevelBest(nLevelBest_) - { - curYPos = MapLevelOffsets[nLevel] + (200 * (nLevel / 2)); - destYPos = MapLevelOffsets[nLevelNew] + (200 * (nLevelNew / 2)); - - if (curYPos < destYPos) { - delta = 2; - } - - if (curYPos > destYPos) { - delta = -2; - } - - // Trim smoke in widescreen -#if 0 - vec2_t mapwinxy1 = windowxy1, mapwinxy2 = windowxy2; - int32_t width = mapwinxy2.x - mapwinxy1.x + 1, height = mapwinxy2.y - mapwinxy1.y + 1; - if (3 * width > 4 * height) - { - mapwinxy1.x += (width - 4 * height / 3) / 2; - mapwinxy2.x -= (width - 4 * height / 3) / 2; - } -#endif - } - - void Draw(double smoothratio) - { - int currentclock = int((ticks + smoothratio) * 120 / GameTicRate); - - twod->ClearScreen(); - - int tileY = curYPos; - - // Draw the background screens - for (i = 0; i < 10; i++) - { - DrawAbs(kTile3353 + i, x, tileY); - tileY -= 200; - } - - // for each level - drawing the 'level completed' on-fire smoke markers - for (i = 0; i < kMap20; i++) - { - int screenY = (i >> 1) * -200; - - if (nLevelBest >= i) // check if the player has finished this level - { - for (int j = 0; j < MapLevelFires[i].nFires; j++) - { - int nFireFrame = ((currentclock >> 4) & 3); - assert(nFireFrame >= 0 && nFireFrame < 4); - - int nFireType = MapLevelFires[i].fires[j].nFireType; - assert(nFireType >= 0 && nFireType < 3); - - int nTile = FireTiles[nFireType][nFireFrame].nTile; - int smokeX = MapLevelFires[i].fires[j].xPos + FireTiles[nFireType][nFireFrame].xOffs; - int smokeY = MapLevelFires[i].fires[j].yPos + FireTiles[nFireType][nFireFrame].yOffs + curYPos + screenY; - - // Use rotatesprite to trim smoke in widescreen - DrawAbs(nTile, smokeX, smokeY); - // Todo: mask out the sides of the screen if the background is not widescreen. - } - } - - int t = (((currentclock & 16) >> 4)); - - int nTile = mapNamePlaques[i].tiles[t].nTile; - - int nameX = mapNamePlaques[i].xPos + mapNamePlaques[i].tiles[t].xOffs; - int nameY = mapNamePlaques[i].yPos + mapNamePlaques[i].tiles[t].yOffs + curYPos + screenY; - - // Draw level name plaque - DrawAbs(nTile, nameX, nameY); - - int8_t shade = 96; - - if (nLevelNew == i) - { - shade = (bsin(16 * currentclock) + 31) >> 8; - } - else if (nLevelBest >= i) - { - shade = 31; - } - - int textY = mapNamePlaques[i].yPos + mapNamePlaques[i].text.yOffs + curYPos + screenY; - int textX = mapNamePlaques[i].xPos + mapNamePlaques[i].text.xOffs; - nTile = mapNamePlaques[i].text.nTile; - - // draw the text, alternating between red and black - DrawAbs(nTile, textX, textY, shade); - } - - selectedlevelnew = nLevelNew + 1; - } - - void OnTick() override - { - if (curYPos != destYPos) - { - // scroll the map every couple of ms - curYPos += delta; - - if (curYPos > destYPos && delta > 0) { - curYPos = destYPos; - } - - if (curYPos < destYPos && delta < 0) { - curYPos = destYPos; - } - nIdleSeconds = 0; - } - else nIdleSeconds++; - if (nIdleSeconds > 300) state = finished; - } - - bool OnEvent(event_t* ev) override - { - int key = ev->data1; - if (ev->type == EV_KeyDown) - { - auto binding = Bindings.GetBinding(ev->data1); - if (!binding.CompareNoCase("+move_forward")) key = KEY_UPARROW; - if (!binding.CompareNoCase("+move_backward")) key = KEY_DOWNARROW; - - if (key == KEY_UPARROW || key == KEY_PAD_DPAD_UP || key == sc_kpad_8) - { - if (curYPos == destYPos && nLevelNew <= nLevelBest) - { - nLevelNew++; - assert(nLevelNew < 20); - - destYPos = MapLevelOffsets[nLevelNew] + (200 * (nLevelNew / 2)); - - if (curYPos <= destYPos) { - delta = 2; - } - else { - delta = -2; - } - - nIdleSeconds = 0; - } - return true; - } - - if (key == KEY_DOWNARROW || key == KEY_PAD_DPAD_DOWN || key == sc_kpad_2) - { - if (curYPos == destYPos && nLevelNew > 0) - { - nLevelNew--; - assert(nLevelNew >= 0); - - destYPos = MapLevelOffsets[nLevelNew] + (200 * (nLevelNew / 2)); - - if (curYPos <= destYPos) { - delta = 2; - } - else { - delta = -2; - } - - nIdleSeconds = 0; - } - return true; - } - if (!specialKeyEvent(ev)) state = skipped; - return true; - } - return false; - } -}; - - void menu_DrawTheMap(int nLevel, int nLevelNew, int nLevelBest, TArray &jobs) - { - if (nLevel > kMap20 || nLevelNew > kMap20) // max single player levels - { - return; - } -#ifdef _DEBUG - nLevelBest = kMap20; -#endif - - if (nLevel < 1) nLevel = 1; - if (nLevelNew < 1) nLevelNew = nLevel; - - // 0-offset the level numbers - jobs.Push( { Create(nLevel-1, nLevelNew-1, nLevelBest-1) }); - } - -//--------------------------------------------------------------------------- -// -// text overlay -// -//--------------------------------------------------------------------------- + lastclock = 0; + FString ttext = GStrings(text); + screentext = ttext.Split("\n"); + ComputeCinemaText(); +} void TextOverlay::Start(double starttime) { @@ -856,10 +411,9 @@ void TextOverlay::ComputeCinemaText() nHeight = screentext.Size() * 10; } -void TextOverlay::ReadyCinemaText(uint16_t nVal) +void TextOverlay::ReadyCinemaText(const char* nVal) { - FStringf label("TXT_EX_CINEMA%d", nVal); - label = GStrings(label); + FString label = nVal[0] == '$'? GStrings(nVal +1) : nVal; screentext = label.Split("\n"); ComputeCinemaText(); } @@ -902,33 +456,6 @@ bool TextOverlay::AdvanceCinemaText(double clock) // //--------------------------------------------------------------------------- -enum EScenes -{ - CINEMA_BEFORE_LEVEL_5, - CINEMA_AFTER_LEVEL_10, - CINEMA_BEFORE_LEVEL_11, - CINEMA_AFTER_LEVEL_15, - CINEMA_LOSE_SCENE, - CINEMA_AFTER_LEVEL_20, -}; - -struct CinemaDef -{ - short tile; - short palette; - short text; - short track; -}; - -static CinemaDef cinemas[] = { - { 3449, 3, 2, 2}, - { 3451, 5, 4, 3}, - { 3454, 1, 3, 4}, - { 3446, 7, 6, 6}, - { 3445, 4, 7, 7}, - { 3448, 6, 8, 8} -}; - static const char * const cinpalfname[] = { "3454.pal", "3452.pal", @@ -964,419 +491,59 @@ void uploadCinemaPalettes() //--------------------------------------------------------------------------- // -// cinema +// this accesses the tile data and needs to remain native. // //--------------------------------------------------------------------------- -class DCinema : public DSkippableScreenJob +static int DoStatic(int a, int b) { - TextOverlay text; - short cinematile; - int currentCinemaPalette; - int edx; - int check; - int cont = 1; - -public: - DCinema(int nVal, int checklevel = -1) : DSkippableScreenJob(fadein|fadeout) - { - if (nVal < 0 || nVal >5) return; - cinematile = cinemas[nVal].tile; - currentCinemaPalette = cinemas[nVal].palette; - text.Start(0); - text.ReadyCinemaText(cinemas[nVal].text); - text.SetPalette(currentCinemaPalette); - edx = cinemas[nVal].track; - check = checklevel; - } - - void Start() override - { - if (check > 0 && check != selectedlevelnew) - { - state = finished; - return; // immediately abort if the player selected a different level on the map - } - - check = -1; - StopAllSounds(); - if (edx != -1) - { - playCDtrack(edx + 2, false); - } - } - - void OnTick() override - { - if (!cont) - { - state = finished; - // quit the game if we've finished level 4 and displayed the advert text - if (isShareware() && currentCinemaPalette == 3) - { - gameaction = ga_mainmenu; - } - return; - } - } - - void Draw(double smoothratio) override - { - twod->ClearScreen(); - if (check == 0) return; - DrawTexture(twod, tileGetTexture(cinematile), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_TranslationIndex, TRANSLATION(Translation_BasePalettes, currentCinemaPalette), TAG_DONE); - - text.DisplayText(); - cont = text.AdvanceCinemaText((ticks + smoothratio) * (120. / GameTicRate)); - } -}; - -//--------------------------------------------------------------------------- -// -// last level cinema -// -//--------------------------------------------------------------------------- - -class DLastLevelCinema : public DScreenJob -{ - int var_24 = 16; - int var_28 = 12; - - int ebp; - int phase = 0; - int nextclock = 4; - unsigned int nStringTypeOn, nCharTypeOn; - int screencnt = 0; - bool skiprequest = false; - - TArray screentext; - -public: - DLastLevelCinema() : DScreenJob(fadein | fadeout) {} - -private: - void DoStatic(int a, int b) - { + TileFiles.tileMakeWritable(kTileLoboLaptop); + auto tex = dynamic_cast(tileGetTexture(kTileLoboLaptop)->GetTexture()->GetImage()); + if (tex) tex->Reload(); auto pixels = TileFiles.tileMakeWritable(kTileLoboLaptop); - int v2 = 160 - a / 2; - int v4 = 81 - b / 2; + int y = 160 - a / 2; + int left = 81 - b / 2; - int var_18 = v2 + a; - int v5 = v4 + b; + int bottom = y + a; + int right = left + b; - auto pTile = (pixels + (200 * v2)) + v4; + auto pTile = (pixels + (200 * y)) + left; TileFiles.InvalidateTile(kTileLoboLaptop); - while (v2 < var_18) + for(;y < bottom; y++) { - uint8_t* pStart = pTile; + uint8_t* pixel = pTile; pTile += 200; - int v7 = v4; - - while (v7 < v5) + for (int x = left; x < right; x++) { - *pStart = RandomBit() * 16; - - v7++; - pStart++; + *pixel++ = RandomBit() * 16; } - v2++; } - } - - void Phase1() - { - if (var_24 >= 116) - { - if (var_28 < 192) - var_28 += 20; - } - else - { - var_24 += 20; - } - - DoStatic(var_28, var_24); - } - - bool InitPhase2() - { - FStringf label("TXT_EX_LASTLEVEL%d", screencnt + 1); - label = GStrings(label); - screentext = label.Split("\n"); - if (screentext.Size() == 0) return false; - - nStringTypeOn = 0; - nCharTypeOn = 0; - - ebp = screentext.Size() * 4; // half height of the entire text - ebp = 81 - ebp; // offset from the screen's center. + return tileGetTexture(kTileLoboLaptop)->GetID().GetIndex(); +} +static int UndoStatic() +{ auto tex = dynamic_cast(tileGetTexture(kTileLoboLaptop)->GetTexture()->GetImage()); if (tex) tex->Reload(); TileFiles.InvalidateTile(kTileLoboLaptop); - return true; - } - - bool Phase3() - { - DoStatic(var_28, var_24); - - if (var_28 > 20) { - var_28 -= 20; - return true; - } - - if (var_24 > 20) { - var_24 -= 20; - return true; - } - return false; - } - - void DisplayPhase2() - { - int yy = ebp; - - auto p = GStrings["REQUIRED_CHARACTERS"]; - if (p && *p) - { - yy *= 2; - for (int i = 0; i < nStringTypeOn; i++, yy += 10) - { - DrawText(twod, ConFont, CR_GREEN, 140, yy, screentext[i], DTA_FullscreenScale, FSMode_Fit640x400, TAG_DONE); - } - DrawText(twod, ConFont, CR_GREEN, 140, yy, screentext[nStringTypeOn], DTA_FullscreenScale, FSMode_Fit640x400, DTA_TextLen, nCharTypeOn, TAG_DONE); - } - else - { - for (int i = 0; i < nStringTypeOn; i++, yy += 8) - { - DrawText(twod, SmallFont2, CR_UNTRANSLATED, 70, yy, screentext[i], DTA_FullscreenScale, FSMode_Fit320x200, TAG_DONE); - } - DrawText(twod, SmallFont2, CR_UNTRANSLATED, 70, yy, screentext[nStringTypeOn], DTA_FullscreenScale, FSMode_Fit320x200, DTA_TextLen, nCharTypeOn, TAG_DONE); - } - } - - bool OnEvent(event_t* ev) - { - if (ev->type == EV_KeyDown && !specialKeyEvent(ev)) skiprequest = true; - return true; - } - - void Start() override - { - PlayLocalSound(StaticSound[kSound75], 0, false, CHANF_UI); - phase = 1; - } - - void OnTick() override - { - switch (phase) - { - case 1: - Phase1(); - if (skiprequest || ticks >= nextclock) - { - InitPhase2(); - phase = 2; - skiprequest = false; - } - break; - - case 2: - if (screentext[nStringTypeOn][nCharTypeOn] != ' ') - PlayLocalSound(StaticSound[kSound71], 0, false, CHANF_UI); - - nCharTypeOn++; - if (screentext[nStringTypeOn][nCharTypeOn] == 0) - { - nCharTypeOn = 0; - nStringTypeOn++; - if (nStringTypeOn >= screentext.Size()) - { - nextclock = (GameTicRate * (screentext.Size() + 2)) + ticks; - phase = 3; - } - - } - if (skiprequest) - { - nextclock = (GameTicRate * (screentext.Size() + 2)) + ticks; - phase = 4; - } - break; - - case 3: - if (ticks >= nextclock || skiprequest) - { - PlayLocalSound(StaticSound[kSound75], 0, false, CHANF_UI); - phase = 4; - nextclock = ticks + 60; - skiprequest = false; - } - - case 4: - if (ticks >= nextclock) - { - skiprequest |= !Phase3(); - } - if (skiprequest) - { - // Go to the next text page. - if (screencnt != 2) - { - screencnt++; - nextclock = ticks + 60; - skiprequest = 0; - phase = 1; - } - else state = finished; - } - - if (skiprequest) - { - state = finished; - } - } - } - - void Draw(double) override - { - twod->ClearScreen(); - DrawTexture(twod, tileGetTexture(kTileLoboLaptop), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE); - if (phase == 2 || phase == 3) DisplayPhase2(); - } - -}; - -//--------------------------------------------------------------------------- -// -// Credits roll -// -//--------------------------------------------------------------------------- - -class DExCredits : public DScreenJob -{ - TArray credits; - TArray pagelines; - uint64_t page; - uint64_t pagetime; - bool skiprequest = false; - -public: - DExCredits() - { - auto textdata = fileSystem.LoadFile("credits.txt", 1); - FString text = (char*)textdata.Data(); - text.Substitute("\r", ""); - credits = text.Split("\n\n"); - } - - bool OnEvent(event_t* ev) - { - if (ev->type == EV_KeyDown && !specialKeyEvent(ev)) skiprequest = true; - return true; - } - - void Start() override - { - if (credits.Size() == 0) - { - state = finished; - return; - } - playCDtrack(19, false); - pagetime = 0; - page = -1; - } - - void OnTick() override - { - if (ticks >= pagetime || skiprequest) - { - page++; - if (page < credits.Size()) - pagelines = credits[page].Split("\n"); - else - { - if (skiprequest || !CDplaying()) - { - state = finished; - return; - } - pagelines.Clear(); - } - pagetime = ticks + 60; // - } - } - - void Draw(double smoothratio) override - { - twod->ClearScreen(); - - int y = 100 - ((10 * (pagelines.Size() - 1)) / 2); - - for (unsigned i = 0; i < pagelines.Size(); i++) - { - int ptime = clamp((pagetime - ticks - smoothratio) * 1000 / GameTicRate, 0, 2000); // in milliseconds - int light; - - if (ptime < 255) light = ptime; - else if (ptime > 2000 - 255) light = 2000 - ptime; - else light = 255; - - auto color = PalEntry(255, light, light, light); - - int nStringWidth = SmallFont->StringWidth(pagelines[i]); - DrawText(twod, SmallFont, CR_UNTRANSLATED, 160 - nStringWidth / 2, y, pagelines[i], DTA_FullscreenScale, FSMode_Fit320x200, DTA_Color, color, TAG_DONE); - y += 10; - } - } -}; - -//--------------------------------------------------------------------------- -// -// player died -// -//--------------------------------------------------------------------------- - -void DoGameOverScene(bool finallevel) -{ - JobDesc job; - - if (finallevel) - { - job = { Create(CINEMA_LOSE_SCENE) }; - } - else - { - StopCD(); - PlayGameOverSound(); - job = { Create(tileGetTexture(kTile3591), DScreenJob::fadein | DScreenJob::fadeout, 0x7fffffff, TRANSLATION(Translation_BasePalettes, 16)) }; - } - RunScreenJob(&job, 1, [](bool) { gameaction = ga_mainmenu; }); + return tileGetTexture(kTileLoboLaptop)->GetID().GetIndex(); } - -void DoAfterCinemaScene(int nLevel, TArray& jobs) +DEFINE_ACTION_FUNCTION_NATIVE(DLastLevelCinema, DoStatic, DoStatic) { - int scene = -1; - if (nLevel == 10) scene = CINEMA_AFTER_LEVEL_10; - if (nLevel == 15) scene = CINEMA_AFTER_LEVEL_15; - if (nLevel == 20) scene = CINEMA_AFTER_LEVEL_20; - if (scene > 0) jobs.Push({ Create(scene) }); - if (nLevel == 19) { jobs.Push({ Create() }); selectedlevelnew = 20; } - if (nLevel == 20) jobs.Push({ Create() }); + PARAM_PROLOGUE; + PARAM_INT(x); + PARAM_INT(y); + ACTION_RETURN_INT(DoStatic(x, y)); } -void DoBeforeCinemaScene(int nLevel, TArray& jobs) +DEFINE_ACTION_FUNCTION_NATIVE(DLastLevelCinema, UndoStatic, UndoStatic) { - if (nLevel == 5) jobs.Push({ Create(CINEMA_BEFORE_LEVEL_5) }); - else if (nLevel == 11) jobs.Push({ Create(CINEMA_BEFORE_LEVEL_11, 11) }); + ACTION_RETURN_INT(UndoStatic()); } END_PS_NS diff --git a/source/games/exhumed/src/anims.cpp b/source/games/exhumed/src/anims.cpp index 02951960c..83542258f 100644 --- a/source/games/exhumed/src/anims.cpp +++ b/source/games/exhumed/src/anims.cpp @@ -270,7 +270,7 @@ void FuncAnim(int a, int, int nRun) case 0x90000: { seq_PlotSequence(a & 0xFFFF, nSeq, AnimList[nAnim].field_2, 0x101); - tsprite[a & 0xFFFF].owner = -1; + mytsprite[a & 0xFFFF].owner = -1; return; } diff --git a/source/games/exhumed/src/bubbles.cpp b/source/games/exhumed/src/bubbles.cpp index 273b872f8..64b6a9046 100644 --- a/source/games/exhumed/src/bubbles.cpp +++ b/source/games/exhumed/src/bubbles.cpp @@ -203,7 +203,7 @@ void FuncBubble(int a, int, int nRun) case 0x90000: { seq_PlotSequence(a & 0xFFFF, nSeq, BubbleList[nBubble].nFrame, 1); - tsprite[a & 0xFFFF].owner = -1; + mytsprite[a & 0xFFFF].owner = -1; return; } @@ -236,7 +236,7 @@ void DoBubbleMachines() void BuildBubbleMachine(int nSprite) { if (nMachineCount >= kMaxMachines) { - I_Error("too many bubble machines in level %d\n", currentLevel->levelNumber); + I_Error("too many bubble machines in level %s\n", currentLevel->labelName.GetChars()); exit(-1); } diff --git a/source/games/exhumed/src/bullet.cpp b/source/games/exhumed/src/bullet.cpp index 1fbc4db32..22ff887b2 100644 --- a/source/games/exhumed/src/bullet.cpp +++ b/source/games/exhumed/src/bullet.cpp @@ -854,7 +854,7 @@ void FuncBullet(int a, int, int nRun) case 0x90000: { short nSprite2 = a & 0xFFFF; - tsprite[nSprite2].statnum = 1000; + mytsprite[nSprite2].statnum = 1000; if (BulletList[nBullet].nType == 15) { @@ -863,7 +863,7 @@ void FuncBullet(int a, int, int nRun) else { seq_PlotSequence(nSprite2, nSeq, BulletList[nBullet].nFrame, 0); - tsprite[nSprite2].owner = -1; + mytsprite[nSprite2].owner = -1; } break; } diff --git a/source/games/exhumed/src/cheats.cpp b/source/games/exhumed/src/cheats.cpp index 27f4872c2..0dc38b898 100644 --- a/source/games/exhumed/src/cheats.cpp +++ b/source/games/exhumed/src/cheats.cpp @@ -39,7 +39,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "d_protocol.h" #include "gstrings.h" #include "aistuff.h" -#include "mmulti.h" +#include "d_net.h" BEGIN_PS_NS diff --git a/source/games/exhumed/src/d_menu.cpp b/source/games/exhumed/src/d_menu.cpp index 79061cb69..176770f23 100644 --- a/source/games/exhumed/src/d_menu.cpp +++ b/source/games/exhumed/src/d_menu.cpp @@ -67,18 +67,6 @@ void GameInterface::MenuSound(EMenuSounds snd) } } -void GameInterface::QuitToTitle() -{ - gameaction = ga_mainmenu; -} - -bool GameInterface::StartGame(FNewGameStartup& gs) -{ - auto map = FindMapByLevelNum(gs.Skill); // 0 is training, 1 is the regular game - the game does not have skill levels. - DeferedStartGame(map, 1, true); - return true; -} - FSavegameInfo GameInterface::GetSaveSig() { return { SAVESIG_PS, MINSAVEVER_PS, SAVEVER_PS }; @@ -90,12 +78,6 @@ END_PS_NS using namespace Exhumed; -DEFINE_ACTION_FUNCTION(_ListMenuItemExhumedPlasma, Draw) -{ - menu_DoPlasma(); - return 0; -} - DEFINE_ACTION_FUNCTION(_ListMenuItemExhumedLogo, Draw) { auto nLogoTile = GameLogo(); diff --git a/source/games/exhumed/src/enginesubs.cpp b/source/games/exhumed/src/enginesubs.cpp index 7223f822e..76b570bd4 100644 --- a/source/games/exhumed/src/enginesubs.cpp +++ b/source/games/exhumed/src/enginesubs.cpp @@ -36,6 +36,8 @@ void resettiming() void precache() { + if (!r_precache) return; + int i; for (i = 0; i < numsectors; i++) diff --git a/source/games/exhumed/src/exhumed.cpp b/source/games/exhumed/src/exhumed.cpp index 462c2923f..0c46756c0 100644 --- a/source/games/exhumed/src/exhumed.cpp +++ b/source/games/exhumed/src/exhumed.cpp @@ -75,12 +75,6 @@ void ResetEngine() void InstallEngine() { - TileFiles.LoadArtSet("tiles%03d.art"); - - if (engineInit()) - { - G_FatalEngineError(); - } uploadCinemaPalettes(); LoadPaletteLookups(); } @@ -244,6 +238,15 @@ double calc_smoothratio() return I_GetTimeFrac() * MaxSmoothRatio; } +void DoGameOverScene(bool finallevel) +{ + // todo: make these customizable later. + StartCutscene(finallevel ? "ExhumedCutscenes.BuildCinemaLose" : "ExhumedCutscenes.BuildGameoverScene", 0, [](bool) + { + gameaction = ga_mainmenu; + }); +} + void GameMove(void) { FixPalette(); @@ -253,7 +256,7 @@ void GameMove(void) sprite[i].backuploc(); } - if (currentLevel->levelNumber == kMap20) + if (currentLevel->gameflags & LEVEL_EX_COUNTDOWN) { if (lCountDown <= 0) { @@ -462,7 +465,7 @@ void GameInterface::Ticker() void LevelFinished() { - NextMap = currentLevel->levelNumber == 20 ? nullptr : FindMapByLevelNum(currentLevel->levelNumber + 1); // todo: Use the map record for progression + NextMap = FindNextMap(currentLevel); EndLevel = 13; } @@ -486,19 +489,6 @@ void GameInterface::app_init() #if 0 help_disabled = true; #endif - // Create the global level table. Parts of the engine need it, even though the game itself does not. - for (int i = 0; i <= 32; i++) - { - auto mi = AllocateMap(); - mi->fileName.Format("LEV%d.MAP", i); - mi->labelName.Format("LEV%d", i); - mi->name.Format("$TXT_EX_MAP%02d", i); - mi->levelNumber = i; - - int nTrack = i; - if (nTrack != 0) nTrack--; - mi->cdSongId = (nTrack % 8) + 11; - } InitCheats(); registerosdcommands(); @@ -621,7 +611,7 @@ void SerializeState(FSerializer& arc) int loaded = 0; if (arc.BeginObject("state")) { - if (arc.isReading() && currentLevel->levelNumber == 20) + if (arc.isReading() && (currentLevel->gameflags & LEVEL_EX_COUNTDOWN)) { InitEnergyTile(); } @@ -645,7 +635,6 @@ void SerializeState(FSerializer& arc) ("bsnakecam", bSnakeCam) ("slipmode", bSlipMode) ("PlayClock", PlayClock) - ("cinemaseen", nCinemaSeen) ("spiritsprite", nSpiritSprite) .EndObject(); } diff --git a/source/games/exhumed/src/exhumed.h b/source/games/exhumed/src/exhumed.h index 87bdc393e..32ac8f035 100644 --- a/source/games/exhumed/src/exhumed.h +++ b/source/games/exhumed/src/exhumed.h @@ -54,8 +54,6 @@ void SetHiRes(); void BlackOut(); void DoGameOverScene(bool finallevel); -void DoAfterCinemaScene(int nLevel, TArray &jobs); -void DoBeforeCinemaScene(int nLevel, TArray& jobs); int Query(short n, short l, ...); @@ -83,12 +81,11 @@ void DoSpiritHead(); void CheckKeys2(); void GameTicker(); -void InitLevel(int); +void InitLevel(MapRecord*); void InitNewGame(); int showmap(short nLevel, short nLevelNew, short nLevelBest); void menu_DoPlasma(); -void menu_DrawTheMap(int nLevel, int nLevelNew, int nLevelBest, TArray& jobs); void DoEnergyTile(); void InitEnergyTile(); @@ -127,7 +124,6 @@ extern short nCurBodyNum; extern short nBodyTotal; extern short bSnakeCam; -extern uint8_t nCinemaSeen; extern short nButtonColor; @@ -155,11 +151,6 @@ extern short bDoFlashes; extern int bVanilla; -inline int PublisherLogo() -{ - return (g_gameType & GAMEFLAG_EXHUMED) ? kTileBMGLogo : kTilePIELogo; -} - inline int GameLogo() { return (g_gameType & GAMEFLAG_EXHUMED) ? kExhumedLogo : kPowerslaveLogo; @@ -197,10 +188,11 @@ public: void Start(double starttime); void ComputeCinemaText(); - void ReadyCinemaText(uint16_t nVal); + void ReadyCinemaText(const char* nVal); void DisplayText(); bool AdvanceCinemaText(double clock); void SetPalette(int pal) { currentCinemaPalette = pal; } + void Create(const FString& text, int pal); }; @@ -229,12 +221,10 @@ struct GameInterface : ::GameInterface bool GenerateSavePic() override; void MenuOpened() override; void MenuSound(EMenuSounds snd) override; - bool StartGame(FNewGameStartup& gs) override; FSavegameInfo GetSaveSig() override; void SerializeGameState(FSerializer& arc); bool CanSave() override; ReservedSpace GetReservedScreenSpace(int viewsize) override { return { 0, 24 }; } - void QuitToTitle() override; void UpdateSounds() override; void ErrorCleanup() override; void Ticker() override; @@ -255,6 +245,7 @@ struct GameInterface : ::GameInterface int chaseCamX(binangle ang) { return -ang.bcos() / 12; } int chaseCamY(binangle ang) { return -ang.bsin() / 12; } int chaseCamZ(fixedhoriz horiz) { return horiz.asq16() / 384; } + void processSprites(spritetype* tsprite, int& spritesortcnt, int viewx, int viewy, int viewz, binangle viewang, double smoothRatio) override; ::GameStats getStats() override; }; diff --git a/source/games/exhumed/src/fish.cpp b/source/games/exhumed/src/fish.cpp index d85f901a1..0948c8615 100644 --- a/source/games/exhumed/src/fish.cpp +++ b/source/games/exhumed/src/fish.cpp @@ -330,7 +330,7 @@ void FuncFish(int a, int nDamage, int nRun) case 0x90000: { seq_PlotSequence(a & 0xFFFF, SeqOffsets[kSeqFish] + FishSeq[nAction].a, FishList[nFish].nFrame, FishSeq[nAction].b); - tsprite[a & 0xFFFF].owner = -1; + mytsprite[a & 0xFFFF].owner = -1; return; } diff --git a/source/games/exhumed/src/gameloop.cpp b/source/games/exhumed/src/gameloop.cpp index d810eab56..ab08b2721 100644 --- a/source/games/exhumed/src/gameloop.cpp +++ b/source/games/exhumed/src/gameloop.cpp @@ -49,32 +49,18 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "automap.h" #include "raze_music.h" #include "v_draw.h" +#include "vm.h" BEGIN_PS_NS short nBestLevel; -extern uint8_t nCinemaSeen; - void RunCinemaScene(int num); void GameMove(void); void DrawClock(); double calc_smoothratio(); void DoTitle(CompletionFunc completion); -static void showmap(short nLevel, short nLevelNew, short nLevelBest, TArray &jobs) -{ - if (nLevelNew == 5 && !(nCinemaSeen & 1)) { - nCinemaSeen |= 1; - DoBeforeCinemaScene(5, jobs); - } - - menu_DrawTheMap(nLevel, nLevelNew, nLevelBest, jobs); - - if (nLevelNew == 11 && !(nCinemaSeen & 2)) { - DoBeforeCinemaScene(11, jobs); - } -} void GameInterface::Render() @@ -83,7 +69,7 @@ void GameInterface::Render() drawtime.Reset(); drawtime.Clock(); - if (currentLevel && currentLevel->levelNumber == kMap20) + if (currentLevel && (currentLevel->gameflags & LEVEL_EX_COUNTDOWN)) { DoEnergyTile(); DrawClock(); @@ -127,104 +113,89 @@ void GameInterface::DrawBackground() DrawRel(kTile3512 + ((dword_9AB5F + 2) & 3), 270, 150, 0); } +void GameInterface::NextLevel(MapRecord *map, int skill) +{ + InitLevel(map); + + if (map->levelNumber >= nBestLevel) + { + nBestLevel = map->levelNumber - 1; + } + + STAT_NewLevel(currentLevel->labelName); + +} + //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- -static void Intermission(MapRecord *from_map, MapRecord *to_map) -{ - TArray jobs; - - if (from_map) StopAllSounds(); - bCamera = false; - automapMode = am_off; - - if (to_map) - { - if (to_map->levelNumber != 0) - nBestLevel = to_map->levelNumber - 1; - - STAT_Update(false); - if (to_map->levelNumber == kMap20) - nPlayerLives[0] = 0; - - if (to_map->levelNumber == 0) // skip all intermission stuff when going to the training map. - { - gameaction = ga_nextlevel; - return; - } - else - { - DoAfterCinemaScene(to_map->levelNumber - 1, jobs); - } - if (to_map->levelNumber > -1 && to_map->levelNumber < kMap20) - { - // start a new game at the given level - if (!nNetPlayerCount && to_map->levelNumber > 0) - { - showmap(from_map ? from_map->levelNumber : -1, to_map->levelNumber, nBestLevel, jobs); - } - else - jobs.Push({ Create(1) }); // we need something in here even in the multiplayer case. - } - } - else - { - DoAfterCinemaScene(20, jobs); - STAT_Update(true); - } - - - if (jobs.Size() > 0) - { - RunScreenJob(jobs.Data(), jobs.Size(), [=](bool) - { - if (!to_map) gameaction = ga_startup; // this was the end of the game - else - { - if (to_map->levelNumber != selectedlevelnew) - { - // User can switch destination on the scrolling map. - g_nextmap = FindMapByLevelNum(selectedlevelnew); - STAT_Cancel(); - } - gameaction = ga_nextlevel; - - } - }); - } - -} - -void GameInterface::NextLevel(MapRecord *map, int skill) -{ - InitLevel(map->levelNumber); - - if (map->levelNumber > nBestLevel) - { - nBestLevel = selectedlevelnew; - } - - if (map->levelNumber == 11) nCinemaSeen |= 2; - STAT_NewLevel(currentLevel->labelName); - -} - void GameInterface::NewGame(MapRecord *map, int skill, bool frommenu) { // start a new game on the given level InitNewGame(); - if (map->levelNumber == 1) STAT_StartNewGame("Exhumed", 1); - Intermission(nullptr, map); + InitLevel(map); + gameaction = ga_level; } -void GameInterface::LevelCompleted(MapRecord *map, int skill) +int selectedlevelnew; + +DEFINE_ACTION_FUNCTION(DMapScreen, SetNextLevel) +{ + PARAM_PROLOGUE; + PARAM_INT(v); + selectedlevelnew = v; + return 0; +} + +void GameInterface::LevelCompleted(MapRecord *to_map, int skill) { Mus_Stop(); - if (currentLevel->levelNumber == 0) gameaction = ga_mainmenu; - Intermission(currentLevel, map); + if (currentLevel->gameflags & LEVEL_EX_TRAINING) + { + gameaction = ga_mainmenu; + return; + } + + StopAllSounds(); + bCamera = false; + automapMode = am_off; + + STAT_Update(to_map == nullptr); + if (to_map) + { + if (to_map->levelNumber > nBestLevel) nBestLevel = to_map->levelNumber - 1; + + if (to_map->gameflags & LEVEL_EX_COUNTDOWN) nPlayerLives[0] = 0; + if (to_map->gameflags & LEVEL_EX_TRAINING) + { + gameaction = ga_nextlevel; + return; + } + } + SummaryInfo info{}; + info.kills = nCreaturesKilled; + info.maxkills = nCreaturesTotal; + info.supersecrets = nBestLevel; + info.time = PlayClock * GameTicRate / 120; + selectedlevelnew = to_map->levelNumber; + ShowIntermission(currentLevel, to_map, &info, [=](bool) + { + if (!to_map) gameaction = ga_startup; // this was the end of the game + else + { + if (to_map->levelNumber != selectedlevelnew) + { + // User can switch destination on the scrolling map. + g_nextmap = FindMapByLevelNum(selectedlevelnew); + STAT_Cancel(); + } + gameaction = ga_nextlevel; + + } + }); } //--------------------------------------------------------------------------- @@ -237,22 +208,7 @@ void GameInterface::Startup() { resettiming(); EndLevel = 0; - - if (userConfig.CommandMap.IsNotEmpty()) - { - /* - auto map = FindMapByName(userConfig.CommandMap); - if (map) DeferedStartMap(map, 0); - userConfig.CommandMap = ""; - goto again; - */ - } - else - { - if (!userConfig.nologo) DoTitle([](bool) { gameaction = ga_mainmenu; }); - else gameaction = ga_mainmenu; - } - + PlayLogos(ga_mainmenu, ga_mainmenu, false); } void GameInterface::ErrorCleanup() diff --git a/source/games/exhumed/src/gun.cpp b/source/games/exhumed/src/gun.cpp index 76320baeb..c44154707 100644 --- a/source/games/exhumed/src/gun.cpp +++ b/source/games/exhumed/src/gun.cpp @@ -964,9 +964,19 @@ void DrawWeapons(double smooth) if (cl_weaponsway) { - // CHECKME - not & 0x7FF? - double nBobAngle = obobangle + MulScaleF(((bobangle + 1024 - obobangle) & 2047) - 1024, smooth, 16); - double nVal = (ototalvel[nLocalPlayer] + MulScaleF(totalvel[nLocalPlayer] - ototalvel[nLocalPlayer], smooth, 16)) * 0.5; + double nBobAngle, nVal; + + if (cl_hudinterpolation) + { + nBobAngle = interpolatedangle(buildang(obobangle), buildang(bobangle), smooth).asbuildf(); + nVal = interpolatedvaluef(ototalvel[nLocalPlayer], totalvel[nLocalPlayer], smooth, 17); + } + else + { + nBobAngle = bobangle; + nVal = totalvel[nLocalPlayer]; + } + yOffset = MulScaleF(nVal, bsinf(fmod(nBobAngle, 1024.), -8), 9); if (var_34 == 1) @@ -988,7 +998,7 @@ void DrawWeapons(double smooth) } double const look_anghalf = PlayerList[nLocalPlayer].angle.look_anghalf(smooth); - double const looking_arc = fabs(look_anghalf) / 4.5; + double const looking_arc = PlayerList[nLocalPlayer].angle.looking_arc(smooth); xOffset -= look_anghalf; yOffset += looking_arc; diff --git a/source/games/exhumed/src/init.cpp b/source/games/exhumed/src/init.cpp index ad81fc807..5c2c1db7c 100644 --- a/source/games/exhumed/src/init.cpp +++ b/source/games/exhumed/src/init.cpp @@ -66,9 +66,9 @@ uint8_t bIsVersion6 = true; -uint8_t LoadLevel(int nMap) +uint8_t LoadLevel(MapRecord* map) { - if (nMap == kMap20) + if (map->gameflags & LEVEL_EX_COUNTDOWN) { lCountDown = 81000; nAlarmTicks = 30; @@ -116,12 +116,12 @@ uint8_t LoadLevel(int nMap) InitItems(); InitInput(); - if (nMap == kMap20) { + if (map->gameflags & LEVEL_EX_COUNTDOWN) { InitEnergyTile(); } } - if (nMap > 15) + if (map->gameflags & LEVEL_EX_ALTSOUND) { nSwitchSound = 35; nStoneSound = 23; @@ -136,10 +136,6 @@ uint8_t LoadLevel(int nMap) nStopSound = 66; } - if (nMap < 0) { - return false; - } - vec3_t startPos; engineLoadBoard(currentLevel->fileName, 0, &startPos, &inita, &initsect); initx = startPos.x; @@ -172,12 +168,12 @@ uint8_t LoadLevel(int nMap) return true; } -void InitLevel(int level) // todo: use a map record +void InitLevel(MapRecord* map) { StopCD(); - currentLevel = FindMapByLevelNum(level); - if (!LoadLevel(level)) { - I_Error("Can't load level %d...\n", level); + currentLevel = map; + if (!LoadLevel(map)) { + I_Error("Cannot load %s...\n", map->fileName.GetChars()); } for (int i = 0; i < nTotalPlayers; i++) @@ -200,17 +196,14 @@ void InitLevel(int level) // todo: use a map record RefreshStatus(); - int nTrack = level; - if (nTrack != 0) nTrack--; - - playCDtrack((nTrack % 8) + 11, true); + if (!mus_redbook && map->music.IsNotEmpty()) Mus_Play(map->labelName, map->music, true); // Allow non-CD music if defined for the current level + playCDtrack(map->cdSongId, true); setLevelStarted(currentLevel); } void InitNewGame() { bCamera = false; - nCinemaSeen = 0; PlayerCount = 0; for (int i = 0; i < nTotalPlayers; i++) diff --git a/source/games/exhumed/src/input.cpp b/source/games/exhumed/src/input.cpp index a471f6fc7..83e5abc6f 100644 --- a/source/games/exhumed/src/input.cpp +++ b/source/games/exhumed/src/input.cpp @@ -124,8 +124,8 @@ void GameInterface::GetInput(InputPacket* packet, ControlInfo* const hidInput) { if (!nFreeze) { - applylook(&pPlayer->angle, input.avel, &sPlayerInput[nLocalPlayer].actions, scaleAdjust); - sethorizon(&pPlayer->horizon, input.horz, &sPlayerInput[nLocalPlayer].actions, scaleAdjust); + pPlayer->angle.applyinput(input.avel, &sPlayerInput[nLocalPlayer].actions, scaleAdjust); + pPlayer->horizon.applyinput(input.horz, &sPlayerInput[nLocalPlayer].actions, scaleAdjust); if (input.horz) { diff --git a/source/games/exhumed/src/items.cpp b/source/games/exhumed/src/items.cpp index edcd344f6..25b2bb945 100644 --- a/source/games/exhumed/src/items.cpp +++ b/source/games/exhumed/src/items.cpp @@ -432,7 +432,7 @@ void StartRegenerate(short nSprite) pSprite->extra = 1350; pSprite->ang = nFirstRegenerate; - if (currentLevel->levelNumber <= kMap20) + if (!(currentLevel->gameflags & LEVEL_EX_MULTI)) { pSprite->ang /= 5; } diff --git a/source/games/exhumed/src/lavadude.cpp b/source/games/exhumed/src/lavadude.cpp index cd4499760..6be1de29c 100644 --- a/source/games/exhumed/src/lavadude.cpp +++ b/source/games/exhumed/src/lavadude.cpp @@ -232,7 +232,7 @@ void FuncLava(int a, int nDamage, int nRun) case 0x90000: { seq_PlotSequence(a & 0xFFFF, nSeq, LavaList[nLava].nFrame, LavadudeSeq[nAction].b); - tsprite[a & 0xFFFF].owner = -1; + mytsprite[a & 0xFFFF].owner = -1; return; } diff --git a/source/games/exhumed/src/map.cpp b/source/games/exhumed/src/map.cpp index ed481a612..80d7357cc 100644 --- a/source/games/exhumed/src/map.cpp +++ b/source/games/exhumed/src/map.cpp @@ -23,7 +23,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "view.h" #include "v_2ddrawer.h" #include "automap.h" -#include "mmulti.h" #include "v_draw.h" BEGIN_PS_NS @@ -77,8 +76,6 @@ bool GameInterface::DrawAutomapPlayer(int x, int y, int z, int a, double const s // [MR]: Confirm that this is correct as math doesn't match the variable names. int nCos = z * -bsin(a); int nSin = z * -bcos(a); - int nCos2 = MulScale(nCos, yxaspect, 16); - int nSin2 = MulScale(nSin, yxaspect, 16); for (int i = connecthead; i >= 0; i = connectpoint2[i]) { @@ -94,7 +91,7 @@ bool GameInterface::DrawAutomapPlayer(int x, int y, int z, int a, double const s getzrange_old(pSprite->x, pSprite->y, pSprite->z, pSprite->sectnum, &ceilZ, &ceilHit, &floorZ, &floorHit, (pSprite->clipdist << 2) + 16, CLIPMASK0); int nTop, nBottom; GetSpriteExtents(pSprite, &nTop, &nBottom); - int nScale = MulScale((pSprite->yrepeat + ((floorZ - nBottom) >> 8)) * z, yxaspect, 16); + int nScale = (pSprite->yrepeat + ((floorZ - nBottom) >> 8)) * z; nScale = clamp(nScale, 8000, 65536 << 1); // Players on automap double x = xdim / 2. + x1 / double(1 << 12); diff --git a/source/games/exhumed/src/menu.cpp b/source/games/exhumed/src/menu.cpp index f47158246..52d05404e 100644 --- a/source/games/exhumed/src/menu.cpp +++ b/source/games/exhumed/src/menu.cpp @@ -43,8 +43,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. BEGIN_PS_NS -uint8_t nCinemaSeen; - uint8_t energytile[66 * 66] = {0}; uint8_t *cur; diff --git a/source/games/exhumed/src/move.cpp b/source/games/exhumed/src/move.cpp index 7a7ab0c94..fdd31dede 100644 --- a/source/games/exhumed/src/move.cpp +++ b/source/games/exhumed/src/move.cpp @@ -207,7 +207,7 @@ void MoveThings() DoMovingSects(); DoRegenerates(); - if (currentLevel->levelNumber == kMap20) + if (currentLevel->gameflags & LEVEL_EX_COUNTDOWN) { DoFinale(); if (lCountDown < 1800 && nDronePitch < 2400 && !lFinaleStart) diff --git a/source/games/exhumed/src/movie.cpp b/source/games/exhumed/src/movie.cpp index cb632cc05..b5c57a221 100644 --- a/source/games/exhumed/src/movie.cpp +++ b/source/games/exhumed/src/movie.cpp @@ -28,6 +28,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "s_music.h" #include "screenjob.h" #include "v_draw.h" +#include "vm.h" BEGIN_PS_NS @@ -63,9 +64,34 @@ class LMFPlayer AudioData audio{}; AnimTextures animtex; + FileReader fp; + int nFrame = 0; + uint64_t nextclock = 0; + public: + + LMFPlayer(const char *filename) + { + fp = fileSystem.OpenFileReader(filename); + Open(fp); + } + + bool Frame(uint64_t clock) + { + if (clock >= nextclock) + { + nextclock += 100'000'000; + if (ReadFrame(fp) == 0) + { + return true; + } + } + + return false; + } + int ReadFrame(FileReader& fp) { nFrame++; @@ -188,105 +214,56 @@ public: S_StopCustomStream(stream); } - AnimTextures& animTex() + FTextureID GetTexture() { - return animtex; + return animtex.GetFrameID(); } }; -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -class DLmfPlayer : public DSkippableScreenJob +int IdentifyLMF(const FString* fn) { - LMFPlayer decoder; - double angle = 1536; - double z = 0; - uint64_t nextclock = 0, lastclock = 0; - FileReader fp; - -public: - - DLmfPlayer(FileReader& fr) - { - decoder.Open(fr); - lastclock = 0; - nextclock = 0; - fp = std::move(fr); - pausable = false; - } - - //--------------------------------------------------------------------------- - // - // - // - //--------------------------------------------------------------------------- - - void Draw(double smoothratio) override - { - uint64_t clock = (ticks + smoothratio) * 1'000'000'000. / GameTicRate; - if (clock >= nextclock) - { - nextclock += 100'000'000; - if (decoder.ReadFrame(fp) == 0) - { - state = finished; - return; - } - } - - double duration = (clock - lastclock) * double(120. / 8'000'000'000); - if (z < 65536) { // Zoom - normal zoom is 65536. - z += 2048 * duration; - } - if (z > 65536) z = 65536; - if (angle != 0) { - angle += 16. * duration; - if (angle >= 2048) { - angle = 0; - } - } - - { - twod->ClearScreen(); - DrawTexture(twod, decoder.animTex().GetFrame(), 160, 100, DTA_FullscreenScale, FSMode_Fit320x200, - DTA_CenterOffset, true, DTA_FlipY, true, DTA_ScaleX, z / 65536., DTA_ScaleY, z / 65536., DTA_Rotate, (-angle - 512) * BAngToDegree, TAG_DONE); - } - - lastclock = clock; - } - - void OnDestroy() override - { - decoder.Close(); - fp.Close(); - } -}; - - - -DScreenJob* PlayMovie(const char* fileName) -{ - // clear keys - - auto fp = fileSystem.OpenFileReader(fileName); - if (!fp.isOpen()) - { - return Create(1); - } + auto fp = fileSystem.OpenFileReader(*fn); + if (!fp.isOpen()) return false; char buffer[4]; fp.Read(buffer, 4); - if (memcmp(buffer, "LMF ", 4)) - { - fp.Close(); - // Allpw replacement with more modern formats. - return PlayVideo(fileName); - } - fp.Seek(0, FileReader::SeekSet); - return Create(fp); + return (0 == memcmp(buffer, "LMF ", 4)); } +DEFINE_ACTION_FUNCTION(_LMFDecoder, Create) +{ + PARAM_PROLOGUE; + PARAM_STRING(fn); + ACTION_RETURN_POINTER(new LMFPlayer(fn)); +} + +DEFINE_ACTION_FUNCTION_NATIVE(_LMFDecoder, Identify, IdentifyLMF) +{ + PARAM_PROLOGUE; + PARAM_STRING(fn); + ACTION_RETURN_BOOL(IdentifyLMF(&fn)); +} + +DEFINE_ACTION_FUNCTION(_LMFDecoder, Frame) +{ + PARAM_SELF_STRUCT_PROLOGUE(LMFPlayer); + PARAM_FLOAT(clock); + ACTION_RETURN_BOOL(self->Frame(uint64_t(clock))); +} + +DEFINE_ACTION_FUNCTION(_LMFDecoder, GetTexture) +{ + PARAM_SELF_STRUCT_PROLOGUE(LMFPlayer); + ACTION_RETURN_INT(self->GetTexture().GetIndex()); +} + +DEFINE_ACTION_FUNCTION(_LMFDecoder, Close) +{ + PARAM_SELF_STRUCT_PROLOGUE(LMFPlayer); + self->Close(); + delete self; + return 0; +} + + + END_PS_NS diff --git a/source/games/exhumed/src/namelist.h b/source/games/exhumed/src/namelist.h index 2a6a22d87..50787ef8d 100644 --- a/source/games/exhumed/src/namelist.h +++ b/source/games/exhumed/src/namelist.h @@ -1,3 +1,4 @@ +x(SkullJaw, 3437) x(PowerslaveLogo, 3442) x(MenuNewGameTile, 3460) x(MenuLoadGameTile, 3461) @@ -6,6 +7,7 @@ x(MenuSoundFxTile, 3466) x(MenuCursorTile, 3468) x(MenuBlank, 3469) x(SkullHead, 3582) +x(SkullJaw2, 3583) x(ExhumedLogo, 3592) x(Energy1, 3604) x(Energy2, 3605) @@ -27,4 +29,100 @@ x(ClockSymbol15, 3620) x(ClockSymbol16, 3621) x(TileLoboLaptop, 3623) x(TileBMGLogo, 3368) +x(LobotomyLogo, 3348) x(TilePIELogo, 3349) +x(Gameover, 3591) + +x(MapPlaque1_01, 3376) +x(MapPlaque1_02, 3378) +x(MapPlaque1_03, 3380) +x(MapPlaque1_04, 3382) +x(MapPlaque1_05, 3384) +x(MapPlaque1_06, 3371) +x(MapPlaque1_07, 3387) +x(MapPlaque1_08, 3389) +x(MapPlaque1_09, 3391) +x(MapPlaque1_10, 3409) +x(MapPlaque1_11, 3393) +x(MapPlaque1_12, 3395) +x(MapPlaque1_13, 3397) +x(MapPlaque1_14, 3399) +x(MapPlaque1_15, 3401) +x(MapPlaque1_16, 3403) +x(MapPlaque1_17, 3405) +x(MapPlaque1_18, 3407) +x(MapPlaque1_19, 3412) +x(MapPlaque1_20, 3415) + +x(MapPlaque2_01, 3377) +x(MapPlaque2_02, 3379) +x(MapPlaque2_03, 3381) +x(MapPlaque2_04, 3383) +x(MapPlaque2_05, 3385) +x(MapPlaque2_06, 3386) +x(MapPlaque2_07, 3388) +x(MapPlaque2_08, 3390) +x(MapPlaque2_09, 3392) +x(MapPlaque2_10, 3410) +x(MapPlaque2_11, 3394) +x(MapPlaque2_12, 3396) +x(MapPlaque2_13, 3398) +x(MapPlaque2_14, 3400) +x(MapPlaque2_15, 3402) +x(MapPlaque2_16, 3404) +x(MapPlaque2_17, 3406) +x(MapPlaque2_18, 3408) +x(MapPlaque2_19, 3413) +x(MapPlaque2_20, 3416) + +x(MapPlaqueText_01, 3411) +x(MapPlaqueText_02, 3414) +x(MapPlaqueText_03, 3417) +x(MapPlaqueText_04, 3420) +x(MapPlaqueText_05, 3423) +x(MapPlaqueText_06, 3426) +x(MapPlaqueText_07, 3429) +x(MapPlaqueText_08, 3432) +x(MapPlaqueText_09, 3435) +x(MapPlaqueText_10, 3418) +x(MapPlaqueText_11, 3438) +x(MapPlaqueText_12, 3441) +x(MapPlaqueText_13, 3444) +x(MapPlaqueText_14, 3447) +x(MapPlaqueText_15, 3450) +x(MapPlaqueText_16, 3453) +x(MapPlaqueText_17, 3456) +x(MapPlaqueText_18, 3459) +x(MapPlaqueText_19, 3419) +x(MapPlaqueText_20, 3421) + +x(MapFire_11, 3484) +x(MapFire_12, 3485) +x(MapFire_13, 3486) +x(MapFire_14, 3487) +x(MapFire_21, 3488) +x(MapFire_22, 3489) +x(MapFire_23, 3490) +x(MapFire_24, 3491) +x(MapFire_31, 3492) +x(MapFire_32, 3493) +x(MapFire_33, 3494) +x(MapFire_34, 3495) + +x(MapBG01, 3353) +x(MapBG02, 3354) +x(MapBG03, 3355) +x(MapBG04, 3356) +x(MapBG05, 3357) +x(MapBG06, 3358) +x(MapBG07, 3359) +x(MapBG08, 3360) +x(MapBG09, 3361) +x(MapBG10, 3362) + +x(TileCinema5, 3449) +x(TileCinema10, 3451) +x(TileCinema11, 3454) +x(TileCinema15, 3446) +x(TileCinemaLose, 3445) +x(TileCinema20, 3448) diff --git a/source/games/exhumed/src/names.h b/source/games/exhumed/src/names.h index 6f0292256..ed087cf72 100644 --- a/source/games/exhumed/src/names.h +++ b/source/games/exhumed/src/names.h @@ -29,136 +29,10 @@ kTileStatusBar = 657, kTile985 = 985, kTile986 = 986, kTile3000 = 3000, -kTile3117 = 3117, +kQueenChunk = 3117, kTile3126 = 3126, -kTile3353 = 3353, -kTile3370 = 3370, -kTile3371 = 3371, -kTile3372 = 3372, -kTile3373 = 3373, -kTile3374 = 3374, -kTile3375 = 3375, -kTile3376 = 3376, -kTile3377 = 3377, -kTile3378 = 3378, -kTile3379 = 3379, -kTile3380 = 3380, -kTile3381 = 3381, -kTile3382 = 3382, -kTile3383 = 3383, -kTile3384 = 3384, -kTile3385 = 3385, -kTile3386 = 3386, -kTile3387 = 3387, -kTile3388 = 3388, -kTile3389 = 3389, -kTile3390 = 3390, -kTile3391 = 3391, -kTile3392 = 3392, -kTile3393 = 3393, -kTile3394 = 3394, -kTile3395 = 3395, -kTile3396 = 3396, -kTile3397 = 3397, -kTile3398 = 3398, -kTile3399 = 3399, -kTile3400 = 3400, -kTile3401 = 3401, -kTile3402 = 3402, -kTile3403 = 3403, -kTile3404 = 3404, -kTile3405 = 3405, -kTile3406 = 3406, -kTile3407 = 3407, -kTile3408 = 3408, -kTile3409 = 3409, -kTile3410 = 3410, -kTile3411 = 3411, -kTile3412 = 3412, -kTile3413 = 3413, -kTile3414 = 3414, -kTile3415 = 3415, -kTile3416 = 3416, -kTile3417 = 3417, -kTile3418 = 3418, -kTile3419 = 3419, -kTile3420 = 3420, -kTile3421 = 3421, -kTile3422 = 3422, -kTile3423 = 3423, -kTile3424 = 3424, -kTile3425 = 3425, -kTile3426 = 3426, -kTile3427 = 3427, -kTile3428 = 3428, -kTile3429 = 3429, -kTile3430 = 3430, -kTile3431 = 3431, -kTile3432 = 3432, -kTile3433 = 3433, -kTile3434 = 3434, -kTile3435 = 3435, -kTile3436 = 3436, -kSkullJaw = 3437, -kTile3438 = 3438, -kTile3439 = 3439, -kTile3440 = 3440, -kTile3441 = 3441, -kTile3443 = 3443, -kTile3444 = 3444, -kTile3445 = 3445, -kTile3446 = 3446, -kTile3447 = 3447, -kTile3448 = 3448, -kTile3449 = 3449, -kTile3450 = 3450, -kTile3451 = 3451, -kTile3452 = 3452, -kTile3453 = 3453, -kTile3454 = 3454, -kTile3455 = 3455, -kTile3456 = 3456, -kTile3457 = 3457, -kTile3458 = 3458, -kTile3459 = 3459, -kTile3462 = 3462, -kTile3463 = 3463, -kTile3464 = 3464, -kTile3467 = 3467, -kTile3470 = 3470, -kTile3471 = 3471, -kTile3472 = 3472, -kTile3473 = 3473, -kTile3474 = 3474, -kTile3475 = 3475, -kTile3476 = 3476, -kTile3477 = 3477, -kTile3478 = 3478, -kTile3479 = 3479, -kTile3480 = 3480, -kTile3481 = 3481, -kTile3482 = 3482, -kTile3483 = 3483, -kTile3484 = 3484, -kTile3485 = 3485, -kTile3486 = 3486, -kTile3487 = 3487, -kTile3488 = 3488, -kTile3489 = 3489, -kTile3490 = 3490, -kTile3491 = 3491, -kTile3492 = 3492, -kTile3493 = 3493, -kTile3494 = 3494, -kTile3495 = 3495, -kTile3496 = 3496, -kTile3497 = 3497, -kTile3498 = 3498, -kTile3499 = 3499, kTile3512 = 3512, kTile3571 = 3571, -kTile3583 = 3583, -kTile3591 = 3591, kTile3593 = 3593, kTile3603 = 3603, kTile4092 = 4092, diff --git a/source/games/exhumed/src/object.cpp b/source/games/exhumed/src/object.cpp index 69701a94e..b73c04415 100644 --- a/source/games/exhumed/src/object.cpp +++ b/source/games/exhumed/src/object.cpp @@ -2254,7 +2254,7 @@ FUNCOBJECT_GOTO: } } - if (currentLevel->levelNumber <= 20 || nStat != kStatExplodeTrigger) + if (!(currentLevel->gameflags & LEVEL_EX_MULTI) || nStat != kStatExplodeTrigger) { runlist_SubRunRec(sprite[nSprite].owner); runlist_SubRunRec(ObjectList[nObject].field_4); @@ -2689,7 +2689,7 @@ void PostProcess() } } - if (currentLevel->levelNumber != kMap20) + if (!(currentLevel->gameflags & LEVEL_EX_COUNTDOWN)) { // esi is i for (i = 0; i < numsectors; i++) diff --git a/source/games/exhumed/src/osdcmds.cpp b/source/games/exhumed/src/osdcmds.cpp index 81efd81e8..469843e22 100644 --- a/source/games/exhumed/src/osdcmds.cpp +++ b/source/games/exhumed/src/osdcmds.cpp @@ -31,7 +31,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "cheathandler.h" #include "gamestate.h" #include "gamefuncs.h" -#include "mmulti.h" BEGIN_PS_NS diff --git a/source/games/exhumed/src/player.cpp b/source/games/exhumed/src/player.cpp index 1f02a17f3..7bc0d3fc3 100644 --- a/source/games/exhumed/src/player.cpp +++ b/source/games/exhumed/src/player.cpp @@ -417,7 +417,7 @@ void RestartPlayer(short nPlayer) plr->nAir = 100; airpages = 0; - if (currentLevel->levelNumber <= kMap20) + if (!(currentLevel->gameflags & LEVEL_EX_MULTI)) { RestoreMinAmmo(nPlayer); } @@ -570,7 +570,7 @@ void StartDeathSeq(int nPlayer, int nVal) BuildStatusAnim((3 * (nLives - 1)) + 7, 0); } - if (currentLevel->levelNumber > 0) { // if not on the training level + if (!(currentLevel->gameflags & LEVEL_EX_TRAINING)) { // if not on the training level nPlayerLives[nPlayer]--; } @@ -679,7 +679,7 @@ void FuncPlayer(int a, int nDamage, int nRun) { int var_48 = 0; int var_40; - bool mplevel = currentLevel->levelNumber > 20; + bool mplevel = (currentLevel->gameflags & LEVEL_EX_MULTI); short nPlayer = RunData[nRun].nVal; assert(nPlayer >= 0 && nPlayer < kMaxPlayers); @@ -915,7 +915,7 @@ void FuncPlayer(int a, int nDamage, int nRun) if (SyncInput()) { Player* pPlayer = &PlayerList[nPlayer]; - applylook(&pPlayer->angle, sPlayerInput[nPlayer].nAngle, &sPlayerInput[nLocalPlayer].actions); + pPlayer->angle.applyinput(sPlayerInput[nPlayer].nAngle, &sPlayerInput[nLocalPlayer].actions); UpdatePlayerSpriteAngle(pPlayer); } @@ -1033,14 +1033,7 @@ void FuncPlayer(int a, int nDamage, int nRun) InitSpiritHead(); PlayerList[nPlayer].nDestVertPan = q16horiz(0); - if (currentLevel->levelNumber == 11) - { - PlayerList[nPlayer].horizon.settarget(46); - } - else - { - PlayerList[nPlayer].horizon.settarget(11); - } + PlayerList[nPlayer].horizon.settarget(currentLevel->ex_ramses_horiz); } } else @@ -2640,7 +2633,7 @@ loc_1BD2E: if (SyncInput()) { - sethorizon(&pPlayer->horizon, sPlayerInput[nPlayer].pan, &sPlayerInput[nLocalPlayer].actions); + pPlayer->horizon.applyinput(sPlayerInput[nPlayer].pan, &sPlayerInput[nLocalPlayer].actions); } if (actions & (SB_LOOK_UP | SB_LOOK_DOWN) || sPlayerInput[nPlayer].pan) diff --git a/source/games/exhumed/src/queen.cpp b/source/games/exhumed/src/queen.cpp index 02839887d..0e4b73bda 100644 --- a/source/games/exhumed/src/queen.cpp +++ b/source/games/exhumed/src/queen.cpp @@ -1445,7 +1445,7 @@ void FuncQueen(int a, int nDamage, int nRun) { short nChunkSprite = BuildCreatureChunk(nSprite, seq_GetSeqPicnum(kSeqQueen, 57, 0)) & 0xFFFF; - sprite[nChunkSprite].picnum = kTile3117 + (i % 3); + sprite[nChunkSprite].picnum = kQueenChunk + (i % 3); sprite[nChunkSprite].xrepeat = 100; sprite[nChunkSprite].yrepeat = 100; } diff --git a/source/games/exhumed/src/ra.cpp b/source/games/exhumed/src/ra.cpp index 56d299c3a..00f80f0d9 100644 --- a/source/games/exhumed/src/ra.cpp +++ b/source/games/exhumed/src/ra.cpp @@ -306,7 +306,7 @@ void FuncRa(int a, int, int nRun) { short nSprite2 = a & 0xFFFF; seq_PlotSequence(nSprite2, nSeq, Ra[nPlayer].nFrame, 1); - tsprite[nSprite2].owner = -1; + mytsprite[nSprite2].owner = -1; return; } } diff --git a/source/games/exhumed/src/ramses.cpp b/source/games/exhumed/src/ramses.cpp index 546acfcf2..404e9e281 100644 --- a/source/games/exhumed/src/ramses.cpp +++ b/source/games/exhumed/src/ramses.cpp @@ -64,8 +64,6 @@ short nTalkTime = 0; void InitSpiritHead() { - char filename[20]; - nPixels = 0; nSpiritRepeatX = sprite[nSpiritSprite].xrepeat; @@ -137,26 +135,15 @@ void InitSpiritHead() fadecdaudio(); - int nTrack; - - if (currentLevel->levelNumber == 1) - { - nTrack = 3; - } - else - { - nTrack = 7; - } - + int nTrack = currentLevel->ex_ramses_cdtrack; playCDtrack(nTrack, false); StartSwirlies(); - sprintf(filename, "LEV%d.PUP", currentLevel->levelNumber); lNextStateChange = PlayClock; lHeadStartClock = PlayClock; - auto headfd = fileSystem.OpenFileReader(filename); + auto headfd = fileSystem.OpenFileReader(currentLevel->ex_ramses_pup); if (!headfd.isOpen()) { memset(cPupData, 0, sizeof(cPupData)); diff --git a/source/games/exhumed/src/save.cpp b/source/games/exhumed/src/save.cpp index 61976e148..de96ac106 100644 --- a/source/games/exhumed/src/save.cpp +++ b/source/games/exhumed/src/save.cpp @@ -22,7 +22,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "raze_music.h" #include "engine.h" #include "exhumed.h" -#include "mmulti.h" #include "savegamehelp.h" #include "sound.h" #include "mapinfo.h" @@ -117,7 +116,7 @@ void GameInterface::SerializeGameState(FSerializer& arc) parallaxtype = 2; g_visibility = 1024; - if (currentLevel->levelNumber > 15) + if (currentLevel->gameflags & LEVEL_EX_ALTSOUND) { nSwitchSound = 35; nStoneSound = 23; diff --git a/source/games/exhumed/src/sequence.cpp b/source/games/exhumed/src/sequence.cpp index 49e05e1c4..d6315508b 100644 --- a/source/games/exhumed/src/sequence.cpp +++ b/source/games/exhumed/src/sequence.cpp @@ -471,7 +471,7 @@ int seq_GetSeqPicnum(short nSeq, short edx, short ebx) int seq_PlotArrowSequence(short nSprite, short nSeq, int nVal) { - tspriteptr_t pTSprite = &tsprite[nSprite]; + tspriteptr_t pTSprite = &mytsprite[nSprite]; int nAngle = GetMyAngle(nCamerax - pTSprite->x, nCameray - pTSprite->y); int nSeqOffset = ((((pTSprite->ang + 512) - nAngle) + 128) & kAngleMask) >> 8; @@ -519,7 +519,7 @@ int seq_PlotArrowSequence(short nSprite, short nSeq, int nVal) int seq_PlotSequence(short nSprite, short edx, short nFrame, short ecx) { - tspriteptr_t pTSprite = &tsprite[nSprite]; + tspriteptr_t pTSprite = &mytsprite[nSprite]; int nAngle = GetMyAngle(nCamerax - pTSprite->x, nCameray - pTSprite->y); int val; @@ -572,7 +572,7 @@ int seq_PlotSequence(short nSprite, short edx, short nFrame, short ecx) break; } - tspriteptr_t tsp = &tsprite[spritesortcnt++]; + tspriteptr_t tsp = &mytsprite[(*myspritesortcnt)++]; tsp->x = pTSprite->x; tsp->y = pTSprite->y; tsp->z = pTSprite->z; diff --git a/source/games/exhumed/src/snake.cpp b/source/games/exhumed/src/snake.cpp index 13e154dde..72e1e20c9 100644 --- a/source/games/exhumed/src/snake.cpp +++ b/source/games/exhumed/src/snake.cpp @@ -417,7 +417,7 @@ SEARCH_ENEMY: seq_PlotSequence(nSprite, SeqOffsets[kSeqSnakBody], 0, 0); } - tsprite[nSprite].owner = -1; + mytsprite[nSprite].owner = -1; break; } diff --git a/source/games/exhumed/src/sound.cpp b/source/games/exhumed/src/sound.cpp index d7343d0e9..3504bc2f6 100644 --- a/source/games/exhumed/src/sound.cpp +++ b/source/games/exhumed/src/sound.cpp @@ -16,7 +16,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ //------------------------------------------------------------------------- #include "ns.h" -#include "mmulti.h" #include "compat.h" #include "build.h" #include "engine.h" @@ -724,7 +723,7 @@ void CheckAmbience(short nSector) void UpdateCreepySounds() { - if (currentLevel->levelNumber == 20 || nFreeze || !SoundEnabled()) + if ((currentLevel->gameflags & LEVEL_EX_COUNTDOWN) || nFreeze || !SoundEnabled()) return; spritetype* pSprite = &sprite[PlayerList[nLocalPlayer].nSprite]; nCreepyTimer--; @@ -800,4 +799,35 @@ void PlayGameOverSound(void) PlayLocalSound(StaticSound[kSoundJonLaugh2], 0, false, CHANF_UI); } +DEFINE_ACTION_FUNCTION(_Exhumed, PlayLocalSound) +{ + PARAM_PROLOGUE; + PARAM_INT(snd); + PARAM_INT(pitch); + PARAM_BOOL(unatt); + PARAM_INT(flags); + PlayLocalSound(StaticSound[snd], pitch, unatt, EChanFlags::FromInt(flags)); + return 0; +} + +DEFINE_ACTION_FUNCTION_NATIVE(_Exhumed, StopLocalSound, StopLocalSound) +{ + StopLocalSound(); + return 0; +} + +DEFINE_ACTION_FUNCTION(_Exhumed, LocalSoundPlaying) +{ + ACTION_RETURN_BOOL(soundEngine->IsSourcePlayingSomething(SOURCE_None, nullptr, CHAN_AUTO, -1)); +} + +DEFINE_ACTION_FUNCTION(_Exhumed, PlayCDTrack) +{ + PARAM_PROLOGUE; + PARAM_INT(track); + PARAM_BOOL(loop); + playCDtrack(track, loop); + return 0; +} + END_PS_NS diff --git a/source/games/exhumed/src/view.cpp b/source/games/exhumed/src/view.cpp index 485a783d8..a98975f63 100644 --- a/source/games/exhumed/src/view.cpp +++ b/source/games/exhumed/src/view.cpp @@ -30,8 +30,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "v_video.h" #include "interpolate.h" #include "v_draw.h" +#include "render.h" #include +EXTERN_CVAR(Bool, testnewrenderer) + BEGIN_PS_NS short bSubTitles = true; @@ -61,11 +64,18 @@ short enemy; short nEnemyPal = 0; +// We cannot drag these through the entire event system... :( +spritetype* mytsprite; +int* myspritesortcnt; + // NOTE - not to be confused with Ken's analyzesprites() -static void analyzesprites(double const smoothratio) +static void analyzesprites(spritetype* tsprite, int& spritesortcnt, int x, int y, int z, double const smoothratio) { tspritetype *pTSprite; + mytsprite = tsprite; + myspritesortcnt = &spritesortcnt; + for (int i = 0; i < spritesortcnt; i++) { pTSprite = &tsprite[i]; @@ -86,11 +96,6 @@ static void analyzesprites(double const smoothratio) besttarget = -1; - int x = pPlayerSprite->x; - int y = pPlayerSprite->y; - - int z = pPlayerSprite->z - (GetSpriteHeight(nPlayerSprite) / 2); - short nSector = pPlayerSprite->sectnum; int nAngle = (2048 - pPlayerSprite->ang) & kAngleMask; @@ -174,6 +179,10 @@ static void analyzesprites(double const smoothratio) besttarget = -1; } } + + mytsprite = nullptr; + myspritesortcnt = nullptr; + } void ResetView() @@ -184,11 +193,6 @@ void ResetView() #endif } -static inline int interpolate16(int a, int b, int smooth) -{ - return a + MulScale(b - a, smooth, 16); -} - static TextOverlay subtitleOverlay; void DrawView(double smoothRatio, bool sceneonly) @@ -197,9 +201,8 @@ void DrawView(double smoothRatio, bool sceneonly) int playerY; int playerZ; short nSector; - binangle nAngle; + binangle nAngle, rotscrnang; fixedhoriz pan; - lookangle rotscrnang; fixed_t dang = IntToFixed(1024); @@ -221,6 +224,7 @@ void DrawView(double smoothRatio, bool sceneonly) playerZ = sprite[nSprite].z; nSector = sprite[nSprite].sectnum; nAngle = buildang(sprite[nSprite].ang); + rotscrnang = buildang(0); SetGreenPal(); @@ -241,7 +245,7 @@ void DrawView(double smoothRatio, bool sceneonly) auto psp = &sprite[nPlayerSprite]; playerX = psp->interpolatedx(smoothRatio); playerY = psp->interpolatedy(smoothRatio); - playerZ = psp->interpolatedz(smoothRatio) + interpolate16(oeyelevel[nLocalPlayer], eyelevel[nLocalPlayer], smoothRatio); + playerZ = psp->interpolatedz(smoothRatio) + interpolatedvalue(oeyelevel[nLocalPlayer], eyelevel[nLocalPlayer], smoothRatio); nSector = nPlayerViewSect[nLocalPlayer]; updatesector(playerX, playerY, &nSector); @@ -269,9 +273,6 @@ void DrawView(double smoothRatio, bool sceneonly) sprite[nPlayerSprite].cstat |= CSTAT_SPRITE_TRANSLUCENT; sprite[nDoppleSprite[nLocalPlayer]].cstat |= CSTAT_SPRITE_INVISIBLE; } - - renderSetRollAngle(rotscrnang.asbuildf()); - pan = q16horiz(clamp(pan.asq16(), gi->playerHorizMin(), gi->playerHorizMax())); } @@ -357,9 +358,17 @@ void DrawView(double smoothRatio, bool sceneonly) } } - renderDrawRoomsQ16(nCamerax, nCameray, viewz, nCameraa.asq16(), nCamerapan.asq16(), nSector); - analyzesprites(smoothRatio); - renderDrawMasks(); + if (!testnewrenderer) + { + renderSetRollAngle(rotscrnang.asbuildf()); + renderDrawRoomsQ16(nCamerax, nCameray, viewz, nCameraa.asq16(), nCamerapan.asq16(), nSector); + analyzesprites(pm_tsprite, pm_spritesortcnt, nCamerax, nCameray, viewz, smoothRatio); + renderDrawMasks(); + } + else + { + render_drawrooms(nullptr, { nCamerax, nCameray, viewz }, nSector, nCameraa, nCamerapan, rotscrnang, smoothRatio); + } if (HavePLURemap()) { @@ -374,9 +383,6 @@ void DrawView(double smoothRatio, bool sceneonly) } } - - renderSetAspect(viewingRange, DivScale(ydim * 8, xdim * 5, 16)); - if (nFreeze) { nSnakeCam = -1; @@ -402,10 +408,7 @@ void DrawView(double smoothRatio, bool sceneonly) if (bSubTitles) { subtitleOverlay.Start(I_GetTimeNS() * (120. / 1'000'000'000)); - if (currentLevel->levelNumber == 1) - subtitleOverlay.ReadyCinemaText(1); - else - subtitleOverlay.ReadyCinemaText(5); + subtitleOverlay.ReadyCinemaText(currentLevel->ex_ramses_text); } inputState.ClearAllInput(); } @@ -461,6 +464,12 @@ bool GameInterface::GenerateSavePic() return true; } +void GameInterface::processSprites(spritetype* tsprite, int& spritesortcnt, int viewx, int viewy, int viewz, binangle viewang, double smoothRatio) +{ + analyzesprites(tsprite, spritesortcnt, viewx, viewy, viewz, smoothRatio); +} + + void NoClip() { } diff --git a/source/games/exhumed/src/view.h b/source/games/exhumed/src/view.h index 04f558c54..255169453 100644 --- a/source/games/exhumed/src/view.h +++ b/source/games/exhumed/src/view.h @@ -40,10 +40,8 @@ extern short bTouchFloor; extern short nChunkTotal; extern int gFov; -static inline int angle_interpolate16(int a, int b, int smooth) -{ - return a + MulScale(((b+1024-a)&2047)-1024, smooth, 16); -} +extern spritetype* mytsprite; +extern int* myspritesortcnt; END_PS_NS diff --git a/source/games/sw/all.cpp b/source/games/sw/all.cpp index 8087a4922..0e7dd2257 100644 --- a/source/games/sw/all.cpp +++ b/source/games/sw/all.cpp @@ -1,4 +1,3 @@ -#include "src/2d.cpp" #include "src/actor.cpp" #include "src/ai.cpp" #include "src/break.cpp" @@ -60,3 +59,5 @@ #include "src/zilla.cpp" #include "src/zombie.cpp" #include "src/d_menu.cpp" + +#include "src/_polymost.cpp" diff --git a/source/games/sw/src/2d.cpp b/source/games/sw/src/2d.cpp deleted file mode 100644 index 9c982f0c6..000000000 --- a/source/games/sw/src/2d.cpp +++ /dev/null @@ -1,675 +0,0 @@ -//------------------------------------------------------------------------- -/* -Copyright (C) 1997, 2005 - 3D Realms Entertainment - -This file is part of Shadow Warrior version 1.2 - -Shadow Warrior is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - -See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -Original Source: 1997 - Frank Maddin and Jim Norwood -Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms -*/ -//------------------------------------------------------------------------- - -#include "ns.h" -#include "screenjob.h" -#include "game.h" -#include "sounds.h" -#include "v_draw.h" -#include "menus.h" -#include "gamecontrol.h" -#include "mapinfo.h" -#include "misc.h" -#include "network.h" -#include "pal.h" - - -BEGIN_SW_NS - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -class DSWDRealmsScreen : public DSkippableScreenJob -{ -public: - DSWDRealmsScreen() : DSkippableScreenJob(fadein | fadeout) {} - - void OnTick() override - { - if (ticks > 5 * GameTicRate) state = finished; - } - - void Draw(double) override - { - const auto tex = tileGetTexture(THREED_REALMS_PIC, true); - const int translation = TRANSLATION(Translation_BasePalettes, DREALMSPAL); - - twod->ClearScreen(); - DrawTexture(twod, tex, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_TranslationIndex, translation, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE); - } -}; - - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -void Logo(const CompletionFunc& completion) -{ - StopSound(); - PlaySong(nullptr, ThemeSongs[0], ThemeTrack[0]); - - static const AnimSound logosound[] = - { - { 1, DIGI_NOMESSWITHWANG }, - { 5, DIGI_INTRO_SLASH }, - { 15, DIGI_INTRO_WHIRL }, - { -1, -1 } - }; - static const int logoframetimes[] = { 360, 8, 128 }; - - if (!userConfig.nologo) - { - JobDesc jobs[3]; - int job = 0; - jobs[job++] = { Create() }; - jobs[job++] = { PlayVideo("sw.anm", logosound, logoframetimes, true)}; - RunScreenJob(jobs, job, completion, true, true); - } - else completion(false); -} - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -DScreenJob* GetFinishAnim(int num) -{ - static const AnimSound serpsound[] = - { - { 1, DIGI_SERPTAUNTWANG }, - { 16, DIGI_SHAREND_TELEPORT }, - { 35, DIGI_WANGTAUNTSERP1 }, - { 51, DIGI_SHAREND_UGLY1 }, - { 64, DIGI_SHAREND_UGLY2 }, - { -1, -1 } - }; - static const int serpzillaframetimes[] = { 16, 16, 140 }; - - static const AnimSound sumosound[] = - { - { 2, DIGI_JG41012 }, - { 30, DIGI_HOTHEADSWITCH }, - { 42, DIGI_HOTHEADSWITCH }, - { 59, DIGI_JG41028 }, - { -1, -1 } - }; - static const int sumoframetimes[] = { 40, 10, 130 }; - - static const AnimSound zillasound[] = - { - { 1, DIGI_ZC1 }, - { 5, DIGI_JG94024 }, - { 14, DIGI_ZC2 }, - { 30, DIGI_ZC3 }, - { 32, DIGI_ZC4 }, - { 37, DIGI_ZC5 }, - { 63, DIGI_Z16043 }, - { 63, DIGI_ZC6 }, - { 63, DIGI_ZC7 }, - { 72, DIGI_ZC7 }, - { 73, DIGI_ZC4 }, - { 77, DIGI_ZC5 }, - { 87, DIGI_ZC8 }, - { 103, DIGI_ZC7 }, - { 108, DIGI_ZC9 }, - { 120, DIGI_JG94039 }, - { -1, -1 } - }; - - static const char* const ANIMname[] = - { - "swend.anm", - "sumocinm.anm", - "zfcin.anm", - }; - - switch (num) - { - case ANIM_SERP: return PlayVideo("swend.anm", serpsound, serpzillaframetimes); - case ANIM_SUMO: return PlayVideo("sumocinm.anm", sumosound, sumoframetimes); - case ANIM_ZILLA:return PlayVideo("zfcin.anm", zillasound, serpzillaframetimes); - default: return nullptr; - } -} - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -class DSWCreditsScreen : public DSkippableScreenJob -{ - enum - { - CREDITS1_PIC = 5111, - CREDITS2_PIC = 5118 - }; - int state = 0; - int starttime; - int curpic; - - void Skipped() override - { - StopSound(); - } - - void Start() override - { - // Lo Wang feel like singing! - PlaySound(DIGI_JG95012, v3df_none, CHAN_VOICE, CHANF_UI); - } - - void OnTick() override - { - if (state == 0) - { - if (!soundEngine->IsSourcePlayingSomething(SOURCE_None, nullptr, CHAN_VOICE)) - { - starttime = ticks; - state = 1; - StopSound(); - curpic = CREDITS1_PIC; - - // try 14 then 2 then quit - if (!PlaySong(nullptr, ThemeSongs[5], ThemeTrack[5], true)) - { - PlaySong(nullptr, nullptr, 2, true); - } - } - } - else - { - if (ticks >= starttime + 8 * GameTicRate) - { - curpic = CREDITS1_PIC + CREDITS2_PIC - curpic; - starttime = ticks; - } - } - } - - void Draw(double) override - { - twod->ClearScreen(); - if (state == 1) - DrawTexture(twod, tileGetTexture(curpic, true), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE); - } -}; - -//--------------------------------------------------------------------------- -// -// Summary screen -// -//--------------------------------------------------------------------------- - -static int BonusPunchSound(short) -{ - PlaySound(DIGI_PLAYERYELL3, v3df_none); - return 0; -} - -static int BonusKickSound(short) -{ - PlaySound(DIGI_PLAYERYELL2, v3df_none); - return 0; -} - -static int BonusGrabSound(short) -{ - PlaySound(DIGI_BONUS_GRAB, v3df_none); - return 0; -} - - -enum -{ - BONUS_SCREEN_PIC = 5120, - BONUS_ANIM = 5121, - BONUS_ANIM_FRAMES = (5159 - 5121), - - BREAK_LIGHT_RATE = 18, - - BONUS_PUNCH = 5121, - BONUS_KICK = 5136, - BONUS_GRAB = 5151, - BONUS_REST = 5121, - - BONUS_TICS = 8, - BONUS_GRAB_TICS = 20, - BONUS_REST_TICS = 50, -}; - -static STATE s_BonusPunch[] = -{ - {BONUS_PUNCH + 0, BONUS_TICS, NULL, &s_BonusPunch[1]}, - {BONUS_PUNCH + 1, BONUS_TICS, NULL, &s_BonusPunch[2]}, - {BONUS_PUNCH + 2, BONUS_TICS, NULL, &s_BonusPunch[3]}, - {BONUS_PUNCH + 2, 0 | SF_QUICK_CALL, BonusPunchSound, &s_BonusPunch[4]}, - {BONUS_PUNCH + 3, BONUS_TICS, NULL, &s_BonusPunch[5]}, - {BONUS_PUNCH + 4, BONUS_TICS, NULL, &s_BonusPunch[6]}, - {BONUS_PUNCH + 5, BONUS_TICS, NULL, &s_BonusPunch[7]}, - {BONUS_PUNCH + 6, BONUS_TICS, NULL, &s_BonusPunch[8]}, - {BONUS_PUNCH + 7, BONUS_TICS, NULL, &s_BonusPunch[9]}, - {BONUS_PUNCH + 8, BONUS_TICS, NULL, &s_BonusPunch[10]}, - {BONUS_PUNCH + 9, BONUS_TICS, NULL, &s_BonusPunch[11]}, - {BONUS_PUNCH + 10, BONUS_TICS, NULL, &s_BonusPunch[12]}, - {BONUS_PUNCH + 11, BONUS_TICS, NULL, &s_BonusPunch[13]}, - {BONUS_PUNCH + 12, BONUS_TICS, NULL, &s_BonusPunch[14]}, - {BONUS_PUNCH + 14, 90, NULL, &s_BonusPunch[15]}, - {BONUS_PUNCH + 14, BONUS_TICS, NULL, &s_BonusPunch[15]}, -}; - -static STATE s_BonusKick[] = -{ - {BONUS_KICK + 0, BONUS_TICS, NULL, &s_BonusKick[1]}, - {BONUS_KICK + 1, BONUS_TICS, NULL, &s_BonusKick[2]}, - {BONUS_KICK + 2, BONUS_TICS, NULL, &s_BonusKick[3]}, - {BONUS_KICK + 2, 0 | SF_QUICK_CALL, BonusKickSound, &s_BonusKick[4]}, - {BONUS_KICK + 3, BONUS_TICS, NULL, &s_BonusKick[5]}, - {BONUS_KICK + 4, BONUS_TICS, NULL, &s_BonusKick[6]}, - {BONUS_KICK + 5, BONUS_TICS, NULL, &s_BonusKick[7]}, - {BONUS_KICK + 6, BONUS_TICS, NULL, &s_BonusKick[8]}, - {BONUS_KICK + 7, BONUS_TICS, NULL, &s_BonusKick[9]}, - {BONUS_KICK + 8, BONUS_TICS, NULL, &s_BonusKick[10]}, - {BONUS_KICK + 9, BONUS_TICS, NULL, &s_BonusKick[11]}, - {BONUS_KICK + 10, BONUS_TICS, NULL, &s_BonusKick[12]}, - {BONUS_KICK + 11, BONUS_TICS, NULL, &s_BonusKick[13]}, - {BONUS_KICK + 12, BONUS_TICS, NULL, &s_BonusKick[14]}, - {BONUS_KICK + 14, 90, NULL, &s_BonusKick[15]}, - {BONUS_KICK + 14, BONUS_TICS, NULL, &s_BonusKick[15]}, -}; - -static STATE s_BonusGrab[] = -{ - {BONUS_GRAB + 0, BONUS_GRAB_TICS, NULL, &s_BonusGrab[1]}, - {BONUS_GRAB + 1, BONUS_GRAB_TICS, NULL, &s_BonusGrab[2]}, - {BONUS_GRAB + 2, BONUS_GRAB_TICS, NULL, &s_BonusGrab[3]}, - {BONUS_GRAB + 2, 0 | SF_QUICK_CALL, BonusGrabSound, &s_BonusGrab[4]}, - {BONUS_GRAB + 3, BONUS_GRAB_TICS, NULL, &s_BonusGrab[5]}, - {BONUS_GRAB + 4, BONUS_GRAB_TICS, NULL, &s_BonusGrab[6]}, - {BONUS_GRAB + 5, BONUS_GRAB_TICS, NULL, &s_BonusGrab[7]}, - {BONUS_GRAB + 6, BONUS_GRAB_TICS, NULL, &s_BonusGrab[8]}, - {BONUS_GRAB + 7, BONUS_GRAB_TICS, NULL, &s_BonusGrab[9]}, - {BONUS_GRAB + 8, BONUS_GRAB_TICS, NULL, &s_BonusGrab[10]}, - {BONUS_GRAB + 9, 90, NULL, &s_BonusGrab[11]}, - {BONUS_GRAB + 9, BONUS_GRAB_TICS, NULL, &s_BonusGrab[11]}, -}; - -static STATE s_BonusRest[] = -{ - {BONUS_REST + 0, BONUS_REST_TICS, NULL, &s_BonusRest[1]}, - {BONUS_REST + 1, BONUS_REST_TICS, NULL, &s_BonusRest[2]}, - {BONUS_REST + 2, BONUS_REST_TICS, NULL, &s_BonusRest[3]}, - {BONUS_REST + 1, BONUS_REST_TICS, NULL, &s_BonusRest[0]}, -}; - -static STATE * s_BonusAnim[] = -{ - s_BonusPunch, - s_BonusKick, - s_BonusGrab -}; - -class DSWLevelSummaryScreen : public DScreenJob -{ - int minutes, seconds, second_tics; - int nextclock = synctics; - STATE * State = s_BonusRest; - int Tics = 0; - -public: - DSWLevelSummaryScreen() - { - second_tics = (PlayClock / 120); - minutes = (second_tics / 60); - seconds = (second_tics % 60); - } -private: - static void gNextState(STATE** State) - { - // Transition to the next state - *State = (*State)->NextState; - - if (TEST((*State)->Tics, SF_QUICK_CALL)) - { - (*(*State)->Animator)(0); - *State = (*State)->NextState; - } - } - - - // Generic state control - static void gStateControl(STATE** State, int* tics) - { - *tics += synctics; - - // Skip states if too much time has passed - while (*tics >= (*State)->Tics) - { - // Set Tics - *tics -= (*State)->Tics; - gNextState(State); - } - - // Call the correct animator - if ((*State)->Animator) - (*(*State)->Animator)(0); - } - - bool OnEvent(event_t* ev) override - { - if (ev->type == EV_KeyDown && !specialKeyEvent(ev)) - { - if (State >= s_BonusRest && State < &s_BonusRest[countof(s_BonusRest)]) - { - State = s_BonusAnim[STD_RANDOM_RANGE(countof(s_BonusAnim))]; - Tics = 0; - nextclock = ticks; - } - } - return true; - } - - void Start() override - { - PlaySong(nullptr, ThemeSongs[1], ThemeTrack[1]); - } - - void OnTick() override - { - while (ticks > nextclock) - { - nextclock++; - gStateControl(&State, &Tics); - } - - if (State == State->NextState) - { - state = finished; - StopSound(); - } - } - - void Draw(double) override - { - twod->ClearScreen(); - DrawTexture(twod, tileGetTexture(BONUS_SCREEN_PIC, true), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE); - MNU_DrawString(160, 20, currentLevel->DisplayName(), 1, 19, 0); - MNU_DrawString(170, 30, GStrings("COMPLETED"), 1, 19, 0); - - DrawTexture(twod, tileGetTexture(State->Pic), 158, 86, DTA_FullscreenScale, FSMode_Fit320x200, - DTA_TopLeft, true, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE); - - auto BONUS_LINE = [](int i) { return (50 + ((i) * 20)); }; - - int line = 0; - FString ds; - ds.Format("%s %2d:%02d", GStrings("TXT_YOURTIME"), minutes, seconds); - MNU_DrawString(60, BONUS_LINE(line++), ds, 1, 16); - - if (currentLevel->designerTime > 0) - { - ds.Format("%s %d:%02d", GStrings("TXT_3DRTIME"), currentLevel->designerTime / 60, currentLevel->designerTime % 60); - MNU_DrawString(40, BONUS_LINE(line++), ds, 1, 16); - } - - if (currentLevel->parTime > 0) - { - ds.Format("%s %d:%02d", GStrings("TXT_PARTIME"), currentLevel->parTime / 60, currentLevel->parTime % 60); - MNU_DrawString(40, BONUS_LINE(line++), ds, 1, 16); - } - - // always read secrets and kills from the first player - ds.Format("%s: %d / %d", GStrings("TXT_SECRETS"), Player->SecretsFound, LevelSecrets); - MNU_DrawString(60, BONUS_LINE(line++), ds, 1, 16); - - ds.Format("%s: %d / %d", GStrings("KILLS"), Player->Kills, TotalKillable); - MNU_DrawString(60, BONUS_LINE(line), ds, 1, 16); - - MNU_DrawString(160, 185, GStrings("PRESSKEY"), 1, 19, 0); - } - -}; - -//--------------------------------------------------------------------------- -// -// Deathmatch summary screen -// -//--------------------------------------------------------------------------- - -static constexpr int SM_SIZ(int num) { return (num * 4); } - -enum -{ - STAT_SCREEN_PIC = 5114, - - STAT_START_X = 20, - STAT_START_Y = 85, - STAT_OFF_Y = 9, - STAT_HEADER_Y = 14, - STAT_TABLE_X = (STAT_START_X + SM_SIZ(15)), - STAT_TABLE_XOFF = SM_SIZ(6) -}; - - -class DSWMultiSummaryScreen : public DSkippableScreenJob -{ - short death_total[MAX_SW_PLAYERS_REG]{}; - short kills[MAX_SW_PLAYERS_REG]{}; - - void Skipped() override - { - StopSound(); - } - - void Draw(double) override - { - if (clock == 0) PlaySong(nullptr, ThemeSongs[1], ThemeTrack[1]); - - twod->ClearScreen(); - DrawTexture(twod, tileGetTexture(STAT_SCREEN_PIC, true), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE); - MNU_DrawString(160, 68, GStrings("MULTIPLAYER TOTALS"), 0, 0); - MNU_DrawString(160, 189, GStrings("PRESSKEY"), 0, 0, 0); - - int x = STAT_START_X; - int y = STAT_START_Y; - - // Hm.... how to translate this without messing up the formatting? - MNU_DrawSmallString(x, y, " NAME 1 2 3 4 5 6 7 8 KILLS", 0, 0); - int rows = OrigCommPlayers; - int cols = OrigCommPlayers; - - y += STAT_HEADER_Y; - - FString ds; - for (int i = 0; i < rows; i++) - { - x = STAT_START_X; - auto pp = Player + i; - - ds.Format("%d", i + 1); - MNU_DrawSmallString(x, y, ds, 0, 0); - - ds.Format(" %-13s", pp->PlayerName); - MNU_DrawSmallString(x, y, ds, 0, User[pp->PlayerSprite]->spal); - - x = STAT_TABLE_X; - for (int j = 0; j < cols; j++) - { - int pal = 0; - death_total[j] += pp->KilledPlayer[j]; - - if (i == j) - { - // don't add kill for self or team player - pal = PALETTE_PLAYER0 + 4; - kills[i] -= pp->KilledPlayer[j]; // subtract self kills - } - else if (gNet.TeamPlay) - { - if (User[pp->PlayerSprite]->spal == User[Player[j].PlayerSprite]->spal) - { - // don't add kill for self or team player - pal = PALETTE_PLAYER0 + 4; - kills[i] -= pp->KilledPlayer[j]; // subtract self kills - } - else - kills[i] += pp->KilledPlayer[j]; // kills added here - } - else - { - kills[i] += pp->KilledPlayer[j]; // kills added here - } - - ds.Format("%d", pp->KilledPlayer[j]); - MNU_DrawSmallString(x, y, ds, 0, pal); - x += STAT_TABLE_XOFF; - } - - y += STAT_OFF_Y; - } - - - // Deaths - - x = STAT_START_X; - y += STAT_OFF_Y; - - ds.Format(" %s", GStrings("DEATHS")); - MNU_DrawSmallString(x, y, ds, 0, 0); - x = STAT_TABLE_X; - - for (int j = 0; j < cols; j++) - { - ds.Format("%d", death_total[j]); - MNU_DrawSmallString(x, y, ds, 0, 0); - x += STAT_TABLE_XOFF; - } - - x = STAT_START_X; - y += STAT_OFF_Y; - - // Kills - x = STAT_TABLE_X + SM_SIZ(50); - y = STAT_START_Y + STAT_HEADER_Y; - - for (int i = 0; i < rows; i++) - { - auto pp = Player + i; - - ds.Format("%d", kills[i]); //pp->Kills); - MNU_DrawSmallString(x, y, ds, 0, 0); - - y += STAT_OFF_Y; - } - } -}; - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -void StatScreen(int FinishAnim, CompletionFunc completion) -{ - JobDesc jobs[5]; - int job = 0; - - if (FinishAnim) - { - StopSound(); - jobs[job++] = { GetFinishAnim(FinishAnim), []() { soundEngine->StopAllChannels(); } }; - jobs[job++] = { Create() }; - if (FinishAnim == ANIM_ZILLA) - jobs[job++] = { Create() }; - } - else if (gNet.MultiGameType != MULTI_GAME_COMMBAT) - { - jobs[job++] = { Create() }; - } - else - { - jobs[job++] = { Create() }; - } - RunScreenJob(jobs, job, completion, true); -} - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -void SybexScreen(CompletionFunc completion) -{ - if (!SW_SHAREWARE || CommEnabled) completion(false); - else - { - JobDesc job = { Create(tileGetTexture(5261), DScreenJob::fadein | DScreenJob::fadeout, 0x7fffffff) }; - RunScreenJob(&job, 1, completion, true, true); - } -} - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -class DSWLoadScreen : public DScreenJob -{ - MapRecord* rec; - -public: - DSWLoadScreen(MapRecord* maprec) : DScreenJob(0), rec(maprec) {} - - void Draw(double) override - { - const int TITLE_PIC = 2324; - twod->ClearScreen(); - DrawTexture(twod, tileGetTexture(TITLE_PIC), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE); - - MNU_DrawString(160, 170, /*DemoMode ? GStrings("TXT_LBDEMO") :*/ GStrings("TXT_ENTERING"), 1, 16, 0); - MNU_DrawString(160, 180, rec->DisplayName(), 1, 16, 0); - } -}; - -void loadscreen(MapRecord* rec, CompletionFunc func) -{ - JobDesc job = { Create(rec) }; - RunScreenJob(&job, 1, func); -} - -END_SW_NS diff --git a/source/games/sw/src/_polymost.cpp b/source/games/sw/src/_polymost.cpp new file mode 100644 index 000000000..903fb96f2 --- /dev/null +++ b/source/games/sw/src/_polymost.cpp @@ -0,0 +1,348 @@ +BEGIN_SW_NS + +bool FindCeilingView(short match, int32_t* x, int32_t* y, int32_t z, int16_t* sectnum); +bool FindFloorView(short match, int32_t* x, int32_t* y, int32_t z, int16_t* sectnum); + + +short +ViewSectorInScene(short cursectnum, short level) +{ + int i; + SPRITEp sp; + short match; + + StatIterator it(STAT_FAF); + while ((i = it.NextIndex()) >= 0) + { + sp = &sprite[i]; + + if (sp->hitag == level) + { + if (cursectnum == sp->sectnum) + { + // ignore case if sprite is pointing up + if (sp->ang == 1536) + continue; + + // only gets to here is sprite is pointing down + + // found a potential match + match = sp->lotag; + + if (!PicInView(FAF_MIRROR_PIC, true)) + return -1; + + return match; + } + } + } + + return -1; +} + + + +void +DrawOverlapRoom(int tx, int ty, int tz, fixed_t tq16ang, fixed_t tq16horiz, short tsectnum) +{ + short i; + short match; + + save.zcount = 0; + + match = ViewSectorInScene(tsectnum, VIEW_LEVEL1); + if (match != -1) + { + FindCeilingView(match, &tx, &ty, tz, &tsectnum); + + if (tsectnum < 0) + return; + + renderDrawRoomsQ16(tx, ty, tz, tq16ang, tq16horiz, tsectnum); + + // reset Z's + for (i = 0; i < save.zcount; i++) + { + sector[save.sectnum[i]].floorz = save.zval[i]; + sector[save.sectnum[i]].floorpicnum = save.pic[i]; + sector[save.sectnum[i]].floorheinum = save.slope[i]; + } + + analyzesprites(pm_tsprite, pm_spritesortcnt, tx, ty, tz, false); + post_analyzesprites(pm_tsprite, pm_spritesortcnt); + renderDrawMasks(); + + } + else + { + match = ViewSectorInScene(tsectnum, VIEW_LEVEL2); + if (match != -1) + { + FindFloorView(match, &tx, &ty, tz, &tsectnum); + + if (tsectnum < 0) + return; + + renderDrawRoomsQ16(tx, ty, tz, tq16ang, tq16horiz, tsectnum); + + // reset Z's + for (i = 0; i < save.zcount; i++) + { + sector[save.sectnum[i]].ceilingz = save.zval[i]; + sector[save.sectnum[i]].ceilingpicnum = save.pic[i]; + sector[save.sectnum[i]].ceilingheinum = save.slope[i]; + } + + analyzesprites(pm_tsprite, pm_spritesortcnt, tx, ty, tz, false); + post_analyzesprites(pm_tsprite, pm_spritesortcnt); + renderDrawMasks(); + + } + } +} + +void FAF_DrawRooms(int x, int y, int z, fixed_t q16ang, fixed_t q16horiz, short sectnum) +{ + int i; + StatIterator it(STAT_CEILING_FLOOR_PIC_OVERRIDE); + while ((i = it.NextIndex()) >= 0) + { + if (SPRITE_TAG3(i) == 0) + { + // back up ceilingpicnum and ceilingstat + SPRITE_TAG5(i) = sector[sprite[i].sectnum].ceilingpicnum; + sector[sprite[i].sectnum].ceilingpicnum = SPRITE_TAG2(i); + SPRITE_TAG4(i) = sector[sprite[i].sectnum].ceilingstat; + //SET(sector[sprite[i].sectnum].ceilingstat, ((int)SPRITE_TAG7(i))<<7); + SET(sector[sprite[i].sectnum].ceilingstat, SPRITE_TAG6(i)); + RESET(sector[sprite[i].sectnum].ceilingstat, CEILING_STAT_PLAX); + } + else if (SPRITE_TAG3(i) == 1) + { + SPRITE_TAG5(i) = sector[sprite[i].sectnum].floorpicnum; + sector[sprite[i].sectnum].floorpicnum = SPRITE_TAG2(i); + SPRITE_TAG4(i) = sector[sprite[i].sectnum].floorstat; + //SET(sector[sprite[i].sectnum].floorstat, ((int)SPRITE_TAG7(i))<<7); + SET(sector[sprite[i].sectnum].floorstat, SPRITE_TAG6(i)); + RESET(sector[sprite[i].sectnum].floorstat, FLOOR_STAT_PLAX); + } + } + + renderDrawRoomsQ16(x,y,z,q16ang,q16horiz,sectnum); + + it.Reset(STAT_CEILING_FLOOR_PIC_OVERRIDE); + while ((i = it.NextIndex()) >= 0) + { + // manually set gotpic + if (gotsector[sprite[i].sectnum]) + { + SET_GOTPIC(FAF_MIRROR_PIC); + } + + if (SPRITE_TAG3(i) == 0) + { + // restore ceilingpicnum and ceilingstat + sector[sprite[i].sectnum].ceilingpicnum = SPRITE_TAG5(i); + sector[sprite[i].sectnum].ceilingstat = SPRITE_TAG4(i); + //RESET(sector[sprite[i].sectnum].ceilingstat, CEILING_STAT_TYPE_MASK); + RESET(sector[sprite[i].sectnum].ceilingstat, CEILING_STAT_PLAX); + } + else if (SPRITE_TAG3(i) == 1) + { + sector[sprite[i].sectnum].floorpicnum = SPRITE_TAG5(i); + sector[sprite[i].sectnum].floorstat = SPRITE_TAG4(i); + //RESET(sector[sprite[i].sectnum].floorstat, FLOOR_STAT_TYPE_MASK); + RESET(sector[sprite[i].sectnum].floorstat, FLOOR_STAT_PLAX); + } + } +} + +void polymost_drawscreen(PLAYERp pp, int tx, int ty, int tz, binangle tang, fixedhoriz thoriz, int tsectnum) +{ + videoSetCorrectedAspect(); + renderSetAspect(xs_CRoundToInt(double(viewingrange) * tan(r_fov * (pi::pi() / 360.))), yxaspect); + OverlapDraw = true; + DrawOverlapRoom(tx, ty, tz, tang.asq16(), thoriz.asq16(), tsectnum); + OverlapDraw = false; + + if (automapMode != am_full)// && !ScreenSavePic) + { + // TEST this! Changed to camerapp + //JS_DrawMirrors(camerapp, tx, ty, tz, tang.asq16(), thoriz.asq16()); + JS_DrawMirrors(pp, tx, ty, tz, tang.asq16(), thoriz.asq16()); + } + + // TODO: This call is redundant if the tiled overhead map is shown, but the + // HUD elements should be properly outputted with hardware rendering first. + if (!FAF_DebugView) + FAF_DrawRooms(tx, ty, tz, tang.asq16(), thoriz.asq16(), tsectnum); + + analyzesprites(pm_tsprite, pm_spritesortcnt, tx, ty, tz, tang.asbuild()); + post_analyzesprites(pm_tsprite, pm_spritesortcnt); + renderDrawMasks(); + +} + +void JS_DrawMirrors(PLAYERp pp, int tx, int ty, int tz, fixed_t tpq16ang, fixed_t tpq16horiz) +{ + int j, cnt; + int dist; + int tposx, tposy; // Camera + int *longptr; + fixed_t tang; + +// int tx, ty, tz, tpang; // Interpolate so mirror doesn't + // drift! + bool bIsWallMirror = false; + + // WARNING! Assuming (MIRRORLABEL&31) = 0 and MAXMIRRORS = 64 <-- JBF: wrong + longptr = (int *)&gotpic[MIRRORLABEL >> 3]; + if (longptr && (longptr[0] || longptr[1])) + { + for (cnt = MAXMIRRORS - 1; cnt >= 0; cnt--) + //if (TEST_GOTPIC(cnt + MIRRORLABEL) || TEST_GOTPIC(cnt + CAMSPRITE)) + if (TEST_GOTPIC(cnt + MIRRORLABEL) || ((unsigned)mirror[cnt].campic < MAXTILES && TEST_GOTPIC(mirror[cnt].campic))) + { + bIsWallMirror = false; + if (TEST_GOTPIC(cnt + MIRRORLABEL)) + { + bIsWallMirror = true; + RESET_GOTPIC(cnt + MIRRORLABEL); + } + //else if (TEST_GOTPIC(cnt + CAMSPRITE)) + else if ((unsigned)mirror[cnt].campic < MAXTILES && TEST_GOTPIC(mirror[cnt].campic)) + { + //RESET_GOTPIC(cnt + CAMSPRITE); + RESET_GOTPIC(mirror[cnt].campic); + } + + mirrorinview = true; + +// tx = interpolatedvalue(pp->oposx, pp->posx, smoothratio); +// ty = interpolatedvalue(pp->oposy, pp->posy, smoothratio); +// tz = interpolatedvalue(pp->oposz, pp->posz, smoothratio); +// tpq16ang = pp->angle.ang.asq16(); + + + dist = 0x7fffffff; + + if (bIsWallMirror) + { + j = abs(wall[mirror[cnt].mirrorwall].x - tx); + j += abs(wall[mirror[cnt].mirrorwall].y - ty); + if (j < dist) + dist = j; + } + else + { + SPRITEp tp; + + tp = &sprite[mirror[cnt].camsprite]; + + j = abs(tp->x - tx); + j += abs(tp->y - ty); + if (j < dist) + dist = j; + } + + if (mirror[cnt].ismagic) + { + SPRITEp sp=NULL; + int camhoriz; + short w; + int dx, dy, dz, tdx, tdy, tdz, midx, midy; + + + ASSERT(mirror[cnt].camera != -1); + + sp = &sprite[mirror[cnt].camera]; + + ASSERT(sp); + + // Calculate the angle of the mirror wall + w = mirror[cnt].mirrorwall; + + // Get wall midpoint for offset in mirror view + midx = (wall[w].x + wall[wall[w].point2].x) / 2; + midy = (wall[w].y + wall[wall[w].point2].y) / 2; + + // Finish finding offsets + tdx = abs(midx - tx); + tdy = abs(midy - ty); + + if (midx >= tx) + dx = sp->x - tdx; + else + dx = sp->x + tdx; + + if (midy >= ty) + dy = sp->y - tdy; + else + dy = sp->y + tdy; + + tdz = abs(tz - sp->z); + if (tz >= sp->z) + dz = sp->z + tdz; + else + dz = sp->z - tdz; + + + // Is it a TV cam or a teleporter that shows destination? + // true = It's a TV cam + mirror[cnt].mstate = m_normal; + if (TEST_BOOL1(sp)) + mirror[cnt].mstate = m_viewon; + + // Show teleport destination + // NOTE: Adding MAXSECTORS lets you draw a room, even if + // you are outside of it! + if (mirror[cnt].mstate != m_viewon) + { + tileDelete(MIRROR); + // Set TV camera sprite size to 0 to show mirror + // behind in this case! + + if (mirror[cnt].campic != -1) + tileDelete(mirror[cnt].campic); + renderDrawRoomsQ16(dx, dy, dz, tpq16ang, tpq16horiz, sp->sectnum + MAXSECTORS); + analyzesprites(pm_tsprite, pm_spritesortcnt, dx, dy, dz, false); + renderDrawMasks(); + } + } + else + { + // It's just a mirror + // Prepare drawrooms for drawing mirror and calculate + // reflected + // position into tposx, tposy, and tang (tposz == cposz) + // Must call preparemirror before drawrooms and + // completemirror after drawrooms + display_mirror = true; + renderPrepareMirror(tx, ty, tz, tpq16ang, tpq16horiz, + mirror[cnt].mirrorwall, /*mirror[cnt].mirrorsector,*/ &tposx, &tposy, &tang); + + renderDrawRoomsQ16(tposx, tposy, tz, (tang), tpq16horiz, mirror[cnt].mirrorsector + MAXSECTORS); + + analyzesprites(pm_tsprite, pm_spritesortcnt, tposx, tposy, tz, tang >> 16); + renderDrawMasks(); + + renderCompleteMirror(); // Reverse screen x-wise in this + display_mirror = false; + } + + + // g_visibility = tvisibility; + // g_visibility = NormalVisibility; + + // renderDrawRoomsQ16(tx, ty, tz, tpq16ang, tpq16horiz, pp->cursectnum); + // Clean up anything that the camera view might have done + tileDelete(MIRROR); + wall[mirror[cnt].mirrorwall].overpicnum = MIRRORLABEL + cnt; + } + else + mirrorinview = false; + } +} + + +END_SW_NS diff --git a/source/games/sw/src/cheats.cpp b/source/games/sw/src/cheats.cpp index 6897cdd6a..dc33c96e4 100644 --- a/source/games/sw/src/cheats.cpp +++ b/source/games/sw/src/cheats.cpp @@ -116,7 +116,7 @@ bool NextCheat(cheatseq_t* c) { if (!checkCheat(c)) return false; if (!currentLevel) return true; - auto map = FindMapByLevelNum(currentLevel->levelNumber + 1); + auto map = FindNextMap(currentLevel); if (map) DeferedStartGame(map, -1); return true; } diff --git a/source/games/sw/src/d_menu.cpp b/source/games/sw/src/d_menu.cpp index eff540c1c..f9cb0b8e5 100644 --- a/source/games/sw/src/d_menu.cpp +++ b/source/games/sw/src/d_menu.cpp @@ -61,13 +61,6 @@ BEGIN_SW_NS // //---------------------------------------------------------------------------- -void GameInterface::QuitToTitle() -{ - Mus_Stop(); - gameaction = ga_mainmenu; -} - - void GameInterface::MenuSound(EMenuSounds snd) { switch (snd) @@ -102,22 +95,8 @@ bool GameInterface::StartGame(FNewGameStartup& gs) int handle = 0; int zero = 0; - MapRecord* map; - if (gs.Episode >= 1) - { - if (g_gameType & GAMEFLAG_SHAREWARE) - { - M_StartMessage(GStrings("BUYSW"), 1, NAME_None); - return false; - } - map = FindMapByLevelNum(5); - } - else - map = FindMapByLevelNum(1); - - if (!map) return false; CameraTestMode = false; - StopFX(); + StopAmbientSound(); //InitNewGame(); @@ -140,7 +119,6 @@ bool GameInterface::StartGame(FNewGameStartup& gs) } Net_ClearFifo(); } - DeferedStartGame(map, gs.Skill); return true; } diff --git a/source/games/sw/src/draw.cpp b/source/games/sw/src/draw.cpp index c384de81a..524e2ef0b 100644 --- a/source/games/sw/src/draw.cpp +++ b/source/games/sw/src/draw.cpp @@ -55,9 +55,12 @@ Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms #include "v_2ddrawer.h" #include "v_video.h" #include "v_draw.h" +#include "render.h" +EXTERN_CVAR(Bool, testnewrenderer) BEGIN_SW_NS +int display_mirror; static int OverlapDraw = false; extern bool QuitFlag, SpriteInfo; extern bool Voxel; @@ -67,7 +70,7 @@ extern short f_c; extern ParentalStruct aVoxelArray[MAXTILES]; -int ConnectCopySprite(uspritetype const * tsp); +int ConnectCopySprite(spritetype const * tsp); void PreDrawStackedWater(void); void SW_InitMultiPsky(void) @@ -97,7 +100,7 @@ ShadeSprite(tspriteptr_t tsp) short -GetRotation(short tSpriteNum, int viewx, int viewy) +GetRotation(spritetype* tsprite, int& spritesortcnt, short tSpriteNum, int viewx, int viewy) { static short RotTable8[] = {0, 7, 6, 5, 4, 3, 2, 1}; static short RotTable5[] = {0, 1, 2, 3, 4, 3, 2, 1}; @@ -168,7 +171,7 @@ directions was not standardized. */ int -SetActorRotation(short tSpriteNum, int viewx, int viewy) +SetActorRotation(spritetype* tsprite, int& spritesortcnt, short tSpriteNum, int viewx, int viewy) { tspriteptr_t tsp = &tsprite[tSpriteNum]; USERp tu = User[tsp->owner].Data(); @@ -185,7 +188,7 @@ SetActorRotation(short tSpriteNum, int viewx, int viewy) StateOffset = State - StateStart; // Get the rotation angle - Rotation = GetRotation(tSpriteNum, viewx, viewy); + Rotation = GetRotation(tsprite, spritesortcnt, tSpriteNum, viewx, viewy); ASSERT(Rotation < 5); @@ -261,7 +264,7 @@ DoShadowFindGroundPoint(tspriteptr_t sp) } void -DoShadows(tspriteptr_t tsp, int viewz, bool mirror) +DoShadows(spritetype* tsprite, int& spritesortcnt, tspriteptr_t tsp, int viewz, int camang) { tspriteptr_t New = &tsprite[spritesortcnt]; USERp tu = User[tsp->owner].Data(); @@ -346,14 +349,14 @@ DoShadows(tspriteptr_t tsp, int viewz, bool mirror) New->clipdist |= TSPR_FLAGS_MDHACK; New->cstat |= 512; } - else + else if (!testnewrenderer) { int const camang = mirror ? NORM_ANGLE(2048 - Player[screenpeek].siang) : Player[screenpeek].siang; - vec2_t const ofs = { bcos(camang, -11), bsin(camang, -11) }; - - New->x += ofs.x; - New->y += ofs.y; + New->x += bcos(camang, -11); + New->y += bsin(camang, -11); + } + else New->time = 1; // Check for voxel items and use a round generic pic if so //DoVoxelShadow(New); @@ -362,7 +365,7 @@ DoShadows(tspriteptr_t tsp, int viewz, bool mirror) } void -DoMotionBlur(tspritetype const * const tsp) +DoMotionBlur(spritetype* tsprite, int& spritesortcnt, tspritetype const * const tsp) { USERp tu = User[tsp->owner].Data(); int nx,ny,nz = 0,dx,dy,dz; @@ -449,7 +452,7 @@ void SetVoxelSprite(SPRITEp sp, short pic) sp->picnum = pic; } -void WarpCopySprite(void) +void WarpCopySprite(spritetype* tsprite, int& spritesortcnt) { SPRITEp sp1, sp2, sp; int sn, sn2; @@ -486,7 +489,7 @@ void WarpCopySprite(void) if (sprite[spnum].picnum == ST1) continue; - tspriteptr_t New = renderAddTSpriteFromSprite(spnum); + tspriteptr_t New = renderAddTSpriteFromSprite(tsprite, spritesortcnt, spnum); New->statnum = 0; xoff = sp1->x - New->x; @@ -508,7 +511,7 @@ void WarpCopySprite(void) if (sprite[spnum].picnum == ST1) continue; - tspriteptr_t New = renderAddTSpriteFromSprite(spnum); + tspriteptr_t New = renderAddTSpriteFromSprite(tsprite, spritesortcnt, spnum); New->statnum = 0; xoff = sp2->x - New->x; @@ -548,8 +551,7 @@ void DoStarView(tspriteptr_t tsp, USERp tu, int viewz) } } -void -analyzesprites(int viewx, int viewy, int viewz, bool mirror) +void analyzesprites(spritetype* tsprite, int& spritesortcnt, int viewx, int viewy, int viewz, int camang) { int tSpriteNum; short SpriteNum; @@ -626,7 +628,7 @@ analyzesprites(int viewx, int viewy, int viewz, bool mirror) if (r_shadows && TEST(tu->Flags, SPR_SHADOW)) { - DoShadows(tsp, viewz, mirror); + DoShadows(tsprite, spritesortcnt, tsp, viewz, camang); } //#define UK_VERSION 1 @@ -657,11 +659,11 @@ analyzesprites(int viewx, int viewy, int viewz, bool mirror) // rotation if (tu->RotNum > 0) - SetActorRotation(tSpriteNum, viewx, viewy); + SetActorRotation(tsprite, spritesortcnt, tSpriteNum, viewx, viewy); if (tu->motion_blur_num) { - DoMotionBlur(tsp); + DoMotionBlur(tsprite, spritesortcnt, tsp); } // set palette lookup correctly @@ -734,7 +736,7 @@ analyzesprites(int viewx, int viewy, int viewz, bool mirror) if ((Player + screenpeek)->PlayerSprite == tu->SpriteNum) { PLAYERp pp = Player + screenpeek; - if (mirror || TEST(pp->Flags, PF_VIEW_FROM_OUTSIDE|PF_VIEW_FROM_CAMERA)) + if (display_mirror || TEST(pp->Flags, PF_VIEW_FROM_OUTSIDE|PF_VIEW_FROM_CAMERA)) { if (TEST(pp->Flags, PF_VIEW_FROM_OUTSIDE)) SET(tsp->cstat, CSTAT_SPRITE_TRANSLUCENT); @@ -775,8 +777,7 @@ analyzesprites(int viewx, int viewy, int viewz, bool mirror) if (OverlapDraw && FAF_ConnectArea(tsp->sectnum) && tsp->owner >= 0) { - static_assert(sizeof(uspritetype) == sizeof(tspritetype)); // see TSPRITE_SIZE - ConnectCopySprite((uspriteptr_t)tsp); + ConnectCopySprite(tsp); } // @@ -847,12 +848,13 @@ analyzesprites(int viewx, int viewy, int viewz, bool mirror) } } - WarpCopySprite(); + WarpCopySprite(tsprite, spritesortcnt); } + #if 1 -tspriteptr_t get_tsprite(short SpriteNum) +tspriteptr_t get_tsprite(spritetype* tsprite, int& spritesortcnt, int SpriteNum) { int tSpriteNum; @@ -866,7 +868,7 @@ tspriteptr_t get_tsprite(short SpriteNum) } void -post_analyzesprites(void) +post_analyzesprites(spritetype* tsprite, int& spritesortcnt) { int tSpriteNum; short SpriteNum; @@ -883,8 +885,7 @@ post_analyzesprites(void) { if (tu->ID == FIREBALL_FLAMES && tu->Attach >= 0) { - //uspritetype * const atsp = &sprite[tu->Attach]; - tspriteptr_t const atsp = get_tsprite(tu->Attach); + tspriteptr_t const atsp = get_tsprite(tsprite, spritesortcnt, tu->Attach); if (!atsp) { @@ -1077,34 +1078,6 @@ void PrintSpriteInfo(PLAYERp pp) } -void SpriteSortList2D(int tx, int ty) -{ - SPRITEp sp; - int i; - int dist,a,b,c; - - spritesortcnt = 0; - for (i = 0; i < MAXSPRITES; i++) - { - if (sprite[i].statnum < MAXSTATUS) - { - sp = &sprite[i]; - - if (!TEST(sp->cstat, CSTAT_SPRITE_INVISIBLE) && - (sp->xrepeat > 0) && (sp->yrepeat > 0) && - (spritesortcnt < MAXSPRITESONSCREEN)) - { - DISTANCE(tx,ty,sp->x,sp->y,dist,a,b,c); - - if (dist < 22000) - { - renderAddTSpriteFromSprite(i); - } - } - } - } -} - void DrawCrosshair(PLAYERp pp) { extern bool CameraTestMode; @@ -1133,7 +1106,7 @@ void CameraView(PLAYERp pp, int *tx, int *ty, int *tz, short *tsectnum, binangle { sp = &sprite[i]; - ang = q16ang(gethiq16angle(*tx - sp->x, *ty - sp->y)); + ang = bvectangbam(*tx - sp->x, *ty - sp->y); ang_test = getincangle(ang.asbuild(), sp->ang) < sp->lotag; FAFcansee_test = @@ -1262,7 +1235,7 @@ PostDraw(void) } } -int CopySprite(uspritetype const * tsp, short newsector) +int CopySprite(spritetype const * tsp, short newsector) { short New; SPRITEp sp; @@ -1291,7 +1264,7 @@ int CopySprite(uspritetype const * tsp, short newsector) return New; } -int ConnectCopySprite(uspritetype const * tsp) +int ConnectCopySprite(spritetype const * tsp) { short newsector; int testz; @@ -1356,7 +1329,7 @@ void PreDrawStackedWater(void) sp = &sprite[i]; u = User[i].Data(); - New = ConnectCopySprite((uspritetype const *)sp); + New = ConnectCopySprite((spritetype const *)sp); if (New >= 0) { // spawn a user @@ -1390,8 +1363,39 @@ void PreDrawStackedWater(void) } -void FAF_DrawRooms(int x, int y, int z, fixed_t q16ang, fixed_t q16horiz, short sectnum) +short ScreenSavePic = false; + +bool PicInView(short, bool); +void DoPlayerDiveMeter(PLAYERp pp); + +void polymost_drawscreen(PLAYERp pp, int tx, int ty, int tz, binangle tang, fixedhoriz thoriz, int tsectnum); + + +void UpdateWallPortalState() { + // This is too obtuse to be maintained statically, but with 8 mirrors at most easy to be kept up to date. + for (int i = 0; i < MAXMIRRORS; i++) + { + auto wal = &wall[mirror[i].mirrorwall]; + wal->portalflags = 0; + wal->portalnum = 0; + + if (!mirror[i].ismagic) + { + // a simple mirror + wal->portalflags = PORTAL_WALL_MIRROR; + } + else + { + auto sp = &sprite[mirror[i].camera]; + if (!TEST_BOOL1(sp)) + { + wal->portalflags = PORTAL_WALL_TO_SPRITE; + wal->portalnum = mirror[i].camera; + } + } + } + int i; StatIterator it(STAT_CEILING_FLOOR_PIC_OVERRIDE); while ((i = it.NextIndex()) >= 0) @@ -1417,17 +1421,14 @@ void FAF_DrawRooms(int x, int y, int z, fixed_t q16ang, fixed_t q16horiz, short } } - renderDrawRoomsQ16(x,y,z,q16ang,q16horiz,sectnum); +} - it.Reset(STAT_CEILING_FLOOR_PIC_OVERRIDE); +void RestorePortalState() +{ + int i; + StatIterator it(STAT_CEILING_FLOOR_PIC_OVERRIDE); while ((i = it.NextIndex()) >= 0) { - // manually set gotpic - if (TEST_GOTSECTOR(sprite[i].sectnum)) - { - SET_GOTPIC(FAF_MIRROR_PIC); - } - if (SPRITE_TAG3(i) == 0) { // restore ceilingpicnum and ceilingstat @@ -1446,18 +1447,12 @@ void FAF_DrawRooms(int x, int y, int z, fixed_t q16ang, fixed_t q16horiz, short } } -short ScreenSavePic = false; - -bool PicInView(short, bool); -void DoPlayerDiveMeter(PLAYERp pp); - void drawscreen(PLAYERp pp, double smoothratio) { extern bool CameraTestMode; int tx, ty, tz; - lookangle trotscrnang; - binangle tang; + binangle tang, trotscrnang; fixedhoriz thoriz; short tsectnum; short i,j; @@ -1493,9 +1488,9 @@ drawscreen(PLAYERp pp, double smoothratio) else camerapp = pp; - tx = camerapp->oposx + xs_CRoundToInt(MulScaleF(camerapp->posx - camerapp->oposx, smoothratio, 16)); - ty = camerapp->oposy + xs_CRoundToInt(MulScaleF(camerapp->posy - camerapp->oposy, smoothratio, 16)); - tz = camerapp->oposz + xs_CRoundToInt(MulScaleF(camerapp->posz - camerapp->oposz, smoothratio, 16)); + tx = interpolatedvalue(camerapp->oposx, camerapp->posx, smoothratio); + ty = interpolatedvalue(camerapp->oposy, camerapp->posy, smoothratio); + tz = interpolatedvalue(camerapp->oposz, camerapp->posz, smoothratio); // Interpolate the player's angle while on a sector object, just like VoidSW. // This isn't needed for the turret as it was fixable, but moving sector objects are problematic. @@ -1513,8 +1508,6 @@ drawscreen(PLAYERp pp, double smoothratio) } tsectnum = camerapp->cursectnum; - renderSetRollAngle(trotscrnang.asbuildf()); - COVERupdatesector(tx, ty, &tsectnum); if (tsectnum >= 0) @@ -1558,7 +1551,7 @@ drawscreen(PLAYERp pp, double smoothratio) if (TEST_BOOL1(pp->remote_sprite)) tang = buildang(pp->remote_sprite->ang); else - tang = q16ang(gethiq16angle(pp->sop_remote->xmid - tx, pp->sop_remote->ymid - ty)); + tang = bvectangbam(pp->sop_remote->xmid - tx, pp->sop_remote->ymid - ty); } if (TEST(pp->Flags, PF_VIEW_FROM_OUTSIDE)) @@ -1586,7 +1579,7 @@ drawscreen(PLAYERp pp, double smoothratio) if (cl_viewbob) { tz += bob_amt; - tz += pp->obob_z + xs_CRoundToInt(MulScaleF(pp->bob_z - pp->obob_z, smoothratio, 16)); + tz += interpolatedvalue(pp->obob_z, pp->bob_z, smoothratio); } // recoil only when not in camera @@ -1596,34 +1589,23 @@ drawscreen(PLAYERp pp, double smoothratio) if (automapMode != am_full)// && !ScreenSavePic) { // Cameras must be done before the main loop. - JS_DrawCameras(pp, tx, ty, tz); + if (!testnewrenderer) JS_DrawCameras(pp, tx, ty, tz, smoothratio); + else JS_CameraParms(pp, tx, ty, tz); } - - videoSetCorrectedAspect(); - renderSetAspect(xs_CRoundToInt(double(viewingrange)* tan(r_fov * (pi::pi() / 360.))), yxaspect); - OverlapDraw = true; - DrawOverlapRoom(tx, ty, tz, tang.asq16(), thoriz.asq16(), tsectnum); - OverlapDraw = false; - - if (automapMode != am_full)// && !ScreenSavePic) + if (!testnewrenderer) { - // TEST this! Changed to camerapp - //JS_DrawMirrors(camerapp, tx, ty, tz, tang.asq16(), thoriz.asq16()); - JS_DrawMirrors(pp, tx, ty, tz, tang.asq16(), thoriz.asq16()); + renderSetRollAngle(trotscrnang.asbuildf()); + polymost_drawscreen(pp, tx, ty, tz, tang, thoriz, tsectnum); + } + else + { + UpdateWallPortalState(); + render_drawrooms(pp->SpriteP, { tx, ty, tz }, tsectnum, tang, thoriz, trotscrnang, smoothratio); + RestorePortalState(); } - // TODO: This call is redundant if the tiled overhead map is shown, but the - // HUD elements should be properly outputted with hardware rendering first. - if (!FAF_DebugView) - FAF_DrawRooms(tx, ty, tz, tang.asq16(), thoriz.asq16(), tsectnum); - analyzesprites(tx, ty, tz, false); - post_analyzesprites(); - renderDrawMasks(); - - - renderSetAspect(viewingRange, DivScale(ydim * 8, xdim * 5, 16)); if (!ScreenSavePic) UpdatePanel(smoothratio); #define SLIME 2305 @@ -1699,7 +1681,9 @@ drawscreen(PLAYERp pp, double smoothratio) if (paused && !M_Active()) { - MNU_DrawString(160, 100, "Game Paused", 0, 0, 0); + auto str = GStrings("Game Paused"); + int w = SmallFont->StringWidth(str); + DrawText(twod, SmallFont, CR_UNDEFINED, 160-w, 100, str, DTA_FullscreenScale, FSMode_Fit320x200, TAG_DONE); } if (!CommEnabled && TEST(pp->Flags, PF_DEAD)) @@ -1730,7 +1714,7 @@ bool GameInterface::DrawAutomapPlayer(int cposx, int cposy, int czoom, int cang, int i, j, k, l, x1, y1, x2, y2, x3, y3, x4, y4, ox, oy, xoff, yoff; int dax, day, cosang, sinang, xspan, yspan, sprx, spry; int xrepeat, yrepeat, z1, z2, startwall, endwall, tilenum, daang; - int xvect, yvect, xvect2, yvect2; + int xvect, yvect; walltype* wal, * wal2; spritetype* spr; short p; @@ -1740,8 +1724,6 @@ bool GameInterface::DrawAutomapPlayer(int cposx, int cposy, int czoom, int cang, xvect = -bsin(cang) * czoom; yvect = -bcos(cang) * czoom; - xvect2 = MulScale(xvect, yxaspect, 16); - yvect2 = MulScale(yvect, yxaspect, 16); // Draw sprites @@ -1791,7 +1773,7 @@ bool GameInterface::DrawAutomapPlayer(int cposx, int cposy, int czoom, int cang, x1 = sprx - cposx; y1 = spry - cposy; - if (((gotsector[i >> 3] & (1 << (i & 7))) > 0) && (czoom > 192)) + if (czoom > 192) { daang = ((!SyncInput() ? spr->ang : spr->interpolatedang(smoothratio)) - cang) & 2047; @@ -1808,7 +1790,7 @@ bool GameInterface::DrawAutomapPlayer(int cposx, int cposy, int czoom, int cang, double xd = ((x1 << 4) + (xdim << 15)) / 65536.; double yd = ((y1 << 4) + (ydim << 15)) / 65536.; - double sc = MulScale(czoom * (spr->yrepeat), yxaspect, 16) / 32768.; + double sc = czoom * (spr->yrepeat) / 32768.; if (spnum >= 0) { DrawTexture(twod, tileGetTexture(1196 + pspr_ndx[myconnectindex], true), xd, yd, DTA_ScaleX, sc, DTA_ScaleY, sc, DTA_Rotate, daang * -BAngToDegree, @@ -1839,12 +1821,12 @@ bool GameInterface::DrawAutomapPlayer(int cposx, int cposy, int czoom, int cang, ox = x1 - cposx; oy = y1 - cposy; x1 = MulScale(ox, xvect, 16) - MulScale(oy, yvect, 16); - y1 = MulScale(oy, xvect2, 16) + MulScale(ox, yvect2, 16); + y1 = MulScale(oy, xvect, 16) + MulScale(ox, yvect, 16); ox = x2 - cposx; oy = y2 - cposy; x2 = MulScale(ox, xvect, 16) - MulScale(oy, yvect, 16); - y2 = MulScale(oy, xvect2, 16) + MulScale(ox, yvect2, 16); + y2 = MulScale(oy, xvect, 16) + MulScale(ox, yvect, 16); drawlinergb(x1 + (xdim << 11), y1 + (ydim << 11), x2 + (xdim << 11), y2 + (ydim << 11), col); @@ -1887,22 +1869,22 @@ bool GameInterface::DrawAutomapPlayer(int cposx, int cposy, int czoom, int cang, ox = x1 - cposx; oy = y1 - cposy; x1 = MulScale(ox, xvect, 16) - MulScale(oy, yvect, 16); - y1 = MulScale(oy, xvect2, 16) + MulScale(ox, yvect2, 16); + y1 = MulScale(oy, xvect, 16) + MulScale(ox, yvect, 16); ox = x2 - cposx; oy = y2 - cposy; x2 = MulScale(ox, xvect, 16) - MulScale(oy, yvect, 16); - y2 = MulScale(oy, xvect2, 16) + MulScale(ox, yvect2, 16); + y2 = MulScale(oy, xvect, 16) + MulScale(ox, yvect, 16); ox = x3 - cposx; oy = y3 - cposy; x3 = MulScale(ox, xvect, 16) - MulScale(oy, yvect, 16); - y3 = MulScale(oy, xvect2, 16) + MulScale(ox, yvect2, 16); + y3 = MulScale(oy, xvect, 16) + MulScale(ox, yvect, 16); ox = x4 - cposx; oy = y4 - cposy; x4 = MulScale(ox, xvect, 16) - MulScale(oy, yvect, 16); - y4 = MulScale(oy, xvect2, 16) + MulScale(ox, yvect2, 16); + y4 = MulScale(oy, xvect, 16) + MulScale(ox, yvect, 16); drawlinergb(x1 + (xdim << 11), y1 + (ydim << 11), x2 + (xdim << 11), y2 + (ydim << 11), col); @@ -1925,6 +1907,12 @@ bool GameInterface::DrawAutomapPlayer(int cposx, int cposy, int czoom, int cang, return true; } +void GameInterface::processSprites(spritetype* tsprite, int& spritesortcnt, int viewx, int viewy, int viewz, binangle viewang, double smoothRatio) +{ + analyzesprites(tsprite, spritesortcnt, viewx, viewy, viewz, viewang.asbuild()); + post_analyzesprites(tsprite, spritesortcnt); +} + END_SW_NS diff --git a/source/games/sw/src/game.cpp b/source/games/sw/src/game.cpp index 0c3af8e89..c49766fc9 100644 --- a/source/games/sw/src/game.cpp +++ b/source/games/sw/src/game.cpp @@ -88,10 +88,6 @@ CVAR(Bool, sw_bunnyrockets, false, CVAR_SERVERINFO | CVAR_CHEAT); // This is a BEGIN_SW_NS -void Logo(const CompletionFunc& completion); -void StatScreen(int FinishAnim, CompletionFunc completion); - - void pClearSpriteList(PLAYERp pp); extern int sw_snd_scratch; @@ -154,7 +150,6 @@ FString ThemeSongs[6]; int ThemeTrack[6]; /// L O C A L P R O T O T Y P E S ///////////////////////////////////////////////////////// -void SybexScreen(void); ///////////////////////////////////////////////////////////////////////////////////////////// #define x(a, b) registerName(#a, b); @@ -198,7 +193,6 @@ void GameInterface::app_init() registerosdcommands(); - engineInit(); auto pal = fileSystem.LoadFile("3drealms.pal", 0); if (pal.Size() >= 768) { @@ -219,8 +213,6 @@ void GameInterface::app_init() "the full version. Read the Ordering Info screens for details."); } - TileFiles.LoadArtSet("tiles%03d.art"); - //Connect(); SortBreakInfo(); parallaxtype = 1; @@ -256,12 +248,6 @@ void GameInterface::DrawBackground(void) twod->ClearScreen(); DrawTexture(twod, tileGetTexture(TITLE_PIC), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, DTA_Color, shadeToLight(20), TAG_DONE); - - if (CommEnabled) - { - MNU_DrawString(160, 170, "Lo Wang is waiting for other players...", 1, 16, 0); - MNU_DrawString(160, 180, "They are afraid!", 1, 16, 0); - } } //--------------------------------------------------------------------------- @@ -285,9 +271,8 @@ void InitLevelGlobals(void) gNet.TimeLimitClock = gNet.TimeLimit; - serpwasseen = false; - sumowasseen = false; - zillawasseen = false; + + for (auto& b : bosswasseen) b = false; memset(BossSpriteNum,-1,sizeof(BossSpriteNum)); } @@ -347,16 +332,6 @@ void InitLevel(MapRecord *maprec) engineLoadBoard(maprec->fileName, SW_SHAREWARE ? 1 : 0, &Player[0].pos, &ang, &Player[0].cursectnum); currentLevel = maprec; - if (!maprec->labelName.CompareNoCase("$hidtemp") && !maprec->name.CompareNoCase("$TXTS_T_MAP10")) - { - // flip the inverted card reader in TD's level 10. - if (sprite[179].picnum == 1852 && sprite[179].cstat == 92) sprite[179].cstat &= ~12; - } - if (!maprec->labelName.CompareNoCase("$outpost") && !maprec->name.CompareNoCase("$TXTS_MAP09")) - { - // silence a misplaced and *very* annoying ambient sound. - if (sprite[442].picnum == ST1 && sprite[442].hitag == 1002 && sprite[442].lotag == 31) sprite[442].lotag = -1; - } SECRET_SetMapName(currentLevel->DisplayName(), currentLevel->name); STAT_NewLevel(currentLevel->fileName); Player[0].angle.ang = buildang(ang); @@ -402,6 +377,7 @@ void InitLevel(MapRecord *maprec) PostSetupSectorObject(); SetupMirrorTiles(); initlava(); + CollectPortals(); // reset NewGame NewGame = false; @@ -545,30 +521,37 @@ static void PlayOrderSound() -void GameInterface::LevelCompleted(MapRecord *map, int skill) +void GameInterface::LevelCompleted(MapRecord* map, int skill) { - //ResetPalette(mpp); - COVER_SetReverb(0); // Reset reverb - Player[myconnectindex].Reverb = 0; - StopSound(); + //ResetPalette(mpp); + COVER_SetReverb(0); // Reset reverb + Player[myconnectindex].Reverb = 0; + StopSound(); STAT_Update(map == nullptr); - StatScreen(FinishAnim, [=](bool) - { + SummaryInfo info{}; + + info.kills = Player->Kills; + info.maxkills = TotalKillable; + info.secrets = Player->SecretsFound; + info.maxsecrets = LevelSecrets; + info.time = PlayClock / 120; + + ShowIntermission(currentLevel, map, &info, [=](bool) + { if (map == nullptr) - { - FinishAnim = false; - PlaySong(nullptr, ThemeSongs[0], ThemeTrack[0]); - if (SW_SHAREWARE) + { + FinishAnim = false; + PlaySong(nullptr, ThemeSongs[0], ThemeTrack[0]); + if (isShareware()) { PlayOrderSound(); gameaction = ga_creditsmenu; } - else gameaction = ga_mainmenu; - } - else gameaction = ga_nextlevel; - }); - + else gameaction = ga_mainmenu; + } + else gameaction = ga_nextlevel; + }); } //--------------------------------------------------------------------------- // @@ -596,6 +579,7 @@ void GameInterface::NewGame(MapRecord *map, int skill, bool) ShadowWarrior::NewGame = true; InitLevel(map); InitRunLevel(); + gameaction = ga_level; } //--------------------------------------------------------------------------- @@ -645,17 +629,7 @@ void GameInterface::Render() void GameInterface::Startup() { - if (userConfig.CommandMap.IsNotEmpty()) - { - } - else - { - if (!userConfig.nologo) Logo([](bool) - { - gameaction = ga_mainmenunostopsound; - }); - else gameaction = ga_mainmenu; - } + PlayLogos(ga_mainmenunostopsound, ga_mainmenu, false); } diff --git a/source/games/sw/src/game.h b/source/games/sw/src/game.h index 804525de2..da6f93897 100644 --- a/source/games/sw/src/game.h +++ b/source/games/sw/src/game.h @@ -38,7 +38,7 @@ Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms #include "build.h" #include "compat.h" -#include "mmulti.h" +#include "d_net.h" #include "mytypes.h" #include "sounds.h" @@ -77,7 +77,6 @@ extern GAME_SET gs; enum { DREALMSPAL = 1, - THREED_REALMS_PIC = 2325, MAXMIRRORS = 8, // This is just some, high, blank tile number not used @@ -220,11 +219,11 @@ inline int32_t FIXED(int32_t msw, int32_t lsw) #define SP_TAG10(sp) (LSB_VAR((sp)->owner)) #define SP_TAG11(sp) ((sp)->shade) #define SP_TAG12(sp) ((sp)->pal) -#define SP_TAG13(sp) B_LITTLE16(*((short*)&(sp)->xoffset)) -#define SP_TAG14(sp) B_LITTLE16(*((short*)&(sp)->xrepeat)) +#define SP_TAG13(sp) LittleShort(*((short*)&(sp)->xoffset)) +#define SP_TAG14(sp) LittleShort(*((short*)&(sp)->xrepeat)) #define SP_TAG15(sp) ((sp)->z) -#define SET_SP_TAG13(sp,val) (*((short*)&(sp)->xoffset)) = B_LITTLE16((short)val) -#define SET_SP_TAG14(sp,val) (*((short*)&(sp)->xrepeat)) = B_LITTLE16((short)val) +#define SET_SP_TAG13(sp,val) (*((short*)&(sp)->xoffset)) = LittleShort((short)val) +#define SET_SP_TAG14(sp,val) (*((short*)&(sp)->xrepeat)) = LittleShort((short)val) #define SPRITE_TAG1(sp) (sprite[sp].hitag) #define SPRITE_TAG2(sp) (sprite[sp].lotag) @@ -238,11 +237,11 @@ inline int32_t FIXED(int32_t msw, int32_t lsw) #define SPRITE_TAG10(sp) (LSB_VAR(sprite[sp].owner)) #define SPRITE_TAG11(sp) (sprite[sp].shade) #define SPRITE_TAG12(sp) (sprite[sp].pal) -#define SPRITE_TAG13(sp) B_LITTLE16(*((short*)&sprite[sp].xoffset)) -#define SPRITE_TAG14(sp) B_LITTLE16(*((short*)&sprite[sp].xrepeat)) +#define SPRITE_TAG13(sp) LittleShort(*((short*)&sprite[sp].xoffset)) +#define SPRITE_TAG14(sp) LittleShort(*((short*)&sprite[sp].xrepeat)) #define SPRITE_TAG15(sp) (sprite[sp].z) -#define SET_SPRITE_TAG13(sp,val) (*((short*)&sprite[sp].xoffset)) = B_LITTLE16((short)val) -#define SET_SPRITE_TAG14(sp,val) (*((short*)&sprite[sp].xrepeat)) = B_LITTLE16((short)val) +#define SET_SPRITE_TAG13(sp,val) (*((short*)&sprite[sp].xoffset)) = LittleShort((short)val) +#define SET_SPRITE_TAG14(sp,val) (*((short*)&sprite[sp].xrepeat)) = LittleShort((short)val) // OVER and UNDER water macros #define SpriteInDiveArea(sp) (TEST(sector[(sp)->sectnum].extra, SECTFX_DIVE_AREA) ? true : false) @@ -355,10 +354,6 @@ inline int SPRITEp_SIZE_BOS(const spritetype* sp) // just determine if the player is moving #define PLAYER_MOVING(pp) ((pp)->xvect|(pp)->yvect) -#define TEST_GOTSECTOR(sect_num) (TEST(gotsector[(sect_num) >> 3], 1 << ((sect_num) & 7))) -#define RESET_GOTSECTOR(sect_num) (RESET(gotsector[(sect_num) >> 3], 1 << ((sect_num) & 7))) -#define SET_GOTSECTOR(sect_num) (SET(gotsector[(sect_num) >> 3], 1 << ((sect_num) & 7))) - #define TEST_GOTPIC(tile_num) (TEST(gotpic[(tile_num) >> 3], 1 << ((tile_num) & 7))) #define RESET_GOTPIC(tile_num) (RESET(gotpic[(tile_num) >> 3], 1 << ((tile_num) & 7))) #define SET_GOTPIC(tile_num) (SET(gotpic[(tile_num) >> 3], 1 << ((tile_num) & 7))) @@ -1920,7 +1915,6 @@ ANIMATOR NullAnimator; int Distance(int x1, int y1, int x2, int y2); -int SetActorRotation(short SpriteNum,int,int); int NewStateGroup(short SpriteNum, STATEp SpriteGroup[]); void SectorMidPoint(short sectnum, int *xmid, int *ymid, int *zmid); USERp SpawnUser(short SpriteNum, short id, STATEp state); @@ -2001,23 +1995,16 @@ int DoPickTarget(SPRITEp sp, uint32_t max_delta_ang, int skip_targets); void change_sprite_stat(short, short); void SetOwner(short, short); void SetAttach(short, short); -void analyzesprites(int,int,int,bool); +void analyzesprites(spritetype* tsprite, int& spritesortcnt, int viewx, int viewy, int viewz, int camang); void ChangeState(short SpriteNum, STATEp statep); +void CollectPortals(); -void UpdateSectorFAF_Connect(short SpriteNum, int newz); -#if 0 -bool FAF_ConnectCeiling(short sectnum); -bool FAF_ConnectFloor(short sectnum); -#else #define FAF_PLACE_MIRROR_PIC 341 #define FAF_MIRROR_PIC 2356 #define FAF_ConnectCeiling(sectnum) (sector[(sectnum)].ceilingpicnum == FAF_MIRROR_PIC) #define FAF_ConnectFloor(sectnum) (sector[(sectnum)].floorpicnum == FAF_MIRROR_PIC) #define FAF_ConnectArea(sectnum) (FAF_ConnectCeiling(sectnum) || FAF_ConnectFloor(sectnum)) -#endif -//void updatesectorz(int, int, int, short *); -void FAF_ConnectPlayerCeiling(PLAYERp pp); -void FAF_ConnectPlayerFloor(PLAYERp pp); + bool PlayerCeilingHit(PLAYERp pp, int zlimit); bool PlayerFloorHit(PLAYERp pp, int zlimit); @@ -2159,7 +2146,6 @@ void getsyncstat(void); // sync.c void SyncStatMessage(void); // sync.c void drawscreen(PLAYERp pp, double smoothratio); // draw.c -void post_analyzesprites(void); // draw.c int COVERsetgamemode(int mode, int xdim, int ydim, int bpp); // draw.c void ScreenCaptureKeys(void); // draw.c @@ -2238,9 +2224,7 @@ extern short wait_active_check_offset; //extern short Zombies; extern int PlaxCeilGlobZadjust, PlaxFloorGlobZadjust; extern bool left_foot; -extern bool serpwasseen; -extern bool sumowasseen; -extern bool zillawasseen; +extern bool bosswasseen[3]; extern short BossSpriteNum[3]; extern int ChopTics; extern short Bunny_Count; @@ -2266,8 +2250,7 @@ struct GameInterface : ::GameInterface void SetAmbience(bool on) override { if (on) StartAmbientSound(); else StopAmbientSound(); } FString GetCoordString() override; ReservedSpace GetReservedScreenSpace(int viewsize) override; - void QuitToTitle() override; - void UpdateSounds() override; + void UpdateSounds() override; void ErrorCleanup() override; void GetInput(InputPacket* input, ControlInfo* const hidInput) override; void DrawBackground(void) override; @@ -2287,7 +2270,11 @@ struct GameInterface : ::GameInterface int chaseCamX(binangle ang) override { return -ang.bcos(-3); } int chaseCamY(binangle ang) override { return -ang.bsin(-3); } int chaseCamZ(fixedhoriz horiz) override { return horiz.asq16() >> 8; } - int Voxelize(int sprnum) override; + void processSprites(spritetype* tsprite, int& spritesortcnt, int viewx, int viewy, int viewz, binangle viewang, double smoothRatio) override; + void UpdateCameras(double smoothratio) override; + void EnterPortal(spritetype* viewer, int type) override; + void LeavePortal(spritetype* viewer, int type) override; + int Voxelize(int sprnum); GameStats getStats() override; diff --git a/source/games/sw/src/interpso.cpp b/source/games/sw/src/interpso.cpp index 197e5c68b..d7b757458 100644 --- a/source/games/sw/src/interpso.cpp +++ b/source/games/sw/src/interpso.cpp @@ -70,7 +70,7 @@ static struct so_interp bool hasvator; } so_interpdata[MAX_SECTOR_OBJECTS]; -static int &getvalue(int element) +static int &getvalue(int element, bool write) { static int scratch; int index = element & soi_base; @@ -78,8 +78,10 @@ static int &getvalue(int element) switch (type) { case soi_wallx: + if (write) sector[wall[index].sector].dirty = 255; return wall[index].x; case soi_wally: + if (write) sector[wall[index].sector].dirty = 255; return wall[index].y; case soi_ceil: return sector[index].ceilingz; @@ -119,7 +121,7 @@ static void so_setpointinterpolation(so_interp *interp, int element) data->curelement = element; data->oldipos = data->lastipos = - data->lastoldipos = getvalue(element); + data->lastoldipos = getvalue(element, false); data->spriteofang = -1; } @@ -278,7 +280,7 @@ void so_updateinterpolations(void) // Stick at beginning of domovethings data->oldipos = sprite[data->spriteofang].ang; } else - data->oldipos = getvalue(data->curelement); + data->oldipos = getvalue(data->curelement, false); if (!interpolating) data->lastipos = data->lastoldipos = data->oldipos; @@ -307,7 +309,7 @@ void so_dointerpolations(int32_t smoothratio) // Stick at b for (i = 0; i < interp->numinterpolations; i++) interp->data[i].bakipos = (interp->data[i].spriteofang >= 0) ? sprite[interp->data[i].spriteofang].ang : - getvalue(interp->data[i].curelement); + getvalue(interp->data[i].curelement, false); if (interp->tic == 0) // Only if the SO has just moved { @@ -371,7 +373,7 @@ void so_dointerpolations(int32_t smoothratio) // Stick at b else { delta = data->lastipos - data->lastoldipos; - getvalue(data->curelement) = data->lastoldipos + MulScale(delta, ratio, 16); + getvalue(data->curelement, true) = data->lastoldipos + MulScale(delta, ratio, 16); } } } @@ -395,7 +397,7 @@ void so_restoreinterpolations(void) // Stick at end of drawscree if (data->spriteofang >= 0) sprite[data->spriteofang].ang = data->bakipos; else - getvalue(data->curelement) = data->bakipos; + getvalue(data->curelement, true) = data->bakipos; } } diff --git a/source/games/sw/src/jsector.cpp b/source/games/sw/src/jsector.cpp index 05c6fc3d3..aefe4c442 100644 --- a/source/games/sw/src/jsector.cpp +++ b/source/games/sw/src/jsector.cpp @@ -44,9 +44,13 @@ Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms #include "pal.h" #include "parent.h" #include "v_video.h" +#include "render.h" + +EXTERN_CVAR(Bool, testnewrenderer) BEGIN_SW_NS + // V A R I A B L E D E C L A R A T I O N S ////////////////////////////////////////////////////// MIRRORTYPE mirror[MAXMIRRORS]; @@ -349,7 +353,7 @@ void JS_InitMirrors(void) if (sp->hitag == MIRROR_CAM && sp->lotag == wall[i].hitag) { mirror[mirrorcnt].camera = ii; - // Set up camera varialbes + // Set up camera variables SP_TAG5(sp) = sp->ang; // Set current angle to // sprite angle Found_Cam = true; @@ -365,7 +369,7 @@ void JS_InitMirrors(void) if (sp->hitag == MIRROR_CAM && sp->lotag == wall[i].hitag) { mirror[mirrorcnt].camera = ii; - // Set up camera varialbes + // Set up camera variables SP_TAG5(sp) = sp->ang; // Set current angle to // sprite angle Found_Cam = true; @@ -459,7 +463,7 @@ void JS_InitMirrors(void) // Draw a 3d screen to a specific tile ///////////////////////////////////////////////////// void drawroomstotile(int daposx, int daposy, int daposz, - fixed_t daq16ang, fixed_t daq16horiz, short dacursectnum, short tilenume) + binangle ang, fixedhoriz horiz, short dacursectnum, short tilenume, double smoothratio) { TileFiles.MakeCanvas(tilenume, tileWidth(tilenume), tileHeight(tilenume)); @@ -468,9 +472,16 @@ void drawroomstotile(int daposx, int daposy, int daposz, screen->RenderTextureView(canvas, [=](IntRect& rect) { - renderDrawRoomsQ16(daposx, daposy, daposz, daq16ang, daq16horiz, dacursectnum); - analyzesprites(daposx, daposy, daposz, false); - renderDrawMasks(); + if (!testnewrenderer) + { + renderDrawRoomsQ16(daposx, daposy, daposz, ang.asq16(), horiz.asq16(), dacursectnum); + analyzesprites(pm_tsprite, pm_spritesortcnt, daposx, daposy, daposz, ang.asbuild()); + renderDrawMasks(); + } + else + { + render_camtex(nullptr, { daposx, daposy, daposz }, dacursectnum, ang, horiz, buildang(0), tileGetTexture(tilenume), rect, smoothratio); + } }); renderRestoreTarget(); @@ -524,10 +535,11 @@ int lastcamclock; // views short camplayerview = 1; // Don't show yourself! + // Hack job alert! // Mirrors and cameras are maintained in the same data structure, but for hardware rendering they cannot be interleaved. // So this function replicates JS_DrawMirrors to only process the camera textures but not change any global state. -void JS_DrawCameras(PLAYERp pp, int tx, int ty, int tz) +void JS_DrawCameras(PLAYERp pp, int tx, int ty, int tz, double smoothratio) { int j, cnt; int dist; @@ -708,11 +720,11 @@ void JS_DrawCameras(PLAYERp pp, int tx, int ty, int tz) if (TEST_BOOL11(sp) && numplayers > 1) { - drawroomstotile(cp->posx, cp->posy, cp->posz, cp->angle.ang.asq16(), cp->horizon.horiz.asq16(), cp->cursectnum, mirror[cnt].campic); + drawroomstotile(cp->posx, cp->posy, cp->posz, cp->angle.ang, cp->horizon.horiz, cp->cursectnum, mirror[cnt].campic, smoothratio); } else { - drawroomstotile(sp->x, sp->y, sp->z, IntToFixed(SP_TAG5(sp)), IntToFixed(camhoriz), sp->sectnum, mirror[cnt].campic); + drawroomstotile(sp->x, sp->y, sp->z, buildang(SP_TAG5(sp)), buildhoriz(camhoriz), sp->sectnum, mirror[cnt].campic, smoothratio); } } } @@ -722,167 +734,36 @@ void JS_DrawCameras(PLAYERp pp, int tx, int ty, int tz) } } -void JS_DrawMirrors(PLAYERp pp, int tx, int ty, int tz, fixed_t tpq16ang, fixed_t tpq16horiz) +// Workaround until the camera code can be refactored to process all camera textures that were visible last frame. +// Need to stash the parameters for later use. This is only used to find the nearest camera. +static PLAYERp cam_pp; +static int cam_tx, cam_ty, cam_tz; +static int oldstat; + +void JS_CameraParms(PLAYERp pp, int tx, int ty, int tz) { - int j, cnt; - int dist; - int tposx, tposy; // Camera - int *longptr; - fixed_t tang; - -// int tx, ty, tz, tpang; // Interpolate so mirror doesn't - // drift! - bool bIsWallMirror = false; - - // WARNING! Assuming (MIRRORLABEL&31) = 0 and MAXMIRRORS = 64 <-- JBF: wrong - longptr = (int *)&gotpic[MIRRORLABEL >> 3]; - if (longptr && (longptr[0] || longptr[1])) - { - for (cnt = MAXMIRRORS - 1; cnt >= 0; cnt--) - //if (TEST_GOTPIC(cnt + MIRRORLABEL) || TEST_GOTPIC(cnt + CAMSPRITE)) - if (TEST_GOTPIC(cnt + MIRRORLABEL) || ((unsigned)mirror[cnt].campic < MAXTILES && TEST_GOTPIC(mirror[cnt].campic))) - { - bIsWallMirror = false; - if (TEST_GOTPIC(cnt + MIRRORLABEL)) - { - bIsWallMirror = true; - RESET_GOTPIC(cnt + MIRRORLABEL); - } - //else if (TEST_GOTPIC(cnt + CAMSPRITE)) - else if ((unsigned)mirror[cnt].campic < MAXTILES && TEST_GOTPIC(mirror[cnt].campic)) - { - //RESET_GOTPIC(cnt + CAMSPRITE); - RESET_GOTPIC(mirror[cnt].campic); - } - - mirrorinview = true; - -// tx = pp->oposx + MulScale(pp->posx - pp->oposx, smoothratio, 16); -// ty = pp->oposy + MulScale(pp->posy - pp->oposy, smoothratio, 16); -// tz = pp->oposz + MulScale(pp->posz - pp->oposz, smoothratio, 16); -// tpq16ang = pp->angle.ang.asq16(); - - - dist = 0x7fffffff; - - if (bIsWallMirror) - { - j = abs(wall[mirror[cnt].mirrorwall].x - tx); - j += abs(wall[mirror[cnt].mirrorwall].y - ty); - if (j < dist) - dist = j; - } - else - { - SPRITEp tp; - - tp = &sprite[mirror[cnt].camsprite]; - - j = abs(tp->x - tx); - j += abs(tp->y - ty); - if (j < dist) - dist = j; - } - - if (mirror[cnt].ismagic) - { - SPRITEp sp=NULL; - int camhoriz; - short w; - int dx, dy, dz, tdx, tdy, tdz, midx, midy; - - - ASSERT(mirror[cnt].camera != -1); - - sp = &sprite[mirror[cnt].camera]; - - ASSERT(sp); - - // Calculate the angle of the mirror wall - w = mirror[cnt].mirrorwall; - - // Get wall midpoint for offset in mirror view - midx = (wall[w].x + wall[wall[w].point2].x) / 2; - midy = (wall[w].y + wall[wall[w].point2].y) / 2; - - // Finish finding offsets - tdx = abs(midx - tx); - tdy = abs(midy - ty); - - if (midx >= tx) - dx = sp->x - tdx; - else - dx = sp->x + tdx; - - if (midy >= ty) - dy = sp->y - tdy; - else - dy = sp->y + tdy; - - tdz = abs(tz - sp->z); - if (tz >= sp->z) - dz = sp->z + tdz; - else - dz = sp->z - tdz; - - - // Is it a TV cam or a teleporter that shows destination? - // true = It's a TV cam - mirror[cnt].mstate = m_normal; - if (TEST_BOOL1(sp)) - mirror[cnt].mstate = m_viewon; - - // Show teleport destination - // NOTE: Adding MAXSECTORS lets you draw a room, even if - // you are outside of it! - if (mirror[cnt].mstate != m_viewon) - { - tileDelete(MIRROR); - // Set TV camera sprite size to 0 to show mirror - // behind in this case! - - if (mirror[cnt].campic != -1) - tileDelete(mirror[cnt].campic); - renderDrawRoomsQ16(dx, dy, dz, tpq16ang, tpq16horiz, sp->sectnum + MAXSECTORS); - analyzesprites(dx, dy, dz, false); - renderDrawMasks(); - } - } - else - { - // It's just a mirror - // Prepare drawrooms for drawing mirror and calculate - // reflected - // position into tposx, tposy, and tang (tposz == cposz) - // Must call preparemirror before drawrooms and - // completemirror after drawrooms - - renderPrepareMirror(tx, ty, tz, tpq16ang, tpq16horiz, - mirror[cnt].mirrorwall, /*mirror[cnt].mirrorsector,*/ &tposx, &tposy, &tang); - - renderDrawRoomsQ16(tposx, tposy, tz, (tang), tpq16horiz, mirror[cnt].mirrorsector + MAXSECTORS); - - analyzesprites(tposx, tposy, tz, true); - renderDrawMasks(); - - renderCompleteMirror(); // Reverse screen x-wise in this - // function - } - - - // g_visibility = tvisibility; - // g_visibility = NormalVisibility; - - // renderDrawRoomsQ16(tx, ty, tz, tpq16ang, tpq16horiz, pp->cursectnum); - // Clean up anything that the camera view might have done - tileDelete(MIRROR); - wall[mirror[cnt].mirrorwall].overpicnum = MIRRORLABEL + cnt; - } - else - mirrorinview = false; - } + cam_pp = pp; + cam_tx = tx; + cam_ty = ty; + cam_tz = tz; } +void GameInterface::UpdateCameras(double smoothratio) +{ + JS_DrawCameras(cam_pp, cam_tx, cam_ty, cam_tz, smoothratio); +} + +void GameInterface::EnterPortal(spritetype* viewer, int type) +{ + if (type == PORTAL_WALL_MIRROR) display_mirror++; +} + +void GameInterface::LeavePortal(spritetype* viewer, int type) +{ + if (type == PORTAL_WALL_MIRROR) display_mirror--; +} + + void DoAutoSize(tspriteptr_t tspr) { diff --git a/source/games/sw/src/jsector.h b/source/games/sw/src/jsector.h index 245567bf9..5a26cf222 100644 --- a/source/games/sw/src/jsector.h +++ b/source/games/sw/src/jsector.h @@ -70,7 +70,8 @@ extern bool mirrorinview; extern short NormalVisibility; void JAnalyzeSprites(tspriteptr_t tspr); -void JS_DrawCameras(PLAYERp pp, int tx, int ty, int tz); +void JS_DrawCameras(PLAYERp pp, int tx, int ty, int tz, double smoothratio); +void JS_CameraParms(PLAYERp pp, int tx, int ty, int tz); void JS_DrawMirrors(PLAYERp pp,int tx,int ty,int tz,fixed_t tpq16ang,fixed_t tpq16horiz); void JS_InitMirrors(void); void JS_ProcessEchoSpot(void); diff --git a/source/games/sw/src/menus.cpp b/source/games/sw/src/menus.cpp index ef66a628e..4bf74c606 100644 --- a/source/games/sw/src/menus.cpp +++ b/source/games/sw/src/menus.cpp @@ -222,30 +222,4 @@ void DoPaletteFlash(PLAYERp pp) } - -bool MNU_ShareWareMessage() -{ - const char* extra_text; - short w, h; - - if (SW_SHAREWARE) - { - extra_text = "Be sure to call 800-3DREALMS today"; - MNU_DrawString(160, 110, extra_text, 1, 16, 0); - extra_text = "and order the game."; - MNU_DrawString(160, 120, extra_text, 1, 16, 0); - extra_text = "You are only playing the first "; - MNU_DrawString(160, 130, extra_text, 1, 16, 0); - extra_text = "four levels, and are missing most"; - MNU_DrawString(160, 140, extra_text, 1, 16, 0); - extra_text = "of the game, weapons and monsters."; - MNU_DrawString(160, 150, extra_text, 1, 16, 0); - extra_text = "See the ordering information."; - MNU_DrawString(160, 160, extra_text, 1, 16, 0); - //SET(item->flags, mf_disabled); - } - return true; -} - - END_SW_NS diff --git a/source/games/sw/src/menus.h b/source/games/sw/src/menus.h index 7105d669b..6ee40e181 100644 --- a/source/games/sw/src/menus.h +++ b/source/games/sw/src/menus.h @@ -32,7 +32,6 @@ Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms BEGIN_SW_NS -void MNU_DrawString(int x, int y, const char* string, int shade, int pal, int align = -1); void MNU_DrawSmallString(int x, int y, const char* string, int shade, int pal, int align = -1, double alpha = 1); END_SW_NS diff --git a/source/games/sw/src/namelist.h b/source/games/sw/src/namelist.h index 30ffa5fc9..40809cc19 100644 --- a/source/games/sw/src/namelist.h +++ b/source/games/sw/src/namelist.h @@ -66,6 +66,7 @@ x(BLACK___, 2306) x(FragBarErase, 2375) x(FragBarErase2, 2376) x(MENUBAR, 2427) +x(THREED_REALMS_PIC, 2325) x(MIRROR, 340) x(FLOORMIRROR, 341) @@ -168,3 +169,50 @@ x(CREDITS2, 5118) x(SUPPORT, 4979) x(ONLINE, 5113) +x(STAT_SCREEN_PIC, 5114) +x(BONUS_SCREEN_PIC, 5120) + +x(BONUS_PUNCH00, 5121) +x(BONUS_PUNCH01, 5122) +x(BONUS_PUNCH02, 5123) +x(BONUS_PUNCH03, 5124) +x(BONUS_PUNCH04, 5125) +x(BONUS_PUNCH05, 5126) +x(BONUS_PUNCH06, 5127) +x(BONUS_PUNCH07, 5128) +x(BONUS_PUNCH08, 5129) +x(BONUS_PUNCH09, 5130) +x(BONUS_PUNCH10, 5131) +x(BONUS_PUNCH11, 5132) +x(BONUS_PUNCH12, 5133) +x(BONUS_PUNCH13, 5134) +x(BONUS_PUNCH14, 5135) + +x(BONUS_KICK00, 5136) +x(BONUS_KICK01, 5137) +x(BONUS_KICK02, 5138) +x(BONUS_KICK03, 5139) +x(BONUS_KICK04, 5140) +x(BONUS_KICK05, 5141) +x(BONUS_KICK06, 5142) +x(BONUS_KICK07, 5143) +x(BONUS_KICK08, 5144) +x(BONUS_KICK09, 5145) +x(BONUS_KICK10, 5146) +x(BONUS_KICK11, 5147) +x(BONUS_KICK12, 5148) +x(BONUS_KICK13, 5149) +x(BONUS_KICK14, 5150) + +x(BONUS_GRAB00, 5151) +x(BONUS_GRAB01, 5152) +x(BONUS_GRAB02, 5153) +x(BONUS_GRAB03, 5154) +x(BONUS_GRAB04, 5155) +x(BONUS_GRAB05, 5156) +x(BONUS_GRAB06, 5157) +x(BONUS_GRAB07, 5158) +x(BONUS_GRAB08, 5159) +x(BONUS_GRAB09, 5160) + +x(TITLE_PIC, 2324) diff --git a/source/games/sw/src/network.cpp b/source/games/sw/src/network.cpp index e449f6754..ac9592e19 100644 --- a/source/games/sw/src/network.cpp +++ b/source/games/sw/src/network.cpp @@ -26,7 +26,6 @@ Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms #include "ns.h" #include "build.h" -#include "mmulti.h" #include "gamecontrol.h" diff --git a/source/games/sw/src/panel.cpp b/source/games/sw/src/panel.cpp index 89e7927d6..e8c0aee6c 100644 --- a/source/games/sw/src/panel.cpp +++ b/source/games/sw/src/panel.cpp @@ -6898,15 +6898,28 @@ pDisplaySprites(PLAYERp pp, double smoothratio) int flags; double const look_anghalf = pp->angle.look_anghalf(smoothratio); - double const looking_arc = fabs(look_anghalf) / 4.5; + double const looking_arc = pp->angle.looking_arc(smoothratio); TRAVERSE(&pp->PanelSpriteList, psp, next) { ang = psp->rotate_ang; shade = 0; flags = 0; - x = (psp->ox + MulScaleF(psp->x - psp->ox, smoothratio, 16)) - look_anghalf; - y = (psp->oy + MulScaleF(psp->y - psp->oy, smoothratio, 16)) + looking_arc; + if (cl_hudinterpolation) + { + x = interpolatedvaluef(psp->ox, psp->x, smoothratio); + y = interpolatedvaluef(psp->oy, psp->y, smoothratio); + + } + else + { + x = psp->x; + y = psp->y; + } + + x -= look_anghalf; + y += looking_arc; + // initilize pal here - jack with it below pal = psp->pal; diff --git a/source/games/sw/src/player.cpp b/source/games/sw/src/player.cpp index 90367ccdb..3ae5d7172 100644 --- a/source/games/sw/src/player.cpp +++ b/source/games/sw/src/player.cpp @@ -1516,7 +1516,7 @@ UpdatePlayerSpriteAngle(PLAYERp pp) void DoPlayerTurn(PLAYERp pp, float const avel, double const scaleAdjust) { - applylook(&pp->angle, avel, &pp->input.actions, scaleAdjust); + pp->angle.applyinput(avel, &pp->input.actions, scaleAdjust); UpdatePlayerSpriteAngle(pp); } @@ -1606,8 +1606,7 @@ DoPlayerTurnVehicleRect(PLAYERp pp, int *x, int *y, int *ox, int *oy) void DoPlayerTurnTurret(PLAYERp pp, float avel) { - lookangle diff; - binangle new_ang; + binangle new_ang, diff; SECTOR_OBJECTp sop = pp->sop; if (sop->drive_angspeed) @@ -1668,8 +1667,8 @@ void DoPlayerHorizon(PLAYERp pp, float const horz, double const scaleAdjust) { bool const canslopetilt = !TEST(pp->Flags, PF_FLYING|PF_SWIMMING|PF_DIVING|PF_CLIMBING|PF_JUMPING|PF_FALLING) && TEST(sector[pp->cursectnum].floorstat, FLOOR_STAT_SLOPE); - calcviewpitch(pp->pos.vec2, &pp->horizon.horizoff, pp->angle.ang, pp->input.actions & SB_AIMMODE, canslopetilt, pp->cursectnum, scaleAdjust, TEST(pp->Flags, PF_CLIMBING)); - sethorizon(&pp->horizon, horz, &pp->input.actions, scaleAdjust); + pp->horizon.calcviewpitch(pp->pos.vec2, pp->angle.ang, pp->input.actions & SB_AIMMODE, canslopetilt, pp->cursectnum, scaleAdjust, TEST(pp->Flags, PF_CLIMBING)); + pp->horizon.applyinput(horz, &pp->input.actions, scaleAdjust); } void @@ -5471,7 +5470,7 @@ DoPlayerStopOperate(PLAYERp pp) if (TEST_BOOL1(pp->remote_sprite)) pp->angle.ang = pp->angle.oang = buildang(pp->remote_sprite->ang); else - pp->angle.ang = pp->angle.oang = q16ang(gethiq16angle(pp->sop_remote->xmid - pp->posx, pp->sop_remote->ymid - pp->posy)); + pp->angle.ang = pp->angle.oang = bvectangbam(pp->sop_remote->xmid - pp->posx, pp->sop_remote->ymid - pp->posy); } if (pp->sop_control) @@ -6126,7 +6125,7 @@ void DoPlayerDeathFollowKiller(PLAYERp pp) if (FAFcansee(kp->x, kp->y, SPRITEp_TOS(kp), kp->sectnum, pp->posx, pp->posy, pp->posz, pp->cursectnum)) { - pp->angle.addadjustment(getincanglebam(pp->angle.ang, bvectangbam(kp->x - pp->posx, kp->y - pp->posy)) >> 4); + pp->angle.addadjustment(getincanglebam(pp->angle.ang, bvectangbam(kp->x - pp->posx, kp->y - pp->posy)).signedbuild() >> 4); } } } @@ -7042,9 +7041,7 @@ void MultiPlayLimits(void) gNet.TimeLimitClock = gNet.TimeLimit; MapRecord *next = nullptr; - // do not increment if level is 23 thru 28 (should be done smarter.) - if (currentLevel->levelNumber <= 22) - next = FindMapByLevelNum(currentLevel->levelNumber + 1); + next = FindNextMap(currentLevel); ChangeLevel(next, -1); } } @@ -7154,7 +7151,9 @@ domovethings(void) // auto tracking mode for single player multi-game if (numplayers <= 1 && PlayerTrackingMode && pnum == screenpeek && screenpeek != myconnectindex) { - Player[screenpeek].angle.settarget(bvectangf(Player[myconnectindex].posx - Player[screenpeek].posx, Player[myconnectindex].posy - Player[screenpeek].posy)); + int deltax = Player[myconnectindex].posx - Player[screenpeek].posx; + int deltay = Player[myconnectindex].posy - Player[screenpeek].posy; + Player[screenpeek].angle.settarget(bvectangbam(deltax, deltay)); } if (!TEST(pp->Flags, PF_DEAD)) @@ -7202,7 +7201,7 @@ domovethings(void) MapRecord *map = nullptr; if (FinishAnim == ANIM_SUMO) { - map = FindMapByLevelNum(currentLevel->levelNumber+1); + map = FindNextMap(currentLevel); } ChangeLevel(map, -1); } diff --git a/source/games/sw/src/rooms.cpp b/source/games/sw/src/rooms.cpp index e0684d681..4dd1098b7 100644 --- a/source/games/sw/src/rooms.cpp +++ b/source/games/sw/src/rooms.cpp @@ -29,11 +29,10 @@ Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms #include "names2.h" #include "panel.h" #include "misc.h" +#include "hw_drawinfo.h" BEGIN_SW_NS -void FAF_DrawRooms(int posx, int posy, int posz, fixed_t q16ang, fixed_t q16horiz, short cursectnum); - //////////////////////////////////////////////////////////////////// // // FLOOR ABOVE FLOOR @@ -690,7 +689,7 @@ GetUpperLowerSector(short match, int x, int y, short *upper, short *lower) int sln = 0; int SpriteNum; SPRITEp sp; - +#if 0 // keep a list of the last stacked sectors the view was in and // check those fisrt sln = 0; @@ -721,12 +720,13 @@ GetUpperLowerSector(short match, int x, int y, short *upper, short *lower) sln++; } } +#endif // didn't find it yet so test ALL sectors if (sln < 2) { sln = 0; - for (i = numsectors - 1; i >= 0; i--) + for (i = 0; i < numsectors; i++)// - 1; i >= 0; i--) { if (inside(x, y, (short) i) == 1) { @@ -859,30 +859,33 @@ FindCeilingView(short match, int32_t* x, int32_t* y, int32_t z, int16_t* sectnum pix_diff = labs(z - sector[sp->sectnum].floorz) >> 8; newz = sector[sp->sectnum].floorz + ((pix_diff / 128) + 1) * Z(128); - it.Reset(STAT_FAF); - while ((i = it.NextIndex()) >= 0) + if (!testnewrenderer) { - sp = &sprite[i]; - - if (sp->lotag == match) + it.Reset(STAT_FAF); + while ((i = it.NextIndex()) >= 0) { - // move lower levels ceilings up for the correct view - if (sp->hitag == VIEW_LEVEL2) + sp = &sprite[i]; + + if (sp->lotag == match) { - // save it off - save.sectnum[save.zcount] = sp->sectnum; - save.zval[save.zcount] = sector[sp->sectnum].floorz; - save.pic[save.zcount] = sector[sp->sectnum].floorpicnum; - save.slope[save.zcount] = sector[sp->sectnum].floorheinum; + // move lower levels ceilings up for the correct view + if (sp->hitag == VIEW_LEVEL2) + { + // save it off + save.sectnum[save.zcount] = sp->sectnum; + save.zval[save.zcount] = sector[sp->sectnum].floorz; + save.pic[save.zcount] = sector[sp->sectnum].floorpicnum; + save.slope[save.zcount] = sector[sp->sectnum].floorheinum; - sector[sp->sectnum].floorz = newz; - // don't change FAF_MIRROR_PIC - ConnectArea - if (sector[sp->sectnum].floorpicnum != FAF_MIRROR_PIC) - sector[sp->sectnum].floorpicnum = FAF_MIRROR_PIC+1; - sector[sp->sectnum].floorheinum = 0; + sector[sp->sectnum].floorz = newz; + // don't change FAF_MIRROR_PIC - ConnectArea + if (sector[sp->sectnum].floorpicnum != FAF_MIRROR_PIC) + sector[sp->sectnum].floorpicnum = FAF_MIRROR_PIC + 1; + sector[sp->sectnum].floorheinum = 0; - save.zcount++; - PRODUCTION_ASSERT(save.zcount < ZMAX); + save.zcount++; + PRODUCTION_ASSERT(save.zcount < ZMAX); + } } } } @@ -951,40 +954,42 @@ FindFloorView(short match, int32_t* x, int32_t* y, int32_t z, int16_t* sectnum) pix_diff = labs(z - sector[sp->sectnum].ceilingz) >> 8; newz = sector[sp->sectnum].ceilingz - ((pix_diff / 128) + 1) * Z(128); - it.Reset(STAT_FAF); - while ((i = it.NextIndex()) >= 0) + if (!testnewrenderer) { - sp = &sprite[i]; - - if (sp->lotag == match) + it.Reset(STAT_FAF); + while ((i = it.NextIndex()) >= 0) { - // move upper levels floors down for the correct view - if (sp->hitag == VIEW_LEVEL1) + sp = &sprite[i]; + + if (sp->lotag == match) { - // save it off - save.sectnum[save.zcount] = sp->sectnum; - save.zval[save.zcount] = sector[sp->sectnum].ceilingz; - save.pic[save.zcount] = sector[sp->sectnum].ceilingpicnum; - save.slope[save.zcount] = sector[sp->sectnum].ceilingheinum; + // move upper levels floors down for the correct view + if (sp->hitag == VIEW_LEVEL1) + { + // save it off + save.sectnum[save.zcount] = sp->sectnum; + save.zval[save.zcount] = sector[sp->sectnum].ceilingz; + save.pic[save.zcount] = sector[sp->sectnum].ceilingpicnum; + save.slope[save.zcount] = sector[sp->sectnum].ceilingheinum; - sector[sp->sectnum].ceilingz = newz; + sector[sp->sectnum].ceilingz = newz; - // don't change FAF_MIRROR_PIC - ConnectArea - if (sector[sp->sectnum].ceilingpicnum != FAF_MIRROR_PIC) - sector[sp->sectnum].ceilingpicnum = FAF_MIRROR_PIC+1; - sector[sp->sectnum].ceilingheinum = 0; + // don't change FAF_MIRROR_PIC - ConnectArea + if (sector[sp->sectnum].ceilingpicnum != FAF_MIRROR_PIC) + sector[sp->sectnum].ceilingpicnum = FAF_MIRROR_PIC + 1; + sector[sp->sectnum].ceilingheinum = 0; - save.zcount++; - PRODUCTION_ASSERT(save.zcount < ZMAX); + save.zcount++; + PRODUCTION_ASSERT(save.zcount < ZMAX); + } } } } - return true; } short -ViewSectorInScene(short cursectnum, short level) +FindViewSectorInScene(short cursectnum, short level) { int i; SPRITEp sp; @@ -1008,9 +1013,6 @@ ViewSectorInScene(short cursectnum, short level) // found a potential match match = sp->lotag; - if (!PicInView(FAF_MIRROR_PIC, true)) - return -1; - return match; } } @@ -1019,65 +1021,179 @@ ViewSectorInScene(short cursectnum, short level) return -1; } -void -DrawOverlapRoom(int tx, int ty, int tz, fixed_t tq16ang, fixed_t tq16horiz, short tsectnum) +struct PortalGroup { - short i; - short match; + TArray sectors; + int othersector = -1; + vec3_t offset = { 0,0,0 }; +}; - save.zcount = 0; + // This is very messy because some portals are linked outside the actual portal sectors, so we have to use the complicated original linking logic to find the connection. :? +void CollectPortals() +{ + int t = testnewrenderer; + testnewrenderer = true; + TArray floorportals; + TArray ceilingportals; + FixedBitArray floordone, ceilingdone; - match = ViewSectorInScene(tsectnum, VIEW_LEVEL1); - if (match != -1) + floordone.Zero(); + ceilingdone.Zero(); + + for (int i = 0; i < numsectors; i++) { - FindCeilingView(match, &tx, &ty, tz, &tsectnum); - - if (tsectnum < 0) - return; - - renderDrawRoomsQ16(tx, ty, tz, tq16ang, tq16horiz, tsectnum); - //FAF_DrawRooms(tx, ty, tz, tq16ang, tq16horiz, tsectnum); - - // reset Z's - for (i = 0; i < save.zcount; i++) + if (sector[i].floorpicnum == FAF_MIRROR_PIC && !floordone[i]) { - sector[save.sectnum[i]].floorz = save.zval[i]; - sector[save.sectnum[i]].floorpicnum = save.pic[i]; - sector[save.sectnum[i]].floorheinum = save.slope[i]; - } - - analyzesprites(tx, ty, tz, false); - post_analyzesprites(); - renderDrawMasks(); - - } - else - { - match = ViewSectorInScene(tsectnum, VIEW_LEVEL2); - if (match != -1) - { - FindFloorView(match, &tx, &ty, tz, &tsectnum); - - if (tsectnum < 0) - return; - - renderDrawRoomsQ16(tx, ty, tz, tq16ang, tq16horiz, tsectnum); - //FAF_DrawRooms(tx, ty, tz, tq16ang, tq16horiz, tsectnum); - - // reset Z's - for (i = 0; i < save.zcount; i++) + auto& fp = floorportals[floorportals.Reserve(1)]; + fp.sectors.Push(i); + floordone.Set(i); + for (unsigned ii = 0; ii < fp.sectors.Size(); ii++) { - sector[save.sectnum[i]].ceilingz = save.zval[i]; - sector[save.sectnum[i]].ceilingpicnum = save.pic[i]; - sector[save.sectnum[i]].ceilingheinum = save.slope[i]; + auto sec = §or[fp.sectors[ii]]; + for (int w = 0; w < sec->wallnum; w++) + { + auto ns = wall[sec->wallptr + w].nextsector; + if (ns < 0 || floordone[ns] || sector[ns].floorpicnum != FAF_MIRROR_PIC) continue; + fp.sectors.Push(ns); + floordone.Set(ns); + } + } + } + if (sector[i].ceilingpicnum == FAF_MIRROR_PIC && !ceilingdone[i]) + { + auto& fp = ceilingportals[ceilingportals.Reserve(1)]; + fp.sectors.Push(i); + ceilingdone.Set(i); + for (unsigned ii = 0; ii < fp.sectors.Size(); ii++) + { + auto sec = §or[fp.sectors[ii]]; + for (int w = 0; w < sec->wallnum; w++) + { + auto ns = wall[sec->wallptr + w].nextsector; + if (ns < 0 || ceilingdone[ns] || sector[ns].ceilingpicnum != FAF_MIRROR_PIC) continue; + fp.sectors.Push(ns); + ceilingdone.Set(ns); + } } - - analyzesprites(tx, ty, tz, false); - post_analyzesprites(); - renderDrawMasks(); - } } + // now try to find connections. + for (auto& fp : ceilingportals) + { + // pick one sprite out of the sectors, repeat until we get a valid connection + for (auto sec : fp.sectors) + { + SectIterator it(sec); + int spr; + while ((spr = it.NextIndex()) >= 0) + { + int tx = sprite[spr].x; + int ty = sprite[spr].y; + int tz = sprite[spr].z; + int16_t tsectnum = sec; + + int match = FindViewSectorInScene(tsectnum, VIEW_LEVEL1); + if (match != -1) + { + FindCeilingView(match, &tx, &ty, tz, &tsectnum); + if (tsectnum >= 0 && sector[tsectnum].floorpicnum == FAF_MIRROR_PIC) + { + // got something! + fp.othersector = tsectnum; + fp.offset = { tx, ty, tz }; + fp.offset -= sprite[spr].pos; + goto nextfg; + } + } + } + } + nextfg:; + } + + for (auto& fp : floorportals) + { + for (auto sec : fp.sectors) + { + SectIterator it(sec); + int spr; + while ((spr = it.NextIndex()) >= 0) + { + int tx = sprite[spr].x; + int ty = sprite[spr].y; + int tz = sprite[spr].z; + int16_t tsectnum = sec; + + int match = FindViewSectorInScene(tsectnum, VIEW_LEVEL2); + if (match != -1) + { + FindFloorView(match, &tx, &ty, tz, &tsectnum); + if (tsectnum >= 0 && sector[tsectnum].ceilingpicnum == FAF_MIRROR_PIC) + { + // got something! + fp.othersector = tsectnum; + fp.offset = { tx, ty, tz }; + fp.offset -= sprite[spr].pos; + goto nextcg; + } + } + } + } + nextcg:; + } + for (auto& pt : floorportals) + { + if (pt.othersector > -1) + { + auto findother = [&](int other) -> PortalGroup* + { + for (auto& pt2 : ceilingportals) + { + if (pt2.sectors.Find(other) != pt2.sectors.Size()) return &pt2; + } + return nullptr; + }; + + auto pt2 = findother(pt.othersector); + if (pt2) + { + int pnum = portalAdd(PORTAL_SECTOR_FLOOR, -1, pt.offset.x, pt.offset.y, 0); + allPortals[pnum].targets = pt2->sectors; // do not move! We still need the original. + for (auto sec : pt.sectors) + { + sector[sec].portalflags = PORTAL_SECTOR_FLOOR; + sector[sec].portalnum = pnum; + } + } + } + } + for (auto& pt : ceilingportals) + { + if (pt.othersector > -1) + { + auto findother = [&](int other) -> PortalGroup* + { + for (auto& pt2 : floorportals) + { + if (pt2.sectors.Find(other) != pt2.sectors.Size()) return &pt2; + } + return nullptr; + }; + + auto pt2 = findother(pt.othersector); + if (pt2) + { + int pnum = portalAdd(PORTAL_SECTOR_FLOOR, -1, pt.offset.x, pt.offset.y, 0); + allPortals[pnum].targets = std::move(pt2->sectors); + for (auto sec : pt.sectors) + { + sector[sec].portalflags = PORTAL_SECTOR_CEILING; + sector[sec].portalnum = pnum; + } + } + } + } + testnewrenderer = t; } + END_SW_NS diff --git a/source/games/sw/src/save.cpp b/source/games/sw/src/save.cpp index e71037235..efe2be333 100644 --- a/source/games/sw/src/save.cpp +++ b/source/games/sw/src/save.cpp @@ -78,9 +78,7 @@ extern int FinishAnim; extern int GameVersion; //extern short Zombies; -extern bool serpwasseen; -extern bool sumowasseen; -extern bool zillawasseen; +extern bool bosswasseen[3]; extern short BossSpriteNum[3]; #define ANIM_SAVE 1 @@ -1269,9 +1267,7 @@ void GameInterface::SerializeGameState(FSerializer& arc) ("GodMode", GodMode) ("FinishTimer", FinishTimer) ("FinishAnim", FinishAnim) - ("serpwasseen", serpwasseen) - ("sumowasseen", sumowasseen) - ("zillawasseen", zillawasseen) + .Array("bosswasseen", bosswasseen, 3) .Array("BossSpriteNum", BossSpriteNum, 3); arc.Array("tracks", Track, countof(Track)) ; diff --git a/source/games/sw/src/saveable.h b/source/games/sw/src/saveable.h index cf4a30d63..98c47880b 100644 --- a/source/games/sw/src/saveable.h +++ b/source/games/sw/src/saveable.h @@ -44,7 +44,7 @@ typedef struct } saveable_module; template -constexpr enable_if_t::value, size_t> SAVE_SIZEOF(T const & obj) noexcept +constexpr std::enable_if_t::value, size_t> SAVE_SIZEOF(T const & obj) noexcept { return sizeof(obj); } diff --git a/source/games/sw/src/sbar.cpp b/source/games/sw/src/sbar.cpp index 4da1d6bd0..325c5f6b9 100644 --- a/source/games/sw/src/sbar.cpp +++ b/source/games/sw/src/sbar.cpp @@ -216,7 +216,7 @@ private: } font_pic = font_base[color] + (ch - '0'); - DrawGraphic(tileGetTexture(font_pic), x, ys, DI_ITEM_LEFT_TOP, 1, -1, -1, 1, 1, shadeToLight(shade)); + DrawGraphic(tileGetTexture(font_pic), x, ys, DI_ITEM_LEFT_TOP, 1, -1, -1, 1, 1, STYLE_Translucent, shadeToLight(shade)); x += tileWidth(font_pic) + 1; } } @@ -651,7 +651,7 @@ private: for (i = 0, x = COMPASS_X; i < 10; i++) { - DrawGraphic(tileGetTexture(CompassPic[NORM_CANG(start_ang + i)]), x, COMPASS_Y, DI_ITEM_LEFT_TOP, 1, -1, -1, 1, 1, shadeToLight(CompassShade[i])); + DrawGraphic(tileGetTexture(CompassPic[NORM_CANG(start_ang + i)]), x, COMPASS_Y, DI_ITEM_LEFT_TOP, 1, -1, -1, 1, 1, STYLE_Translucent, shadeToLight(CompassShade[i])); x += x_size; } } diff --git a/source/games/sw/src/scrip2.cpp b/source/games/sw/src/scrip2.cpp index 30774a115..fc619ac5d 100644 --- a/source/games/sw/src/scrip2.cpp +++ b/source/games/sw/src/scrip2.cpp @@ -40,6 +40,7 @@ Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms #include "razemenu.h" #include "quotemgr.h" #include "mapinfo.h" +#include "hw_voxels.h" BEGIN_SW_NS @@ -226,7 +227,7 @@ void LoadKVXFromScript(const char* filename) GetToken(false); // Load the voxel file into memory - if (!qloadkvx(lNumber,token)) + if (!voxDefine(lNumber,token)) { // Store the sprite and voxel numbers for later use aVoxelArray[lTile].Voxel = lNumber; // Voxel num @@ -447,6 +448,7 @@ void LoadCustomInfoFromScript(const char *filename) { curMap = AllocateMap(); curMap->levelNumber = mapno; + curMap->cluster = mapno < 5 ? 1 : 2; } if (sc.CheckString("{")) @@ -523,13 +525,19 @@ void LoadCustomInfoFromScript(const char *filename) case CM_TITLE: { sc.MustGetString(); - if (curep != -1) gVolumeNames[curep] = sc.String; + auto vol = MustFindVolume(curep); + auto clust = MustFindCluster(curep); + vol->name = clust->name = sc.String; break; } case CM_SUBTITLE: { sc.MustGetString(); - if (curep != -1) gVolumeSubtitles[curep] = sc.String; + if (curep != -1) + { + auto vol = MustFindVolume(curep); + vol->subtitle = sc.String; + } break; } default: @@ -800,6 +808,12 @@ void LoadCustomInfoFromScript(const char *filename) break; } } + auto vol0 = MustFindVolume(0); + auto vol1 = MustFindVolume(1); + auto map1 = FindMapByLevelNum(1); + auto map5 = FindMapByLevelNum(5); + if (vol0 && map1) vol0->startmap = map1->labelName; + if (vol1 && map5) vol1->startmap = map5->labelName; } END_SW_NS diff --git a/source/games/sw/src/sector.cpp b/source/games/sw/src/sector.cpp index 892bd91e8..dbd0f8844 100644 --- a/source/games/sw/src/sector.cpp +++ b/source/games/sw/src/sector.cpp @@ -1913,7 +1913,7 @@ OperateSprite(short SpriteNum, short player_is_operating) if (sp->hitag) map = FindMapByLevelNum(sp->hitag); else - map = FindMapByLevelNum(currentLevel->levelNumber + 1); + map = FindNextMap(currentLevel); ChangeLevel(map, -1); return true; @@ -2095,7 +2095,7 @@ OperateTripTrigger(PLAYERp pp) if (sectp->hitag) map = FindMapByLevelNum(sectp->hitag); else - map = FindMapByLevelNum(currentLevel->levelNumber + 1); + map = FindNextMap(currentLevel); ChangeLevel(map, -1); break; } diff --git a/source/games/sw/src/sounds.cpp b/source/games/sw/src/sounds.cpp index 8d0a364b3..16ce58cf3 100644 --- a/source/games/sw/src/sounds.cpp +++ b/source/games/sw/src/sounds.cpp @@ -48,6 +48,7 @@ Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms #include "serializer.h" #include "gamecontrol.h" #include "gamestate.h" +#include "vm.h" BEGIN_SW_NS @@ -981,5 +982,37 @@ void StopFX() if (soundEngine) soundEngine->StopAllChannels(); } +DEFINE_ACTION_FUNCTION(_SW, PlaySound) +{ + PARAM_PROLOGUE; + PARAM_INT(sound); + PARAM_INT(vflags); + PARAM_INT(channel); + PARAM_INT(cflags); + PlaySound(sound, Voc3D_Flags(vflags), channel, EChanFlags::FromInt(cflags)); + return 0; +} + +DEFINE_ACTION_FUNCTION_NATIVE(_SW, StopSound, StopSound) +{ + StopSound(); + return 0; +} + +DEFINE_ACTION_FUNCTION(_SW, IsSoundPlaying) +{ + PARAM_PROLOGUE; + PARAM_INT(channel); + ACTION_RETURN_BOOL(soundEngine->IsSourcePlayingSomething(SOURCE_None, nullptr, channel)); + +} + +DEFINE_ACTION_FUNCTION(_SW, PlaySong) +{ + PARAM_PROLOGUE; + PARAM_INT(song); + PlaySong(nullptr, ThemeSongs[song], ThemeTrack[song], true); + return 0; +} END_SW_NS diff --git a/source/games/sw/src/sprite.cpp b/source/games/sw/src/sprite.cpp index c35ad0e52..6646bae45 100644 --- a/source/games/sw/src/sprite.cpp +++ b/source/games/sw/src/sprite.cpp @@ -870,6 +870,7 @@ SpawnUser(short SpriteNum, short id, STATEp state) ASSERT(!Prediction); + User[SpriteNum].Clear(); // make sure to delete old, stale content first! User[SpriteNum].Alloc(); u = User[SpriteNum].Data(); @@ -1080,10 +1081,8 @@ ActorTestSpawn(SPRITEp sp) sp->picnum == SAILORGIRL_R0) && (g_gameType & GAMEFLAG_ADDON)) return true; // spawn Bouncing Betty (mine) in TD map 09 Warehouse -#if 0 // needs to be done smarter. - if (sp->picnum == 817 && swGetAddon() == 2 && currentLevel->levelNumber == 9) + if (sp->picnum == 817 && (currentLevel->flags & LEVEL_SW_SPAWNMINES)) return true; -#endif return false; } diff --git a/source/games/sw/src/sumo.cpp b/source/games/sw/src/sumo.cpp index df5ed88e6..628770099 100644 --- a/source/games/sw/src/sumo.cpp +++ b/source/games/sw/src/sumo.cpp @@ -41,9 +41,7 @@ Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms BEGIN_SW_NS extern uint8_t playTrack; -bool serpwasseen = false; -bool sumowasseen = false; -bool zillawasseen = false; +bool bosswasseen[3]; short BossSpriteNum[3] = {-1,-1,-1}; @@ -819,22 +817,19 @@ BossHealthMeter(void) int y; extern bool NoMeters; short health; - bool bosswasseen; static bool triedplay = false; if (NoMeters) return; - if (currentLevel->levelNumber != 20 && currentLevel->levelNumber != 4 && currentLevel->levelNumber != 11 && currentLevel->levelNumber != 5) return; + if (!(currentLevel->gameflags & (LEVEL_SW_BOSSMETER_SERPENT | LEVEL_SW_BOSSMETER_SUMO | LEVEL_SW_BOSSMETER_ZILLA))) return; // Don't draw bar for other players if (pp != Player+myconnectindex) return; // all enemys - if ((currentLevel->levelNumber == 20 && (BossSpriteNum[0] == -1 || BossSpriteNum[1] == -1 || BossSpriteNum[2] == -1)) || - (currentLevel->levelNumber == 4 && BossSpriteNum[0] == -1) || - (currentLevel->levelNumber == 5 && BossSpriteNum[0] == -1) || - (currentLevel->levelNumber == 11 && BossSpriteNum[1] == -1)) + if (currentLevel->gameflags & (LEVEL_SW_BOSSMETER_SERPENT|LEVEL_SW_BOSSMETER_SUMO|LEVEL_SW_BOSSMETER_ZILLA) && + BossSpriteNum[0] <= -1 && BossSpriteNum[1] <= -1 && BossSpriteNum[2] <= -1) { StatIterator it(STAT_ENEMY); while ((i = it.NextIndex()) >= 0) @@ -844,11 +839,11 @@ BossHealthMeter(void) if ((u->ID == SERP_RUN_R0 || u->ID == SUMO_RUN_R0 || u->ID == ZILLA_RUN_R0) && sp->pal != 16) { - if (u->ID == SERP_RUN_R0) + if (u->ID == SERP_RUN_R0 && (currentLevel->gameflags & LEVEL_SW_BOSSMETER_SERPENT)) BossSpriteNum[0] = i; - else if (u->ID == SUMO_RUN_R0) + else if (u->ID == SUMO_RUN_R0 && (currentLevel->gameflags & LEVEL_SW_BOSSMETER_SUMO)) BossSpriteNum[1] = i; - else if (u->ID == ZILLA_RUN_R0) + else if (u->ID == ZILLA_RUN_R0 && (currentLevel->gameflags & LEVEL_SW_BOSSMETER_ZILLA)) BossSpriteNum[2] = i; } } @@ -857,45 +852,39 @@ BossHealthMeter(void) if (BossSpriteNum[0] <= -1 && BossSpriteNum[1] <= -1 && BossSpriteNum[2] <= -1) return; - // Frank, good optimization for other levels, but it broke level 20. :( - // I kept this but had to add a fix. - bosswasseen = serpwasseen || sumowasseen || zillawasseen; // Only show the meter when you can see the boss - if ((currentLevel->levelNumber == 20 && (!serpwasseen || !sumowasseen || !zillawasseen)) || !bosswasseen) + for (i=0; i<3; i++) { - for (i=0; i<3; i++) + if (BossSpriteNum[i] >= 0 && !bosswasseen[i]) { - if (BossSpriteNum[i] >= 0) - { - sp = &sprite[BossSpriteNum[i]]; - u = User[BossSpriteNum[i]].Data(); + sp = &sprite[BossSpriteNum[i]]; + u = User[BossSpriteNum[i]].Data(); - if (cansee(sp->x, sp->y, SPRITEp_TOS(sp), sp->sectnum, pp->posx, pp->posy, pp->posz - Z(40), pp->cursectnum)) + if (cansee(sp->x, sp->y, SPRITEp_TOS(sp), sp->sectnum, pp->posx, pp->posy, pp->posz - Z(40), pp->cursectnum)) + { + if (i == 0 && !bosswasseen[0]) { - if (i == 0 && !serpwasseen) + bosswasseen[0] = true; + if (!SW_SHAREWARE) { - serpwasseen = true; - if (!SW_SHAREWARE) - { - PlaySong(nullptr, ThemeSongs[2], ThemeTrack[2], true); - } + PlaySong(nullptr, ThemeSongs[2], ThemeTrack[2], true); } - else if (i == 1 && !sumowasseen) + } + else if (i == 1 && !bosswasseen[1]) + { + bosswasseen[1] = true; + if (!SW_SHAREWARE) { - sumowasseen = true; - if (!SW_SHAREWARE) - { - PlaySong(nullptr, ThemeSongs[3], ThemeTrack[3], true); - } + PlaySong(nullptr, ThemeSongs[3], ThemeTrack[3], true); } - else if (i == 2 && !zillawasseen) + } + else if (i == 2 && !bosswasseen[2]) + { + bosswasseen[2] = true; + if (!SW_SHAREWARE) { - zillawasseen = true; - if (!SW_SHAREWARE) - { - PlaySong(nullptr, ThemeSongs[4], ThemeTrack[4], true); - } + PlaySong(nullptr, ThemeSongs[4], ThemeTrack[4], true); } } } @@ -906,17 +895,17 @@ BossHealthMeter(void) for (i=0; i<3; i++) { - if (i == 0 && (!serpwasseen || BossSpriteNum[0] < 0)) + if (i == 0 && (!bosswasseen[0] || BossSpriteNum[0] < 0)) continue; - if (i == 1 && (!sumowasseen || BossSpriteNum[1] < 0)) + if (i == 1 && (!bosswasseen[1] || BossSpriteNum[1] < 0)) continue; - if (i == 2 && (!zillawasseen || BossSpriteNum[2] < 0)) + if (i == 2 && (!bosswasseen[2] || BossSpriteNum[2] < 0)) continue; sp = &sprite[BossSpriteNum[i]]; u = User[BossSpriteNum[i]].Data(); - if (u->ID == SERP_RUN_R0 && serpwasseen) + if (u->ID == SERP_RUN_R0 && bosswasseen[0]) { if (Skill == 0) health = 1100; else if (Skill == 1) health = 2200; @@ -924,7 +913,7 @@ BossHealthMeter(void) health = HEALTH_SERP_GOD; meterunit = health / 30; } - else if (u->ID == SUMO_RUN_R0 && sumowasseen) + else if (u->ID == SUMO_RUN_R0 && bosswasseen[1]) { if (Skill == 0) health = 2000; else if (Skill == 1) health = 4000; @@ -932,7 +921,7 @@ BossHealthMeter(void) health = 6000; meterunit = health / 30; } - else if (u->ID == ZILLA_RUN_R0 && zillawasseen) + else if (u->ID == ZILLA_RUN_R0 && bosswasseen[2]) { if (Skill == 0) health = 2000; else if (Skill == 1) health = 4000; @@ -963,10 +952,10 @@ BossHealthMeter(void) else y = 30; - if (currentLevel->levelNumber == 20 && numplayers >= 2) + if ((currentLevel->gameflags & (LEVEL_SW_BOSSMETER_SUMO|LEVEL_SW_BOSSMETER_ZILLA)) == (LEVEL_SW_BOSSMETER_SUMO | LEVEL_SW_BOSSMETER_ZILLA) && numplayers >= 2) { - if (u->ID == SUMO_RUN_R0 && sumowasseen) y += 10; - else if (u->ID == ZILLA_RUN_R0 && zillawasseen) y += 20; + if (u->ID == SUMO_RUN_R0 && bosswasseen[1]) y += 10; + else if (u->ID == ZILLA_RUN_R0 && bosswasseen[2]) y += 20; } if (metertics <= 12 && metertics > 6) diff --git a/source/games/sw/src/text.cpp b/source/games/sw/src/text.cpp index 751e0ee3a..53d806abc 100644 --- a/source/games/sw/src/text.cpp +++ b/source/games/sw/src/text.cpp @@ -108,24 +108,6 @@ void InitFonts() // //--------------------------------------------------------------------------- -void MNU_DrawString(int x, int y, const char* string, int shade, int pal, int align) -{ - if (align > -1) - { - int w = SmallFont->StringWidth(string); - if (align == 0) x -= w / 2; - else x -= w; - } - DrawText(twod, SmallFont, CR_UNDEFINED, x, y, string, DTA_FullscreenScale, FSMode_Fit320x200, - DTA_Color, shadeToLight(shade), DTA_TranslationIndex, TRANSLATION(Translation_Remap, pal), TAG_DONE); -} - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - void MNU_DrawSmallString(int x, int y, const char* string, int shade, int pal, int align, double alpha) { if (align > -1) diff --git a/source/games/sw/src/track.cpp b/source/games/sw/src/track.cpp index fdb44f96e..509b7e765 100644 --- a/source/games/sw/src/track.cpp +++ b/source/games/sw/src/track.cpp @@ -639,7 +639,7 @@ TrackSetup(void) if (t->NumPoints == 0) { int i; - auto const sp = (uspritetype const *)&sprite[StatIterator::First(STAT_TRACK+ndx)]; + auto const sp = (spritetype const *)&sprite[StatIterator::First(STAT_TRACK+ndx)]; Printf("WARNING: Did not find first point of Track Number %d, x %d, y %d\n", ndx, sp->x, sp->y); StatIterator it(STAT_TRACK + ndx); while ((i = it.NextIndex()) >= 0) diff --git a/source/games/sw/src/wallmove.cpp b/source/games/sw/src/wallmove.cpp index 7da5ff07b..220b92c0b 100644 --- a/source/games/sw/src/wallmove.cpp +++ b/source/games/sw/src/wallmove.cpp @@ -123,6 +123,7 @@ int DoWallMove(SPRITEp sp) { wallp->x = sp->x + nx; wallp->y = sp->y + ny; + sector[wallp->sector].dirty = 255; } if (shade1) diff --git a/source/games/sw/src/weapon.cpp b/source/games/sw/src/weapon.cpp index d2ea1f4d6..c0eaf4278 100644 --- a/source/games/sw/src/weapon.cpp +++ b/source/games/sw/src/weapon.cpp @@ -5322,7 +5322,7 @@ ActorHealth(short SpriteNum, short amt) u->Health += amt; - if (u->ID == SERP_RUN_R0 && sp->pal != 16 && currentLevel->levelNumber == 4) + if (u->ID == SERP_RUN_R0 && sp->pal != 16 && (currentLevel->gameflags & LEVEL_SW_DEATHEXIT_SERPENT)) { if (u->Health < u->MaxHealth/2) { @@ -5332,7 +5332,7 @@ ActorHealth(short SpriteNum, short amt) } } - if (u->ID == SUMO_RUN_R0 && currentLevel->levelNumber == 11) + if (u->ID == SUMO_RUN_R0 && (currentLevel->gameflags & LEVEL_SW_DEATHEXIT_SUMO)) { if (u->Health <= 0) { @@ -5341,7 +5341,7 @@ ActorHealth(short SpriteNum, short amt) } } - if (u->ID == ZILLA_RUN_R0 && currentLevel->levelNumber == 20) + if (u->ID == ZILLA_RUN_R0 && (currentLevel->gameflags & LEVEL_SW_DEATHEXIT_ZILLA)) { if (u->Health <= 0) //if (u->Health < u->MaxHealth) diff --git a/source/glbackend/gl_texture.cpp b/source/glbackend/gl_texture.cpp index 023c9475d..783e02d83 100644 --- a/source/glbackend/gl_texture.cpp +++ b/source/glbackend/gl_texture.cpp @@ -86,20 +86,3 @@ bool GLInstance::SetTexture(FGameTexture* tex, int paletteid, int sampler, bool return true; } -//=========================================================================== -// -// stand-ins for the texture system. Nothing of this is used right now, but needs to be present to satisfy the linker -// -//=========================================================================== - -void InitBuildTiles() -{ - -} - -TArray usershaders; - -void UpdateVRModes(bool considerQuadBuffered = true) -{ - // should update the menu. -} diff --git a/source/glbackend/glbackend.cpp b/source/glbackend/glbackend.cpp index f8d19af10..258bad5d6 100644 --- a/source/glbackend/glbackend.cpp +++ b/source/glbackend/glbackend.cpp @@ -49,10 +49,18 @@ #include "hw_renderstate.h" #include "hw_cvars.h" #include "gamestruct.h" -#include "gl_models.h" +#include "hw_models.h" +#include "gamefuncs.h" #include "gamehud.h" -CVAR(Bool, gl_texture, true, 0) +CVARD(Bool, hw_hightile, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG, "enable/disable hightile texture rendering") +bool hw_int_useindexedcolortextures; +CUSTOM_CVARD(Bool, hw_useindexedcolortextures, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG, "enable/disable indexed color texture rendering") +{ + if (screen) screen->SetTextureFilterMode(); +} + +EXTERN_CVAR(Bool, gl_texture) static int BufferLock = 0; @@ -62,33 +70,16 @@ void Draw2D(F2DDrawer* drawer, FRenderState& state); GLInstance GLInterface; GLInstance::GLInstance() - :palmanager(this) { VSMatrix mat(0); matrixArray.Push(mat); } -IHardwareTexture *setpalettelayer(int layer, int translation) -{ - if (layer == 1) - return GLInterface.palmanager.GetPalette(GetTranslationType(translation) - Translation_Remap); - else if (layer == 2) - return GLInterface.palmanager.GetLookup(GetTranslationIndex(translation)); - else return nullptr; -} - void GLInstance::Init(int ydim) { - FMaterial::SetLayerCallback(setpalettelayer); new(&renderState) PolymostRenderState; // reset to defaults. } -void GLInstance::Deinit() -{ - palmanager.DeleteAll(); - lastPalswapIndex = -1; -} - void GLInstance::Draw(EDrawType type, size_t start, size_t count) { assert (BufferLock > 0); @@ -128,13 +119,14 @@ void GLInstance::DoDraw() state.SetDepthFunc(DF_LEqual); state.EnableTexture(true); rs.model->BuildVertexBuffer(&mr); - mr.SetupFrame(rs.model, rs.mframes[0], rs.mframes[1], rs.mfactor); + mr.SetupFrame(rs.model, rs.mframes[0], rs.mframes[1], 0); rs.model->RenderFrame(&mr, rs.mMaterial.mTexture, rs.mframes[0], rs.mframes[1], 0.f, rs.mMaterial.mTranslation); state.SetDepthFunc(DF_Less); state.SetVertexBuffer(screen->mVertexData); } } } + state.SetNpotEmulation(0, 0); // make sure we do not leave this in an undefined state. renderState.Apply(*screen->RenderState(), lastState); // apply any pending change before returning. rendercommands.Clear(); hw_int_useindexedcolortextures = false; @@ -182,6 +174,7 @@ bool PolymostRenderState::Apply(FRenderState& state, GLState& oldState) else { state.EnableFog(0); + state.SetFog(0, 0); state.SetSoftLightLevel(ShadeDiv >= 1 / 1000.f ? 255 - Scale(Shade, 255, numshades) : 255); state.SetLightParms(VisFactor, ShadeDiv / (numshades - 2)); } @@ -197,7 +190,8 @@ bool PolymostRenderState::Apply(FRenderState& state, GLState& oldState) state.SetMaterial(mMaterial.mTexture, mMaterial.uFlags, mMaterial.mScaleFlags, mMaterial.mClampMode, mMaterial.mTranslation, mMaterial.mOverrideShader); } - state.SetColor(Color[0], Color[1], Color[2], Color[3]); + if (!drawblack) state.SetColor(Color[0], Color[1], Color[2], Color[3]); + else state.SetColor(0, 0, 0, Color[3]); if (StateFlags != oldState.Flags) { state.EnableDepthTest(StateFlags & STF_DEPTHTEST); @@ -274,7 +268,7 @@ bool PolymostRenderState::Apply(FRenderState& state, GLState& oldState) return true; } -void DoWriteSavePic(FileWriter* file, ESSType ssformat, uint8_t* scr, int width, int height, bool upsidedown) +static void PM_DoWriteSavePic(FileWriter* file, ESSType ssformat, uint8_t* scr, int width, int height, bool upsidedown) { int pixelsize = 3; int pitch = width * pixelsize; @@ -293,7 +287,7 @@ void DoWriteSavePic(FileWriter* file, ESSType ssformat, uint8_t* scr, int width, // //=========================================================================== -void WriteSavePic(FileWriter* file, int width, int height) +void PM_WriteSavePic(FileWriter* file, int width, int height) { IntRect bounds; bounds.left = 0; @@ -350,7 +344,7 @@ void WriteSavePic(FileWriter* file, int width, int height) uint8_t* scr = (uint8_t*)M_Malloc(numpixels * 3); screen->CopyScreenToBuffer(width, height, scr); - DoWriteSavePic(file, SS_RGB, scr, width, height, screen->FlipSavePic()); + PM_DoWriteSavePic(file, SS_RGB, scr, width, height, screen->FlipSavePic()); M_Free(scr); // Switch back the screen render buffers @@ -382,6 +376,11 @@ void renderSetVisibility(float vis) vp.mGlobVis = vis; } +void renderSetViewpoint(float x, float y, float z) +{ + vp.mCameraPos = {x, z, y, 0}; +} + void renderBeginScene() { assert(BufferLock == 0); diff --git a/source/glbackend/glbackend.h b/source/glbackend/glbackend.h index 3a4f360aa..738f0e234 100644 --- a/source/glbackend/glbackend.h +++ b/source/glbackend/glbackend.h @@ -21,31 +21,6 @@ class F2DDrawer; struct palette_t; extern int xdim, ydim; -enum -{ - DM_MAINVIEW, - DM_OFFSCREEN -}; - -class PaletteManager -{ - IHardwareTexture* palettetextures[256] = {}; - IHardwareTexture* lookuptextures[256] = {}; - - GLInstance* const inst; - - unsigned FindPalswap(const uint8_t* paldata, palette_t& fadecolor); - -public: - PaletteManager(GLInstance *inst_) : inst(inst_) - {} - ~PaletteManager(); - void DeleteAll(); - IHardwareTexture *GetPalette(int index); - IHardwareTexture* GetLookup(int index); -}; - - struct glinfo_t { float maxanisotropy; }; @@ -81,9 +56,7 @@ class GLInstance friend IHardwareTexture* setpalettelayer(int layer, int translation); public: - TArray rendercommands; - PaletteManager palmanager; - int lastPalswapIndex = -1; + TArray rendercommands; FGameTexture* currentTexture = nullptr; int MatrixChange = 0; @@ -96,8 +69,6 @@ public: void Init(int y); - void Deinit(); - static int GetTexDimension(int value) { //if (value > gl.max_texturesize) return gl.max_texturesize; @@ -144,6 +115,7 @@ public: void SetShade(int32_t shade, int numshades) { + renderState.drawblack = shade > numshades; renderState.Shade = std::min(shade, numshades-1); } @@ -293,12 +265,6 @@ public: renderState.NPOTEmulation.X = xOffset; } - void SetFadeDisable(bool yes) - { - if (yes) renderState.Flags |= RF_FogDisabled; - else renderState.Flags &= ~RF_FogDisabled; - } - // Hack... bool useMapFog = false; @@ -344,6 +310,7 @@ extern GLInstance GLInterface; void renderSetProjectionMatrix(const float* p); void renderSetViewMatrix(const float* p); void renderSetVisibility(float v); +void renderSetViewpoint(float x, float y, float z); void renderBeginScene(); void renderFinishScene(); void videoShowFrame(int32_t); diff --git a/source/glbackend/pm_renderstate.h b/source/glbackend/pm_renderstate.h index 7127cbf77..c1d45580b 100644 --- a/source/glbackend/pm_renderstate.h +++ b/source/glbackend/pm_renderstate.h @@ -69,6 +69,7 @@ struct PolymostRenderState { int vindex, vcount, primtype; int Shade; + int drawblack = false; float ShadeDiv = 62.f; float VisFactor = 128.f; int Flags = 0; diff --git a/wadsrc/static/engine/compatibility/089c6aaf6ccde94b8dbd295e311df410.mhk b/wadsrc/static/engine/compatibility/089c6aaf6ccde94b8dbd295e311df410.mhk new file mode 100644 index 000000000..ed453bd2b --- /dev/null +++ b/wadsrc/static/engine/compatibility/089c6aaf6ccde94b8dbd295e311df410.mhk @@ -0,0 +1,10 @@ +// Route66's COFFIN.MAP contains one grossly broken mirror that has the mirror tile on all surrounding walls. +wall 657 overpicnum 0 +wall 665 overpicnum 0 +wall 663 overpicnum 0 +wall 680 overpicnum 0 +wall 679 overpicnum 0 +wall 678 overpicnum 0 +wall 1357 overpicnum 0 +wall 1358 overpicnum 0 +wall 1359 overpicnum 0 diff --git a/wadsrc/static/engine/compatibility/2bac4971499306ee6c86462e6a03dae8.mhk b/wadsrc/static/engine/compatibility/2bac4971499306ee6c86462e6a03dae8.mhk new file mode 100644 index 000000000..60e6334da --- /dev/null +++ b/wadsrc/static/engine/compatibility/2bac4971499306ee6c86462e6a03dae8.mhk @@ -0,0 +1,2 @@ +// bad sky setting in WT's $volcano.map (original version) +sector 118 clearflags ceiling 1 diff --git a/wadsrc/static/engine/compatibility/67207fb90130ad561479301c0970c7ba.mhk b/wadsrc/static/engine/compatibility/67207fb90130ad561479301c0970c7ba.mhk new file mode 100644 index 000000000..8242ad80c --- /dev/null +++ b/wadsrc/static/engine/compatibility/67207fb90130ad561479301c0970c7ba.mhk @@ -0,0 +1,2 @@ +// bad sky setting in WT's $volcano.map (fixed version) +sector 118 clearflags ceiling 1 diff --git a/wadsrc/static/engine/compatibility/b4ee363e9d15adc5c9becd01520acd23.mhk b/wadsrc/static/engine/compatibility/b4ee363e9d15adc5c9becd01520acd23.mhk new file mode 100644 index 000000000..8eb89748a --- /dev/null +++ b/wadsrc/static/engine/compatibility/b4ee363e9d15adc5c9becd01520acd23.mhk @@ -0,0 +1,3 @@ +// SW $outpost.map +// silence a misplaced and *very* annoying ambient sound. +sprite 442 lotag -1 diff --git a/wadsrc/static/engine/compatibility/c3bfb6a6e7cded2e5fe16cea86632d79.mhk b/wadsrc/static/engine/compatibility/c3bfb6a6e7cded2e5fe16cea86632d79.mhk new file mode 100644 index 000000000..5d24c10b7 --- /dev/null +++ b/wadsrc/static/engine/compatibility/c3bfb6a6e7cded2e5fe16cea86632d79.mhk @@ -0,0 +1,2 @@ +// CP07 sector bleeds into another area. +sector 33 split 192 196 diff --git a/wadsrc/static/engine/compatibility/d19081afa18bf00c9f151e51104358ee.mhk b/wadsrc/static/engine/compatibility/d19081afa18bf00c9f151e51104358ee.mhk new file mode 100644 index 000000000..aa907ad4d --- /dev/null +++ b/wadsrc/static/engine/compatibility/d19081afa18bf00c9f151e51104358ee.mhk @@ -0,0 +1,7 @@ +// Lunatic Fringe v1.3 + v1.5 +wall 682 blocks 755 blocks 756 blocks 757 +wall 683 blocks 755 blocks 756 blocks 757 +wall 684 blocks 755 blocks 756 blocks 757 +wall 694 blocks 739 blocks 740 blocks 741 +wall 695 blocks 739 blocks 740 blocks 741 +wall 696 blocks 739 blocks 740 blocks 741 diff --git a/wadsrc/static/engine/compatibility/dea84af1e11afd14e38a619728a5f57b.mhk b/wadsrc/static/engine/compatibility/dea84af1e11afd14e38a619728a5f57b.mhk new file mode 100644 index 000000000..f463fb0c5 --- /dev/null +++ b/wadsrc/static/engine/compatibility/dea84af1e11afd14e38a619728a5f57b.mhk @@ -0,0 +1,7 @@ +// Lunatic Fringe WT +wall 682 blocks 755 blocks 756 blocks 757 +wall 683 blocks 755 blocks 756 blocks 757 +wall 684 blocks 755 blocks 756 blocks 757 +wall 694 blocks 739 blocks 740 blocks 741 +wall 695 blocks 739 blocks 740 blocks 741 +wall 696 blocks 739 blocks 740 blocks 741 diff --git a/wadsrc/static/engine/compatibility/ef6331237eb36c84a4f7b9f5c3cd225d.mhk b/wadsrc/static/engine/compatibility/ef6331237eb36c84a4f7b9f5c3cd225d.mhk new file mode 100644 index 000000000..dd4806a2d --- /dev/null +++ b/wadsrc/static/engine/compatibility/ef6331237eb36c84a4f7b9f5c3cd225d.mhk @@ -0,0 +1,2 @@ +// flip the inverted card reader in TD's level 10. +sprite 179 clearflags 12 diff --git a/wadsrc/static/engine/grpinfo.txt b/wadsrc/static/engine/grpinfo.txt index 9f487418b..7c0bc7f73 100644 --- a/wadsrc/static/engine/grpinfo.txt +++ b/wadsrc/static/engine/grpinfo.txt @@ -249,7 +249,7 @@ grpinfo name "Duke Caribbean: Life's a Beach" size 22213819 crc DUKECB_CRC - flags GAMEFLAG_DUKE|GAMEFLAG_ADDON + flags GAMEFLAG_DUKE|GAMEFLAG_ADDON|GAMEFLAG_DUKEVACA dependency DUKE15_CRC gamefilter "Duke.Vacation" FgColor 0x00004f @@ -263,7 +263,7 @@ grpinfo scriptname "VACATION.CON" size 22397273 crc 0x65B5F690 - flags GAMEFLAG_DUKE|GAMEFLAG_ADDON + flags GAMEFLAG_DUKE|GAMEFLAG_ADDON|GAMEFLAG_DUKEVACA dependency DUKE15_CRC gamefilter "Duke.Vacation" FgColor 0x00004f @@ -277,7 +277,7 @@ grpinfo scriptname "NWINTER.CON" size 16169365 crc DUKENW_CRC - flags GAMEFLAG_DUKE|GAMEFLAG_ADDON + flags GAMEFLAG_DUKE|GAMEFLAG_ADDON|GAMEFLAG_DUKENW dependency DUKE15_CRC gamefilter "Duke.NWinter" FgColor 0 @@ -291,7 +291,7 @@ grpinfo scriptname "NWINTER.CON" size 10965909 crc 0xC7EFBFA9 - flags GAMEFLAG_DUKE|GAMEFLAG_ADDON + flags GAMEFLAG_DUKE|GAMEFLAG_ADDON|GAMEFLAG_DUKENW dependency DUKE15_CRC gamefilter "Duke.NWinter" FgColor 0 @@ -423,7 +423,7 @@ grpinfo name "BLOOD: Cryptic Passage" scriptname "CRYPTIC.INI" mustcontain "CRYPTIC.INI", "CP01.MAP", "CP02.MAP" - flags GAMEFLAG_BLOOD|GAMEFLAG_ADDON + flags GAMEFLAG_BLOOD|GAMEFLAG_ADDON|GAMEFLAG_BLOODCP dependency BLOOD_CRC loadart "CPART07.AR_", "CPART15.AR_" gamefilter "Blood.Cryptic" @@ -436,7 +436,7 @@ addon name "BLOOD: Cryptic Passage" scriptname "CRYPTIC.INI" mustcontain "CRYPTIC.INI", "CP01.MAP", "CP02.MAP" - flags GAMEFLAG_BLOOD|GAMEFLAG_ADDON + flags GAMEFLAG_BLOOD|GAMEFLAG_ADDON|GAMEFLAG_BLOODCP dependency BLOOD_CRC loadart "CPART07.AR_", "CPART15.AR_" gamefilter "Blood.Cryptic" @@ -450,7 +450,7 @@ addon scriptname "CRYPTIC.INI" mustcontain "cryptic/CRYPTIC.INI", "cryptic/CP01.MAP", "cryptic/CP02.MAP" loadgrp "cryptic" - flags GAMEFLAG_BLOOD|GAMEFLAG_ADDON + flags GAMEFLAG_BLOOD|GAMEFLAG_ADDON|GAMEFLAG_BLOODCP dependency BLOOD_CRC loadart "CPART07.AR_", "CPART15.AR_" gamefilter "Blood.Cryptic" @@ -464,7 +464,7 @@ addon scriptname "CRYPTIC.INI" mustcontain "addons/Cryptic Passage/CRYPTIC.INI", "addons/Cryptic Passage/CP01.MAP", "addons/Cryptic Passage/CP02.MAP" loadgrp "addons/Cryptic Passage" - flags GAMEFLAG_BLOOD|GAMEFLAG_ADDON + flags GAMEFLAG_BLOOD|GAMEFLAG_ADDON|GAMEFLAG_BLOODCP dependency BLOOD_CRC gamefilter "Blood.Cryptic" GameID "CrypticPassage" @@ -573,7 +573,7 @@ grpinfo grpinfo { name "Shadow Warrior: Wanton Destruction" - flags GAMEFLAG_SW|GAMEFLAG_ADDON + flags GAMEFLAG_SW|GAMEFLAG_ADDON|GAMEFLAG_SWWANTON crc SWWD_CRC size 48698128 defname "sw.def" @@ -585,7 +585,7 @@ grpinfo grpinfo { name "Shadow Warrior: Wanton Destruction" - flags GAMEFLAG_SW|GAMEFLAG_ADDON + flags GAMEFLAG_SW|GAMEFLAG_ADDON|GAMEFLAG_SWWANTON crc 0x8B6E8011 size 48698128 defname "sw.def" @@ -597,7 +597,7 @@ grpinfo grpinfo { name "Shadow Warrior: Wanton Destruction (ProAsm)" - flags GAMEFLAG_SW|GAMEFLAG_ADDON + flags GAMEFLAG_SW|GAMEFLAG_ADDON|GAMEFLAG_SWWANTON crc 0x39893EF4 size 9562689 defname "sw.def" @@ -610,7 +610,7 @@ grpinfo grpinfo { name "Shadow Warrior: Twin Dragon" - flags GAMEFLAG_SW|GAMEFLAG_ADDON + flags GAMEFLAG_SW|GAMEFLAG_ADDON|GAMEFLAG_SWTWINDRAG crc SWTD_CRC size 12499012 defname "twindrag.def" @@ -623,7 +623,7 @@ grpinfo grpinfo { name "Shadow Warrior: Twin Dragon" - flags GAMEFLAG_SW|GAMEFLAG_ADDON + flags GAMEFLAG_SW|GAMEFLAG_ADDON|GAMEFLAG_SWTWINDRAG crc 0xB5B71277 size 6236287 defname "twindrag.def" @@ -635,7 +635,7 @@ grpinfo grpinfo { name "Shadow Warrior: Twin Dragon" - flags GAMEFLAG_SW|GAMEFLAG_ADDON + flags GAMEFLAG_SW|GAMEFLAG_ADDON|GAMEFLAG_SWTWINDRAG crc 0xACC8DCDE size 6235578 defname "twindrag.def" @@ -678,7 +678,7 @@ grpinfo name "Redneck Rampage: Suckin' Grits on Route 66" scriptname "GAME66.CON" mustcontain "TILESA66.ART", "TILESB66.ART", "ROUTE66/CARNIVAL.MAP", "ROUTE66/TRUCKSTP.MAP", "GAME66.CON" - flags GAMEFLAG_RR|GAMEFLAG_ADDON + flags GAMEFLAG_RR|GAMEFLAG_ADDON|GAMEFLAG_ROUTE66 dependency RR_CRC loadart "TILESA66.ART", "TILESB66.ART" // replaces TILES009 and TILES023. gamefilter "Redneck.Route66" @@ -692,7 +692,7 @@ addon name "Redneck Rampage: Suckin' Grits on Route 66" scriptname "GAME66.CON" mustcontain "TILESA66.ART", "TILESB66.ART", "ROUTE66/CARNIVAL.MAP", "ROUTE66/TRUCKSTP.MAP", "GAME66.CON" - flags GAMEFLAG_RR|GAMEFLAG_ADDON + flags GAMEFLAG_RR|GAMEFLAG_ADDON|GAMEFLAG_ROUTE66 dependency RR_CRC loadart "TILESA66.ART", "TILESB66.ART" // replaces TILES009 and TILES023. gamefilter "Redneck.Route66" diff --git a/wadsrc/static/filter/blood/engine/rmapinfo.txt b/wadsrc/static/filter/blood/engine/rmapinfo.txt new file mode 100644 index 000000000..f05bb05c4 --- /dev/null +++ b/wadsrc/static/filter/blood/engine/rmapinfo.txt @@ -0,0 +1,21 @@ + +// Cutscene definitions for Blood + +cutscenes +{ + intro + { + function = BloodCutscenes.BuildIntro + } + loadscreen + { + function = BloodCutscenes.BuildLoading + } + +} + +gameinfo +{ + summaryscreen = BloodCutscenes.BuildSPSummary + mpsummaryscreen = BloodCutscenes.BuildMPSummary +} diff --git a/wadsrc/static/filter/duke/engine/rmapinfo.txt b/wadsrc/static/filter/duke/engine/rmapinfo.txt new file mode 100644 index 000000000..4462a9795 --- /dev/null +++ b/wadsrc/static/filter/duke/engine/rmapinfo.txt @@ -0,0 +1,74 @@ +//This sets up the missing things that cannot be inferred from the setup in the .CON files. + +cluster 1 +{ + outro + { + function = DukeCutscenes.BuildE1End + } +} + +cluster 2 +{ + outro + { + function = DukeCutscenes.BuildE2End + } + interbackground = "BONUSSCREEN2" +} + +cluster 3 +{ + outro + { + function = DukeCutscenes.BuildE3End + } +} + +cluster 4 +{ + outro + { + function = DukeCutscenes.BuildE4End + } +} + +cluster 5 +{ + outro + { + function = DukeCutscenes.BuildE5End + } +} + +map { 4, 1 } +{ + intro + { + function = DukeCutscenes.BuildE4Intro + } +} + +cutscenes +{ + intro + { + function = DukeCutscenes.BuildIntro + } + + sharewareend + { + function = DukeCutscenes.BuildSharewareEnd + } + + loadscreen + { + function = DukeCutscenes.BuildLoading + } +} + +gameinfo +{ + summaryscreen = DukeCutscenes.BuildSPSummary + mpsummaryscreen = DukeCutscenes.BuildMPSummary +} \ No newline at end of file diff --git a/wadsrc/static/filter/exhumed/engine/rmapinfo.txt b/wadsrc/static/filter/exhumed/engine/rmapinfo.txt new file mode 100644 index 000000000..5fafd97b6 --- /dev/null +++ b/wadsrc/static/filter/exhumed/engine/rmapinfo.txt @@ -0,0 +1,306 @@ + +episode LEV1 +{ + name = "Exhumed" + noskillmenu +} + +episode LEV0 +{ + name = "$TXT_EX_MAP00" + noskillmenu +} + +cluster 1 +{ + name = "Exhumed" + gameover + { + function = ExhumedCutscenes.BuildGameoverScene + } +} + +cluster 2 +{ + name = "$TXT_EX_MAP00" +} + +cluster 3 +{ + name = "Multiplayer" +} + +cluster 4 +{ + gameover + { + function = ExhumedCutscenes.BuildCinemaLose + } +} + +map lev0 "$TXT_EX_MAP00" +{ + cdtrack = 11 + ex_training + cluster = 2 +} + +map lev1 "$TXT_EX_MAP01" +{ + cdtrack = 11 + cluster = 1 + ex_ramses_cdtrack = 3 + ex_ramses_pup = "lev1.pup" + ex_ramses_text = "$TXT_EX_CINEMA1" +} + +map lev2 "$TXT_EX_MAP02" +{ + cdtrack = 12 + cluster = 1 +} + +map lev3 "$TXT_EX_MAP03" +{ + cdtrack = 13 + cluster = 1 +} + +map lev4 "$TXT_EX_MAP04" +{ + cdtrack = 14 + cluster = 1 +} + +map lev5 "$TXT_EX_MAP05" +{ + cdtrack = 15 + cluster = 1 + intro + { + function = ExhumedCutscenes.BuildCinemaBefore5 + } +} + +map lev6 "$TXT_EX_MAP06" +{ + cdtrack = 16 + cluster = 1 +} + +map lev7 "$TXT_EX_MAP07" +{ + cdtrack = 17 + cluster = 1 +} + +map lev8 "$TXT_EX_MAP08" +{ + cdtrack = 18 + cluster = 1 +} + +map lev9 "$TXT_EX_MAP09" +{ + cdtrack = 11 + cluster = 1 +} + +map lev10 "$TXT_EX_MAP10" +{ + cdtrack = 12 + cluster = 1 + outro + { + function = ExhumedCutscenes.BuildCinemaAfter10 + } +} + +map lev11 "$TXT_EX_MAP11" +{ + cdtrack = 13 + cluster = 1 + ex_ramses_cdtrack = 7 + ex_ramses_pup = "lev11.pup" + ex_ramses_text = "$TXT_EX_CINEMA5" + ex_ramses_horiz = 46 + intro + { + function = ExhumedCutscenes.BuildCinemaBefore11 + } +} + +map lev12 "$TXT_EX_MAP12" +{ + cdtrack = 14 + cluster = 1 +} + +map lev13 "$TXT_EX_MAP13" +{ + cdtrack = 15 + cluster = 1 +} + +map lev14 "$TXT_EX_MAP14" +{ + cdtrack = 16 + cluster = 1 +} + +map lev15 "$TXT_EX_MAP15" +{ + cdtrack = 17 + cluster = 1 + outro + { + function = ExhumedCutscenes.BuildCinemaAfter15 + } +} + +map lev16 "$TXT_EX_MAP16" +{ + cdtrack = 18 + cluster = 1 + ex_altsound +} + +map lev17 "$TXT_EX_MAP17" +{ + cdtrack = 11 + cluster = 1 + ex_altsound +} + +map lev18 "$TXT_EX_MAP18" +{ + cdtrack = 12 + cluster = 1 + ex_altsound +} + +map lev19 "$TXT_EX_MAP19" +{ + cdtrack = 13 + cluster = 1 + ex_altsound +} + +map lev20 "$TXT_EX_MAP20" +{ + cdtrack = 14 + cluster = 4 + next = "-" + ex_altsound + ex_countdown + intro + { + function = ExhumedCutscenes.BuildCinemaBefore20 + } + outro + { + function = ExhumedCutscenes.BuildCinemaAfter20 + } +} + +map lev21 "$TXT_EX_MAP21" +{ + cdtrack = 15 + cluster = 3 + ex_multi +} + +map lev22 "$TXT_EX_MAP22" +{ + cdtrack = 16 + cluster = 3 + ex_multi +} + +map lev23 "$TXT_EX_MAP23" +{ + cdtrack = 17 + cluster = 3 + ex_multi +} + +map lev24 "$TXT_EX_MAP24" +{ + cdtrack = 18 + cluster = 3 + ex_multi +} + +map lev25 "$TXT_EX_MAP25" +{ + cdtrack = 11 + cluster = 3 + ex_multi +} + +map lev26 "$TXT_EX_MAP26" +{ + cdtrack = 12 + cluster = 3 + ex_multi +} + +map lev27 "$TXT_EX_MAP27" +{ + cdtrack = 13 + cluster = 3 + ex_multi +} + +map lev28 "$TXT_EX_MAP28" +{ + cdtrack = 14 + cluster = 3 + ex_multi +} + +map lev29 "$TXT_EX_MAP29" +{ + cdtrack = 15 + cluster = 3 + ex_multi +} + +map lev30 "$TXT_EX_MAP30" +{ + cdtrack = 16 + cluster = 3 + ex_multi +} + +map lev31 "$TXT_EX_MAP31" +{ + cdtrack = 17 + cluster = 3 + ex_multi +} + +map lev32 "$TXT_EX_MAP32" +{ + cdtrack = 18 + cluster = 3 + ex_multi +} + +cutscenes +{ + intro + { + function = ExhumedCutscenes.BuildIntro + } + + loadscreen + { + function = DukeCutscenes.BuildLoading + } +} + +gameinfo +{ + summaryscreen = ExhumedCutscenes.BuildMap +} diff --git a/wadsrc/static/filter/redneck.redneck/engine/engine.con b/wadsrc/static/filter/redneck.redneck/engine/engine.con new file mode 100644 index 000000000..1910065e9 --- /dev/null +++ b/wadsrc/static/filter/redneck.redneck/engine/engine.con @@ -0,0 +1 @@ +definelevelname 1 7 endgame.map 02:00 02:00 $TXT_CLOSEENCOUNTERS diff --git a/wadsrc/static/filter/redneck.redneck/engine/rmapinfo.txt b/wadsrc/static/filter/redneck.redneck/engine/rmapinfo.txt new file mode 100644 index 000000000..dcbd6a8d9 --- /dev/null +++ b/wadsrc/static/filter/redneck.redneck/engine/rmapinfo.txt @@ -0,0 +1,124 @@ +// Cutscene definitions for RR + +cluster 1 +{ + outro + { + function = RRCutscenes.BuildE1End + } +} + +cluster 2 +{ + outro + { + function = RRCutscenes.BuildE2End + } +} + +map { 1, 1 } +{ + interbackground = "BONUSPIC01" + rr_startsound = 391 // fixme: allow symbolic names +} + +map { 1, 2 } +{ + interbackground = "BONUSPIC02" + rr_startsound = 64 +} + +map { 1, 3 } +{ + interbackground = "BONUSPIC03" + rr_startsound = 77 +} + +map { 1, 4 } +{ + interbackground = "BONUSPIC04" + rr_startsound = 80 +} + +map { 1, 5 } +{ + interbackground = "BONUSPIC05" + rr_startsound = 102 +} + +map { 1, 6 } +{ + interbackground = "BONUSPIC06" + rr_startsound = 103 +} + +map { 1, 7 } +{ + interbackground = "BONUSPIC07" + rr_startsound = 104 +} + +map { 2, 1 } +{ + interbackground = "BONUSPIC08" + rr_startsound = 105 +} + +map { 2, 2 } +{ + interbackground = "BONUSPIC09" + rr_startsound = 176 + clearweapons +} + +map { 2, 3 } +{ + interbackground = "BONUSPIC10" + rr_startsound = 177 + PrecacheTextures = "#03190","#03191","#03192","#03144","#03139","#03132","#03120","#03121","#03122","#03123","#03124" +} + +map { 2, 4 } +{ + interbackground = "BONUSPIC11" + rr_startsound = 198 +} + +map { 2, 5 } +{ + interbackground = "BONUSPIC12" + rr_startsound = 230 +} + +map { 2, 6 } +{ + interbackground = "BONUSPIC13" + rr_startsound = 255 +} + +map { 2, 7 } +{ + interbackground = "BONUSPIC14" + rr_startsound = 283 + PrecacheTextures = "UFO1", "UFO2", "UFO3", "UFO4", "UFO5" +} + +map { 2, 8 } +{ + interbackground = "BONUSPIC14" + rr_startsound = 391 +} + +cutscenes +{ + loadscreen + { + function = DukeCutscenes.BuildLoading // identical with Duke's + } +} + +gameinfo +{ + summaryscreen = RRCutscenes.BuildSPSummary + mpsummaryscreen = DukeCutscenes.BuildMPSummary // identical with Duke's +} diff --git a/wadsrc/static/filter/redneck.ridesagain/engine/rmapinfo.txt b/wadsrc/static/filter/redneck.ridesagain/engine/rmapinfo.txt new file mode 100644 index 000000000..bd8cda805 --- /dev/null +++ b/wadsrc/static/filter/redneck.ridesagain/engine/rmapinfo.txt @@ -0,0 +1,120 @@ +// Cutscene definitions for RR + +cluster 2 +{ + outro + { + function = RRCutscenes.BuildRAE2End + } +} + +map { 1, 1 } +{ + interbackground = "LEVELMAP01" + rr_startsound = 63 +} + +map { 1, 2 } +{ + interbackground = "LEVELMAP02" + rr_startsound = 64 +} + +map { 1, 3 } +{ + interbackground = "LEVELMAP03" + rr_startsound = 77 + clearweapons +} + +map { 1, 4 } +{ + interbackground = "LEVELMAP04" + rr_startsound = 80 + rr_mamaspawn = 5 +} + +map { 1, 5 } +{ + interbackground = "LEVELMAP05" + rr_startsound = 102 + PrecacheTextures = "#02577" +} + +map { 1, 6 } +{ + interbackground = "LEVELMAP06" + rr_startsound = 103 +} + +map { 1, 7 } +{ + interbackground = "LEVELMAP07" + rr_startsound = 104 +} + +map { 2, 1 } +{ + interbackground = "LEVELMAP08" + rr_startsound = 105 +} + +map { 2, 2 } +{ + interbackground = "LEVELMAP09" + rr_startsound = 176 + clearweapons +} + +map { 2, 3 } +{ + interbackground = "LEVELMAP10" + rr_startsound = 177 + rr_mamaspawn = 10 +} + +map { 2, 4 } +{ + interbackground = "LEVELMAP11" + rr_startsound = 198 +} + +map { 2, 5 } +{ + interbackground = "LEVELMAP12" + rr_startsound = 230 + rr_clearmoonshine +} + +map { 2, 6 } +{ + interbackground = "LEVELMAP13" + rr_startsound = 255 +} + +map { 2, 7 } +{ + interbackground = "LEVELMAP14" + rr_startsound = 283 + rrra_hulkspawn + PrecacheTextures = "UFO1_RRRA", "UFO2", "UFO3", "UFO4", "UFO5" +} + +cutscenes +{ + loadscreen + { + function = DukeCutscenes.BuildLoading // identical with Duke's + } + defaultmapintro + { + function = RRCutscenes.BuildMapIntro // this plays the 'travel' animation. + transitiononly + } +} + +gameinfo +{ + summaryscreen = RRCutscenes.BuildSPSummary + mpsummaryscreen = DukeCutscenes.BuildMPSummary // identical with Duke's +} diff --git a/wadsrc/static/filter/redneck.route66/engine/rmapinfo.txt b/wadsrc/static/filter/redneck.route66/engine/rmapinfo.txt new file mode 100644 index 000000000..59246e710 --- /dev/null +++ b/wadsrc/static/filter/redneck.route66/engine/rmapinfo.txt @@ -0,0 +1,109 @@ +// Cutscene definitions for RRR66. This is a little bit different than the main game. + +cluster 2 +{ + outro + { + function = RRCutscenes.BuildE2End + } +} + +map { 1, 1 } +{ + interbackground = "BONUSPIC01" + rr_startsound = 391 // fixme: allow symbolic names +} + +map { 1, 2 } +{ + interbackground = "BONUSPIC02" + rr_startsound = 64 +} + +map { 1, 3 } +{ + interbackground = "BONUSPIC03" + rr_startsound = 77 +} + +map { 1, 4 } +{ + interbackground = "BONUSPIC04" + rr_startsound = 80 +} + +map { 1, 5 } +{ + interbackground = "BONUSPIC05" + rr_startsound = 102 +} + +map { 1, 6 } +{ + interbackground = "BONUSPIC06" + rr_startsound = 103 +} + +map { 1, 7 } +{ + interbackground = "BONUSPIC07" + rr_startsound = 104 +} + +map { 2, 1 } +{ + interbackground = "BONUSPIC08" + rr_startsound = 105 +} + +map { 2, 2 } +{ + interbackground = "BONUSPIC09" + rr_startsound = 176 + clearweapons +} + +map { 2, 3 } +{ + interbackground = "BONUSPIC10" + rr_startsound = 177 +} + +map { 2, 4 } +{ + interbackground = "BONUSPIC11" + rr_startsound = 198 +} + +map { 2, 5 } +{ + interbackground = "BONUSPIC12" + rr_startsound = 230 +} + +map { 2, 6 } +{ + interbackground = "BONUSPIC13" + rr_startsound = 255 +} + +map { 2, 7 } +{ + interbackground = "BONUSPIC14" + rr_startsound = 283 + PrecacheTextures = "UFO1", "UFO2", "UFO3", "UFO4", "UFO5" +} + +cutscenes +{ + loadscreen + { + function = DukeCutscenes.BuildLoading // identical with Duke's + } +} + +gameinfo +{ + summaryscreen = RRCutscenes.BuildSPSummary + mpsummaryscreen = DukeCutscenes.BuildMPSummary // identical with Duke's +} diff --git a/wadsrc/static/filter/shadowwarrior.twindragon/rmapinfo.txt b/wadsrc/static/filter/shadowwarrior.twindragon/rmapinfo.txt new file mode 100644 index 000000000..babfd9e11 --- /dev/null +++ b/wadsrc/static/filter/shadowwarrior.twindragon/rmapinfo.txt @@ -0,0 +1,5 @@ + +map { 9 } +{ + sw_spawnmines +} \ No newline at end of file diff --git a/wadsrc/static/filter/shadowwarrior/engine/rmapinfo.txt b/wadsrc/static/filter/shadowwarrior/engine/rmapinfo.txt new file mode 100644 index 000000000..877cda926 --- /dev/null +++ b/wadsrc/static/filter/shadowwarrior/engine/rmapinfo.txt @@ -0,0 +1,63 @@ +// Cutscene definitions for SW + +Cutscenes +{ + intro + { + function = SWCutscenes.BuildIntro + } + sharewareend + { + function = SWCutscenes.BuildSybexScreen + } + + loadscreen + { + function = SWCutscenes.BuildLoading + } +} + +map { 4 } +{ + outro + { + function = SWCutscenes.BuildSerpentAnim + } + sw_bossmeter_serpent + sw_deathexit_serpent +} + +map { 5 } +{ + sw_bossmeter_serpent +} + +map { 11 } +{ + outro + { + function = SWCutscenes.BuildSumoAnim + } + sw_bossmeter_sumo + sw_deathexit_sumo +} + +map { 20 } +{ + outro + { + function = SWCutscenes.BuildZillaAnim + } + sw_bossmeter_serpent + sw_bossmeter_sumo + sw_bossmeter_zilla + sw_deathexit_zilla + next = "-" +} + +gameinfo +{ + summaryscreen = SWCutscenes.BuildSPSummary + mpsummaryscreen = SWCutscenes.BuildMPSummary +} + diff --git a/wadsrc/static/glstuff/mirror.png b/wadsrc/static/glstuff/mirror.png new file mode 100644 index 000000000..08a6abcbe Binary files /dev/null and b/wadsrc/static/glstuff/mirror.png differ diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index f19c4ccea..61f048173 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -66,9 +66,9 @@ LISTMENU "MainMenu" Position 160, 65 linespacing 22 ExhumedPlasma - ExhumedTextItem "$MNU_NEWGAME", "n", "StartGameNoSkill", 1 + ExhumedTextItem "$MNU_NEWGAME", "n", "StartGameNoSkill", 0 ExhumedTextItem "$MNU_LOADGAME", "l", "LoadGameMenu" - ExhumedTextItem "$TXT_EX_MAP00", "m", "StartGameNoSkill", 0 + ExhumedTextItem "$TXT_EX_MAP00", "m", "StartGameNoSkill", 1 ExhumedTextItem "$MNU_OPTIONS", "v", "OptionsMenu" ExhumedTextItem "$MNU_QUITGAME", "q", "QuitMenu" } @@ -137,9 +137,9 @@ LISTMENU "IngameMenu" Position 160, 65 linespacing 22 ExhumedLogo - ExhumedTextItem "$MNU_NEWGAME", "n", "StartGameNoSkill", 1 + ExhumedTextItem "$MNU_NEWGAME", "n", "StartGameNoSkill", 0 ExhumedTextItem "$MNU_LOADGAME", "l", "LoadGameMenu" - ExhumedTextItem "$TXT_EX_MAP00", "m", "StartGameNoSkill", 0 + ExhumedTextItem "$TXT_EX_MAP00", "m", "StartGameNoSkill", 1 ExhumedTextItem "$MNU_OPTIONS", "v", "OptionsMenu" ExhumedTextItem "$MNU_QUITGAME", "q", "QuitMenu" } @@ -1020,7 +1020,7 @@ OptionMenu "VideoOptions" protected Title "$DSPLYMNU_TITLE" Submenu "$OPTMNU_HUD", "HUDOptions" - Submenu "$OPTMNU_POLYMOST", "PolymostOptions" + Submenu "$GLMNU_TITLE", "RenderOptions" Submenu "$AUTOMAPMNU_TITLE", "AutomapOptions" Submenu "$GLMNU_POSTPROCESS", "PostProcessMenu" StaticText "" @@ -1034,6 +1034,7 @@ OptionMenu "VideoOptions" protected Option "$DSPLYMNU_VOXELS", "r_voxels", "OnOff" Option "$DSPLYMNU_SHADOWS", "r_shadows", "OnOff" Option "$DSPLYMNU_SLOPETILT", "cl_slopetilting", "OnOff" + Option "$GLPREFMNU_ENVIRONMENTMAPMIRROR", gl_mirror_envmap, "OnOff" ifnotgame(Blood, Exhumed) { Option "$DSPLYMNU_VIEWBOB", "cl_viewbob", "OnOff" @@ -1189,20 +1190,20 @@ OptionMenu "HUDOptions" protected //------------------------------------------------------------------------------------------- // -// Polymost options +// Render options // //------------------------------------------------------------------------------------------- -OptionMenu "PolymostOptions" +OptionMenu "RenderOptions" { - Title "Polymost Options" - Option "$POLYMOST_TC", "hw_hightile", "OnOff" - Option "$POLYMOST_CACHE", "r_precache", "OnOff" + Title "Render Options" + Option "$RENDER_TC", "hw_hightile", "OnOff" + Option "$RENDER_CACHE", "r_precache", "OnOff" StaticText "" - Option "$POLYMOST_PALETTEEMU", "hw_useindexedcolortextures", "OnOff" - Option "$POLYMOST_PALINTER", "hw_shadeinterpolate", "OnOff", "hw_useindexedcolortextures" + Option "$RENDER_PALETTEEMU", "hw_useindexedcolortextures", "OnOff" + Option "$RENDER_PALINTER", "hw_shadeinterpolate", "OnOff", "hw_useindexedcolortextures" StaticText "" - Option "$POLYMOST_MODELS", "hw_models", "OnOff" + Option "$RENDER_MODELS", "hw_models", "OnOff" } //------------------------------------------------------------------------------------------- diff --git a/wadsrc/static/zscript.txt b/wadsrc/static/zscript.txt index da000f758..262e72b63 100644 --- a/wadsrc/static/zscript.txt +++ b/wadsrc/static/zscript.txt @@ -28,7 +28,17 @@ version "4.3" #include "zscript/constants.zs" #include "zscript/razebase.zs" +#include "zscript/screenjob.zs" +#include "zscript/games/duke/dukegame.zs" +#include "zscript/games/duke/ui/screens.zs" +#include "zscript/games/duke/ui/cutscenes.zs" #include "zscript/games/duke/ui/menu.zs" +#include "zscript/games/blood/bloodgame.zs" #include "zscript/games/blood/ui/menu.zs" +#include "zscript/games/blood/ui/screens.zs" +#include "zscript/games/sw/swgame.zs" #include "zscript/games/sw/ui/menu.zs" +#include "zscript/games/sw/ui/screens.zs" +#include "zscript/games/exhumed/exhumedgame.zs" #include "zscript/games/exhumed/ui/menu.zs" +#include "zscript/games/exhumed/ui/screens.zs" diff --git a/wadsrc/static/zscript/engine/base.zs b/wadsrc/static/zscript/engine/base.zs index a0b2a0c83..21115a193 100644 --- a/wadsrc/static/zscript/engine/base.zs +++ b/wadsrc/static/zscript/engine/base.zs @@ -187,6 +187,8 @@ struct MusPlayingInfo native native String name; native int baseorder; native bool loop; + native voidptr handle; + }; struct TexMan @@ -218,7 +220,9 @@ struct TexMan AllowSkins = 8, ShortNameOnly = 16, DontCreate = 32, - Localize = 64 + Localize = 64, + ForceLookup = 128, + NoAlias = 256 }; enum ETexReplaceFlags @@ -239,6 +243,7 @@ struct TexMan native static Vector2 GetScaledOffset(TextureID tex); native static int CheckRealHeight(TextureID tex); native static bool OkForLocalization(TextureID patch, String textSubstitute); + native static bool UseGamePalette(TextureID tex); } enum EScaleMode @@ -402,6 +407,8 @@ struct Screen native native static int, int, int, int GetViewWindow(); native static double, double, double, double GetFullscreenRect(double vwidth, double vheight, int fsmode); native static Vector2 SetOffset(double x, double y); + native static void ClearScreen(color col = 0); + native static void SetScreenFade(double factor); } struct Font native @@ -657,6 +664,7 @@ struct StringStruct native native void DeleteLastCharacter(); native int CodePointCount() const; native int, int GetNextCodePoint(int position) const; + native void Substitute(String str, String replace); } struct Translation version("2.4") diff --git a/wadsrc/static/zscript/engine/dynarrays.zs b/wadsrc/static/zscript/engine/dynarrays.zs index 2e80820cd..1db4a6224 100644 --- a/wadsrc/static/zscript/engine/dynarrays.zs +++ b/wadsrc/static/zscript/engine/dynarrays.zs @@ -50,6 +50,7 @@ struct DynArray_I32 native native void Append (DynArray_I32 other); native uint Find(int item) const; native uint Push (int item); + native vararg uint PushV (int item, ...); native bool Pop (); native void Delete (uint index, int deletecount = 1); native void Insert (uint index, int item); diff --git a/wadsrc/static/zscript/engine/inputevents.zs b/wadsrc/static/zscript/engine/inputevents.zs index 95a6d29ab..ba63b59e9 100644 --- a/wadsrc/static/zscript/engine/inputevents.zs +++ b/wadsrc/static/zscript/engine/inputevents.zs @@ -120,6 +120,21 @@ struct InputEvent native play version("2.4") Key_F12 = 0x58, // DIK_F12 Key_Grave = 0x29, // DIK_GRAVE + KEY_kpad_1 = 0x4f, + KEY_kpad_2 = 0x50, + KEY_kpad_3 = 0x51, + KEY_kpad_4 = 0x4b, + KEY_kpad_5 = 0x4c, + KEY_kpad_6 = 0x4d, + KEY_kpad_7 = 0x47, + KEY_kpad_8 = 0x48, + KEY_kpad_9 = 0x49, + KEY_kpad_0 = 0x52, + KEY_kpad_Minus = 0x4a, + KEY_kpad_Plus = 0x4e, + KEY_kpad_Period = 0x53, + + Key_Backspace = 0x0e, // DIK_BACK Key_Equals = 0x0d, // DIK_EQUALS @@ -140,6 +155,9 @@ struct InputEvent native play version("2.4") Key_PgUp = 0xc9, // DIK_PRIOR Key_PgDn = 0xd1, // DIK_NEXT + KEY_VOLUMEDOWN = 0xAE, // DIK_VOLUMEDOWN + KEY_VOLUMEUP = 0xB0, // DIK_VOLUMEUP + Key_Mouse1 = 0x100, Key_Mouse2 = 0x101, Key_Mouse3 = 0x102, diff --git a/wadsrc/static/zscript/engine/ui/menu/menu.zs b/wadsrc/static/zscript/engine/ui/menu/menu.zs index b40c10f0c..8e08870aa 100644 --- a/wadsrc/static/zscript/engine/ui/menu/menu.zs +++ b/wadsrc/static/zscript/engine/ui/menu/menu.zs @@ -40,6 +40,7 @@ struct KeyBindings native version("2.4") native int, int GetKeysForCommand(String cmd); native void GetAllKeysForCommand(out array list, String cmd); + native String GetBinding(int key); native void SetBind(int key, String cmd); native void UnbindACommand (String str); diff --git a/wadsrc/static/zscript/engine/ui/menu/optionmenuitems.zs b/wadsrc/static/zscript/engine/ui/menu/optionmenuitems.zs index bde6114d1..7ece14176 100644 --- a/wadsrc/static/zscript/engine/ui/menu/optionmenuitems.zs +++ b/wadsrc/static/zscript/engine/ui/menu/optionmenuitems.zs @@ -722,7 +722,7 @@ class OptionMenuSliderBase : OptionMenuItem { } - bool IsGrayed(void) + virtual bool IsGrayed(void) { return mGrayCheck != NULL && !mGrayCheck.GetInt(); } diff --git a/wadsrc/static/zscript/engine/ui/statusbar/statusbarcore.zs b/wadsrc/static/zscript/engine/ui/statusbar/statusbarcore.zs index b405bf95c..2300f33c5 100644 --- a/wadsrc/static/zscript/engine/ui/statusbar/statusbarcore.zs +++ b/wadsrc/static/zscript/engine/ui/statusbar/statusbarcore.zs @@ -105,9 +105,9 @@ class StatusBarCore native ui native static String FormatNumber(int number, int minsize = 0, int maxsize = 0, int format = 0, String prefix = ""); native double, double, double, double StatusbarToRealCoords(double x, double y=0, double w=0, double h=0); - native void DrawTexture(TextureID texture, Vector2 pos, int flags = 0, double Alpha = 1., Vector2 box = (-1, -1), Vector2 scale = (1, 1)); - native void DrawImage(String texture, Vector2 pos, int flags = 0, double Alpha = 1., Vector2 box = (-1, -1), Vector2 scale = (1, 1)); - native void DrawString(HUDFont font, String string, Vector2 pos, int flags = 0, int translation = Font.CR_UNTRANSLATED, double Alpha = 1., int wrapwidth = -1, int linespacing = 4, Vector2 scale = (1, 1), int pt = 0); + native void DrawTexture(TextureID texture, Vector2 pos, int flags = 0, double Alpha = 1., Vector2 box = (-1, -1), Vector2 scale = (1, 1), ERenderStyle style = STYLE_Translucent); + native void DrawImage(String texture, Vector2 pos, int flags = 0, double Alpha = 1., Vector2 box = (-1, -1), Vector2 scale = (1, 1), ERenderStyle style = STYLE_Translucent); + native void DrawString(HUDFont font, String string, Vector2 pos, int flags = 0, int translation = Font.CR_UNTRANSLATED, double Alpha = 1., int wrapwidth = -1, int linespacing = 4, Vector2 scale = (1, 1), int pt = 0, ERenderStyle style = STYLE_Translucent); native double, double, double, double TransformRect(double x, double y, double w, double h, int flags = 0); native void Fill(Color col, double x, double y, double w, double h, int flags = 0); native void SetClipRect(double x, double y, double w, double h, int flags = 0); diff --git a/wadsrc/static/zscript/games/blood/bloodgame.zs b/wadsrc/static/zscript/games/blood/bloodgame.zs new file mode 100644 index 000000000..839b2f6cb --- /dev/null +++ b/wadsrc/static/zscript/games/blood/bloodgame.zs @@ -0,0 +1,8 @@ +// contains all global Blood definitions +struct Blood native +{ + native static void PlayIntroMusic(); + native static bool OriginalLoadScreen(); // doing it generically would necessitate exporting the tile manage which we do not want. + native static void sndStartSample(int resid, int volume, int channel, bool loop = false, int chanflags = 0); + native static void sndStartSampleNamed(String sname, int volume, int channel); +} diff --git a/wadsrc/static/zscript/games/blood/ui/menu.zs b/wadsrc/static/zscript/games/blood/ui/menu.zs index 53035e7af..72664e9d6 100644 --- a/wadsrc/static/zscript/games/blood/ui/menu.zs +++ b/wadsrc/static/zscript/games/blood/ui/menu.zs @@ -9,25 +9,7 @@ class BloodMenuDelegate : RazeMenuDelegate { override int DrawCaption(String title, Font fnt, int y, bool drawit) { - let font = generic_ui? NewConsoleFont : BigFont; // this ignores the passed font intentionally. - let texid = TexMan.CheckForTexture("MENUBAR"); - let texsize = TexMan.GetScaledSize(texid); - let fonth = font.GetGlyphHeight("A"); - if (drawit) - { - int width = font.StringWidth(title); - if (texid.isValid()) - { - double scalex = 1.; // Expand the box if the text is longer - if (texsize.X - 10 < width) scalex = width / (texsize.X - 10); - screen.DrawTexture(texid, false, 160, 20, DTA_FullscreenScale, FSMode_Fit320x200Top, DTA_CenterOffsetRel, true, DTA_ScaleX, scalex); - } - screen.DrawText(font, Font.CR_UNDEFINED, 160 - width / 2, 20 - fonth / 2, title, DTA_FullscreenScale, FSMode_Fit320x200Top); - } - double fx, fy, fw, fh; - [fx, fy, fw, fh] = Screen.GetFullscreenRect(320, 200, FSMode_ScaleToFit43Top); - int h = texid.isValid() && texsize.Y < 40? texsize.Y : fonth; - return int((y+h) * fh / 200); // This must be the covered height of the header in true pixels. + return BloodScreen.DrawCaption(title, y, drawit); // this ignores the passed font intentionally. } override bool DrawSelector(ListMenuDescriptor desc) @@ -85,7 +67,7 @@ class ListMenuItemBloodTextItem : ListMenuItemTextItem if (selected) shade = 32 - ((MSTime() * 120 / 1000) & 63); Screen.DrawText(gamefont, Font.CR_UNDEFINED, xpos+1, mYpos+1, mText, DTA_Color, 0xff000000, DTA_FullscreenScale, FSMode_Fit320x200); - Screen.DrawText(gamefont, Font.CR_UNDEFINED, xpos, mYpos, mText, DTA_TranslationIndex, trans, DTA_Color, Build.shadeToLight(shade), DTA_FullscreenScale, FSMode_Fit320x200); + Screen.DrawText(gamefont, Font.CR_UNDEFINED, xpos, mYpos, mText, DTA_TranslationIndex, trans, DTA_Color, Raze.shadeToLight(shade), DTA_FullscreenScale, FSMode_Fit320x200); } } diff --git a/wadsrc/static/zscript/games/blood/ui/screens.zs b/wadsrc/static/zscript/games/blood/ui/screens.zs new file mode 100644 index 000000000..ba0b38585 --- /dev/null +++ b/wadsrc/static/zscript/games/blood/ui/screens.zs @@ -0,0 +1,370 @@ +//------------------------------------------------------------------------- +/* +Copyright (C) 2010-2019 EDuke32 developers and contributors +Copyright (C) 2019 Nuke.YKT +Copyright (C) 2019-2021 Christoph Oelckers + +This file is part of Raze. + +NBlood is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License version 2 +as published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +//------------------------------------------------------------------------- + + +class BloodIntroImage : ImageScreen +{ + bool mus; + + ScreenJob Init(String texture, int flags = 0, bool withmusic = false) + { + Super.InitNamed(texture, flags); + mus = withmusic; + return self; + } + + override void Start() + { + Blood.sndStartSampleNamed("THUNDER2", 128, -1); + if (mus) Blood.PlayIntroMusic(); // this is script defined. + } +} + +struct BloodScreen +{ + enum EConstants + { + kLoadScreenWideBackWidth = 256, + kLoadScreenWideSideWidth = 128, + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void DrawBackground() + { + if (Blood.OriginalLoadScreen() && Screen.GetAspectRatio() >= 1.34) + { + int width = screen.GetWidth() * 240 / screen.GetHeight(); + int nCount = (width + kLoadScreenWideBackWidth - 1) / kLoadScreenWideBackWidth; + for (int i = 0; i < nCount; i++) + { + Screen.DrawTexture(TexMan.CheckForTexture("LoadScreenWideBack"), false, (i * kLoadScreenWideBackWidth), 0, DTA_VirtualWidth, width, DTA_VirtualHeight, 200, DTA_KeepRatio, true); + } + Screen.DrawTexture(TexMan.CheckForTexture("LoadScreenWideLeft"), false, 0, 0, DTA_VirtualWidth, width, DTA_VirtualHeight, 200, DTA_KeepRatio, true, DTA_TopLeft, true); + let texid = TexMan.CheckForTexture("LoadScreenWideRight"); + let size = TexMan.GetScaledSize(texid); + Screen.DrawTexture(texid, false, width - size.x, 0, DTA_TopLeft, true, DTA_VirtualWidth, width, DTA_VirtualHeight, 200, DTA_KeepRatio, true); + texid = TexMan.CheckForTexture("LoadScreenWideMiddle"); + size = TexMan.GetScaledSize(texid); + Screen.DrawTexture(texid, false, (width - size.x) / 2, 0, DTA_TopLeft, true, DTA_VirtualWidth, width, DTA_VirtualHeight, 200, DTA_KeepRatio, true); + } + else + { + Screen.DrawTexture(TexMan.CheckForTexture("LoadScreen"), false, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43); + } + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static int DrawCaption(String title, int y, bool drawit) + { + let font = generic_ui? NewConsoleFont : BigFont; + let texid = TexMan.CheckForTexture("MENUBAR"); + let texsize = TexMan.GetScaledSize(texid); + let fonth = font.GetGlyphHeight("A"); + if (drawit) + { + int width = font.StringWidth(title); + if (texid.isValid()) + { + double scalex = 1.; // Expand the box if the text is longer + if (texsize.X - 10 < width) scalex = width / (texsize.X - 10); + screen.DrawTexture(texid, false, 160, 20, DTA_FullscreenScale, FSMode_Fit320x200Top, DTA_CenterOffsetRel, true, DTA_ScaleX, scalex); + } + screen.DrawText(font, Font.CR_UNDEFINED, 160 - width / 2, 20 - fonth / 2, title, DTA_FullscreenScale, FSMode_Fit320x200Top); + } + double fx, fy, fw, fh; + [fx, fy, fw, fh] = Screen.GetFullscreenRect(320, 200, FSMode_ScaleToFit43Top); + int h = texid.isValid() && texsize.Y < 40? texsize.Y : fonth; + return int((y+h) * fh / 200); // This must be the covered height of the header in true pixels. + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void DrawText(Font pFont, String pString, int x, int y, int position = 0, int nShade = 0, int nPalette = 0, bool shadow = true, float alpha = 1.) + { + if (position > 0) x -= pFont.StringWidth(pString) * position / 2; + if (shadow) Screen.DrawText(pFont, Font.CR_UNDEFINED, x+1, y+1, pString, DTA_FullscreenScale, FSMode_Fit320x200, DTA_Color, 0xff000000, DTA_Alpha, 0.5); + Screen.DrawText(pFont, Font.CR_UNDEFINED, x, y, pString, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TranslationIndex, Translation.MakeID(Translation_Remap, nPalette), + DTA_Color, Raze.shadeToLight(nShade), DTA_Alpha, alpha); + + } + +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class BloodSummaryScreen : SummaryScreenBase +{ + ScreenJob Init(MapRecord map, SummaryInfo info) + { + Super.Init(fadein|fadeout); + SetParameters(map, info); + return self; + } + + override void Start() + { + Blood.sndStartSample(268, 128, -1, false, CHANF_UI); + } + + override bool OnEvent(InputEvent ev) + { + if (ev.type == InputEvent.Type_KeyDown && !Raze.specialKeyEvent(ev)) + { + jobstate = skipped; + return true; + } + return false; + } + + void DrawKills() + { + String pBuffer; + BloodScreen.DrawText(BigFont, Stringtable.Localize("$KILLS") .. ":", 75, 50); + pBuffer = String.Format("%2d", stats.Kills); + BloodScreen.DrawText(BigFont, pBuffer, 160, 50); + BloodScreen.DrawText(BigFont, "$OF", 190, 50); + pBuffer = String.Format( "%2d", stats.MaxKills); + BloodScreen.DrawText(BigFont, pBuffer, 220, 50); + } + + void DrawSecrets() + { + String pBuffer; + BloodScreen.DrawText(BigFont, StringTable.Localize("$TXT_SECRETS") .. ":", 75, 70); + pBuffer = String.Format( "%2d", stats.secrets); + BloodScreen.DrawText(BigFont, pBuffer, 160, 70); + BloodScreen.DrawText(BigFont, "$OF", 190, 70); + pBuffer = String.Format( "%2d", stats.maxsecrets); + BloodScreen.DrawText(BigFont, pBuffer, 220, 70); + if (stats.SuperSecrets > 0) BloodScreen.DrawText(BigFont, "$TXT_SUPERSECRET", 160, 100, 1, 0, 2); + } + + override void Draw(double sm) + { + BloodScreen.DrawBackground(); + BloodScreen.DrawCaption("$TXTB_LEVELSTATS", 0, true); + if (stats.cheated) + { + let text = StringTable.Localize("$TXTB_CHEATED"); + let font = SmallFont2.CanPrint(text)? SmallFont2 : SmallFont; + BloodScreen.DrawText(font, text, 160, 32, 1, shadow:font == SmallFont2); + } + DrawKills(); + DrawSecrets(); + + int myclock = ticks * 120 / GameTicRate; + if ((myclock & 32)) + { + let text = StringTable.Localize("$PRESSKEY"); + let font = SmallFont2.CanPrint(text)? SmallFont2 : SmallFont; + BloodScreen.DrawText(font, text, 160, 134, 1, shadow:font == SmallFont2); + } + } + + override void OnDestroy() + { + Raze.StopAllSounds(); + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class BloodMPSummaryScreen : SkippableScreenJob +{ + int numplayers; + ScreenJob Init(int numplayer) + { + Super.Init(fadein|fadeout); + numplayers = numplayer; + return self; + } + + override void Start() + { + Blood.sndStartSample(268, 128, -1, false, CHANF_UI); + } + + void DrawKills() + { + String pBuffer; + BloodScreen.DrawText(SmallFont2, "#", 85, 35); + BloodScreen.DrawText(SmallFont2, "$NAME", 100, 35); + BloodScreen.DrawText(SmallFont2, "$FRAGS", 210, 35); + + for (int i = 0; i < numplayers; i++) + { + pBuffer = String.Format( "%-2d", i); + BloodScreen.DrawText(SmallFont2, pBuffer, 85, 50 + 8 * i); + pBuffer = String.Format( "%s", Raze.PlayerName(i)); + BloodScreen.DrawText(SmallFont2, pBuffer, 100, 50 + 8 * i); + pBuffer = String.Format( "%d", Raze.playerFrags(i, -1)); + BloodScreen.DrawText(SmallFont2, pBuffer, 210, 50 + 8 * i); + } + } + + override void Draw(double sr) + { + BloodScreen.DrawBackground(); + BloodScreen.DrawCaption("$TXTB_FRAGSTATS", 0, true); + DrawKills(); + + int myclock = ticks * 120 / GameTicRate; + if ((myclock & 32)) + { + let text = StringTable.Localize("$PRESSKEY"); + let font = SmallFont2.CanPrint(text)? SmallFont2 : SmallFont; + BloodScreen.DrawText(font, text, 160, 134, 1, shadow:font == SmallFont2); + } + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class BloodLoadScreen : ScreenJob +{ + String loadtext; + MapRecord rec; + + ScreenJob Init(MapRecord maprec) + { + Super.Init(fadein); + + /*if (gGameOptions.nGameType == 0)*/ loadtext = "$TXTB_LLEVEL"; + //else loadText = String.Format("$TXTB_NETGT%d", gGameOptions.nGameType)); + + rec = maprec; + return self; + } + + override void OnTick() + { + if (fadestate == visible) jobstate = finished; + } + + override void Draw(double sr) + { + BloodScreen.DrawBackground(); + BloodScreen.DrawCaption(loadtext, 0, true); + BloodScreen.DrawText(BigFont, rec.DisplayName(), 160, 50, 1); + + let text = StringTable.Localize("$TXTB_PLSWAIT"); + let font = SmallFont2.CanPrint(text)? SmallFont2 : SmallFont; + BloodScreen.DrawText(font, text, 160, 134, 1, shadow:font == SmallFont2); + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class BloodCutscenes +{ + static void BuildIntro(ScreenJobRunner runner) + { + if (!userConfig.nologo) + { + Array soundinfo; + if (Wads.CheckNumForFullName("logo.smk") != -1) + { + String s = "logo.wav"; // sound name must be converted at run-time, not compile time! + runner.Append(MoviePlayerJob.CreateWithSound("logo.smk", s, MoviePlayer.FIXEDVIEWPORT)); + } + else + { + runner.Append(new("BloodIntroImage").Init("MonolithScreen", ScreenJob.fadein|ScreenJob.fadeout)); + } + if (Wads.CheckNumForFullName("gti.smk") != -1) + { + String s = "gt.wav"; + runner.Append(MoviePlayerJob.CreateWithSound("gti.smk", s, MoviePlayer.FIXEDVIEWPORT)); + } + else + { + runner.Append(new("BloodIntroImage").Init("GTIScreen", ScreenJob.fadein|ScreenJob.fadeout)); + } + } + runner.Append(new("BloodIntroImage").Init("Titlescreen", ScreenJob.fadein, true)); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildMPSummary(ScreenJobRunner runner, MapRecord map, SummaryInfo stats) + { + runner.Append(new("BloodMPSummaryScreen").Init(stats.playercount)); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildSPSummary(ScreenJobRunner runner, MapRecord map, SummaryInfo stats) + { + runner.Append(new("BloodSummaryScreen").Init(map, stats)); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildLoading(ScreenJobRunner runner, MapRecord map) + { + runner.Append(new("BloodLoadScreen").Init(map)); + } +} diff --git a/wadsrc/static/zscript/games/duke/dukegame.zs b/wadsrc/static/zscript/games/duke/dukegame.zs new file mode 100644 index 000000000..ef06f96bd --- /dev/null +++ b/wadsrc/static/zscript/games/duke/dukegame.zs @@ -0,0 +1,1033 @@ +//------------------------------------------------------------------------- +/* +Copyright (C) 1996, 2003 - 3D Realms Entertainment +Copyright (C) 2020-2021 Christoph Oelckers + +This file is part of Raze. + +Duke Nukem 3D is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +aint with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Original Source: 1996 - Todd Replogle +Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms +( not much left of the original code, though... ;) ) +*/ +//------------------------------------------------------------------------- + +// contains all global Duke definitions +struct Duke native +{ + enum ESpecialMusic + { + MUS_INTRO = 0, + MUS_BRIEFING = 1, + MUS_LOADING = 2, + }; + + enum EPalette + { + BASEPAL = 0, + WATERPAL, + SLIMEPAL, + TITLEPAL, + DREALMSPAL, + ENDINGPAL, // 5 + ANIMPAL, // not used anymore. The anim code now generates true color textures. + DRUGPAL, + BASEPALCOUNT + }; + + native static void PlaySpecialMusic(int which); + native static int PlaySound(int num, int channel = CHAN_AUTO, int flags = 0, float vol =0.8f); + native static void StopSound(int num); + native static bool CheckSoundPlaying(int num); + + static void PlayBonusMusic() + { + if (Raze.MusicEnabled()) + PlaySound(DukeSnd.BONUSMUSIC, CHAN_AUTO, CHANF_UI); + } + + //========================================================================== + // + // wrappers around DrawText to allow easier reuse of the old code. + // The vertical displacements are to have the same positioning as with the original code. + // + //========================================================================== + + static void BigText(double x, double y, String text, int align = -1, double alpha = 1.) + { + + if (!Raze.isRR()) + { + if (align != -1) x -= BigFont.StringWidth(text) * (align == 0 ? 0.5 : 1); + Screen.DrawText(BigFont, Font.CR_UNTRANSLATED, x, y - 12, text, DTA_FullscreenScale, FSMode_Fit320x200, DTA_Alpha, alpha); + } + else + { + if (align != -1) x -= BigFont.StringWidth(text) * (align == 0 ? 0.2 : 0.4); + Screen.DrawText(BigFont, Font.CR_UNTRANSLATED, x, y - 12, text, DTA_FullscreenScale, FSMode_Fit320x200, DTA_ScaleX, 0.4, DTA_ScaleY, 0.4, DTA_Alpha, alpha); + } + } + + static void GameText(double x, double y, String t, int shade, int align = -1, int trans = 0) + { + int fsmode = FSMode_Fit320x200; + if (Raze.isRR()) + { + x *= 2; + y *= 2; + fsmode = FSMode_Fit640x400; + } + if (align != -1) x -= SmallFont.StringWidth(t) * (align == 0 ? 0.5 : 1); + Screen.DrawText(SmallFont, Font.CR_UNDEFINED, x, y + 2, t, DTA_FullscreenScale, fsmode, DTA_TranslationIndex, Translation.MakeID(Translation_Remap, trans), DTA_Color, Raze.shadeToLight(shade)); + } + + static void MiniText(double x, double y, String t, int shade, int align = -1, int trans = 0) + { + int fsmode = FSMode_Fit320x200; + if (Raze.isRR()) + { + x *= 2; + y *= 2; + fsmode = FSMode_Fit640x400; + } + if (align != -1) x -= SmallFont2.StringWidth(t) * (align == 0 ? 0.5 : 1); + Screen.DrawText(SmallFont2, Font.CR_UNDEFINED, x, y, t, DTA_FullscreenScale, fsmode, DTA_TranslationIndex, Translation.MakeID(Translation_Remap, trans), DTA_Color, Raze.shadeToLight(shade)); + } + + +} + +struct DukeSnd native +{ + // This really needs to be done better... + enum EDukeSounds + { + KICK_HIT = 0, + PISTOL_RICOCHET = 1, + PISTOL_BODYHIT = 2, + PISTOL_FIRE = 3, + EJECT_CLIP = 4, + INSERT_CLIP = 5, + CHAINGUN_FIRE = 6, + RPG_SHOOT = 7, + POOLBALLHIT = 8, + RPG_EXPLODE = 9, + CAT_FIRE = 10, + SHRINKER_FIRE = 11, + ACTOR_SHRINKING = 12, + PIPEBOMB_BOUNCE = 13, + PIPEBOMB_EXPLODE = 14, + LASERTRIP_ONWALL = 15, + LASERTRIP_ARMING = 16, + LASERTRIP_EXPLODE = 17, + VENT_BUST = 18, + GLASS_BREAKING = 19, + GLASS_HEAVYBREAK = 20, + SHORT_CIRCUIT = 21, + ITEM_SPLASH = 22, + DUKE_BREATHING = 23, + DUKE_EXHALING = 24, + DUKE_GASP = 25, + SLIM_RECOG = 26, + + DUKE_URINATE = 28, + ENDSEQVOL3SND2 = 29, + ENDSEQVOL3SND3 = 30, + DUKE_PASSWIND = 32, + DUKE_CRACK = 33, + SLIM_ATTACK = 34, + SOMETHINGHITFORCE = 35, + DUKE_DRINKING = 36, + DUKE_KILLED1 = 37, + DUKE_GRUNT = 38, + DUKE_HARTBEAT = 39, + DUKE_ONWATER = 40, + DUKE_DEAD = 41, + DUKE_LAND = 42, + DUKE_WALKINDUCTS = 43, + DUKE_GLAD = 44, + DUKE_YES = 45, + DUKE_HEHE = 46, + DUKE_SHUCKS = 47, + DUKE_UNDERWATER = 48, + DUKE_JETPACK_ON = 49, + DUKE_JETPACK_IDLE = 50, + DUKE_JETPACK_OFF = 51, + LIZTROOP_GROWL = 52, + LIZTROOP_TALK1 = 53, + LIZTROOP_TALK2 = 54, + LIZTROOP_TALK3 = 55, + DUKETALKTOBOSS = 56, + LIZCAPT_GROWL = 57, + LIZCAPT_TALK1 = 58, + LIZCAPT_TALK2 = 59, + LIZCAPT_TALK3 = 60, + LIZARD_BEG = 61, + LIZARD_PAIN = 62, + LIZARD_DEATH = 63, + LIZARD_SPIT = 64, + DRONE1_HISSRATTLE = 65, + DRONE1_HISSSCREECH = 66, + DUKE_TIP2 = 67, + FLESH_BURNING = 68, + SQUISHED = 69, + TELEPORTER = 70, + ELEVATOR_ON = 71, + DUKE_KILLED3 = 72, + ELEVATOR_OFF = 73, + DOOR_OPERATE1 = 74, + SUBWAY = 75, + SWITCH_ON = 76, + FAN = 77, + DUKE_GETWEAPON3 = 78, + FLUSH_TOILET = 79, + HOVER_CRAFT = 80, + EARTHQUAKE = 81, + INTRUDER_ALERT = 82, + END_OF_LEVEL_WARN = 83, + ENGINE_OPERATING = 84, + REACTOR_ON = 85, + COMPUTER_AMBIENCE = 86, + GEARS_GRINDING = 87, + BUBBLE_AMBIENCE = 88, + MACHINE_AMBIENCE = 89, + SEWER_AMBIENCE = 90, + WIND_AMBIENCE = 91, + SOMETHING_DRIPPING = 92, + STEAM_HISSING = 93, + THEATER_BREATH = 94, + BAR_MUSIC = 95, + BOS1_ROAM = 96, + BOS1_RECOG = 97, + BOS1_ATTACK1 = 98, + BOS1_PAIN = 99, + BOS1_DYING =100, + BOS2_ROAM =101, + BOS2_RECOG =102, + BOS2_ATTACK =103, + BOS2_PAIN =104, + BOS2_DYING =105, + GETATOMICHEALTH =106, + DUKE_GETWEAPON2 =107, + BOS3_DYING =108, + SHOTGUN_FIRE =109, + PRED_ROAM =110, + PRED_RECOG =111, + PRED_ATTACK =112, + PRED_PAIN =113, + PRED_DYING =114, + CAPT_ROAM =115, + CAPT_ATTACK =116, + CAPT_RECOG =117, + CAPT_PAIN =118, + CAPT_DYING =119, + PIG_ROAM =120, + PIG_RECOG =121, + PIG_ATTACK =122, + PIG_PAIN =123, + PIG_DYING =124, + RECO_ROAM =125, + RECO_RECOG =126, + RECO_ATTACK =127, + RECO_PAIN =128, + RECO_DYING =129, + DRON_ROAM =130, + DRON_RECOG =131, + DRON_ATTACK1 =132, + DRON_PAIN =133, + DRON_DYING =134, + COMM_ROAM =135, + COMM_RECOG =136, + COMM_ATTACK =137, + COMM_PAIN =138, + COMM_DYING =139, + OCTA_ROAM =140, + OCTA_RECOG =141, + OCTA_ATTACK1 =142, + OCTA_PAIN =143, + OCTA_DYING =144, + TURR_ROAM =145, + TURR_RECOG =146, + TURR_ATTACK =147, + DUMPSTER_MOVE =148, + SLIM_DYING =149, + BOS3_ROAM =150, + BOS3_RECOG =151, + BOS3_ATTACK1 =152, + BOS3_PAIN =153, + BOS1_ATTACK2 =154, + COMM_SPIN =155, + BOS1_WALK =156, + DRON_ATTACK2 =157, + THUD =158, + OCTA_ATTACK2 =159, + WIERDSHOT_FLY =160, + TURR_PAIN =161, + TURR_DYING =162, + SLIM_ROAM =163, + LADY_SCREAM =164, + DOOR_OPERATE2 =165, + DOOR_OPERATE3 =166, + DOOR_OPERATE4 =167, + BORNTOBEWILDSND =168, + SHOTGUN_COCK =169, + GENERIC_AMBIENCE1 =170, + GENERIC_AMBIENCE2 =171, + GENERIC_AMBIENCE3 =172, + GENERIC_AMBIENCE4 =173, + GENERIC_AMBIENCE5 =174, + GENERIC_AMBIENCE6 =175, + BOS3_ATTACK2 =176, + GENERIC_AMBIENCE17 =177, + GENERIC_AMBIENCE18 =178, + GENERIC_AMBIENCE19 =179, + GENERIC_AMBIENCE20 =180, + GENERIC_AMBIENCE21 =181, + GENERIC_AMBIENCE22 =182, + SECRETLEVELSND =183, + GENERIC_AMBIENCE8 =184, + GENERIC_AMBIENCE9 =185, + GENERIC_AMBIENCE10 =186, + GENERIC_AMBIENCE11 =187, + GENERIC_AMBIENCE12 =188, + GENERIC_AMBIENCE13 =189, + GENERIC_AMBIENCE14 =190, + GENERIC_AMBIENCE15 =192, + GENERIC_AMBIENCE16 =193, + FIRE_CRACKLE =194, + BONUS_SPEECH1 =195, + BONUS_SPEECH2 =196, + BONUS_SPEECH3 =197, + PIG_CAPTURE_DUKE =198, + BONUS_SPEECH4 =199, + DUKE_LAND_HURT =200, + DUKE_HIT_STRIPPER1 =201, + DUKE_TIP1 =202, + DUKE_KILLED2 =203, + PRED_ROAM2 =204, + PIG_ROAM2 =205, + DUKE_GETWEAPON1 =206, + DUKE_SEARCH2 =207, + DUKE_CRACK2 =208, + DUKE_SEARCH =209, + DUKE_GET =210, + DUKE_LONGTERM_PAIN =211, + MONITOR_ACTIVE =212, + NITEVISION_ONOFF =213, + DUKE_HIT_STRIPPER2 =214, + DUKE_CRACK_FIRST =215, + DUKE_USEMEDKIT =216, + DUKE_TAKEPILLS =217, + DUKE_PISSRELIEF =218, + SELECT_WEAPON =219, + WATER_GURGLE =220, + DUKE_GETWEAPON4 =221, + JIBBED_ACTOR1 =222, + JIBBED_ACTOR2 =223, + JIBBED_ACTOR3 =224, + JIBBED_ACTOR4 =225, + JIBBED_ACTOR5 =226, + JIBBED_ACTOR6 =227, + JIBBED_ACTOR7 =228, + DUKE_GOTHEALTHATLOW =229, + BOSSTALKTODUKE =230, + WAR_AMBIENCE1 =231, + WAR_AMBIENCE2 =232, + WAR_AMBIENCE3 =233, + WAR_AMBIENCE4 =234, + WAR_AMBIENCE5 =235, + WAR_AMBIENCE6 =236, + WAR_AMBIENCE7 =237, + WAR_AMBIENCE8 =238, + WAR_AMBIENCE9 =239, + WAR_AMBIENCE10 =240, + ALIEN_TALK1 =241, + ALIEN_TALK2 =242, + EXITMENUSOUND =243, + FLY_BY =244, + DUKE_SCREAM =245, + SHRINKER_HIT =246, + RATTY =247, + INTO_MENU =248, + BONUSMUSIC =249, + DUKE_BOOBY =250, + DUKE_TALKTOBOSSFALL =251, + DUKE_LOOKINTOMIRROR =252, + PIG_ROAM3 =253, + KILLME =254, + DRON_JETSND =255, + SPACE_DOOR1 =256, + SPACE_DOOR2 =257, + SPACE_DOOR3 =258, + SPACE_DOOR4 =259, + SPACE_DOOR5 =260, + ALIEN_ELEVATOR1 =261, + VAULT_DOOR =262, + JIBBED_ACTOR13 =263, + DUKE_GETWEAPON6 =264, + JIBBED_ACTOR8 =265, + JIBBED_ACTOR9 =266, + JIBBED_ACTOR10 =267, + JIBBED_ACTOR11 =268, + JIBBED_ACTOR12 =269, + DUKE_KILLED4 =270, + DUKE_KILLED5 =271, + ALIEN_SWITCH1 =272, + DUKE_STEPONFECES =273, + DUKE_LONGTERM_PAIN2 =274, + DUKE_LONGTERM_PAIN3 =275, + DUKE_LONGTERM_PAIN4 =276, + COMPANB2 =277, + KTIT =278, + HELICOP_IDLE =279, + STEPNIT =280, + SPACE_AMBIENCE1 =281, + SPACE_AMBIENCE2 =282, + SLIM_HATCH =283, + RIPHEADNECK =284, + FOUNDJONES =285, + ALIEN_DOOR1 =286, + ALIEN_DOOR2 =287, + ENDSEQVOL3SND4 =288, + ENDSEQVOL3SND5 =289, + ENDSEQVOL3SND6 =290, + ENDSEQVOL3SND7 =291, + ENDSEQVOL3SND8 =292, + ENDSEQVOL3SND9 =293, + WHIPYOURASS =294, + ENDSEQVOL2SND1 =295, + ENDSEQVOL2SND2 =296, + ENDSEQVOL2SND3 =297, + ENDSEQVOL2SND4 =298, + ENDSEQVOL2SND5 =299, + ENDSEQVOL2SND6 =300, + ENDSEQVOL2SND7 =301, + GENERIC_AMBIENCE23 =302, + SOMETHINGFROZE =303, + DUKE_LONGTERM_PAIN5 =304, + DUKE_LONGTERM_PAIN6 =305, + DUKE_LONGTERM_PAIN7 =306, + DUKE_LONGTERM_PAIN8 =307, + WIND_REPEAT =308, + MYENEMY_ROAM =309, + MYENEMY_HURT =310, + MYENEMY_DEAD =311, + MYENEMY_SHOOT =312, + STORE_MUSIC =313, + STORE_MUSIC_BROKE =314, + ACTOR_GROWING =315, + NEWBEAST_ROAM =316, + NEWBEAST_RECOG =317, + NEWBEAST_ATTACK =318, + NEWBEAST_PAIN =319, + NEWBEAST_DYING =320, + NEWBEAST_SPIT =321, + VOL4_1 =322, + SUPERMARKET =323, + MOUSEANNOY =324, + BOOKEM =325, + SUPERMARKETCRY =326, + DESTRUCT =327, + EATFOOD =328, + MAKEMYDAY =329, + WITNESSSTAND =330, + VACATIONSPEECH =331, + YIPPEE1 =332, + YOHOO1 =333, + YOHOO2 =334, + DOLPHINSND =335, + TOUGHGALSND1 =336, + TOUGHGALSND2 =337, + TOUGHGALSND3 =338, + TOUGHGALSND4 =339, + TANK_ROAM =340, + BOS4_ROAM =341, + BOS4_RECOG =342, + BOS4_ATTACK =343, + BOS4_PAIN =344, + BOS4_DYING =345, + NEWBEAST_ATTACKMISS =346, + VOL4_2 =347, + COOKINGDEEPFRIER =348, + WHINING_DOG =349, + DEAD_DOG =350, + LIGHTNING_SLAP =351, + THUNDER =352, + HAPPYMOUSESND1 =353, + HAPPYMOUSESND2 =354, + HAPPYMOUSESND3 =355, + HAPPYMOUSESND4 =356, + ALARM =357, + RAIN =358, + DTAG_GREENRUN =359, + DTAG_BROWNRUN =360, + DTAG_GREENSCORE =361, + DTAG_BROWNSCORE =362, + INTRO4_1 =363, + INTRO4_2 =364, + INTRO4_3 =365, + INTRO4_4 =366, + INTRO4_5 =367, + INTRO4_6 =368, + SCREECH =369, + BOSS4_DEADSPEECH =370, + BOSS4_FIRSTSEE =371, + PARTY_SPEECH =372, + POSTAL_SPEECH =373, + TGSPEECH =374, + DOGROOMSPEECH =375, + SMACKED =376, + MDEVSPEECH =377, + AREA51SPEECH =378, + JEEPSOUND =379, + BIGDOORSLAM =380, + BOS4_LAY =381, + WAVESOUND =382, + ILLBEBACK =383, + VOL4ENDSND1 =384, + VOL4ENDSND2 =385, + EXPANDERHIT =386, + SNAKESPEECH =387, + EXPANDERSHOOT =388, + GETBACKTOWORK =389, + JIBBED_ACTOR14 =390, + JIBBED_ACTOR15 =391, + INTRO4_B =392, + BIGBANG =393, + SMACKIT =394, + BELLSND =395, + GOAWAY =396, + JOKE =397, + FLAMETHROWER_INTRO =398, + FLAMETHROWER_LOOP =399, + FLAMETHROWER_END =400, + E5L7_DUKE_QUIT_YOU =401, + } +} + +struct RRSnd native +{ + enum ESnd + { + KICK_HIT = 0 , + RICOCHET = 1 , + BULITHIT = 2 , + CASUL_FIRE = 3 , + PISCOCK = 4 , + PISLOAD = 5 , + AK3 = 6 , + XBOWFIRE = 7 , + BUB_HRT1 = 8 , + XBOWEXPL = 9 , + LASERA = 10 , + SHRINKER = 11 , + CRAPFLOW = 12 , + DYNOCLMP = 13 , + DYNEW = 14 , + CRAPSTIR = 15 , + BRICDOOR = 16 , + BOMBEXPL = 17 , + VENTBUST = 18 , + GLASSSND = 19 , + GLASSHVY = 20 , + BUBBLES = 21 , + SPLASH = 22 , + BUB_HRT2 = 23 , + BUB_HRT3 = 24 , + GASP = 25 , + BUB_HRT4 = 26 , + + ONECART = 27 , // RR + MINEWIND = 28 , + URANUS = 29 , + + MIRROR1 = 27 , // RRRA + MIRROR2 = 28 , + MIRROR3 = 29 , + + COMPUTER = 30 , + NEON = 31 , + VX_FINAL = 32 , + LN_WAIT = 33 , + BUB_LN1 = 34 , + LN_FINAL = 35 , + CLOCKTK = 36 , + LN_STANK = 37 , + LNRD_GRUNT = 38 , + CLOCKCHM = 39 , + WETFEET = 40 , + LNRD_DEAD = 41 , + LAND = 42 , + END_PIPE = 43 , + ICARUMBA = 44 , + BUB_LN2 = 45 , + LN_CRAP = 46 , + WOODBREK = 47 , + SCUBA = 48 , + TRUCK_LP2 = 49 , + COW1 = 50 , + COW2 = 51 , + COW3 = 52 , + COW4 = 53 , + COW5 = 54 , + BUB_LN3 = 55 , + LATCH = 56 , + BUB_LN5 = 57 , + BUB_LN6 = 58 , + BUB_LN7 = 59 , + BUB_PIK1 = 60 , + BUB_PIK2 = 61 , + BUB_PISS = 62 , + E1L1 = 63 , + E1L2 = 64 , + UFOINSID = 65 , + LN_RODE = 66 , + CURTAIN = 67 , + FIRE09 = 68 , + SQUISHED = 69 , + TELEPORT = 70 , + GBELEV01 = 71 , + LN_BNCH = 72 , + GBELEV02 = 73 , + FROG1 = 74 , + TRUCK_LP = 75 , + SWITCH1 = 76 , + E1L3 = 77 , + LN_HOTDM = 78 , + FLUSH = 79 , + E1L4 = 80 , + QUAKE = 81 , + CHKAMMO = 82 , + MONITORA = 83 , + FROG2 = 84 , + AS_AMB2 = 85 , + AS_AMB1 = 86 , + FBOATIDL = 87 , + FBOATRUN = 88 , + FBOATUP = 89 , + FBOATDN = 90 , + FBOATTRN = 91 , + DRIP3 = 92 , + SWAMPAMB = 93 , + MORTAR = 94 , + JUKEBOX = 95 , + AS_DROPN = 96 , + AS_CRYPT = 97 , + AS_DRCLS = 98 , + LOKGATE = 99 , + METLGAT2 = 100, + METLGAT1 = 101, + E1L5 = 102, + E1L6 = 103, + E1L7 = 104, + E2L1 = 105, + PADDLE = 106, + LN_HOLD = 107, + VX_TAKIT = 108, + SHOT6 = 109, + CT_LAF2 = 110, + CT_GET = 111, + CT_LAF = 112, + CT_PAIN = 113, + CT_DIE = 114, + PIGSOUND1 = 115, + PIGSOUND2 = 116, + PIGSOUND3 = 117, + PIGSOUND4 = 118, + PIGSOUND5 = 119, + BR_ROAM1 = 120, + BR_RECOG = 121, + WHISTLE = 122, + BR_PAIN = 123, + BR_DTH = 124, + VX_ISTHT = 125, + LASERH = 126, + PIGSOUND6 = 127, + PIGSOUND7 = 128, + VX_DIE1 = 129, + MJ_JIB1 = 130, + VX_DIE4 = 131, + VX_DIE5 = 132, + VX_DIE6 = 133, + VX_DIE7 = 134, + VX_OOH = 135, + VX_PAIN1 = 136, + VX_SEX1 = 137, + VX_SEX2 = 138, + VX_SEX3 = 139, + VX_GRNT = 140, + RENO = 141, + BK_MAKE1 = 142, + BK_MAKE2 = 143, + VX_BRNG3 = 144, + VX_CLSR1 = 145, + VX_CLSR2 = 146, + VX_2FAR = 147, + KINGHUH = 148, + VX_BRING = 149, + VX_BITE = 150, + MJ_FART = 151, + VX_LAFF2 = 152, + VX_LAFF3 = 153, + VX_HMMM2 = 154, + VX_HURT2 = 155, + VX_BABY2 = 156, + VX_MHMM = 157, + THUD = 158, + VX_ITSOK = 159, + MJ_RECO2 = 160, + // VX_TPOT1 = 161, + VX_TPOT4 = 162, + // VX_TPIN1 = 163, + ROPECRK = 164, + DR_CRK8 = 165, + DR_ROLL = 166, + STEELAMB = 167, + ROULETTE = 168, + GUNCHANG = 169, + FLIES = 170, + AMB_1 = 171, + GRAVAMB = 172, + HOOTOWL = 173, + WOODS2 = 174, + CATAMB = 175, + E2L2 = 176, + E2L3 = 177, + FBOATX_1 = 178, + FBOATX_2 = 179, + FBOATX_3 = 180, + FBOATX_4 = 181, + FBOATSLW = 182, + PLANE = 183, + CNTAMB = 184, + JUNKAMB2 = 185, + BIKESTRT = 186, + BIKEIDLE = 187, + BIKELOOP = 188, + BIKEJMPS = 189, + BIKEJMPL = 190, + BIKELAND = 191, + JACKJMP1 = 192, + JACKJMP2 = 193, + FIRE_CRACKLE = 194, + BNS_SPCH1 = 195, + BNS_SPCH2 = 196, + BNS_SPCH3 = 197, + E2L4 = 198, + BNS_SPCH4 = 199, + LN_LNDHT = 200, + JACKATK2 = 201, + JACKPAIN = 202, + LN_BITCH = 203, + CT_LAND = 204, + BR_ROAM2 = 205, + LN_HUSH = 206, + LN_PAIN4 = 207, + LN_SLOW = 208, + LN_PAIN4A = 209, + JUG = 210, + LN_PAIN8 = 211, + MONITOR = 212, + JACKATK1 = 213, + BIKEUP = 214, + PLANEXP = 215, + JUGALUG7 = 216, + DIDDLP = 217, + ELVISMOD = 218, + // PISCOCK = 219, + BIKESKID = 220, + LN_STINK = 221, + JIBBED1 = 222, + JIBBED2 = 223, + JIBBED3 = 224, + JIBBED4 = 225, + JIBBED5 = 226, + JIBBED6 = 227, + JIBBED7 = 228, + LN_BACON = 229, + E2L5 = 230, + REGISTER = 231, + BBQGRILL = 232, + CRSSBELL = 233, + TRAIN = 234, + SLOTS = 235, + INDIANS = 236, + RADIO = 237, + BIKEX_1 = 238, + BIKEX_2 = 239, + BIKEX_3 = 240, + TVSNOW = 241, + WINDLITE = 242, + EXITMENU = 243, + CHKBOWFR = 244, + DSCREM04 = 245, + SHRNK_HIT = 246, + CHKBOWEX = 247, + INTOMENU = 248, + LAVAFLOW = 249, + LAVA_RK = 250, + BIKELOO2 = 251, + SLINGBL = 252, + BR_ROAM3 = 253, + KILLME = 254, + E2L6 = 255, + RINTRO = 256, + MIRROR4 = 257, + MIRROR5 = 258, + GAMBELEV = 259, + SLINGHIT = 260, + PIANO_P1 = 261, + BANJO1 = 262, + JIBBED13 = 263, + LN_BBQ = 264, + JIBBED8 = 265, + JIBBED9 = 266, + JIBBED10 = 267, + JIBBED11 = 268, + JIBBED12 = 269, + LNRD_KILLED4 = 270, + LNRD_KILLED5 = 271, + BANJO2 = 272, + BANJO3 = 273, + LN_PAIN2 = 274, + LN_PAIN3 = 275, + BK_ALIVE = 276, + BK_BOURB = 277, + BK_CHEER = 278, + BK_DIENB = 279, + BK_DNTLK = 280, + BK_FUN = 281, + BK_HEY = 282, + E2L7 = 283, + BK_HEYNB = 284, + BK_JOYRD = 285, + BK_KEEPA = 286, + BK_PLEAS = 287, + BK_RIDE = 288, + BK_ROAD = 289, + BK_SCRAT = 290, + BK_SHTUP = 291, + BK_SNORT = 292, + BK_TOHEL = 293, + WHIPYOU = 294, + BK_TRYIN = 295, + BK_PAIN1 = 296, + BK_PAIN2 = 297, + BK_PAIN3 = 298, + CH_BALD = 299, + CH_TEAS1 = 300, + CH_TEAS2 = 301, + CH_TEAS3 = 302, + CH_SANDP = 303, + LN_PAIN5 = 304, + LN_PAIN6 = 305, + LN_PAIN7 = 306, + CH_DONIT = 307, + CH_WHOOP = 308, + CH_NIPPL = 309, + CH_BARN = 310, + CH_GTEAM = 311, + CH_GOGOG = 312, + CH_REDOK = 313, + CH_2468 = 314, + CH_BIGON = 315, + HULK_ROAM = 316, + HULK_RECOG = 317, + HULK_ATTACK = 318, + HULK_PAIN = 319, + HULK_DYING = 320, + HULK_SPIT = 321, + CH_PAIN1 = 322, + CH_PAIN2 = 323, + CH_PAIN3 = 324, + CH_HURT = 325, + AK4 = 326, + CHKSCR1 = 327, + SHIPWREK = 328, + HYDROGLY = 329, + PIANO_P2 = 330, + FROGTOSS = 331, + TRAIN2 = 332, + CRICKET1 = 333, + CRICKET2 = 334, + PIGRUNT = 335, + GOCATGO = 336, + ANNOUNC1 = 337, + ANNOUNC2 = 338, + TRACTOR = 339, + PIANO_P3 = 340, + RESIZE = 341, + VX_TPIN2 = 342, + VX_TPIN4 = 343, + VX_HLPME = 344, + ATFSPEAK = 345, + WINDCAVE = 346, + ALARM = 347, + SF_THLAW = 348, + SF_TLAW2 = 349, + LN_SCREW = 350, + THUNDER1 = 351, + THUNDER2 = 352, + THUNDER3 = 353, + BOWLSTRT = 354, + BOWLPIN = 355, + BOWLLOOP = 356, + MJ_JIB2 = 357, + VX_KICK2 = 358, + VX_KICK3 = 359, + MJ_RECO1 = 360, + VX_HIYA = 361, + VX_HIYA2 = 362, + SF_ATTN = 363, + SF_DETH1 = 364, + SF_DETH2 = 365, + SF_DETH3 = 366, + TEDOUT = 367, + SF_FREZ2 = 368, + SF_GETYA = 369, + SF_HANDS = 370, + STEELAM2 = 371, + STEELAM3 = 372, + SF_HEY = 373, + SF_HOLD = 374, + SF_LAFF1 = 375, + LN_FLYOP = 376, + LN_SHTHD = 377, + SF_NAME = 378, + SF_OVER = 379, + SF_PAIN1 = 380, + SF_PAIN2 = 381, + SF_PAIN3 = 382, + SF_RLOAD = 383, + SF_RLOD2 = 384, + SF_SHOOT = 385, + JAWHARP = 386, + LN_TIGHT = 387, + DR_CLS = 388, + SCRAPE_1 = 389, + YEHAA16 = 390, + LN_WHUP = 391, + CHKNFLAP = 392, + CHKN_1 = 393, + CHKN_2 = 394, + CHIKDETH = 395, + AMB_ROOM = 396, + BR_ITCH = 397, + BR_SCRTH = 398, + BR_SNIFF = 399, + TRUKDIE = 400, + ZIPOPEN = 401, + ZIPPSTRK = 402, + MOSQUI4 = 403, + FART1 = 404, + SWITCH2 = 405, + SQUEAKY = 406, + CATDOOR = 407, + JUNKSWCH = 408, + CONVEYR = 409, + SWITCH3 = 410, + BIKEENEM = 411, + BIGDOOR = 412, + FLOODGAT = 413, + JACK_RM1 = 414, + MN_FREAK = 415, + MN_PN = 416, + MN_REC = 417, + MN_AMB = 418, + LOKDOOR = 419, + VOMIT = 420, + TOSS = 421, + FART2 = 422, + FART3 = 423, + FART4 = 424, + CHUG = 425, + CROWUSH = 426, + WUSSLAF = 427, + LN_CITY = 428, + MUNCH2 = 429, + TESLARC = 430, + BUZSAWSND = 431, + ELEVLOOP = 432, + PISSEND = 433, + PISSLOOP = 434, + PISSSTRT = 435, + CRAP = 436, + PEE = 437, + JACK_RM2 = 438, + BELL = 439, + TRAINWRK = 440, + DOOR_PKT = 441, + GAMBDOOR = 442, + OVEN = 443, + CREMATOR = 444, + JOE9000A = 445, + JOE9000B = 446, + JOE9000C = 447, + CHINESE = 448, + SIGNROT = 449, + XBOWCOCK = 450, + PWDERKEG = 451, + DG_BARK1 = 452, + DG_GRWL1 = 453, + DG_YELP = 454, + DG_DIE = 455, + UFO = 456, + UFOLET = 457, + JACKJIB1 = 458, + JACKJIB2 = 459, + JACKJIB3 = 460, + JACKJIB4 = 461, + JACKJIB5 = 462, + WTRFALL = 463, + BK_JIB1 = 464, + FRIDGEOP = 465, + FRIDGECL = 466, + DG_LUNGE = 467, + DRIVTHRU = 468, + FAN = 469, + CRUSHER = 470, + BALLOON = 471, + POOLBUD = 472, + STAMPER = 473, + BK_JIB2 = 474, + MORNING = 475, + DG_BARK2 = 476, + DG_GRWL2 = 477, + REDNECK2 = 478, + XATRIX = 479, + MJ_ATTK1 = 480, + MJ_JUMP = 485, + MJ_PAIN1 = 481, + MJ_PAIN2 = 482, + MJ_ROAM1 = 483, + MJ_ROAM2 = 484, + MJ_ROLL = 486, + DISHES = 487, + BUB_ELV1 = 488, + BUB_ELV2 = 489, + BUB_ELV3 = 490, + BK_JIB3 = 491, + CH_JIB1 = 492, + CH_JIB2 = 493, + CH_JIB3 = 494, + SIGNHIT = 495, + UMHUM = 496, + COYOTE = 497, + BUB_HEY1 = 498, + BUB_HEY2 = 499, + } +} \ No newline at end of file diff --git a/wadsrc/static/zscript/games/duke/ui/cutscenes.zs b/wadsrc/static/zscript/games/duke/ui/cutscenes.zs new file mode 100644 index 000000000..690427294 --- /dev/null +++ b/wadsrc/static/zscript/games/duke/ui/cutscenes.zs @@ -0,0 +1,366 @@ +//------------------------------------------------------------------------- +/* +Copyright (C) 1996, 2003 - 3D Realms Entertainment +Copyright (C) 2020-2021 Christoph Oelckers + +This file is part of Raze. + +Duke Nukem 3D is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +aint with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Original Source: 1996 - Todd Replogle +Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms +( not much left of the original code, though... ;) ) +*/ +//------------------------------------------------------------------------- + +class DukeCutscenes // Note: must be class, not struct, otherwise we cannot easily look up the methods from C++. +{ + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildIntro(ScreenJobRunner runner) + { + if (!userConfig.nologo) + { + if (!Raze.isShareware()) + { + Array soundinfo; + soundinfo.Pushv( + 1, DukeSnd.FLY_BY+1, + 19, DukeSnd.PIPEBOMB_EXPLODE+1); + runner.Append(MoviePlayerJob.CreateWithSoundinfo("logo.anm", soundinfo, 0, 9, 9, 9)); + } + if (!Raze.isNam()) runner.Append(new("DRealmsScreen").Init()); + } + runner.Append(new("DukeTitleScreen").Init()); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildE1End(ScreenJobRunner runner) + { + runner.Append(new("Episode1End1").Init()); + runner.Append(ImageScreen.CreateNamed("E1ENDSCREEN", ScreenJob.fadein|ScreenJob.fadeout|ScreenJob.stopmusic, 0x7fffffff)); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildE2End(ScreenJobRunner runner) + { + Array soundinfo; + soundinfo.Pushv( + 1, DukeSnd.WIND_AMBIENCE+1, + 26, DukeSnd.ENDSEQVOL2SND1+1, + 36, DukeSnd.ENDSEQVOL2SND2+1, + 54, DukeSnd.THUD+1, + 62, DukeSnd.ENDSEQVOL2SND3+1, + 75, DukeSnd.ENDSEQVOL2SND4 + 1, + 81, DukeSnd.ENDSEQVOL2SND5 + 1, + 115, DukeSnd.ENDSEQVOL2SND6 + 1, + 124, DukeSnd.ENDSEQVOL2SND7 + 1); + + runner.Append(MoviePlayerJob.CreateWithSoundinfo("cineov2.anm", soundinfo, 0, 18, 18, 18)); + runner.Append(new("E2EndScreen").Init()); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildE3End(ScreenJobRunner runner) + { + if (gameinfo.gameType & GAMEFLAG_DUKEDC) + { + Array soundinfo; + soundinfo.Pushv(144, DukeSnd.ENDSEQVOL3SND3 + 1); + runner.Append(MoviePlayerJob.CreateWithSoundinfo("radlogo.anm", soundinfo, 0, 10, 10, 10)); + } + else + { + Array soundinfo; + soundinfo.Pushv( + 1, DukeSnd.WIND_REPEAT + 1, + 98, DukeSnd.DUKE_GRUNT + 1, + 102, DukeSnd.THUD + 1, + 102, DukeSnd.SQUISHED + 1, + 124, DukeSnd.ENDSEQVOL3SND3 + 1, + 134, DukeSnd.ENDSEQVOL3SND2 + 1, + 158, DukeSnd.PIPEBOMB_EXPLODE + 1); + + runner.Append(MoviePlayerJob.CreateWithSoundinfo("cineov3.anm", soundinfo, 0, 10, 10, 10)); + runner.Append(BlackScreen.Create(200, ScreenJob.stopsound)); + runner.Append(new("Episode3End").Init()); + if (!Raze.isPlutoPak()) runner.Append(ImageScreen.CreateNamed("DUKETEAM.ANM", ScreenJob.fadein | ScreenJob.fadeout | ScreenJob.stopsound, 0x7fffffff)); + } + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildE4End(ScreenJobRunner runner) + { + Array soundinfo; + + soundinfo.Pushv( + 3, DukeSnd.DUKE_UNDERWATER+1, + 35, DukeSnd.VOL4ENDSND1+1); + runner.Append(MoviePlayerJob.CreateWithSoundinfo("vol4e1.anm", soundinfo, 0, 10, 10, 10)); + + soundinfo.Pushv( + 11, DukeSnd.DUKE_UNDERWATER+1, + 20, DukeSnd.VOL4ENDSND1+1, + 39, DukeSnd.VOL4ENDSND2+1, + 50, -1); + runner.Append(MoviePlayerJob.CreateWithSoundinfo("vol4e2.anm", soundinfo, 0, 10, 10, 10)); + + soundinfo.Pushv( + 1, DukeSnd.BOSS4_DEADSPEECH+1, + 40, DukeSnd.VOL4ENDSND1+1, + 40, DukeSnd.DUKE_UNDERWATER+1, + 50, DukeSnd.BIGBANG+1); + runner.Append(MoviePlayerJob.CreateWithSoundinfo("vol4e3.anm", soundinfo, 0, 10, 10, 10)); + + runner.Append(new("Episode4Text").Init()); + runner.Append(ImageScreen.CreateNamed("DUKETEAM.ANM", ScreenJob.fadein | ScreenJob.fadeout | ScreenJob.stopsound, 0x7fffffff)); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildE5End(ScreenJobRunner runner) + { + runner.Append(new("Episode5End").Init()); + } + + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + + static void BuildE4Intro(ScreenJobRunner runner) + { + Array soundinfo; + + Duke.PlaySpecialMusic(Duke.MUS_BRIEFING); + soundinfo.Pushv( + 1, DukeSnd.INTRO4_B + 1, + 12, DukeSnd.SHORT_CIRCUIT + 1, + 18, DukeSnd.INTRO4_5 + 1, + 34, DukeSnd.SHORT_CIRCUIT + 1); + runner.Append(MoviePlayerJob.CreateWithSoundinfo("vol41a.anm", soundinfo, MoviePlayer.NOSOUNDCUTOFF, 10, 10, 10)); + + soundinfo.Pushv( + 1, DukeSnd.INTRO4_1 + 1, + 7, DukeSnd.INTRO4_3 + 1, + 12, DukeSnd.INTRO4_2 + 1, + 26, DukeSnd.INTRO4_4 + 1); + let m = MoviePlayerJob.CreateWithSoundinfo("vol42a.anm", soundinfo, MoviePlayer.NOSOUNDCUTOFF, 14, 14, 14); + if (m) m.skipover = true; + runner.Append(m); + + soundinfo.Pushv( + 10, DukeSnd.INTRO4_6 + 1); + m = MoviePlayerJob.CreateWithSoundinfo("vol43a.anm", soundinfo, 0, 10, 10, 10); + if (m) m.skipover = true; + runner.Append(m); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildMPSummary(ScreenJobRunner runner, MapRecord map, SummaryInfo stats) + { + runner.Append(new("DukeMultiplayerBonusScreen").Init(stats.playercount)); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildSPSummary(ScreenJobRunner runner, MapRecord map, SummaryInfo stats) + { + let screen = new("DukeLevelSummaryScreen").Init(map, stats); + runner.Append(screen); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildSharewareExit(ScreenJobRunner runner) + { + runner.Append(ImageScreen.CreateNamed("SWEXIT1")); + runner.Append(ImageScreen.CreateNamed("SWEXIT2")); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildSharewareEnd(ScreenJobRunner runner) + { + runner.Append(ImageScreen.CreateNamed("ORDERING")); + runner.Append(ImageScreen.CreateNamed("ORDERING1")); + runner.Append(ImageScreen.CreateNamed("ORDERING2")); + runner.Append(ImageScreen.CreateNamed("ORDERING3")); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildLoading(ScreenJobRunner runner, MapRecord map) + { + runner.Append(new("DukeLoadScreen").Init(map)); + } + +} + +class RRCutscenes +{ + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildIntro(ScreenJobRunner runner) + { + if (!userConfig.nologo) + { + if (!Raze.isRRRA()) + { + Array soundinfo; + soundinfo.Pushv(1, RRSnd.URANUS + 1); + runner.Append(MoviePlayerJob.CreateWithSoundinfo("rr_intro.anm", soundinfo, 0, 9, 9, 9)); + + soundinfo.Pushv(1, RRSnd.REDNECK2 + 1); + runner.Append(MoviePlayerJob.CreateWithSoundinfo("redneck.anm", soundinfo, 0, 9, 9, 9)); + + soundinfo.Pushv(1, RRSnd.XATRIX + 1); + runner.Append(MoviePlayerJob.CreateWithSoundinfo("xatlogo.anm", soundinfo, 0, 9, 9, 9)); + } + else + { + runner.Append(MoviePlayerJob.Create("redint.mve", 0)); + } + } + } + + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildE1End(ScreenJobRunner runner) + { + if (!Raze.isRRRA()) + { + Array soundinfo; + soundinfo.Pushv(1, RRSnd.CHKAMMO + 1); + runner.Append(MoviePlayerJob.CreateWithSoundinfo("turdmov.anm", soundinfo, 0, 9, 9, 9)); + } + } + + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildE2End(ScreenJobRunner runner) + { + Array soundinfo; + soundinfo.Pushv(1, RRSnd.LN_FINAL + 1); + runner.Append(MoviePlayerJob.CreateWithSoundinfo("rr_outro.anm", soundinfo, MoviePlayer.NOSOUNDCUTOFF, 9, 9, 9)); + runner.Append(ImageScreen.CreateNamed("TENSCREEN")); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildRAE2End(ScreenJobRunner runner) + { + runner.Append(new("RRRAEndOfGame").Init()); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildSPSummary(ScreenJobRunner runner, MapRecord map, SummaryInfo stats) + { + runner.Append(new("RRLevelSummaryScreen").Init(map, stats, !Raze.isRRRA() || stats.endOfGame)); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildMapIntro(ScreenJobRunner runner, MapRecord map) + { + int ln = map.levelnumber - 1; + if (ln == 0) return; + if (ln >= 1000) ln -= 1000-7; + + let fn = String.Format("lvl%d.anm", ln); + Array soundinfo; + runner.Append(MoviePlayerJob.CreateWithSoundinfo(fn, soundinfo, 0, 20, 20, 2000)); // wait for a few seconds on the final frame so that the video doesn't stop before the user notices. + } +} \ No newline at end of file diff --git a/wadsrc/static/zscript/games/duke/ui/menu.zs b/wadsrc/static/zscript/games/duke/ui/menu.zs index 6c81fbae3..fb701c7a7 100644 --- a/wadsrc/static/zscript/games/duke/ui/menu.zs +++ b/wadsrc/static/zscript/games/duke/ui/menu.zs @@ -1,3 +1,30 @@ +//------------------------------------------------------------------------- +/* +Copyright (C) 1996, 2003 - 3D Realms Entertainment +Copyright (C) 2020-2021 Christoph Oelckers + +This file is part of Raze. + +Duke Nukem 3D is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +aint with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Original Source: 1996 - Todd Replogle +Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms +( not much left of the original code, though... ;) ) +*/ +//------------------------------------------------------------------------- class DukeMenuDelegate : RazeMenuDelegate { @@ -38,7 +65,7 @@ class DukeMenuDelegate : RazeMenuDelegate String picname; if (!right) picname= String.Format("SPINNINGNUKEICON%d", ((mclock >> 3) % frames)); else picname = String.Format("SPINNINGNUKEICON%d", frames - 1 - ((frames - 1 + (mclock >> 3)) % frames)); - int light = 231 + (Build.calcSinTableValue(mclock<<5) / 768.); + int light = 231 + (Raze.calcSinTableValue(mclock<<5) / 768.); let pe = color(255, light, light, light); Screen.DrawTexture(TexMan.CheckForTexture(picname), false, x, y, DTA_FullscreenScale, FSMode_Fit320x200, DTA_ScaleX, scale, DTA_ScaleY, scale, DTA_Color, pe, DTA_CenterOffsetRel, true); } @@ -110,7 +137,7 @@ class ListMenuItemDukeLogo : ListMenuItem if (gameinfo.gametype & GAMEFLAG_PLUTOPAK) { int mclock = MSTime() * 120 / 1000; - int light = 223 + (Build.calcSinTableValue(mclock<<4) / 512.); + int light = 223 + (Raze.calcSinTableValue(mclock<<4) / 512.); let pe = Color(255, light, light, light); Screen.DrawTexture(TexMan.CheckForTexture("MENUPLUTOPAKSPRITE"), false, x + 100, 36, DTA_FullscreenScale, FSMode_Fit320x200Top, DTA_Color, pe, DTA_CenterOffsetRel, true); } @@ -148,7 +175,7 @@ class ListMenuItemDukeTextItem : ListMenuItemTextItem if (selected) { int mclock = MSTime() * 120 / 1000; - int light = 231 + (Build.calcSinTableValue(mclock<<5) / 512.); + int light = 231 + (Raze.calcSinTableValue(mclock<<5) / 512.); pe = Color(255, light, light, light); } else diff --git a/wadsrc/static/zscript/games/duke/ui/screens.zs b/wadsrc/static/zscript/games/duke/ui/screens.zs new file mode 100644 index 000000000..440e05221 --- /dev/null +++ b/wadsrc/static/zscript/games/duke/ui/screens.zs @@ -0,0 +1,1061 @@ +//------------------------------------------------------------------------- +/* +Copyright (C) 1996, 2003 - 3D Realms Entertainment +Copyright (C) 2020-2021 Christoph Oelckers + +This file is part of Raze. + +Duke Nukem 3D is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +aint with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Original Source: 1996 - Todd Replogle +Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms +( not much left of the original code, though... ;) ) +*/ +//------------------------------------------------------------------------- + + + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class DRealmsScreen : SkippableScreenJob +{ + ScreenJob Init() + { + Super.Init(fadein | fadeout); + return self; + } + + override void Start() + { + Duke.PlaySpecialMusic(Duke.MUS_INTRO); + } + + override void OnTick() + { + if (ticks >= 7 * GameTicRate) jobstate = finished; + } + + override void Draw(double smoothratio) + { + let tex = TexMan.CheckForTexture("DREALMS"); + int translation = TexMan.UseGamePalette(tex)? Translation.MakeID(Translation_BasePalette, Duke.DREALMSPAL) : 0; + + screen.DrawTexture(tex, true, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_TranslationIndex, translation, DTA_LegacyRenderStyle, STYLE_Normal); + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class DukeTitleScreen : SkippableScreenJob +{ + int soundanm; + + ScreenJob Init() + { + Super.Init(fadein | fadeout); + soundanm = 0; + return self; + } + + override void Start() + { + if (Raze.isNam() || userConfig.nologo) Duke.PlaySpecialMusic(Duke.MUS_INTRO); + } + + override void OnTick() + { + int clock = ticks * 120 / GameTicRate; + if (soundanm == 0 && clock >= 120 && clock < 120 + 60) + { + soundanm = 1; + Duke.PlaySound(DukeSnd.PIPEBOMB_EXPLODE, CHAN_AUTO, CHANF_UI); + } + if (soundanm == 1 && clock > 220 && clock < (220 + 30)) + { + soundanm = 2; + Duke.PlaySound(DukeSnd.PIPEBOMB_EXPLODE, CHAN_AUTO, CHANF_UI); + } + if (soundanm == 2 && clock >= 280 && clock < 395) + { + soundanm = 3; + if (Raze.isPlutoPak()) Duke.PlaySound(DukeSnd.FLY_BY, CHAN_AUTO, CHANF_UI); + } + else if (soundanm == 3 && clock >= 395) + { + soundanm = 4; + if (Raze.isPlutoPak()) Duke.PlaySound(DukeSnd.PIPEBOMB_EXPLODE, CHAN_AUTO, CHANF_UI); + } + + if (clock > (860 + 120)) + { + jobstate = finished; + } + } + + override void Draw(double smoothratio) + { + int clock = (ticks + smoothratio) * 120 / GameTicRate; + int etrans = Translation.MakeID(Translation_BasePalette, Duke.TITLEPAL); + + // Only translate if the image depends on the global palette. + let tex = TexMan.CheckForTexture("BETASCREEN"); + int trans = TexMan.UseGamePalette(tex)? etrans : 0; + screen.DrawTexture(tex, true, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_TranslationIndex, trans, DTA_LegacyRenderStyle, STYLE_Normal); + + double scale = clamp(clock - 120, 0, 60) / 64.; + if (scale > 0.) + { + let tex = TexMan.CheckForTexture("DUKENUKEM"); + trans = TexMan.UseGamePalette(tex)? etrans : 0; // re-check for different texture! + + screen.DrawTexture(tex, true, 160, 104, DTA_FullscreenScale, FSMode_Fit320x200, + DTA_CenterOffsetRel, true, DTA_TranslationIndex, trans, DTA_ScaleX, scale, DTA_ScaleY, scale); + } + + scale = clamp(clock - 220, 0, 30) / 32.; + if (scale > 0.) + { + let tex = TexMan.CheckForTexture("THREEDEE"); + trans = TexMan.UseGamePalette(tex)? etrans : 0; // re-check for different texture! + + screen.DrawTexture(tex, true, 160, 129, DTA_FullscreenScale, FSMode_Fit320x200, + DTA_CenterOffsetRel, true, DTA_TranslationIndex, trans, DTA_ScaleX, scale, DTA_ScaleY, scale); + } + + if (Raze.isPlutoPak()) + { + scale = (410 - clamp(clock, 280, 395)) / 16.; + if (scale > 0. && clock > 280) + { + let tex = TexMan.CheckForTexture("TITLEPLUTOPAKSPRITE"); + trans = TexMan.UseGamePalette(tex)? etrans : 0; // re-check for different texture! + + screen.DrawTexture(tex, true, 160, 151, DTA_FullscreenScale, FSMode_Fit320x200, + DTA_CenterOffsetRel, true, DTA_TranslationIndex, trans, DTA_ScaleX, scale, DTA_ScaleY, scale); + } + } + } + + override void OnDestroy() + { + Duke.PlaySound(DukeSnd.NITEVISION_ONOFF, CHAN_AUTO, CHANF_UI); + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class Episode1End1 : SkippableScreenJob +{ + int bonuscnt; + TextureID bossani; + TextureID breatheani; + bool breathebg; + + const breathe_x = 176; + const breathe_y = 59; + const boss_x = 86; + const boss_y = 59; + + ScreenJob Init() + { + bonuscnt = 0; + breathebg = false; + bossani.SetInvalid(); + breatheani.SetInvalid(); + Super.Init(fadein | fadeout); + return self; + } + + + override void OnTick() + { + static const int breathe_time[] = { 0, 30, 60, 90 }; + static const int breathe_time2[] = { 30, 60, 90, 120 }; + static const String breathe_tile[] = { "VICTORY2", "VICTORY3", "VICTORY2", "" }; + + static const int boss_time[] = { 0, 220, 260, 290, 320, 350, 350 }; + static const int boss_time2[] = { 120, 260, 290, 320, 350, 380, 380 }; + static const String boss_tile[] = { "VICTORY4", "VICTORY5", "VICTORY6", "VICTORY7", "VICTORY8", "VICTORY9", "VICTORY9" }; + + int currentclock = ticks * 120 / GameTicRate; + + bossani.SetInvalid(); + breathebg = false; + breatheani.SetInvalid(); + + // boss + if (currentclock > 390 && currentclock < 780) + { + for (int t = 0, tt = 0; t < 35; t +=5, tt++) if ((currentclock % 390) > boss_time[tt] && (currentclock % 390) <= boss_time2[tt]) + { + if (t == 10 && bonuscnt == 1) + { + Duke.PlaySound(DukeSnd.SHOTGUN_FIRE, CHAN_AUTO, CHANF_UI); + Duke.PlaySound(DukeSnd.SQUISHED, CHAN_AUTO, CHANF_UI); + bonuscnt++; + } + bossani = TexMan.CheckForTexture(boss_tile[tt]); + } + } + + // Breathe + if (currentclock < 450 || currentclock >= 750) + { + if (currentclock >= 750) + { + breathebg = true; + if (currentclock >= 750 && bonuscnt == 2) + { + Duke.PlaySound(DukeSnd.DUKETALKTOBOSS, CHAN_AUTO, CHANF_UI); + bonuscnt++; + } + } + for (int t = 0, tt = 0; t < 20; t += 5, tt++) + if (breathe_tile[tt] != "" && (currentclock % 120) > breathe_time[tt] && (currentclock % 120) <= breathe_time2[tt]) + { + if (t == 5 && bonuscnt == 0) + { + Duke.PlaySound(DukeSnd.BOSSTALKTODUKE, CHAN_AUTO, CHANF_UI); + bonuscnt++; + } + breatheani = TexMan.CheckForTexture(breathe_tile[tt]); + } + } + + } + + override void Draw(double sr) + { + int etrans = Translation.MakeID(Translation_BasePalette, Duke.ENDINGPAL); + + let tex = TexMan.CheckForTexture("VICTORY1"); + int trans = TexMan.UseGamePalette(tex)? etrans : 0; + screen.DrawTexture(tex, false, 0, 50, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TranslationIndex, trans, DTA_LegacyRenderStyle, STYLE_Normal, DTA_TopLeft, true); + + if (bossani.isValid()) + { + trans = TexMan.UseGamePalette(tex)? etrans : 0; // re-check for different texture! + screen.DrawTexture(bossani, false, boss_x, boss_y, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TranslationIndex, trans, DTA_TopLeft, true); + } + + if (breathebg) + { + tex = TexMan.CheckForTexture("VICTORY9"); + trans = TexMan.UseGamePalette(tex)? etrans : 0; // re-check for different texture! + screen.DrawTexture(tex, false, 86, 59, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TranslationIndex, trans, DTA_TopLeft, true); + } + + if (breatheani.isValid()) + { + trans = TexMan.UseGamePalette(tex)? etrans : 0; // re-check for different texture! + screen.DrawTexture(breatheani, false, breathe_x, breathe_y, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TranslationIndex, trans, DTA_TopLeft, true); + } + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class E2EndScreen : ImageScreen +{ + ScreenJob Init() + { + Super.InitNamed("E2ENDSCREEN", fadein | fadeout | stopsound, 0x7fffffff, 0); + return self; + } + + override void Start() + { + Duke.PlaySound(DukeSnd.PIPEBOMB_EXPLODE, CHAN_AUTO, CHANF_UI); + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class Episode3End : ImageScreen +{ + int soundstate; + int finishtime; + + ScreenJob Init() + { + Super.InitNamed("radlogo.anm", fadein|fadeout, 0x7fffffff); + soundstate = 0; + finishtime = 0; + return self; + } + + override void OnSkip() + { + Raze.StopAllSounds(); + } + + override void OnTick() + { + switch (soundstate) + { + case 0: + Duke.PlaySound(DukeSnd.ENDSEQVOL3SND5, CHAN_AUTO, CHANF_UI); + soundstate++; + break; + + case 1: + if (!Duke.CheckSoundPlaying(DukeSnd.ENDSEQVOL3SND5)) + { + Duke.PlaySound(DukeSnd.ENDSEQVOL3SND6, CHAN_AUTO, CHANF_UI); + soundstate++; + } + break; + + case 2: + if (!Duke.CheckSoundPlaying(DukeSnd.ENDSEQVOL3SND6)) + { + Duke.PlaySound(DukeSnd.ENDSEQVOL3SND7, CHAN_AUTO, CHANF_UI); + soundstate++; + } + break; + + case 3: + if (!Duke.CheckSoundPlaying(DukeSnd.ENDSEQVOL3SND7)) + { + Duke.PlaySound(DukeSnd.ENDSEQVOL3SND8, CHAN_AUTO, CHANF_UI); + soundstate++; + } + break; + + case 4: + if (!Duke.CheckSoundPlaying(DukeSnd.ENDSEQVOL3SND8)) + { + Duke.PlaySound(DukeSnd.ENDSEQVOL3SND9, CHAN_AUTO, CHANF_UI); + soundstate++; + } + break; + + case 5: + if (!Duke.CheckSoundPlaying(DukeSnd.ENDSEQVOL3SND9)) + { + soundstate++; + finishtime = ticks + GameTicRate * (Raze.SoundEnabled() ? 1 : 5); // if sound is off this wouldn't wait without a longer delay here. + } + break; + + case 6: + if (Raze.isPlutoPak()) + { + if (ticks > finishtime) jobstate = finished; + } + break; + + default: + break; + } + if (jobstate != running) Raze.StopAllSounds(); + } + + override void OnDestroy() + { + if (!Raze.isPlutoPak()) Duke.PlaySound(DukeSnd.ENDSEQVOL3SND4, CHAN_AUTO, CHANF_UI); + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class Episode4Text : SkippableScreenJob +{ + ScreenJob Init() + { + Super.Init(fadein|fadeout); + return self; + } + + + override void Draw(double sm) + { + Duke.BigText(160, 60, "$Thanks to all our", 0); + Duke.BigText(160, 60 + 16, "$fans for giving", 0); + Duke.BigText(160, 60 + 16 + 16, "$us big heads.", 0); + Duke.BigText(160, 70 + 16 + 16 + 16, "$Look for a Duke Nukem 3D", 0); + Duke.BigText(160, 70 + 16 + 16 + 16 + 16, "$sequel soon.", 0); + } + + override void Start() + { + Duke.PlaySound(DukeSnd.ENDSEQVOL3SND4, CHAN_AUTO, CHANF_UI); + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class Episode5End : ImageScreen +{ + ScreenJob Init() + { + Super.InitNamed("FIREFLYGROWEFFECT", fadein|fadeout|stopsound); + return self; + } + + override void OnTick() + { + if (ticks == 1) Duke.PlaySound(DukeSnd.E5L7_DUKE_QUIT_YOU, CHAN_AUTO, CHANF_UI); + } +} + +//--------------------------------------------------------------------------- +// +// This handles both Duke and RR. +// +//--------------------------------------------------------------------------- + +class DukeMultiplayerBonusScreen : SkippableScreenJob +{ + int playerswhenstarted; + + ScreenJob Init(int pws) + { + Super.Init(fadein|fadeout); + playerswhenstarted = pws; + return self; + } + + override void Start() + { + if (!Raze.isRR()) Duke.PlayBonusMusic(); + } + + override void Draw(double smoothratio) + { + bool isRR = Raze.isRR(); + double titlescale = isRR? 0.36 : 1; + + String tempbuf; + int currentclock = int((ticks + smoothratio) * 120 / GameTicRate); + Screen.DrawTexture(TexMan.CheckForTexture("MENUSCREEN"), false, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_Color, 0xff808080, DTA_LegacyRenderStyle, STYLE_Normal); + Screen.DrawTexture(TexMan.CheckForTexture("INGAMEDUKETHREEDEE"), true, 160, 34, DTA_FullscreenScale, FSMode_Fit320x200, DTA_CenterOffsetRel, true, DTA_ScaleX, titlescale, DTA_ScaleY, titlescale); + if (Raze.isPlutoPak()) Screen.DrawTexture(TexMan.CheckForTexture("MENUPLUTOPAKSPRITE"), true, 260, 36, DTA_FullscreenScale, FSMode_Fit320x200, DTA_CenterOffsetRel, true); + + Duke.GameText(160, isRR? 58 : 58 + 2, "$Multiplayer Totals", 0, 0); + Duke.GameText(160, 58 + 10, currentLevel.DisplayName(), 0, 0); + Duke.GameText(160, 165, "$Presskey", 8 - int(sin(currentclock / 10.) * 8), 0); + + int t = 0; + + Duke.MiniText(38, 80, "$Name", 0, -1, isRR? 0 : 8); + Duke.MiniText(269+20, 80, "$Kills", 0, 1, isRR? 0: 8); + + for (int i = 0; i < playerswhenstarted; i++) + { + tempbuf = String.Format("%-4d", i + 1); + Duke.MiniText(92 + (i * 23), 80, tempbuf, 0, -1, isRR? 0: 3); + } + + for (int i = 0; i < playerswhenstarted; i++) + { + int xfragtotal = 0; + tempbuf = String.Format("%d", i + 1); + + Duke.MiniText(30, 90 + t, tempbuf, 0); + Duke.MiniText(38, 90 + t, Raze.PlayerName(i), 0, -1, Raze.playerPalette(i)); + + for (int y = 0; y < playerswhenstarted; y++) + { + int frag = Raze.playerFrags(i, y); + if (i == y) + { + int fraggedself = Raze.playerFraggedSelf(y); + tempbuf = String.Format("%-4d", fraggedself); + Duke.MiniText(92 + (y * 23), 90 + t, tempbuf, 0, -1, isRR? 0: 2); + xfragtotal -= fraggedself; + } + else + { + tempbuf = String.Format("%-4d", frag); + Duke.MiniText(92 + (y * 23), 90 + t, tempbuf, 0); + xfragtotal += frag; + } + /* + if (myconnectindex == connecthead) + { + tempbuf = String.Format("stats %ld killed %ld %ld\n", i + 1, y + 1, frag); + sendscore(tempbuf); + } + */ + } + + tempbuf = String.Format("%-4d", xfragtotal); + Duke.MiniText(101 + (8 * 23), 90 + t, tempbuf, 0, -1, isRR? 0: 2); + + t += 7; + } + + for (int y = 0; y < playerswhenstarted; y++) + { + int yfragtotal = 0; + for (int i = 0; i < playerswhenstarted; i++) + { + if (i == y) + yfragtotal += Raze.playerFraggedself(i); + int frag = Raze.playerFrags(i, y); + yfragtotal += frag; + } + tempbuf = String.Format("%-4d", yfragtotal); + Duke.MiniText(92 + (y * 23), 96 + (8 * 7), tempbuf, 0, -1, isRR? 0: 2); + } + + Duke.MiniText(45, 96 + (8 * 7), "$Deaths", 0, -1, isRR? 0: 8); + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class DukeLevelSummaryScreen : SummaryScreenBase +{ + String lastmapname; + int speech; + int displaystate; + int dukeAnimStart; + + TextureID texBg; + TextureID texOv[4]; + + enum EScreenFlags + { + printTimeText = 1, + printTimeVal = 2, + printKillsText = 4, + printKillsVal = 8, + printSecretsText = 16, + printSecretsVal = 32, + printStatsAll = 63, + dukeAnim = 64, + dukeWait = 128, + + } + + ScreenJob Init(MapRecord m, SummaryInfo s) + { + Super.Init(fadein | fadeout); + SetParameters(m, s); + String basetex = level.InterBackground; + if (basetex.length() == 0) + { + let cluster = level.GetCluster(); + if (cluster != null) basetex = cluster.InterBackground; + } + if (basetex.length() == 0) basetex = "BONUSSCREEN"; + texBg = TexMan.CheckForTexture(basetex); + for(int i = 0; i < 4; i++) + { + String otex = String.Format("%s_O%d", basetex, i+1); + texOv[i] = TexMan.CheckForTexture(otex); + } + lastmapname = level.DisplayName(); + speech = -1; + displaystate = 0; + return self; + } + + override bool OnEvent(InputEvent ev) + { + if (ev.type == InputEvent.Type_KeyDown && !Raze.specialKeyEvent(ev)) + { + if ((displaystate & printStatsAll) != printStatsAll) + { + Duke.PlaySound(DukeSnd.PIPEBOMB_EXPLODE, CHAN_AUTO, CHANF_UI); + displaystate = printStatsAll; + } + else if (!(displaystate & dukeAnim)) + { + displaystate |= dukeAnim; + dukeAnimStart = ticks; + Duke.PlaySound(DukeSnd.SHOTGUN_COCK, CHAN_AUTO, CHANF_UI); + static const int speeches[] = { DukeSnd.BONUS_SPEECH1, DukeSnd.BONUS_SPEECH2, DukeSnd.BONUS_SPEECH3, DukeSnd.BONUS_SPEECH4 }; + speech = speeches[random(0, 3)]; + Duke.PlaySound(speech, CHAN_AUTO, CHANF_UI, 1); + } + return true; + } + return false; + } + + override void Start() + { + Duke.PlayBonusMusic(); + } + + override void OnTick() + { + if ((displaystate & printStatsAll) != printStatsAll) + { + if (ticks == 15 * 3) + { + displaystate |= printTimeText; + } + else if (ticks == 15 * 4) + { + displaystate |= printTimeVal; + Duke.PlaySound(DukeSnd.PIPEBOMB_EXPLODE, CHAN_AUTO, CHANF_UI); + } + else if (ticks == 15 * 6) + { + displaystate |= printKillsText; + Duke.PlaySound(DukeSnd.FLY_BY, CHAN_AUTO, CHANF_UI); + } + else if (ticks == 15 * 7) + { + displaystate |= printKillsVal; + Duke.PlaySound(DukeSnd.PIPEBOMB_EXPLODE, CHAN_AUTO, CHANF_UI); + } + else if (ticks == 15 * 9) + { + displaystate |= printSecretsText; + } + else if (ticks == 15 * 10) + { + displaystate |= printSecretsVal; + Duke.PlaySound(DukeSnd.PIPEBOMB_EXPLODE, CHAN_AUTO, CHANF_UI); + } + } + if (displaystate & dukeAnim) + { + if (ticks >= dukeAnimStart + 60) + { + displaystate ^= dukeAnim | dukeWait; + } + } + if (displaystate & dukeWait) + { + if (speech <= 0 || !Duke.CheckSoundPlaying(speech)) + jobstate = finished; + } + } + + void PrintTime() + { + String tempbuf; + Duke.GameText(10, 59 + 9, "$TXT_YourTime", 0); + Duke.GameText(10, 69 + 9, "$TXT_ParTime", 0); + if (!Raze.isNamWW2GI()) + Duke.GameText(10, 79 + 9, "$TXT_3DRTIME", 0); + + if (displaystate & printTimeVal) + { + tempbuf = FormatTime(stats.time); + Duke.GameText((320 >> 2) + 71, 59 + 9, tempbuf, 0); + + tempbuf = FormatTime(level.parTime); + Duke.GameText((320 >> 2) + 71, 69 + 9, tempbuf, 0); + + if (!Raze.isNamWW2GI()) + { + tempbuf = FormatTime(level.designerTime); + Duke.GameText((320 >> 2) + 71, 79 + 9, tempbuf, 0); + } + } + } + + void PrintKills() + { + String tempbuf; + Duke.GameText(10, 94 + 9, "$TXT_EnemiesKilled", 0); + Duke.GameText(10, 104 + 9, "$TXT_EnemiesLeft", 0); + + if (displaystate & printKillsVal) + { + tempbuf = String.Format("%-3d", stats.kills); + Duke.GameText((320 >> 2) + 70, 94 + 9, tempbuf, 0); + + if (stats.maxkills < 0) + { + tempbuf = "$TXT_N_A"; + } + else + { + tempbuf = String.Format("%-3d", max(0, stats.maxkills - stats.kills)); + } + Duke.GameText((320 >> 2) + 70, 104 + 9, tempbuf, 0); + } + } + + void PrintSecrets() + { + String tempbuf; + Duke.GameText(10, 119 + 9, "$TXT_SECFND", 0); + Duke.GameText(10, 129 + 9, "$TXT_SECMISS", 0); + + if (displaystate & printSecretsVal) + { + tempbuf = String.Format("%-3d", stats.secrets); + Duke.GameText((320 >> 2) + 70, 119 + 9, tempbuf, 0); + tempbuf = String.Format("%-3d", max(0, stats.maxsecrets - stats.secrets)); + Duke.GameText((320 >> 2) + 70, 129 + 9, tempbuf, 0); + } + } + + override void Draw(double sr) + { + Screen.DrawTexture(texBg, true, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal); + + Duke.GameText(160, 190, "$PRESSKEY", 8 - (sin(ticks * 4) * 8), 0); + + if (displaystate & printTimeText) + { + PrintTime(); + } + if (displaystate & printKillsText) + { + PrintKills(); + } + if (displaystate & printSecretsText) + { + PrintSecrets(); + } + + if (displaystate & dukeAnim) + { + switch (((ticks - dukeAnimStart) >> 2) % 15) + { + case 0: + case 1: + case 4: + case 5: + Screen.DrawTexture(texOv[2], true, 199, 31, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TopLeft, true); + break; + case 2: + case 3: + Screen.DrawTexture(texOv[3], true, 199, 31, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TopLeft, true); + break; + } + } + else if (!(displaystate & dukeWait)) + { + switch((ticks >> 3) & 3) + { + case 1: + case 3: + Screen.DrawTexture(texOv[0], true, 199, 31, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TopLeft, true); + break; + case 2: + Screen.DrawTexture(texOv[1], true, 199, 31, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TopLeft, true); + break; + } + } + + if (lastmapname) Duke.BigText(160, 20 - 6, lastmapname, 0); + Duke.BigText(160, 36 - 6, "$Completed", 0); + } + +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class RRLevelSummaryScreen : SummaryScreenBase +{ + String lastmapname; + int speech; + int displaystate; + int exitSoundStart; + TextureID texBg; + + enum EFlags + { + printTimeText = 1, + printTimeVal = 2, + printKillsText = 4, + printKillsVal = 8, + printSecretsText = 16, + printSecretsVal = 32, + printStatsAll = 63, + exitSound = 64, + exitWait = 128, + + } + + ScreenJob Init(MapRecord m, SummaryInfo s, bool dofadeout = true) + { + Super.Init(dofadeout? (fadein | fadeout) : fadein); + SetParameters(m, s); + String basetex = level.InterBackground; + if (basetex.length() == 0) + { + let cluster = level.GetCluster(); + if (cluster != null) basetex = cluster.InterBackground; + } + if (basetex.length() == 0) basetex = "BONUSPIC01"; + + lastmapname = level.DisplayName(); + texBg = TexMan.CheckForTexture(basetex); + return self; + } + + override bool OnEvent(InputEvent ev) + { + if (ev.type == InputEvent.Type_KeyDown && !Raze.specialKeyEvent(ev)) + { + if ((displaystate & printStatsAll) != printStatsAll) + { + Duke.PlaySound(RRSnd.FART1, CHAN_AUTO, CHANF_UI); + displaystate = printStatsAll; + } + else if (!(displaystate & exitSound)) + { + displaystate |= exitSound; + exitSoundStart = ticks; + Duke.PlaySound(RRSnd.CHUG, CHAN_AUTO, CHANF_UI); + static const int speeches[] = { RRSnd.BNS_SPCH1, RRSnd.BNS_SPCH2, RRSnd.BNS_SPCH3, RRSnd.BNS_SPCH4 }; + speech = speeches[random(0, 3)]; + Duke.PlaySound(speech, CHAN_AUTO, CHANF_UI); + } + return true; + } + return false; + } + + override void OnTick() + { + if ((displaystate & printStatsAll) != printStatsAll) + { + if (ticks == 15 * 3) + { + displaystate |= printTimeText; + } + else if (ticks == 15 * 4) + { + displaystate |= printTimeVal; + Duke.PlaySound(RRSnd.FART1, CHAN_AUTO, CHANF_UI); + } + else if (ticks == 15 * 6) + { + displaystate |= printKillsText; + } + else if (ticks == 15 * 7) + { + displaystate |= printKillsVal; + Duke.PlaySound(RRSnd.FART1, CHAN_AUTO, CHANF_UI); + } + else if (ticks == 15 * 9) + { + displaystate |= printSecretsText; + } + else if (ticks == 15 * 10) + { + displaystate |= printSecretsVal; + Duke.PlaySound(RRSnd.FART1, CHAN_AUTO, CHANF_UI); + } + } + if (displaystate & exitSound) + { + if (ticks >= exitSoundStart + 60) + { + displaystate ^= exitSound | exitWait; + } + } + if (displaystate & exitWait) + { + if (speech <= 0 || !Duke.CheckSoundPlaying(speech)) + jobstate = finished; + } + } + + void PrintTime() + { + String tempbuf; + Duke.BigText(30, 48, "$TXT_YerTime", -1); + Duke.BigText(30, 64, "$TXT_ParTime", -1); + Duke.BigText(30, 80, "$TXT_XTRTIME", -1); + + if (displaystate & printTimeVal) + { + tempbuf = FormatTime(stats.time); + Duke.BigText(191, 48, tempbuf, -1); + + tempbuf = FormatTime(level.parTime); + Duke.BigText(191, 64, tempbuf, -1); + + tempbuf = FormatTime(level.designerTime); + Duke.BigText(191, 80, tempbuf, -1); + } + } + + void PrintKills() + { + String tempbuf; + Duke.BigText(30, 112, "$TXT_VarmintsKilled", -1); + Duke.BigText(30, 128, "$TXT_VarmintsLeft", -1); + + if (displaystate & printKillsVal) + { + tempbuf = String.Format("%-3d", stats.kills); + Duke.BigText(231, 112, tempbuf, -1); + if (stats.maxkills < 0) + { + tempbuf = "$TXT_N_A"; + } + else + { + tempbuf = String.Format("%-3d", max(0, stats.maxkills - stats.kills)); + } + Duke.BigText(231, 128, tempbuf, -1); + } + } + + void PrintSecrets() + { + String tempbuf; + Duke.BigText(30, 144, "$TXT_SECFND", -1); + Duke.BigText(30, 160, "$TXT_SECMISS", -1); + + if (displaystate & printSecretsVal) + { + tempbuf = String.Format("%-3d", stats.secrets); + Duke.BigText(231, 144, tempbuf, -1); + tempbuf = String.Format("%-3d", max(0, stats.maxsecrets - stats.secrets)); + Duke.BigText(231, 160, tempbuf, -1); + } + } + + override void Draw(double sr) + { + Screen.DrawTexture(texBg, true, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal); + + Duke.BigText(80, 16, lastmapname, -1); + Duke.BigText(15, 192, "$PRESSKEY", -1); + + if (displaystate & printTimeText) + { + PrintTime(); + } + if (displaystate & printKillsText) + { + PrintKills(); + } + if (displaystate & printSecretsText) + { + PrintSecrets(); + } + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class RRRAEndOfGame : SkippableScreenJob +{ + ScreenJob Init() + { + Super.Init(fadein|fadeout); + return self; + } + + override void OnSkip() + { + Duke.StopSound(RRSnd.LN_FINAL); + } + + override void Start() + { + Duke.PlaySound(RRSnd.LN_FINAL, CHAN_AUTO, CHANF_UI); + } + + override void OnTick() + { + if (!Duke.CheckSoundPlaying(RRSnd.LN_FINAL) && ticks > 15 * GameTicRate) jobstate = finished; // make sure it stays, even if sound is off. + } + + override void Draw(double sr) + { + let tex = TexMan.CheckForTexture(((ticks >> 2) & 1)? "ENDGAME2" : "ENDGAME"); + Screen.DrawTexture(tex, true, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43); + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class DukeLoadScreen : ScreenJob +{ + MapRecord rec; + + ScreenJob Init(MapRecord maprec) + { + Super.Init(fadein); + rec = maprec; + return self; + } + + override void OnTick() + { + if (fadestate == visible) jobstate = finished; + } + + override void Draw(double sr) + { + Screen.DrawTexture(TexMan.CheckForTexture("LOADSCREEN"), false, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal); + + if (!Raze.IsRR()) + { + Duke.BigText(160, 90, (rec.flags & MapRecord.USERMAP)? "$TXT_LOADUM" : "$TXT_LOADING", 0); + Duke.BigText(160, 114, rec.DisplayName(), 0); + } + else + { + int y = Raze.isRRRA()? 140 : 90; + Duke.BigText(160, y, (rec.flags & MapRecord.USERMAP)? "$TXT_ENTRUM" : "$TXT_ENTERIN", 0); + Duke.BigText(160, y+24, rec.DisplayName(), 0); + } + } +} + diff --git a/wadsrc/static/zscript/games/exhumed/exhumedgame.zs b/wadsrc/static/zscript/games/exhumed/exhumedgame.zs new file mode 100644 index 000000000..49597d2f8 --- /dev/null +++ b/wadsrc/static/zscript/games/exhumed/exhumedgame.zs @@ -0,0 +1,104 @@ + + +struct Exhumed native +{ + native static void PlayLocalSound(int snd, int pitch, bool b, int chanf); + native static void StopLocalSound(); + native static bool LocalSoundPlaying(); + native static void playCDTrack(int track, bool looped); + native static void DrawPlasma(); + + + static void DrawAbs(String img, int x, int y, int shade = 0) + { + Screen.DrawTexture(TexMan.CheckForTexture(img, TexMan.Type_Any), false, x, y, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TopLeft, true, DTA_Color, Raze.shadeToLight(shade)); + } + + static void DRawRel(String img, int x, int y, int shade = 0) + { + let tex = TexMan.CheckForTexture(img, TexMan.Type_Any); + if (!tex.IsValid()) return; + let size = TexMan.GetScaledSize(tex); + let offs = TexMan.GetScaledOffset(tex); + // The integer truncation here is important. Old Build versions were bugged here. + x -= (int(size.x) >> 1) + int(offs.x); + y -= (int(size.y) >> 1) + int(offs.y); + Screen.DrawTexture(tex, false, x, y, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TopLeft, true, DTA_Color, Raze.shadeToLight(shade)); + } +} + + +struct ExhumedSnd native +{ + enum ESounds + { + kSound0 = 0, + kSound1, + kSound2, + kSound3, + kSound4, + kSound5, + kSound6, + kSound7, + kSound8, + kSound9, + kSoundItemSpecial, + kSound11, + kSoundTorchOn, + kSound13, + kSound14, + kSound15, + kSound16, + kSound17, + kSound18, + kSound19, + kSound20, + kSound21, + kSound22, + kSound23, + kSound24, + kSound25, + kSound26, + kSound27, + kSoundJonLaugh2, + kSound29, + kSound30, + kSound31, + kSound32, + kSound33, + kSound34, + kSound35, + kSound36, + kSound38 = 38, + kSound39, + kSound40, + kSound41, + kSound42, + kSound43, + kSound47 = 47, + kSound48 = 48, + kSoundQTail = 50, + kSound52 = 52, + kSoundTauntStart = 53, + kSoundJonFDie = 60, + kSound61, + kSound62, + kSound63, + kSound64, + kSound65, + kSound66, + kSoundMana1, + kSoundMana2, + kSoundAmmoPickup, + kSound70, + kSound71, + kSound72, + kSoundAlarm, + kSound74, + kSound75, + kSound76, + kSound77, + kSound78, + kSound79, + } +} \ No newline at end of file diff --git a/wadsrc/static/zscript/games/exhumed/ui/menu.zs b/wadsrc/static/zscript/games/exhumed/ui/menu.zs index 52101751d..46b908fb1 100644 --- a/wadsrc/static/zscript/games/exhumed/ui/menu.zs +++ b/wadsrc/static/zscript/games/exhumed/ui/menu.zs @@ -50,7 +50,10 @@ class ListMenuItemExhumedPlasma : ListMenuItem Super.Init(0, 0); } - native override void Draw(bool selected, ListMenuDescriptor desc); + override void Draw(bool selected, ListMenuDescriptor desc) + { + Exhumed.DrawPlasma(); + } } class ListMenuItemExhumedLogo : ListMenuItem @@ -98,10 +101,10 @@ class ListMenuItemExhumedTextItem : ListMenuItemTextItem double y = mYpos + v.y / 2; int shade; - if (selected) shade = Build.CalcSinTableValue(MSTime() * 16 * 120 / 1000) >> 9; + if (selected) shade = Raze.CalcSinTableValue(MSTime() * 16 * 120 / 1000) >> 9; else if (Selectable()) shade = 0; else shade = 25; - let color = Build.shadeToLight(shade); + let color = Raze.shadeToLight(shade); double scalex = 1.; // Squash the text if it is too wide. Due to design limitations we cannot expand the box here. :( if (texsize.X - 18 < width) diff --git a/wadsrc/static/zscript/games/exhumed/ui/screens.zs b/wadsrc/static/zscript/games/exhumed/ui/screens.zs new file mode 100644 index 000000000..133e16418 --- /dev/null +++ b/wadsrc/static/zscript/games/exhumed/ui/screens.zs @@ -0,0 +1,927 @@ +//------------------------------------------------------------------------- +/* +Copyright (C) 2010-2019 EDuke32 developers and contributors +Copyright (C) 2019 sirlemonhead, Nuke.YKT +Copyright (C) 2020-2021 Christoph Oelckers +This file is part of Raze. +PCExhumed is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License version 2 +as published by the Free Software Foundation. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +See the GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +//------------------------------------------------------------------------- + +struct LMFDecoder native +{ + static native bool Identify(String fn); + static native LMFDecoder Create(String fn); + native bool Frame(double clock); + native TextureID GetTexture(); + native void Close(); +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class LmfPlayer : SkippableScreenJob +{ + LMFDecoder decoder; + double nextclock; + String fn; + + ScreenJob Init(String filename) + { + fn = filename; + return self; + } + + override void Start() + { + decoder = LMFDecoder.Create(fn); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + override void Draw(double smoothratio) + { + double clock = (ticks + smoothratio) * 1000000000. / GameTicRate; + if (clock >= nextclock) + { + if (decoder.Frame(clock)) + { + jobstate = finished; + return; + } + } + + double duration = clock * (120. / 8000000000.); + double z = 2048 * duration; + if (z > 65536) z = 65536; + + double angle = 1536. + 16. * duration; + if (angle >= 2048.) angle = 0.; + + Screen.DrawTexture(decoder.getTexture(), false, 160, 100, DTA_FullscreenScale, FSMode_Fit320x200, + DTA_CenterOffset, true, DTA_FlipY, true, DTA_ScaleX, z / 65536., DTA_ScaleY, z / 65536., DTA_Rotate, (-angle - 512) * (360. / 2048.)); + } + + override void OnDestroy() + { + decoder.Close(); + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class LobotomyScreen : ImageScreen +{ + ScreenJob Init(String texname, int fade) + { + Super.InitNamed(texname, fade); + return self; + } + + override void OnSkip() + { + Exhumed.StopLocalSound(); + } + + override void Start() + { + Exhumed.PlayLocalSound(ExhumedSnd.kSoundJonLaugh2, 7000, false, CHANF_UI); + } + + override void OnTick() + { + Super.OnTick(); + if (jobstate == finished) Exhumed.StopLocalSound(); + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + + +class MainTitle : SkippableScreenJob +{ + String a, b; + int mystate; + int duration; + int var_4; + int esi; + int nCount; + int starttime; + static const short skullDurations[] = { 6, 25, 43, 50, 68, 78, 101, 111, 134, 158, 173, 230, 600 }; + + ScreenJob Init() + { + Super.Init(fadein); + a = StringTable.Localize("$TXT_EX_COPYRIGHT1"); + b = StringTable.Localize("$TXT_EX_COPYRIGHT2"); + duration = skullDurations[0]; + esi = 130; + return self; + } + + override void Start() + { + Exhumed.PlayLocalSound(59, 0, true, CHANF_UI); + Exhumed.playCDtrack(19, true); + } + + override void OnTick() + { + int ticker = ticks * 120 / GameTicRate; + if (ticks > 1 && mystate == 0 && !Exhumed.LocalSoundPlaying()) + { + if (random(0, 15)) + Exhumed.PlayLocalSound(ExhumedSnd.kSoundJonLaugh2, 0, false, CHANF_UI); + else + Exhumed.PlayLocalSound(61, 0, false, CHANF_UI); + mystate = 1; + starttime = ticker; + } + if (mystate == 1) + { + if (ticker > duration) + { + nCount++; + if (nCount > 12) + { + jobstate = finished; + return; + } + duration = starttime + skullDurations[nCount]; + var_4 = var_4 == 0; + } + } + } + + override void Draw(double sr) + { + Exhumed.DrawPlasma(); + Exhumed.DrawRel("SkullHead", 160, 100); + if (mystate == 0) + { + Exhumed.DrawRel("SkullJaw", 161, 130); + } + else + { + int nStringWidth = SmallFont.StringWidth(a); + Screen.DrawText(SmallFont, Font.CR_UNTRANSLATED, 160 - nStringWidth / 2, 200 - 24, a, DTA_FullscreenScale, FSMode_Fit320x200); + nStringWidth = SmallFont.StringWidth(b); + Screen.DrawText(SmallFont, Font.CR_UNTRANSLATED, 160 - nStringWidth / 2, 200 - 16, b, DTA_FullscreenScale, FSMode_Fit320x200); + + + String nTile = "SkullJaw"; + + if (var_4) + { + if (esi >= 135) nTile = "SkullJaw2"; + else esi += 5; + } + else if (esi <= 130) esi = 130; + else esi -= 2; + + int y; + + if (nTile == "SkullJaw2") + { + y = 131; + } + else + { + y = esi; + if (y > 135) y = 135; + } + + Exhumed.DrawRel(nTile, 161, y); + } + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class MapScreen : ScreenJob +{ + static const int MapLevelOffsets[] = { 0, 50, 10, 20, 0, 45, -20, 20, 5, 0, -10, 10, 30, -20, 0, 20, 0, 0, 0, 0 }; + static const int MapPlaqueX[] = { 100, 230, 180, 10, 210, 10, 10, 140, 30, 200, 145, 80, 15, 220, 190, 20, 220, 20, 200, 20 }; + static const int MapPlaqueY[] = { 170, 10, 125, 95, 160, 110, 50, 0, 20, 150, 170, 80, 0, 35, 40, 130, 160, 10, 10, 10 }; + static const int MapPlaqueTextX[] = { 18, 18, 18, 18, 18, 18, 18, 18, 18, 20, 18, 18, 18, 18, 18, 19, 18, 18, 18, 19 }; + static const int MapPlaqueTextY[] = { 6, 6, 6, 6, 6, 6, 6, 6, 6, 4, 6, 6, 5, 6, 6, 6, 6, 6, 5, 4 }; + + static const int FireTilesX[] = { 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1 }; + static const int FireTilesY[] = { 3, 0, 3, 0, 0, 0, 1, 1, 2, 0, 2, 0 }; + + static const int MapLevelFires[] = { + 3, 0, 107, 95 , 1, 58, 140 , 2, 28, 38 , + 3, 2, 240, 0 , 0, 237, 32 , 1, 200, 30 , + 2, 2, 250, 57 , 0, 250, 43 , 2, 200, 70 , + 2, 1, 82, 59 , 2, 84, 16 , 0, 10, 95 , + 2, 2, 237, 50 , 1, 215, 42 , 1, 210, 50 , + 3, 0, 40, 7 , 1, 75, 6 , 2, 100, 10 , + 3, 0, 58, 61 , 1, 85, 80 , 2, 111, 63 , + 3, 0, 260, 65 , 1, 228, 0 , 2, 259, 15 , + 2, 0, 81, 38 , 2, 58, 38 , 2, 30, 20 , + 3, 0, 259, 49 , 1, 248, 76 , 2, 290, 65 , + 3, 2, 227, 66 , 0, 224, 98 , 1, 277, 30 , + 2, 0, 100, 10 , 2, 48, 76 , 2, 80, 80 , + 3, 0, 17, 2 , 1, 29, 49 , 2, 53, 28 , + 3, 0, 266, 42 , 1, 283, 99 , 2, 243, 108 , + 2, 0, 238, 19 , 2, 240, 92 , 2, 190, 40 , + 2, 0, 27, 0 , 1, 70, 40 , 0, 20, 130 , + 3, 0, 275, 65 , 1, 235, 8 , 2, 274, 6 , + 3, 0, 75, 45 , 1, 152, 105 , 2, 24, 68 , + 3, 0, 290, 25 , 1, 225, 63 , 2, 260, 110 , + 0, 1, 20, 10 , 1, 20, 10 , 1, 20, 10 + }; + + const FIRE_SIZE = 10; + const FIRE_TYPE = 1; + const FIRE_XOFS = 2; + const FIRE_YOFS = 3; + const FIRE_ELEMENT_SIZE = 3; + + int x; + int delta; + int nIdleSeconds; + + int curYPos, destYPos; + int nLevel, nLevelNew, nLevelBest; + + native static void SetNextLevel(int num); + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + ScreenJob Init(int oldlevel, int newlevel, int maxlevel) + { + Super.Init(fadein|fadeout); + nLevel = oldlevel - 1; + nLevelNew = newlevel - 1; + nLevelBest = min(maxlevel, 19) - 1; + curYPos = MapLevelOffsets[nLevel] + (200 * (nLevel / 2)); + destYPos = MapLevelOffsets[nLevelNew] + (200 * (nLevelNew / 2)); + if (curYPos < destYPos) delta = 2; + else if (curYPos > destYPos) delta = -2; + // Trim smoke in widescreen + /* + vec2_t mapwinxy1 = windowxy1, mapwinxy2 = windowxy2; + int32_t width = mapwinxy2.x - mapwinxy1.x + 1, height = mapwinxy2.y - mapwinxy1.y + 1; + if (3 * width > 4 * height) + { + mapwinxy1.x += (width - 4 * height / 3) / 2; + mapwinxy2.x -= (width - 4 * height / 3) / 2; + } + */ + return self; + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + override bool OnEvent(InputEvent ev) + { + if (ev.type == InputEvent.Type_KeyDown) + { + int key = ev.KeyScan; + let binding = Bindings.GetBinding(key); + if (key == InputEvent.KEY_UPARROW || key == InputEvent.KEY_PAD_DPAD_UP || key == InputEvent.Key_kpad_8 || binding ~== "+move_forward") + { + if (curYPos == destYPos && nLevelNew <= nLevelBest) + { + nLevelNew++; + SetNextLevel(nLevelNew + 1); + destYPos = MapLevelOffsets[nLevelNew] + (200 * (nLevelNew / 2)); + + if (curYPos <= destYPos) delta = 2; + else delta = -2; + nIdleSeconds = 0; + } + return true; + } + + if (key == InputEvent.KEY_DOWNARROW || key == InputEvent.KEY_PAD_DPAD_DOWN || key == InputEvent.Key_kpad_2 || binding ~== "+move_backward") + { + if (curYPos == destYPos && nLevelNew > 0) + { + nLevelNew--; + SetNextLevel(nLevelNew + 1); + destYPos = MapLevelOffsets[nLevelNew] + (200 * (nLevelNew / 2)); + + if (curYPos <= destYPos) delta = 2; + else delta = -2; + nIdleSeconds = 0; + } + return true; + } + if (!Raze.specialKeyEvent(ev)) jobstate = skipped; + return true; + } + return false; + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + override void OnTick() + { + if (curYPos != destYPos) + { + // scroll the map every couple of ms + curYPos += delta; + + if ((curYPos > destYPos && delta > 0) || (curYPos < destYPos && delta < 0)) + curYPos = destYPos; + + nIdleSeconds = 0; + } + else nIdleSeconds++; + if (nIdleSeconds > 300) jobstate = finished; + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + override void Draw(double smoothratio) + { + int currentclock = int((ticks + smoothratio) * 120 / GameTicRate); + + int tileY = curYPos; + + // Draw the background screens + for (int i = 0; i < 10; i++) + { + let tex = String.Format("MapBG%02d", i+1); + Exhumed.DrawAbs(tex, x, tileY); + tileY -= 200; + } + + // for each level - drawing the 'level completed' on-fire smoke markers + for (int i = 0; i < 20; i++) + { + int screenY = (i >> 1) * -200; + + if (nLevelBest >= i) // check if the player has finished this level + { + for (int j = 0; j < MapLevelFires[i * FIRE_SIZE]; j++) + { + int nFireFrame = ((currentclock >> 4) & 3); + int elem = i * FIRE_SIZE + FIRE_ELEMENT_SIZE * j; + int nFireType = MapLevelFires[elem + FIRE_TYPE]; + int x = MapLevelFires[elem + FIRE_XOFS]; + int y = MapLevelFires[elem + FIRE_YOFS]; + + String nTile = String.Format("MAPFIRE_%d%d", nFireType+1, nFireFrame+1); + int smokeX = x + FireTilesX[nFireType*3 + nFireFrame]; + int smokeY = y + FireTilesY[nFireType*3 + nFireFrame] + curYPos + screenY; + + // Use rotatesprite to trim smoke in widescreen + Exhumed.DrawAbs(nTile, smokeX, smokeY); + // Todo: mask out the sides of the screen if the background is not widescreen. + } + } + + int t = (((currentclock & 16) >> 4)); + + String nTile = String.Format("MapPlaque%d_%02d", t+1, i+1); + + int nameX = mapPlaqueX[i]; + int nameY = mapPlaqueY[i] + curYPos + screenY; + + // Draw level name plaque + Exhumed.DrawAbs(nTile, nameX, nameY); + + int shade = 96; + + if (nLevelNew == i) + { + shade = (Raze.bsin(16 * currentclock) + 31) >> 8; + } + else if (nLevelBest >= i) + { + shade = 31; + } + + int textY = nameY + MapPlaqueTextY[i]; + int textX = nameX + MapPlaqueTextX[i]; + nTile = String.Format("MapPlaqueText_%02d", i+1); + + // draw the text, alternating between red and black + Exhumed.DrawAbs(nTile, textX, textY, shade); + } + } + +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class TextOverlay +{ + int nHeight; + double nCrawlY; + int palette; + BrokenLines screentext; + + void Init(String text, int pal) + { + screentext = SmallFont.BreakLines(StringTable.Localize(text), 320); + nCrawlY = 199; + nHeight = screentext.Count() * 10; + palette = pal; + } + + void DisplayText() + { + if (nHeight + nCrawlY > 0) + { + double y = nCrawlY; + for (int i = 0; i < screentext.Count() && y <= 199; i++) + { + if (y >= -10) + { + int x = 160 - screenText.StringWidth(i)/2; + Screen.DrawText(SmallFont, Font.CR_UNDEFINED, x, y, screentext.StringAt(i), DTA_FullscreenScale, FSMode_Fit320x200, DTA_TranslationIndex, palette); + } + y += 10; + } + } + } + + bool AdvanceCinemaText(double clock) + { + if (nHeight + nCrawlY > 0 || musplaying.handle) + { + nCrawlY = 199 - clock / 15.; + return false; + } + return true; + } +} + +//--------------------------------------------------------------------------- +// +// cinema (this has been stripped off all game logic that was still in here) +// +//--------------------------------------------------------------------------- + +class Cinema : SkippableScreenJob +{ + TextOverlay textov; + TextureID cinematile; + int currentCinemaPalette; + int cdtrack; + int palette; + bool done; + + ScreenJob Init(String bgTexture, String text, int pal, int cdtrk) + { + Super.Init(fadein|fadeout); + cinematile = TexMan.CheckForTexture(bgTexture, TexMan.Type_Any); + textov = new("TextOverlay"); + palette = Translation.MakeID(Translation_BasePalette, pal); + textov.Init(text, palette); + cdtrack = cdtrk; + return self; + } + + override void Start() + { + Raze.StopAllSounds(); + if (cdtrack != -1) + { + Exhumed.playCDtrack(cdtrack, false); + } + } + + override void OnTick() + { + if (done) jobstate = finished; + } + + override void Draw(double smoothratio) + { + Screen.DrawTexture(cinematile, false, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_TranslationIndex, palette); + textov.DisplayText(); + done = textov.AdvanceCinemaText((ticks + smoothratio) * (120. / GameTicRate)); + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class LastLevelCinema : ScreenJob +{ + int var_24; + int var_28; + + int ebp; + int phase; + int nextclock; + uint nStringTypeOn, nCharTypeOn; + int screencnt; + bool skiprequest; + + BrokenLines screentext; + Font printFont; + TextureID tex; + + ScreenJob Init() + { + Super.Init(fadein | fadeout); + var_24 = 16; + var_28 = 12; + nextclock = 4; + let p = StringTable.Localize("REQUIRED_CHARACTERS", false); + if (p == "REQUIRED_CHARACTERS") printFont = SmallFont2; + else printFont = ConFont; + return self; + } + + native static TextureID DoStatic(int a, int b); + native static TextureID UndoStatic(); + + void Phase1() + { + if (var_24 >= 116) + { + if (var_28 < 192) + var_28 += 20; + } + else + { + var_24 += 20; + } + + tex = DoStatic(var_28, var_24); + } + + bool InitPhase2() + { + let label = StringTable.Localize(String.Format("$TXT_EX_LASTLEVEL%d", screencnt + 1)); + screentext = printFont.BreakLines(label, 320); + if (screentext.Count() == 0) return false; + + nStringTypeOn = 0; + nCharTypeOn = 0; + + ebp = screentext.Count() * 4; // half height of the entire text + ebp = 81 - ebp; // offset from the screen's center. + tex = UndoStatic(); + return true; + } + + bool Phase3() + { + tex = DoStatic(var_28, var_24); + + if (var_28 > 20) + { + var_28 -= 20; + return true; + } + + if (var_24 > 20) + { + var_24 -= 20; + return true; + } + return false; + } + + void DisplayPhase2() + { + int yy = ebp; + + // for international content, use the generic 8x8 font. The original one is too small for expansion. + if (printFont == ConFont) + { + yy *= 2; + for (int i = 0; i < nStringTypeOn; i++, yy += 10) Screen.DrawText(ConFont, Font.CR_GREEN, 140, yy, screentext.StringAt(i), DTA_FullscreenScale, FSMode_Fit640x400); + Screen.DrawText(ConFont, Font.CR_GREEN, 140, yy, screentext.StringAt(nStringTypeOn), DTA_FullscreenScale, FSMode_Fit640x400, DTA_TextLen, nCharTypeOn); + } + else + { + for (int i = 0; i < nStringTypeOn; i++, yy += 8) Screen.DrawText(SmallFont2, Font.CR_UNTRANSLATED, 70, yy, screentext.StringAt(i), DTA_FullscreenScale, FSMode_Fit320x200); + Screen.DrawText(SmallFont2, Font.CR_UNTRANSLATED, 70, yy, screentext.StringAt(nStringTypeOn), DTA_FullscreenScale, FSMode_Fit320x200, DTA_TextLen, nCharTypeOn); + } + } + + override bool OnEvent(InputEvent ev) + { + if (ev.type == InputEvent.Type_KeyDown && !Raze.specialKeyEvent(ev)) skiprequest = true; + return true; + } + + override void Start() + { + Exhumed.PlayLocalSound(ExhumedSnd.kSound75, 0, false, CHANF_UI); + phase = 1; + } + + override void OnTick() + { + switch (phase) + { + case 1: + Phase1(); + if (skiprequest || ticks >= nextclock) + { + InitPhase2(); + phase = 2; + skiprequest = false; + } + break; + + case 2: + { + let text = screenText.StringAt(nStringTypeOn); + int chr; + [chr,nCharTypeOn] = text.GetNextCodePoint(nCharTypeOn); + + if (chr == 0) + { + nCharTypeOn = 0; + nStringTypeOn++; + if (nStringTypeOn >= screentext.Count()) + { + nextclock = (GameTicRate * (screentext.Count() + 2)) + ticks; + phase = 3; + } + + } + else + { + nCharTypeOn++; + if (chr != 32) Exhumed.PlayLocalSound(ExhumedSnd.kSound71, 0, false, CHANF_UI); + } + + if (skiprequest) + { + nextclock = (GameTicRate * (screentext.Count() + 2)) + ticks; + phase = 4; + } + break; + } + case 3: + if (ticks >= nextclock || skiprequest) + { + Exhumed.PlayLocalSound(ExhumedSnd.kSound75, 0, false, CHANF_UI); + phase = 4; + nextclock = ticks + 60; + skiprequest = false; + } + + case 4: + if (ticks >= nextclock) + { + skiprequest |= !Phase3(); + } + if (skiprequest) + { + // Go to the next text page. + if (screencnt != 2) + { + screencnt++; + nextclock = ticks + 60; + skiprequest = 0; + phase = 1; + } + else jobstate = finished; + } + + if (skiprequest) + { + jobstate = finished; + } + } + } + + override void Draw(double sm) + { + Screen.DrawTexture(tex, false, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43); + if (phase == 2 || phase == 3) DisplayPhase2(); + } + +} + +//--------------------------------------------------------------------------- +// +// Credits roll +// +//--------------------------------------------------------------------------- + +class ExCredits : ScreenJob +{ + Array credits; + Array pagelines; + int page; + int pagetime; + bool skiprequest; + + ScreenJob Init() + { + Super.Init(); + String text; + int lump = Wads.CheckNumForFullName("credits.txt"); + if (lump > -1) text = Wads.ReadLump(lump); + text.Substitute("\r", ""); + text.Split(credits, "\n\n"); + return self; + } + + override bool OnEvent(InputEvent ev) + { + if (ev.type == InputEvent.Type_KeyDown && !Raze.specialKeyEvent(ev)) skiprequest = true; + return true; + } + + override void Start() + { + if (credits.Size() == 0) + { + jobstate = finished; + return; + } + Exhumed.playCDtrack(19, false); + pagetime = 0; + page = -1; + } + + override void OnTick() + { + if (ticks >= pagetime || skiprequest) + { + page++; + if (page < credits.Size()) + credits[page].Split(pagelines, "\n"); + else + { + if (skiprequest || !musplaying.handle) + { + jobstate = finished; + return; + } + pagelines.Clear(); + } + pagetime = ticks + 60; // + } + } + + override void Draw(double smoothratio) + { + int y = 100 - ((10 * (pagelines.Size() - 1)) / 2); + + for (int i = 0; i < pagelines.Size(); i++) + { + int ptime = clamp((pagetime - ticks - smoothratio) * 1000 / GameTicRate, 0, 2000); // in milliseconds + int light; + + if (ptime < 255) light = ptime; + else if (ptime > 2000 - 255) light = 2000 - ptime; + else light = 255; + + let colr = Color(light, light, light); + + int nStringWidth = SmallFont.StringWidth(pagelines[i]); + Screen.DrawText(SmallFont, Font.CR_UNTRANSLATED, 160 - nStringWidth / 2, y, pagelines[i], DTA_FullscreenScale, FSMode_Fit320x200, DTA_Color, colr); + y += 10; + } + } +} + +class ExhumedCutscenes +{ + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildIntro(ScreenJobRunner runner) + { + let logo = (gameinfo.gameType & GAMEFLAG_EXHUMED) ? "TileBMGLogo" : "TilePIELogo"; + runner.Append(ImageScreen.CreateNamed(logo, ScreenJob.fadein | ScreenJob.fadeout)); + runner.Append(new("LobotomyScreen").Init("LobotomyLogo", ScreenJob.fadein | ScreenJob.fadeout)); + if (LMFDecoder.Identify("book.mov")) runner.Append(new("LMFPlayer").Init("book.mov")); + else runner.Append(MoviePlayerJob.Create("book.mov", 0)); + runner.Append(new("MainTitle").Init()); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildMap(ScreenJobRunner runner, MapRecord frommap, SummaryInfo info, MapRecord tomap) + { + // This is only defined for the regular levels. + int frommapnum = frommap == null? 1 : frommap.levelNumber; + if (fromMapnum < 1 || fromMapNum > 20 || tomap == null || tomap.levelNumber < 1 || tomap.levelNumber > 20) return; + + // hijack the super secret info in the summary info to convey the max. map because we won't need that field for its real purpose. + runner.Append(new("MapScreen").Init(fromMapNum, toMap.levelNumber, info.supersecrets)); + } + + //--------------------------------------------------------------------------- + // + // This removes all the insanity the original setup had with these. + // Simplicity rules! + // + //--------------------------------------------------------------------------- + + static void BuildCinemaBefore5(ScreenJobRunner runner) + { + runner.Append(new("Cinema").Init("TileCinema5", "$TXT_EX_CINEMA2", 3, 2)); + } + + static void BuildCinemaAfter10(ScreenJobRunner runner) + { + runner.Append(new("Cinema").Init("TileCinema10", "$TXT_EX_CINEMA4", 5, 3)); + } + + static void BuildCinemaBefore11(ScreenJobRunner runner) + { + runner.Append(new("Cinema").Init("TileCinema11", "$TXT_EX_CINEMA3", 1, 4)); + } + + static void BuildCinemaAfter15(ScreenJobRunner runner) + { + runner.Append(new("Cinema").Init("TileCinema15", "$TXT_EX_CINEMA6", 7, 6)); + } + + static void BuildCinemaBefore20(ScreenJobRunner runner) + { + runner.Append(new("LastLevelCinema").Init()); + } + + static void BuildCinemaAfter20(ScreenJobRunner runner) + { + runner.Append(new("Cinema").Init("TileCinema20", "$TXT_EX_CINEMA8", 6, 8)); + runner.Append(new("ExCredits").Init()); + } + + static void BuildCinemaLose(ScreenJobRunner runner) + { + runner.Append(new("Cinema").Init("TileCinemaLose", "$TXT_EX_CINEMA7", 4, 7)); + } + + + //--------------------------------------------------------------------------- + // + // player died + // + //--------------------------------------------------------------------------- + + void BuildGameOverScene(ScreenJobRunner runner, MapRecord map) + { + Raze.StopMusic(); + Exhumed.PlayLocalSound(ExhumedSnd.kSoundJonLaugh2, 0, false, CHANF_UI); + runner.Append(ImageScreen.CreateNamed("Gameover", ScreenJob.fadein | ScreenJob.fadeout, 0x7fffffff, Translation.MakeID(Translation_BasePalette, 16))); + } + +} diff --git a/wadsrc/static/zscript/games/sw/swgame.zs b/wadsrc/static/zscript/games/sw/swgame.zs new file mode 100644 index 000000000..3c2f60179 --- /dev/null +++ b/wadsrc/static/zscript/games/sw/swgame.zs @@ -0,0 +1,713 @@ +//------------------------------------------------------------------------- +/* +Copyright (C) 1997, 2005 - 3D Realms Entertainment +Copyright (C) 2019-2021 Christoph Oelckers + +This file is part of Raze + +Shadow Warrior is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +Original Source: 1997 - Frank Maddin and Jim Norwood +Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms +*/ +//------------------------------------------------------------------------- + +struct SW native +{ + enum ESWSoundFlag + { + v3df_none = 0, // Default, take no action, use all defaults + v3df_follow = 1, // 1 = Do coordinate updates on sound + // Use this only if the sprite won't be deleted soon + v3df_kill = 2, // 1 = Sound is to be deleted + v3df_doppler = 4, // 1 = Don't use doppler pitch variance + v3df_dontpan = 8, // 1 = Don't do panning of sound + v3df_ambient = 16, // 1 = Sound is ambient, use ambient struct info. + v3df_intermit = 32, // 1 = Intermittant sound + v3df_init = 64, // 1 = First pass of sound, don't play it. + // This is mainly used for intermittent sounds + v3df_nolookup = 128, // don't use ambient table lookup + } + + native static void PlaySound(int sound, int flags, int channel = CHAN_AUTO, int cflags = 0); + native static void StopSound(); + native static bool IsSoundPlaying(int channel); // soundEngine.IsSourcePlayingSomething(SOURCE_None, nullptr, CHAN_VOICE)) + native static void PlaySong(int trackid); + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void DrawString(int x, int y, String text, int shade, int pal, int align = -1) + { + if (align != -1) x -= SmallFont.StringWidth(text) * (align == 0 ? 0.5 : 1); + Screen.DrawText(SmallFont, Font.CR_UNDEFINED, x, y, text, DTA_FullscreenScale, FSMode_Fit320x200, + DTA_Color, Raze.shadeToLight(shade), DTA_TranslationIndex, Translation.MakeID(Translation_Remap, pal)); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void DrawSmallString(int x, int y, String text, int shade, int pal, int align = -1, double alpha = 1.) + { + if (align != -1) x -= SmallFont2.StringWidth(text) * (align == 0 ? 0.5 : 1); + Screen.DrawText(SmallFont2, Font.CR_UNDEFINED, x, y, text, DTA_FullscreenScale, FSMode_Fit320x200, + DTA_Color, Raze.shadeToLight(shade), DTA_TranslationIndex, Translation.MakeID(Translation_Remap, pal), DTA_Alpha, alpha); + } + + +} + + +struct SWSnd native +{ + enum ESounds + { + DIGI_NULL= 0, + DIGI_SWORDSWOOSH= 1, + DIGI_STAR= 2, + DIGI_STARCLINK= 3, + DIGI_STARWIZ= 4, + DIGI_UZIFIRE= 5, + DIGI_RICHOCHET1= 6, + DIGI_RICHOCHET2= 7, + DIGI_REMOVECLIP= 8, + DIGI_REPLACECLIP= 9, + DIGI_SHELL= 10, + DIGI_RIOTFIRE= 11, + DIGI_RIOTFIRE2= 12, + DIGI_RIOTRELOAD= 13, + DIGI_BOLTEXPLODE= 14, + DIGI_BOLTWIZ= 15, + DIGI_30MMFIRE= 16, + DIGI_30MMRELOAD= 17, + DIGI_30MMEXPLODE= 18, + DIGI_30MMWIZ= 19, + DIGI_HEADFIRE= 20, + DIGI_HEADSHOTWIZ= 21, + DIGI_HEADSHOTHIT= 22, + DIGI_MINETHROW= 23, + DIGI_MINEBOUNCE= 24, + DIGI_MINEBLOW= 25, + DIGI_MINEBEEP= 26, + DIGI_HEARTBEAT= 27, + DIGI_HEARTFIRE= 28, + DIGI_HEARTWIZ= 29, + DIGI_MISSLFIRE= 30, + DIGI_MISSLEXP= 31, + DIGI_RFWIZ= 32, + DIGI_NAPFIRE= 33, + DIGI_NAPWIZ= 34, + DIGI_NAPPUFF= 35, + DIGI_MIRVFIRE= 36, + DIGI_MIRVWIZ= 37, + DIGI_SPIRALFIRE= 38, + DIGI_SPIRALWIZ= 39, + DIGI_MAGIC1= 40, + DIGI_MAGIC2= 41, + DIGI_MAGIC3= 42, + DIGI_MAGIC4= 43, + DIGI_MAGIC5= 44, + DIGI_MAGIC6= 45, + DIGI_MAGIC7= 46, + DIGI_SWCLOAKUNCLOAK= 47, + DIGI_DHVOMIT= 48, + DIGI_DHCLUNK= 49, + DIGI_DHSQUISH= 50, + DIGI_NULL_DHSQUISH= 50, + DIGI_PROJECTILELAVAHIT=51, + DIGI_PROJECTILEWATERHIT=52, + DIGI_KEY= 53, + DIGI_ITEM= 54, + DIGI_BIGITEM= 55, + DIGI_BODYFALL1= 56, + DIGI_HITGROUND= 57, + DIGI_BODYSQUISH1= 58, + DIGI_BODYBURN= 59, + DIGI_BODYBURNSCREAM= 60, + DIGI_BODYCRUSHED1= 61, + DIGI_BODYHACKED1= 62, + DIGI_BODYSINGED= 63, + DIGI_DROWN= 64, + DIGI_SCREAM1= 65, + DIGI_SCREAM2= 66, + DIGI_SCREAM3= 67, + DIGI_HIT1= 68, + DIGI_ELECTRICUTE1= 69, + DIGI_REMOVEME= 70, + DIGI_IMPALED= 71, + DIGI_OOF1= 72, + DIGI_ACTORBODYFALL1= 73, + IGI_ACTORHITGROUND= 74, + DIGI_COOLIEEXPLODE= 75, + DIGI_COOLIESCREAM= 76, + DIGI_COOLIEALERT= 77, + DIGI_COOLIEAMBIENT= 78, + DIGI_COOLIEPAIN= 79, + DIGI_CGMATERIALIZE= 80, + DIGI_CGALERT= 81, + DIGI_CGTHIGHBONE= 82, + DIGI_CGAMBIENT= 83, + DIGI_CGPAIN= 84, + DIGI_CGMAGIC= 85, + DIGI_CGMAGICHIT= 86, + DIGI_CGSCREAM= 87, + DIGI_NINJAAMBIENT= 88, + DIGI_NINJASTAR= 89, + DIGI_NINJAPAIN= 90, + DIGI_NINJASCREAM= 91, + DIGI_NINJAALERT= 92, + DIGI_NINJAUZIATTACK= 93, + DIGI_NINJARIOTATTACK= 94, + DIGI_RIPPERAMBIENT= 95, + DIGI_RIPPERALERT= 96, + DIGI_RIPPERATTACK= 97, + DIGI_RIPPERPAIN= 98, + DIGI_RIPPERSCREAM= 99, + DIGI_RIPPERHEARTOUT= 100, + DIGI_GRDAMBIENT= 101, + DIGI_GRDALERT= 102, + DIGI_GRDPAIN= 103, + DIGI_GRDSCREAM= 104, + DIGI_GRDFIREBALL= 105, + DIGI_GRDSWINGAXE= 106, + DIGI_GRDAXEHIT= 107, + DIGI_SPAMBIENT= 108, + DIGI_SPALERT= 109, + DIGI_SPPAIN= 110, + DIGI_SPSCREAM= 111, + DIGI_SPBLADE= 112, + DIGI_SPELEC= 113, + DIGI_SPTELEPORT= 114, + DIGI_AHAMBIENT= 115, + DIGI_AHSCREAM= 116, + DIGI_AHEXPLODE= 117, + DIGI_AHSWOOSH= 118, + DIGI_HORNETBUZZ= 119, + DIGI_HORNETSTING= 120, + DIGI_HORNETPAIN= 121, + DIGI_HORNETDEATH= 122, + DIGI_SERPAMBIENT= 123, + DIGI_SERPALERT= 124, + DIGI_SERPPAIN= 125, + DIGI_SERPSCREAM= 126, + DIGI_SERPDEATHEXPLODE=127, + DIGI_SERPSWORDATTACK= 128, + DIGI_SERPMAGICLAUNCH= 129, + DIGI_SERPSUMMONHEADS= 130, + DIGI_SERPTAUNTYOU= 131, + DIGI_LAVABOSSAMBIENT= 132, + DIGI_LAVABOSSSWIM= 133, + DIGI_LAVABOSSRISE= 134, + DIGI_LAVABOSSALERT= 135, + DIGI_LAVABOSSFLAME= 136, + DIGI_LAVABOSSMETEOR= 137, + DIGI_LAVABOSSMETEXP= 138, + DIGI_LAVABOSSPAIN= 139, + DIGI_LAVABOSSSIZZLE= 140, + DIGI_LAVABOSSEXPLODE= 141, + DIGI_BOATSTART= 142, + DIGI_BOATRUN= 143, + DIGI_BOATSTOP= 144, + DIGI_BOATFIRE= 145, + DIGI_TANKSTART= 146, + DIGI_TANKRUN= 147, + DIGI_TANKSTOP= 148, + DIGI_TANKIDLE= 149, + DIGI_TANKFIRE= 150, + DIGI_TRUKRUN= 151, + DIGI_TRUKIDLE= 152, + DIGI_SUBRUN= 153, + DIGI_SUBIDLE= 154, + DIGI_SUBDOOR= 155, + DIGI_BOMBRFLYING= 156, + DIGI_BOMBRDROPBOMB= 157, + DIGI_BUBBLES= 158, + DIGI_CHAIN= 159, + DIGI_CHAINDOOR= 160, + DIGI_CRICKETS= 161, + DIGI_WOODDOOROPEN= 162, + DIGI_WOODDOORCLOSE= 163, + DIGI_METALDOOROPEN= 164, + DIGI_METALDOORCLOSE= 165, + DIGI_SLIDEDOOROPEN= 166, + DIGI_SLIDEDOORCLOSE= 167, + DIGI_STONEDOOROPEN= 168, + DIGI_STONEDOORCLOSE= 169, + DIGI_SQUEAKYDOOROPEN= 170, + DIGI_SQUEAKYDOORCLOSE=171, + DIGI_DRILL= 172, + DIGI_CAVEDRIP1= 173, + DIGI_CAVEDRIP2= 174, + DIGI_DRIP= 175, + DIGI_WATERFALL1= 176, + DIGI_WATERFALL2= 177, + DIGI_WATERFLOW1= 178, + DIGI_WATERFLOW2= 179, + DIGI_ELEVATOR= 180, + DIGI_SMALLEXP= 181, + DIGI_MEDIUMEXP= 182, + DIGI_LARGEEXP= 183, + DIGI_HUGEEXP= 184, + DIGI_NULL_HUGEEXP= 184, + DIGI_FIRE1= 185, + DIGI_FIRE2= 186, + DIGI_FIREBALL1= 187, + DIGI_FIREBALL2= 188, + DIGI_GEAR1= 189, + DIGI_GONG= 190, + DIGI_LAVAFLOW1= 191, + DIGI_MACHINE1= 192, + DIGI_MUBBUBBLES1= 193, + DIGI_EARTHQUAKE= 194, + DIGI_SEWERFLOW1= 195, + DIGI_SPLASH1= 196, + DIGI_STEAM1= 197, + DIGI_VOLCANOSTEAM1= 198, + DIGI_STOMPER= 199, + DIGI_SWAMP= 200, + DIGI_REGULARSWITCH= 201, + DIGI_BIGSWITCH= 202, + DIGI_STONESWITCH= 203, + DIGI_GLASSSWITCH= 204, + DIGI_HUGESWITCH= 205, + DIGI_THUNDER= 206, + DIGI_TELEPORT= 207, + DIGI_UNDERWATER= 208, + DIGI_UNLOCK= 209, + DIGI_SQUEAKYVALVE= 210, + DIGI_VOID1= 211, + DIGI_VOID2= 212, + DIGI_VOID3= 213, + DIGI_VOID4= 214, + DIGI_VOID5= 215, + DIGI_ERUPTION= 216, + DIGI_VOLCANOPROJECTILE= 217, + DIGI_LIGHTWIND= 218, + DIGI_STRONGWIND= 219, + DIGI_BREAKINGWOOD= 220, + DIGI_BREAKSTONES= 221, + DIGI_ENGROOM1= 222, + DIGI_ENGROOM2= 223, + DIGI_ENGROOM3= 224, + DIGI_ENGROOM4= 225, + DIGI_ENGROOM5= 226, + DIGI_BREAKGLASS= 227, + DIGI_MUSSTING= 228, + DIGI_HELI= 229, + DIGI_BIGHART= 230, + DIGI_WIND4= 231, + DIGI_SPOOKY1= 232, + DIGI_DRILL1= 233, + DIGI_JET= 234, + DIGI_DRUMCHANT= 235, + DIGI_BUZZZ= 236, + DIGI_CHOP_CLICK= 237, + DIGI_SWORD_UP= 238, + DIGI_UZI_UP= 239, + DIGI_SHOTGUN_UP= 240, + DIGI_ROCKET_UP= 241, + DIGI_GRENADE_UP= 242, + DIGI_RAIL_UP= 243, + DIGI_MINE_UP= 244, + DIGI_TAUNTAI1= 246, + DIGI_TAUNTAI2= 247, + DIGI_TAUNTAI3= 248, + DIGI_TAUNTAI4= 249, + DIGI_TAUNTAI5= 250, + DIGI_TAUNTAI6= 251, + DIGI_TAUNTAI7= 252, + DIGI_TAUNTAI8= 253, + DIGI_TAUNTAI9= 254, + DIGI_TAUNTAI10= 255, + DIGI_PLAYERPAIN1= 256, + DIGI_PLAYERPAIN2= 257, + DIGI_PLAYERPAIN3= 258, + DIGI_PLAYERPAIN4= 259, + DIGI_PLAYERPAIN5= 260, + DIGI_PLAYERYELL1= 261, + DIGI_PLAYERYELL2= 262, + DIGI_PLAYERYELL3= 263, + DIGI_SEARCHWALL= 264, + DIGI_NOURINAL= 265, + DIGI_FALLSCREAM= 266, + DIGI_GOTITEM1= 267, + DIGI_LASTPLAYERVOICE= 268, + DIGI_RAILFIRE= 269, + DIGI_NULL_RAILFIRE= 269, + DIGI_RAILREADY= 270, + DIGI_RAILPWRUP= 271, + DIGI_NUCLEAREXP= 272, + DIGI_NUKESTDBY= 273, + DIGI_NUKECDOWN= 274, + DIGI_NUKEREADY= 275, + DIGI_CHEMGAS= 276, + DIGI_CHEMBOUNCE= 277, + DIGI_THROW= 278, + DIGI_PULL= 279, + DIGI_MINEARM= 280, + DIGI_HEARTDOWN= 281, + DIGI_TOOLBOX= 282, + DIGI_NULL_TOOLBOX= 282, + DIGI_GASPOP= 283, + DIGI_40MMBNCE= 284, + DIGI_BURGLARALARM= 285, + DIGI_CARALARM= 286, + DIGI_CARALARMOFF= 287, + DIGI_CALTROPS= 288, + DIGI_NIGHTON= 289, + DIGI_NIGHTOFF= 290, + DIGI_SHOTSHELLSPENT= 291, + DIGI_BUSSKID= 292, + DIGI_BUSCRASH= 293, + DIGI_BUSENGINE= 294, + DIGI_ARMORHIT= 295, + DIGI_ASIREN1= 296, + DIGI_FIRETRK1= 297, + DIGI_TRAFFIC1= 298, + DIGI_TRAFFIC2= 299, + DIGI_TRAFFIC3= 300, + DIGI_TRAFFIC4= 301, + DIGI_TRAFFIC5= 302, + DIGI_TRAFFIC6= 303, + DIGI_HELI1= 304, + DIGI_JET1= 305, + DIGI_MOTO1= 306, + DIGI_MOTO2= 307, + DIGI_NEON1= 308, + DIGI_SUBWAY= 309, + DIGI_TRAIN1= 310, + DIGI_COINS= 311, + DIGI_SWORDCLANK= 312, + DIGI_RIPPER2AMBIENT= 313, + DIGI_RIPPER2ALERT= 314, + DIGI_RIPPER2ATTACK= 315, + DIGI_RIPPER2PAIN= 316, + DIGI_RIPPER2SCREAM= 317, + DIGI_RIPPER2HEARTOUT=318, + DIGI_M60= 319, + DIGI_SUMOSCREAM= 320, + DIGI_SUMOALERT= 321, + DIGI_SUMOAMBIENT= 322, + DIGI_SUMOPAIN= 323, + DIGI_RAMUNLOCK= 324, + DIGI_CARDUNLOCK= 325, + DIGI_ANCIENTSECRET= 326, + DIGI_AMERICANDRIVER= 327, + DIGI_DRIVELIKEBABOON= 328, + DIGI_BURNBABY= 329, + DIGI_LIKEBIGWEAPONS= 330, + DIGI_COWABUNGA= 331, + DIGI_NOCHARADE= 332, + DIGI_TIMETODIE= 333, + DIGI_EATTHIS= 334, + DIGI_FIRECRACKERUPASS=335, + DIGI_HOLYCOW= 336, + DIGI_HOLYPEICESOFCOW= 337, + DIGI_HOLYSHIT= 338, + DIGI_HOLYPEICESOFSHIT=339, + DIGI_PAYINGATTENTION= 340, + DIGI_EVERYBODYDEAD= 341, + DIGI_KUNGFU= 342, + DIGI_HOWYOULIKEMOVE= 343, + DIGI_NOMESSWITHWANG= 344, + DIGI_RAWREVENGE= 345, + DIGI_YOULOOKSTUPID= 346, + DIGI_TINYDICK= 347, + DIGI_NOTOURNAMENT= 348, + DIGI_WHOWANTSWANG= 349, + DIGI_MOVELIKEYAK= 350, + DIGI_ALLINREFLEXES= 351, + DIGI_EVADEFOREVER= 352, + DIGI_MRFLY= 353, + DIGI_SHISEISI= 354, + DIGI_LIKEFIREWORKS= 355, + DIGI_LIKEHIROSHIMA= 356, + DIGI_LIKENAGASAKI= 357, + DIGI_LIKEPEARL= 358, + DIGI_IAMSHADOW= 359, + DIGI_ILIKENUKES= 360, + DIGI_ILIKESWORD= 361, + DIGI_ILIKESHURIKEN= 362, + DIGI_BADLUCK= 363, + DIGI_NOMOVIEMRCHAN= 364, + DIGI_REALLIFEMRCHAN= 365, + DIGI_NOLIKEMUSIC= 366, + DIGI_NODIFFERENCE= 367, + DIGI_NOFEAR= 368, + DIGI_NOPAIN= 369, + DIGI_NOREPAIRMAN= 370, + DIGI_SONOFABITCH= 371, + DIGI_PAINFORWEAK= 372, + DIGI_GOSPEEDY= 373, + DIGI_GETTINGSTIFF= 374, + DIGI_TOMBRAIDER= 375, + DIGI_STICKYGOTU1= 376, + DIGI_STICKYGOTU2= 377, + DIGI_STICKYGOTU3= 378, + DIGI_STICKYGOTU4= 379, + DIGI_SWORDGOTU1= 380, + DIGI_SWORDGOTU2= 381, + DIGI_SWORDGOTU3= 382, + DIGI_HURTBAD1= 383, + DIGI_HURTBAD2= 384, + DIGI_HURTBAD3= 385, + DIGI_HURTBAD4= 386, + DIGI_HURTBAD5= 387, + DIGI_TOILETGIRLSCREAM= 388, + DIGI_TOILETGIRLALERT= 389, + DIGI_TOILETGIRLAMBIENT=390, + DIGI_TOILETGIRLPAIN= 391, + DIGI_TOILETGIRLTAUNT1= 392, + DIGI_TOILETGIRLTAUNT2= 393, + DIGI_SUMOFART= 394, + DIGI_GIBS1= 395, + DIGI_GIBS2= 396, + DIGI_BIRDS1= 397, + DIGI_BIRDS2= 398, + DIGI_TOILET= 399, + DIGI_FORKLIFTIDLE= 400, + DIGI_FORKLIFTRUN= 401, + DIGI_TOYCAR= 402, + DIGI_UZIMATIC= 403, + DIGI_COMPUTERPOWER= 404, + DIGI_GENERATORON= 405, + DIGI_GENERATORRUN= 406, + DIGI_BIGDRILL= 407, + DIGI_FLUORLIGHT= 408, + DIGI_AMOEBA= 409, + DIGI_BODYFALL2= 410, + DIGI_GIBS3= 411, + DIGI_NINJACHOKE= 412, + DIGI_TRAIN3= 413, + DIGI_TRAINR02= 414, + DIGI_TRAIN8= 415, + DIGI_TRASHLID= 416, + DIGI_GETMEDKIT= 417, + DIGI_AHH= 418, + DIGI_PALARM= 419, + DIGI_PFLIP= 420, + DIGI_PROLL1= 421, + DIGI_PROLL2= 422, + DIGI_PROLL3= 423, + DIGI_BUNNYATTACK= 424, + DIGI_BUNNYDIE1= 425, + DIGI_BUNNYDIE2= 426, + DIGI_BUNNYDIE3= 427, + DIGI_BUNNYAMBIENT= 428, + DIGI_STONESLIDE= 429, + DIGI_NINJAINHALF= 430, + DIGI_RIPPER2CHEST= 431, + DIGI_WHIPME= 432, + DIGI_ENDLEV= 433, + DIGI_MDALARM= 434, + DIGI_BREAKMETAL= 435, + DIGI_BREAKDEBRIS= 436, + DIGI_BREAKMARBELS= 437, + DIGI_BANZAI= 438, + DIGI_HAHA1= 439, + DIGI_HAHA2= 440, + DIGI_HAHA3= 441, + DIGI_ITEM_SPAWN= 442, + DIGI_NOREPAIRMAN2= 443, + DIGI_NOPOWER= 444, + DIGI_DOUBLEUZI= 445, + DIGI_NOTORDBUNNY= 446, + DIGI_CANBEONLYONE= 447, + DIGI_MIRROR1= 448, + DIGI_MIRROR2= 449, + DIGI_HITTINGWALLS= 450, + DIGI_GOTRAILGUN= 451, + DIGI_RABBITHUMP1= 452, + DIGI_RABBITHUMP2= 453, + DIGI_RABBITHUMP3= 454, + DIGI_RABBITHUMP4= 455, + DIGI_FAGRABBIT1= 456, + DIGI_FAGRABBIT2= 457, + DIGI_FAGRABBIT3= 458, + DIGI_STINKLIKEBABBOON= 459, + DIGI_WHATYOUEATBABY= 460, + DIGI_WHATDIEDUPTHERE= 461, + DIGI_YOUGOPOOPOO= 462, + DIGI_PULLMYFINGER= 463, + DIGI_SOAPYOUGOOD= 464, + DIGI_WASHWANG= 465, + DIGI_DROPSOAP= 466, + DIGI_REALTITS= 467, + DIGI_MSTRLEEP= 468, + DIGI_SEEKLEEPADVICE= 469, + DIGI_AVENGELEEPDEATH= 470, + DIGI_LEEPGHOST= 471, + DIGI_DOOR1= 472, + DIGI_DOOR2= 473, + DIGI_DOOR3= 474, + DIGI_FLAGWAVE= 475, + DIGI_SURFACE= 476, + DIGI_GASHURT= 477, + DIGI_BONUS_GRAB= 478, + DIGI_ANIMECRY= 479, + DIGI_ANIMESING1= 480, + DIGI_ANIMEMAD1= 481, + DIGI_ANIMESING2= 482, + DIGI_ANIMEMAD2= 483, + DIGI_PLAYER_TELEPORT= 484, + DIGI_INTRO_SLASH= 485, + DIGI_WARNING= 486, + DIGI_INTRO_WHIRL= 487, + DIGI_TOILETGIRLFART1= 488, + DIGI_TOILETGIRLFART2= 489, + DIGI_TOILETGIRLFART3= 490, + DIGI_WINDCHIMES= 491, + DIGI_MADATCARPET= 492, + DIGI_JUMPONCARPET= 493, + DIGI_USEBROKENVEHICLE= 494, + DIGI_STEPONCALTROPS= 495, + DIGI_WANGSEESERP= 496, + DIGI_SERPTAUNTWANG= 497, + DIGI_WANGTAUNTSERP1= 498, + DIGI_WANGTAUNTSERP2= 499, + DIGI_WANGORDER1= 500, + DIGI_WANGORDER2= 501, + DIGI_WANGDROWNING= 502, + DIGI_ZILLAREGARDS= 503, + DIGI_PMESSAGE= 504, + DIGI_SHAREND_UGLY1= 505, + DIGI_SHAREND_UGLY2= 506, + DIGI_SHAREND_TELEPORT= 507, + DIGI_HOTHEADSWITCH= 508, + DIGI_BOATCREAK= 509, + DIGI_BOATRUN2= 510, + DIGI_BOATIDLE= 511, + DIGI_SHIPBELL= 512, + DIGI_FOGHORN= 513, + DIGI_CANNON= 514, + DIGI_JG41001= 515, + DIGI_JG41012= 516, + DIGI_JG41018= 517, + DIGI_JG41028= 518, + DIGI_JG41048= 519, + DIGI_JG41052= 520, + DIGI_JG41058= 521, + DIGI_JG41060= 522, + DIGI_JG41075= 523, + DIGI_JG42004= 524, + DIGI_JG42019= 525, + DIGI_JG42021= 526, + DIGI_JG42028= 527, + DIGI_JG42033= 528, + DIGI_JG42034= 529, + DIGI_JG42050= 530, + DIGI_JG42056= 531, + DIGI_JG42061= 532, + DIGI_JG43004= 533, + DIGI_JG43015= 534, + DIGI_JG43019= 535, + DIGI_JG43021= 536, + DIGI_JG44011= 537, + DIGI_JG44014= 538, + DIGI_JG44027= 539, + DIGI_JG44038= 540, + DIGI_JG44039= 541, + DIGI_JG44048= 542, + DIGI_JG44052= 543, + DIGI_JG45014= 544, + DIGI_JG44068= 545, + DIGI_JG45010= 546, + DIGI_JG45018= 547, + DIGI_JG45030= 548, + DIGI_JG45033= 549, + DIGI_JG45043= 550, + DIGI_JG45053= 551, + DIGI_JG45067= 552, + DIGI_JG46005= 553, + DIGI_JG46010= 554, + DIGI_LANI049= 555, + DIGI_LANI051= 556, + DIGI_LANI052= 557, + DIGI_LANI054= 558, + DIGI_LANI060= 559, + DIGI_LANI063= 560, + DIGI_LANI065= 561, + DIGI_LANI066= 562, + DIGI_LANI073= 563, + DIGI_LANI075= 564, + DIGI_LANI077= 565, + DIGI_LANI079= 566, + DIGI_LANI089= 567, + DIGI_LANI091= 568, + DIGI_LANI093= 569, + DIGI_LANI095= 570, + DIGI_VENTWALK= 571, + DIGI_CARWALK= 572, + DIGI_JETSOAR= 573, + DIGI_VACUUM= 574, + DIGI_GIRLNINJAALERTT= 575, + DIGI_GIRLNINJASCREAM= 576, + DIGI_GIRLNINJAALERT= 577, + DIGI_PRUNECACKLE= 578, + DIGI_PRUNECACKLE2= 579, + DIGI_PRUNECACKLE3= 580, + DIGI_SUMOSTOMP= 581, + DIGI_VATOR= 582, + DIGI_JG9009= 583, + DIGI_Z16004= 584, + DIGI_Z16012= 585, + DIGI_Z16022= 586, + DIGI_Z16027= 587, + DIGI_JG93030= 588, + DIGI_JG94002= 589, + DIGI_Z17010= 590, + DIGI_Z17052= 591, + DIGI_Z17025= 592, + DIGI_ML25014= 593, + DIGI_ML250101= 594, + DIGI_JG9022= 595, + DIGI_JG9032= 596, + DIGI_JG9038= 597, + DIGI_JG9055= 598, + DIGI_JG9060= 599, + DIGI_JG92055= 600, + DIGI_ML25032= 601, + DIGI_JG92036= 602, + DIGI_JG92042= 603, + DIGI_ML26001= 604, + DIGI_JG93000= 605, + DIGI_JG93011= 606, + DIGI_JG93018= 607, + DIGI_JG93023= 608, + DIGI_ML26008= 609, + DIGI_ML26011= 610, + DIGI_JG94007= 611, + DIGI_JG94024= 612, + DIGI_JG94039= 613, + DIGI_JG95012= 614, + DIGI_ZILLASTOMP= 615, + DIGI_ZC1= 616, + DIGI_ZC2= 617, + DIGI_ZC3= 618, + DIGI_ZC4= 619, + DIGI_ZC5= 620, + DIGI_ZC6= 621, + DIGI_ZC7= 622, + DIGI_ZC8= 623, + DIGI_ZC9= 624, + DIGI_Z16043= 625, + } +} diff --git a/wadsrc/static/zscript/games/sw/ui/screens.zs b/wadsrc/static/zscript/games/sw/ui/screens.zs new file mode 100644 index 000000000..6b2a15fde --- /dev/null +++ b/wadsrc/static/zscript/games/sw/ui/screens.zs @@ -0,0 +1,591 @@ +//------------------------------------------------------------------------- +/* +Copyright (C) 1997, 2005 - 3D Realms Entertainment +Copyright (C) 2019-2021 Christoph Oelckers + +This file is part of Raze + +Shadow Warrior is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +Original Source: 1997 - Frank Maddin and Jim Norwood +Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms +*/ +//------------------------------------------------------------------------- + + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class SWDRealmsScreen : SkippableScreenJob +{ + const DREALMSPAL = 1; + + ScreenJob Init() + { + Super.Init(fadein | fadeout); + return self; + } + + override void Start() + { + SW.PlaySong(0); + } + + override void OnTick() + { + if (ticks > 5 * GameTicRate) jobstate = finished; + } + + override void Draw(double sm) + { + let tex = TexMan.CheckForTexture("THREED_REALMS_PIC", TexMan.Type_Any); + int translation = Translation.MakeID(Translation_BasePalette, DREALMSPAL); + Screen.DrawTexture(tex, false, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_TranslationIndex, translation, DTA_LegacyRenderStyle, STYLE_Normal); + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class SWCreditsScreen : SkippableScreenJob +{ + int mystate; + int starttime; + TextureID curpic; + TextureID pic1, pic2; + + ScreenJob Init() + { + Super.Init(fadein|fadeout); + pic1 = TexMan.CheckForTexture("CREDITS1", TexMan.Type_Any); + pic2 = TexMan.CheckForTexture("CREDITS2", TexMan.Type_Any); + return self; + + } + override void OnSkip() + { + SW.StopSound(); + } + + override void Start() + { + // Lo Wang feel like singing! + SW.PlaySound(SWSnd.DIGI_JG95012, SW.v3df_none, CHAN_VOICE, CHANF_UI); + } + + override void OnTick() + { + if (mystate == 0) + { + if (!SW.IsSoundPlaying(CHAN_VOICE)) + { + starttime = ticks; + mystate = 1; + SW.StopSound(); + curpic = pic1; + SW.PlaySong(5); //) PlaySong(2); + } + } + else + { + if (ticks >= starttime + 8 * GameTicRate) + { + curpic = curpic == pic1? pic2 : pic1; + starttime = ticks; + } + } + } + + override void Draw(double sr) + { + if (mystate == 1) + Screen.DrawTexture(curpic, false, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal); + } +} + +//--------------------------------------------------------------------------- +// +// Summary screen animation +// +//--------------------------------------------------------------------------- + +struct SWSummaryAnimation +{ + TextureID frames[15]; + Sound snd; + int delay, length; + int tics; + + int curframe; + + void Init(String nametemplate, int length_, Sound sound_, int delay_) + { + for (int i = 0; i < length_; i++) + { + String name = String.Format(nametemplate, i); + frames[i] = TexMan.CheckForTexture(name, TexMan.Type_Any); + } + snd = sound_; + delay = delay_; + length = length_; + } + + bool Tick() + { + tics += 3; + if (curframe < length-1) + { + if (tics >= delay) + { + tics -= delay; + curframe++; + if (curframe == 3) SW.PlaySound(snd, SW.v3df_none); + } + return false; + } + return tics >= 90; + } + + TextureID getTex() + { + return frames[curframe]; + } +} + +//--------------------------------------------------------------------------- +// +// Summary screen +// +//--------------------------------------------------------------------------- + +class SWSummaryScreen : SummaryScreenBase +{ + SWSummaryAnimation anim; + int animstate; + TextureID rest[4]; + + ScreenJob Init(MapRecord mr, SummaryInfo info) + { + Super.Init(fadein|fadeout); + SetParameters(mr, info); + switch (random(0, 2)) + { + case 0: + anim.Init("BONUS_PUNCH%02d", 15, SWSnd.DIGI_PLAYERYELL3, 8); + break; + case 1: + anim.Init("BONUS_KICK%02d", 15, SWSnd.DIGI_PLAYERYELL2, 8); + break; + case 2: + anim.Init("BONUS_GRAB%02d", 15, SWSnd.DIGI_BONUS_GRAB, 20); + break; + } + rest[0] = TexMan.CheckForTexture("BONUS_PUNCH00", TexMan.Type_Any); + rest[3] = rest[1] = TexMan.CheckForTexture("BONUS_PUNCH01", TexMan.Type_Any); + rest[2] = TexMan.CheckForTexture("BONUS_PUNCH02", TexMan.Type_Any); + return self; + } + + override bool OnEvent(InputEvent ev) + { + if (ev.type == InputEvent.Type_KeyDown && !Raze.specialKeyEvent(ev)) + { + if (animstate == 0) animstate = 1; + } + return true; + } + + override void Start() + { + Raze.StopAllSounds(); + SW.PlaySong(1); + } + + override void OnTick() + { + if (animstate == 1) + { + if (anim.Tick()) + { + SW.StopSound(); + jobstate = finished; + } + } + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + private static int BONUS_LINE(int i) { return (50 + ((i) * 20)); } + + override void Draw(double sm) + { + Screen.DrawTexture(TexMan.CheckForTexture("BONUS_SCREEN_PIC", TexMan.Type_Any), true, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal); + SW.DrawString(160, 20, currentLevel.DisplayName(), 1, 19, 0); + SW.DrawString(170, 30, "$COMPLETED", 1, 19, 0); + + Textureid animtex; + if (animstate == 0) animtex = rest[(ticks / 17) & 3]; + else animtex = anim.getTex(); + + Screen.DrawTexture(animtex, false, 158, 86, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TopLeft, true, DTA_LegacyRenderStyle, STYLE_Normal); + + int line = 0; + String ds; + + ds = String.Format("%s %s", StringTable.Localize("$TXT_YOURTIME"), FormatTime(stats.time)); + SW.DrawString(60, BONUS_LINE(line++), ds, 1, 16); + + if (currentLevel.designerTime > 0) + { + ds = String.Format("%s %d:%02d", StringTable.Localize("$TXT_3DRTIME"), currentLevel.designerTime / 60, currentLevel.designerTime % 60); + SW.DrawString(40, BONUS_LINE(line++), ds, 1, 16); + } + + if (currentLevel.parTime > 0) + { + ds = String.Format("%s %d:%02d", StringTable.Localize("$TXT_PARTIME"), currentLevel.parTime / 60, currentLevel.parTime % 60); + SW.DrawString(40, BONUS_LINE(line++), ds, 1, 16); + } + + // always read secrets and kills from the first player + ds = String.Format("%s: %d / %d", StringTable.Localize("$TXT_SECRETS"), stats.Secrets, stats.MaxSecrets); + SW.DrawString(60, BONUS_LINE(line++), ds, 1, 16); + + ds = String.Format("%s: %d / %d", StringTable.Localize("$KILLS"), stats.Kills, stats.MaxKills); + SW.DrawString(60, BONUS_LINE(line), ds, 1, 16); + + SW.DrawString(160, 185, "$PRESSKEY", 1, 19, 0); + } + +} + +//--------------------------------------------------------------------------- +// +// Deathmatch summary screen +// +//--------------------------------------------------------------------------- + +class SWMultiSummaryScreen : SkippableScreenJob +{ + enum EConst + { + + STAT_START_X = 20, + STAT_START_Y = 85, + STAT_OFF_Y = 9, + STAT_HEADER_Y = 14, + STAT_TABLE_X = (STAT_START_X + 15*4), + STAT_TABLE_XOFF = 6*4, + MAXPLAYERS = 8, + PALETTE_PLAYER0 = 16 + + } + + int numplayers; + + ScreenJob Init(int numplayer_) + { + numplayers = numplayer_; + Super.Init(fadein | fadeout); + return self; + } + + override void Start() + { + SW.PlaySong(1); + } + + override void OnSkip() + { + SW.StopSound(); + } + + override void Draw(double sr) + { + int death_total[MAXPLAYERS]; + int kills[MAXPLAYERS]; + + Screen.DrawTexture(TexMan.CheckForTexture("STAT_SCREEN_PIC", TexMan.Type_Any), true, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal); + SW.DrawString(160, 68, "$MULTIPLAYER TOTALS", 0, 0); + SW.DrawString(160, 189, "$PRESSKEY", 0, 0, 0); + + int x = STAT_START_X; + int y = STAT_START_Y; + + // Hm.... how to translate this without messing up the formatting? + SW.DrawSmallString(x, y, " NAME 1 2 3 4 5 6 7 8 KILLS", 0, 0); + int rows = numplayers; + int cols = numplayers; + + y += STAT_HEADER_Y; + + String ds; + for (int i = 0; i < rows; i++) + { + x = STAT_START_X; + + ds = String.Format("%d", i + 1); + SW.DrawSmallString(x, y, ds, 0, 0); + + ds = String.Format(" %-13s", Raze.PlayerName(i)); + SW.DrawSmallString(x, y, ds, 0, Raze.playerPalette(i)); + + x = STAT_TABLE_X; + for (int j = 0; j < cols; j++) + { + int pal = 0; + int frags = Raze.PlayerFrags(i, j); + death_total[j] += frags; + + if (i == j) + { + // don't add kill for self or team player + pal = PALETTE_PLAYER0 + 4; + kills[i] -= frags; // subtract self kills + } + else if (false/*gNet.TeamPlay*/) + { + if (Raze.playerPalette(i) == Raze.playerPalette(j)) + { + // don't add kill for self or team player + pal = PALETTE_PLAYER0 + 4; + kills[i] -= frags; // subtract self kills + } + else + kills[i] += frags; // kills added here + } + else + { + kills[i] += frags; // kills added here + } + + ds = String.Format("%d", frags); + SW.DrawSmallString(x, y, ds, 0, pal); + x += STAT_TABLE_XOFF; + } + + y += STAT_OFF_Y; + } + + + // Deaths + + x = STAT_START_X; + y += STAT_OFF_Y; + + ds = String.Format(" %s", StringTable.Localize("$DEATHS")); + SW.DrawSmallString(x, y, ds, 0, 0); + x = STAT_TABLE_X; + + for (int j = 0; j < cols; j++) + { + ds = String.Format("%d", death_total[j]); + SW.DrawSmallString(x, y, ds, 0, 0); + x += STAT_TABLE_XOFF; + } + + x = STAT_START_X; + y += STAT_OFF_Y; + + // Kills + x = STAT_TABLE_X + 200; + y = STAT_START_Y + STAT_HEADER_Y; + + for (int i = 0; i < rows; i++) + { + ds = String.Format("%d", kills[i]); //pp.Kills); + SW.DrawSmallString(x, y, ds, 0, 0); + + y += STAT_OFF_Y; + } + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class SWLoadScreen : ScreenJob +{ + MapRecord rec; + + ScreenJob Init(MapRecord maprec) + { + Super.Init(fadein); + rec = maprec; + return self; + } + + override void OnTick() + { + if (fadestate == visible) jobstate = finished; + } + + override void Draw(double sr) + { + Screen.DrawTexture(TexMan.CheckForTexture("TITLE_PIC", TexMan.Type_Any), true, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal); + SW.DrawString(160, 170, "$TXT_ENTERING", 1, 16, 0); + SW.DrawString(160, 180, rec.DisplayName(), 1, 16, 0); + } +} + + +class SWCutscenes +{ + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildIntro(ScreenJobRunner runner) + { + if (!userConfig.nologo) + { + SW.StopSound(); + SW.PlaySong(0); + Array soundinfo; + soundinfo.Pushv( + 1, SWSnd.DIGI_NOMESSWITHWANG, + 5, SWSnd.DIGI_INTRO_SLASH, + 15, SWSnd.DIGI_INTRO_WHIRL); + runner.Append(new("SWDRealmsScreen").Init()); + runner.Append(MoviePlayerJob.CreateWithSoundinfo("sw.anm", soundinfo, MoviePlayer.NOSOUNDCUTOFF, 8, 360, 128)); + } + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildSerpentAnim(ScreenJobRunner runner) + { + Array soundinfo; + soundinfo.Pushv( + 1, SWSnd.DIGI_SERPTAUNTWANG, + 16, SWSnd.DIGI_SHAREND_TELEPORT, + 35, SWSnd.DIGI_WANGTAUNTSERP1, + 51, SWSnd.DIGI_SHAREND_UGLY1, + 64, SWSnd.DIGI_SHAREND_UGLY2); + runner.Append(MoviePlayerJob.CreateWithSoundinfo("swend.anm", soundinfo, MoviePlayer.NOSOUNDCUTOFF, 16, 16, 140)); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildSumoAnim(ScreenJobRunner runner) + { + Array soundinfo; + soundinfo.Pushv( + 2, SWSnd.DIGI_JG41012, + 30, SWSnd.DIGI_HOTHEADSWITCH, + 42, SWSnd.DIGI_HOTHEADSWITCH, + 59, SWSnd.DIGI_JG41028); + runner.Append(MoviePlayerJob.CreateWithSoundinfo("sumocinm.anm", soundinfo, MoviePlayer.NOSOUNDCUTOFF, 10, 40, 130)); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildZillaAnim(ScreenJobRunner runner) + { + Array soundinfo; + soundinfo.Pushv( + 1, SWSnd.DIGI_ZC1, + 5, SWSnd.DIGI_JG94024, + 14, SWSnd.DIGI_ZC2, + 30, SWSnd.DIGI_ZC3, + 32, SWSnd.DIGI_ZC4, + 37, SWSnd.DIGI_ZC5, + 63, SWSnd.DIGI_Z16043, + 63, SWSnd.DIGI_ZC6, + 63, SWSnd.DIGI_ZC7, + 72, SWSnd.DIGI_ZC7, + 73, SWSnd.DIGI_ZC4, + 77, SWSnd.DIGI_ZC5, + 87, SWSnd.DIGI_ZC8, + 103, SWSnd.DIGI_ZC7, + 108, SWSnd.DIGI_ZC9, + 120, SWSnd.DIGI_JG94039); + runner.Append(MoviePlayerJob.CreateWithSoundinfo("zfcin.anm", soundinfo, MoviePlayer.NOSOUNDCUTOFF, 16, 16, 140)); + runner.Append(new("SWCreditsScreen").Init()); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildSybexScreen(ScreenJobRunner runner) + { + if (Raze.isShareware() && !netgame) + runner.Append(ImageScreen.CreateNamed("#05261", TexMan.Type_Any)); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildMPSummary(ScreenJobRunner runner, MapRecord map, SummaryInfo stats) + { + runner.Append(new("SWMultiSummaryScreen").Init(stats.playercount)); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildSPSummary(ScreenJobRunner runner, MapRecord map, SummaryInfo stats) + { + runner.Append(new("SWSummaryScreen").Init(map, stats)); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildLoading(ScreenJobRunner runner, MapRecord map) + { + runner.Append(new("SWLoadScreen").Init(map)); + } +} diff --git a/wadsrc/static/zscript/razebase.zs b/wadsrc/static/zscript/razebase.zs index 3dc64c532..67098cc3c 100644 --- a/wadsrc/static/zscript/razebase.zs +++ b/wadsrc/static/zscript/razebase.zs @@ -17,8 +17,14 @@ enum EGameType GAMEFLAG_POWERSLAVE = 0x00002000, GAMEFLAG_EXHUMED = 0x00004000, GAMEFLAG_PSEXHUMED = GAMEFLAG_POWERSLAVE | GAMEFLAG_EXHUMED, // the two games really are the same, except for the name and the publisher. - GAMEFLAG_WORLDTOUR = 0x00008000, - GAMEFLAG_DUKEDC = 0x00010000, + GAMEFLAG_WORLDTOUR = 0x00008000, + GAMEFLAG_DUKEDC = 0x00010000, + GAMEFLAG_DUKENW = 0x00020000, + GAMEFLAG_DUKEVACA = 0x00040000, + GAMEFLAG_BLOODCP = 0x00080000, + GAMEFLAG_ROUTE66 = 0x00100000, + GAMEFLAG_SWWANTON = 0x00200000, + GAMEFLAG_SWTWINDRAG = 0x00400000, GAMEFLAGMASK = 0x0000FFFF, // flags allowed from grpinfo // We still need these for the parsers. @@ -27,8 +33,80 @@ enum EGameType }; +struct UserConfigStruct native +{ + native readonly bool nomonsters; + native readonly bool nosound; + native readonly bool nologo; +} -struct Build +extend struct _ +{ + native @UserConfigStruct userConfig; + native readonly MapRecord currentLevel; + native readonly int paused; +} + +struct MapRecord native +{ + enum MIFlags + { + FORCEEOG = 1, + USERMAP = 2, + } + + native readonly int parTime; + native readonly int designerTime; + native readonly String fileName; + native readonly String labelName; + native readonly String name; + native readonly String music; + native readonly int cdSongId; + native readonly int flags; + native readonly int levelNumber; + native readonly int cluster; + native readonly String InterBackground; + + native readonly String nextMap; + native readonly String nextSecret; + + //native readonly String messages[MAX_MESSAGES]; + native readonly String author; + + String GetLabelName() + { + if (flags & USERMAP) return "$TXT_USERMAP"; + return labelName; + } + String DisplayName() + { + if (name == "") return labelName; + return name; + } + + native ClusterDef GetCluster(); +} + +struct ClusterDef +{ + native readonly String name; + native readonly String InterBackground; +} + +struct SummaryInfo native +{ + native readonly int kills; + native readonly int maxkills; + native readonly int secrets; + native readonly int maxsecrets; + native readonly int supersecrets; + native readonly int time; + native readonly int playercount; + native readonly bool cheated; + native readonly bool endofgame; +} + +struct Raze { static int calcSinTableValue(int ang) { @@ -36,6 +114,87 @@ struct Build } native static Color shadeToLight(int shade); + native static void StopAllSounds(); + native static bool SoundEnabled(); + native static void StopMusic(); + native static bool MusicEnabled(); + native static String PlayerName(int i); + native static double GetTimeFrac(); + native static int bsin(int angle, int shift = 0); + native static int bcos(int angle, int shift = 0); + + static bool specialKeyEvent(InputEvent ev) + { + if (ev.type == InputEvent.Type_KeyDown || ev.type == InputEvent.Type_KeyUp) + { + int key = ev.KeyScan; + if (key == InputEvent.KEY_VOLUMEDOWN || key == InputEvent.KEY_VOLUMEUP || (key > InputEvent.KEY_LASTJOYBUTTON && key < InputEvent.KEY_PAD_LTHUMB_RIGHT)) return true; + } + return false; + } + + // game check shortcuts + static bool isNam() + { + return gameinfo.gametype & (GAMEFLAG_NAM | GAMEFLAG_NAPALM); + } + + static bool isNamWW2GI() + { + return gameinfo.gametype & (GAMEFLAG_NAM | GAMEFLAG_NAPALM |GAMEFLAG_WW2GI); + } + + static bool isWW2GI() + { + return gameinfo.gametype & (GAMEFLAG_WW2GI); + } + + static bool isRR() + { + return gameinfo.gametype & (GAMEFLAG_RRALL); + } + + static bool isRRRA() + { + return gameinfo.gametype & (GAMEFLAG_RRRA); + } + + static bool isWorldTour() + { + return gameinfo.gametype & GAMEFLAG_WORLDTOUR; + } + + static bool isPlutoPak() + { + return gameinfo.gametype & GAMEFLAG_PLUTOPAK; + } + + static bool isShareware() + { + return gameinfo.gametype & GAMEFLAG_SHAREWARE; + } + + static bool isBlood() + { + return gameinfo.gametype & GAMEFLAG_BLOOD; + } + + // Dont know yet how to best export this, so for now these are just placeholders as MP is not operational anyway. + static int playerPalette(int i) + { + return 0; + } + + static int playerFrags(int i, int j) + { + return 0; + } + + static int playerFraggedSelf(int i) + { + return 0; + } + } /* diff --git a/wadsrc/static/zscript/screenjob.zs b/wadsrc/static/zscript/screenjob.zs new file mode 100644 index 000000000..dd7763cd9 --- /dev/null +++ b/wadsrc/static/zscript/screenjob.zs @@ -0,0 +1,565 @@ + +class ScreenJob : Object +{ + int flags; + float fadetime; // in milliseconds + int fadestate; + + int ticks; + int jobstate; + + bool skipover; + + enum EJobState + { + running = 0, // normal operation + skipped = 1, // finished by user skipping + finished = 2, // finished by completing its sequence + stopping = 3, // running ending animations / fadeout, etc. Will not accept more input. + stopped = 4, // we're done here. + }; + enum EJobFlags + { + visible = 0, + fadein = 1, + fadeout = 2, + stopmusic = 4, + stopsound = 8, + }; + + void Init(int fflags = 0, float fadet = 250.f) + { + flags = fflags; + fadetime = fadet; + jobstate = running; + } + + virtual bool ProcessInput() + { + return false; + } + + virtual void Start() {} + virtual bool OnEvent(InputEvent evt) { return false; } + virtual void OnTick() {} + virtual void Draw(double smoothratio) {} + virtual void OnSkip() {} + + int DrawFrame(double smoothratio) + { + if (jobstate != running) smoothratio = 1; // this is necessary to avoid having a negative time span because the ticker won't be incremented anymore. + Draw(smoothratio); + if (jobstate == skipped) return -1; + if (jobstate == finished) return 0; + return 1; + } + + int GetFadeState() { return fadestate; } + override void OnDestroy() + { + if (flags & stopmusic) Raze.StopMusic(); + if (flags & stopsound) Raze.StopAllSounds(); + } + +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class SkippableScreenJob : ScreenJob +{ + void Init(int flags = 0, float fadet = 250.f) + { + Super.Init(flags, fadet); + } + + override bool OnEvent(InputEvent evt) + { + if (evt.type == InputEvent.Type_KeyDown && !Raze.specialKeyEvent(evt)) + { + jobstate = skipped; + OnSkip(); + } + return true; + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class BlackScreen : ScreenJob +{ + int wait; + bool cleared; + + ScreenJob Init(int w, int flags = 0) + { + Super.Init(flags & ~(fadein|fadeout)); + wait = w; + cleared = false; + return self; + } + + static ScreenJob Create(int w, int flags = 0) + { + return new("BlackScreen").Init(w, flags); + } + + override void OnTick() + { + if (cleared) + { + int span = ticks * 1000 / GameTicRate; + if (span > wait) jobstate = finished; + } + } + + override void Draw(double smooth) + { + cleared = true; + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class ImageScreen : SkippableScreenJob +{ + int tilenum; + int trans; + int waittime; // in ms. + bool cleared; + TextureID texid; + + ScreenJob Init(TextureID tile, int fade = fadein | fadeout, int wait = 3000, int translation = 0) + { + Super.Init(fade); + waittime = wait; + texid = tile; + trans = translation; + cleared = false; + return self; + } + + ScreenJob InitNamed(String tex, int fade = fadein | fadeout, int wait = 3000, int translation = 0) + { + Super.Init(fade); + waittime = wait; + texid = TexMan.CheckForTexture(tex, TexMan.Type_Any, TexMan.TryAny | TexMan.ForceLookup); + trans = translation; + cleared = false; + return self; + } + + static ScreenJob Create(TextureID tile, int fade = fadein | fadeout, int wait = 3000, int translation = 0) + { + return new("ImageScreen").Init(tile, fade, wait, translation); + } + + static ScreenJob CreateNamed(String tex, int fade = fadein | fadeout, int wait = 3000, int translation = 0) + { + return new("ImageScreen").InitNamed(tex, fade, wait, translation); + } + + override void OnTick() + { + if (cleared) + { + int span = ticks * 1000 / GameTicRate; + if (span > waittime) jobstate = finished; + } + } + + override void Draw(double smooth) + { + if (texid.IsValid()) Screen.DrawTexture(texid, true, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, DTA_TranslationIndex, trans); + cleared = true; + } +} + +//--------------------------------------------------------------------------- +// +// this is to have a unified interface to the summary screens +// that can be set up automatically by the games to avoid direct access to game data. +// +//--------------------------------------------------------------------------- + +class SummaryScreenBase : ScreenJob +{ + MapRecord level; + SummaryInfo stats; + + void SetParameters(MapRecord map, SummaryInfo thestats) + { + level = map; + stats = thestats; + } + + String FormatTime(int time) + { + if (time >= 60 * 50) + return String.Format("%02d:%02d:%02d", time / (60*60), (time / 60) % 60, time % 60); + else + return String.Format("%02d:%02d", (time / 60) % 60, time % 60); + } + +} + +//--------------------------------------------------------------------------- +// +// internal polymorphic movie player object +// +//--------------------------------------------------------------------------- + +struct MoviePlayer native +{ + enum EMovieFlags + { + NOSOUNDCUTOFF = 1, + FIXEDVIEWPORT = 2, // Forces fixed 640x480 screen size like for Blood's intros. + } + + native static MoviePlayer Create(String filename, Array soundinfo, int flags, int frametime, int firstframetime, int lastframetime); + native void Start(); + native bool Frame(double clock); + native void Destroy(); + native TextureID GetTexture(); +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class MoviePlayerJob : SkippableScreenJob +{ + MoviePlayer player; + bool started; + int flag; + + ScreenJob Init(MoviePlayer mp, int flags) + { + Super.Init(); + flag = flags; + player = mp; + return self; + } + + static ScreenJob CreateWithSoundInfo(String filename, Array soundinfo, int flags, int frametime, int firstframetime = -1, int lastframetime = -1) + { + let movie = MoviePlayer.Create(filename, soundinfo, flags, frametime, firstframetime, lastframetime); + if (movie) return new("MoviePlayerJob").Init(movie, flags); + return null; + } + static ScreenJob Create(String filename, int flags, int frametime = -1) + { + Array empty; + return CreateWithSoundInfo(filename, empty, flags, frametime); + } + static ScreenJob CreateWithSound(String filename, Sound soundname, int flags, int frametime = -1) + { + Array empty; + empty.Push(1); + empty.Push(int(soundname)); + return CreateWithSoundInfo(filename, empty, flags, frametime); + } + + virtual void DrawFrame() + { + let tex = player.GetTexture(); + let size = TexMan.GetScaledSize(tex); + + if (!(flag & MoviePlayer.FIXEDVIEWPORT) || (size.x <= 320 && size.y <= 200) || size.x >= 640 || size.y >= 480) + { + Screen.DrawTexture(tex, false, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_Masked, false); + } + else + { + Screen.DrawTexture(tex, false, 320, 240, DTA_VirtualWidth, 640, DTA_VirtualHeight, 480, DTA_CenterOffset, true, DTA_Masked, false); + } + + } + + override void Draw(double smoothratio) + { + if (!player) + { + jobstate = stopped; + return; + } + if (!started) + { + started = true; + player.Start(); + } + double clock = (ticks + smoothratio) * 1000000000. / GameTicRate; + if (jobstate == running && !player.Frame(clock)) + { + jobstate = finished; + } + DrawFrame(); + } + + override void OnDestroy() + { + if (player) + { + player.Destroy(); + } + player = null; + } +} + + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class ScreenJobRunner : Object +{ + enum ERunState + { + State_Clear, + State_Run, + State_Fadeout, + } + Array jobs; + //CompletionFunc completion; + int index; + float screenfade; + bool clearbefore; + bool skipall; + bool advance; + int actionState; + int terminateState; + int fadeticks; + int last_paused_tic; + + void Init(bool clearbefore_, bool skipall_) + { + clearbefore = clearbefore_; + skipall = skipall_; + index = -1; + fadeticks = 0; + last_paused_tic = -1; + } + + override void OnDestroy() + { + DeleteJobs(); + } + + protected void DeleteJobs() + { + // Free all allocated resources now. + for (int i = 0; i < jobs.Size(); i++) + { + if (jobs[i]) jobs[i].Destroy(); + } + jobs.Clear(); + } + + void Append(ScreenJob job) + { + if (job != null) jobs.Push(job); + } + + virtual bool Validate() + { + return jobs.Size() > 0; + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + protected void AdvanceJob(bool skip) + { + if (index == jobs.Size()-1) + { + index++; + return; // we need to retain the last element until the runner is done. + } + + if (index >= 0) jobs[index].Destroy(); + index++; + while (index < jobs.Size() && (jobs[index] == null || (skip && jobs[index].skipover))) + { + if (jobs[index] != null && index < jobs.Size() - 1) jobs[index].Destroy(); // may not delete the last element - we still need it for shutting down. + index++; + } + actionState = clearbefore ? State_Clear : State_Run; + if (index < jobs.Size()) + { + jobs[index].fadestate = !paused && jobs[index].flags & ScreenJob.fadein? ScreenJob.fadein : ScreenJob.visible; + jobs[index].Start(); + } + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + virtual int DisplayFrame(double smoothratio) + { + if (jobs.Size() == 0) + { + return 1; + } + int x = index >= jobs.Size()? jobs.Size()-1 : index; + let job = jobs[x]; + bool processed = job.ProcessInput(); + + if (job.fadestate == ScreenJob.fadein) + { + double ms = (job.ticks + smoothratio) * 1000 / GameTicRate / job.fadetime; + double screenfade = clamp(ms, 0., 1.); + Screen.SetScreenFade(screenfade); + if (screenfade == 1.) job.fadestate = ScreenJob.visible; + } + int state = job.DrawFrame(smoothratio); + Screen.SetScreenFade(1.); + return state; + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + virtual int FadeoutFrame(double smoothratio) + { + int x = index >= jobs.Size()? jobs.Size()-1 : index; + let job = jobs[x]; + double ms = (fadeticks + smoothratio) * 1000 / GameTicRate / job.fadetime; + float screenfade = 1. - clamp(ms, 0., 1.); + Screen.SetScreenFade(screenfade); + job.DrawFrame(1.); + Screen.SetScreenFade(1.); + return (screenfade > 0.); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + virtual bool OnEvent(InputEvent ev) + { + if (paused || index < 0 || index >= jobs.Size()) return false; + if (jobs[index].jobstate != ScreenJob.running) return false; + return jobs[index].OnEvent(ev); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + virtual bool OnTick() + { + if (paused) return false; + if (index >= jobs.Size() || jobs.Size() == 0) return true; + if (advance || index < 0) + { + advance = false; + AdvanceJob(terminateState < 0); + if (index >= jobs.Size()) + { + return true; + } + } + if (jobs[index].jobstate == ScreenJob.running) + { + jobs[index].ticks++; + jobs[index].OnTick(); + } + else if (jobs[index].jobstate == ScreenJob.stopping) + { + fadeticks++; + } + return false; + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + virtual bool RunFrame(double smoothratio) + { + if (index < 0) + { + AdvanceJob(false); + } + // ensure that we won't go back in time if the menu is dismissed without advancing our ticker + if (index < jobs.Size()) + { + bool menuon = paused; + if (menuon) last_paused_tic = jobs[index].ticks; + else if (last_paused_tic == jobs[index].ticks) menuon = true; + if (menuon) smoothratio = 1.; + } + else smoothratio = 1.; + + if (actionState == State_Clear) + { + actionState = State_Run; + } + else if (actionState == State_Run) + { + terminateState = DisplayFrame(smoothratio); + if (terminateState < 1 && index < jobs.Size()) + { + if (jobs[index].flags & ScreenJob.fadeout) + { + jobs[index].fadestate = ScreenJob.fadeout; + jobs[index].jobstate = ScreenJob.stopping; + actionState = State_Fadeout; + fadeticks = 0; + } + else + { + advance = true; + } + } + } + else if (actionState == State_Fadeout) + { + int ended = FadeoutFrame(smoothratio); + if (ended < 1 && index < jobs.Size()) + { + jobs[index].jobstate = ScreenJob.stopped; + advance = true; + } + } + return true; + } + + void AddGenericVideo(String fn, int snd, int framerate) + { + Array sounds; + if (snd > 0) sounds.Pushv(1, snd); + Append(MoviePlayerJob.CreateWithSoundInfo(fn, sounds, 0, framerate)); + } +}