2021-04-06 13:07:12 +00:00
/*
2021-04-21 20:30:44 +00:00
* * def . cpp
2021-04-06 13:07:12 +00:00
* *
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* * 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 .
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* *
* *
*/
2021-04-20 22:46:32 +00:00
# include "build.h"
# include "buildtiles.h"
# include "bitmap.h"
# include "m_argv.h"
2021-08-22 23:00:30 +00:00
# include "gamestruct.h"
2021-04-20 22:46:32 +00:00
# include "gamecontrol.h"
# include "palettecontainer.h"
# include "mapinfo.h"
# include "hw_voxels.h"
2022-01-12 22:33:44 +00:00
# include "psky.h"
2022-09-30 17:40:37 +00:00
# include "gamefuncs.h"
2022-12-07 16:10:27 +00:00
# include "tilesetbuilder.h"
2022-09-30 14:25:21 +00:00
# include "models/modeldata.h"
2022-12-07 16:10:27 +00:00
# include "texturemanager.h"
2022-12-31 14:14:29 +00:00
int tileSetHightileReplacement ( int tilenum , int palnum , FTextureID texid , float alphacut , float xscale , float yscale , float specpower , float specfactor , bool indexed ) ;
2021-04-20 22:46:32 +00:00
2022-12-31 14:14:29 +00:00
int tileSetSkybox ( int tilenum , int palnum , FString * facenames , bool indexed = false ) ;
2021-04-07 22:47:07 +00:00
void tileRemoveReplacement ( int num ) ;
2022-09-30 17:40:37 +00:00
void AddUserMapHack ( const FString & title , const FString & mhkfile , uint8_t * md4 ) ;
2021-04-07 22:47:07 +00:00
2022-12-07 16:10:27 +00:00
static TilesetBuildInfo * tbuild ;
2021-04-20 22:46:32 +00:00
static void defsparser ( FScanner & sc ) ;
2022-12-07 16:10:27 +00:00
const char * G_DefaultDefFile ( void ) ;
2021-04-20 22:46:32 +00:00
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 ) ;
}
2022-12-05 16:14:01 +00:00
//===========================================================================
//
// Helpers for tile parsing
//
//===========================================================================
bool ValidateTileRange ( const char * cmd , int & begin , int & end , FScriptPosition pos , bool allowswap = true )
{
if ( end < begin )
{
pos . Message ( MSG_WARNING , " %s: tile range [%d..%d] is backwards. Indices were swapped. " , cmd , begin , end ) ;
std : : swap ( begin , end ) ;
}
if ( ( unsigned ) begin > = MAXUSERTILES | | ( unsigned ) end > = MAXUSERTILES )
{
pos . Message ( MSG_ERROR , " %s: Invalid tile range [%d..%d] " , cmd , begin , end ) ;
return false ;
}
return true ;
}
bool ValidateTilenum ( const char * cmd , int tile , FScriptPosition pos )
{
if ( ( unsigned ) tile > = MAXUSERTILES )
{
pos . Message ( MSG_ERROR , " %s: Invalid tile number %d " , cmd , tile ) ;
return false ;
}
return true ;
}
2022-12-07 16:10:27 +00:00
//===========================================================================
//
//
//
//===========================================================================
2022-12-31 14:14:29 +00:00
static int tileSetHightileReplacement ( FScanner & sc , int tilenum , int palnum , const char * filename , float alphacut , float xscale , float yscale , float specpower , float specfactor , bool indexed = false )
2022-12-07 16:10:27 +00:00
{
2022-12-31 14:14:29 +00:00
if ( ( uint32_t ) tilenum > = ( uint32_t ) MAXTILES ) return - 1 ;
2022-12-07 16:10:27 +00:00
if ( ( uint32_t ) palnum > = ( uint32_t ) MAXPALOOKUPS ) return - 1 ;
2022-12-31 14:14:29 +00:00
auto tex = tbuild - > tile [ tilenum ] . tileimage ;
2022-12-07 16:10:27 +00:00
if ( tex = = nullptr | | tex - > GetWidth ( ) < = 0 | | tex - > GetHeight ( ) < = 0 )
{
2022-12-31 14:14:29 +00:00
sc . ScriptMessage ( " Warning: defined hightile replacement for empty tile %d. " , tilenum ) ;
2022-12-07 16:10:27 +00:00
return - 1 ; // cannot add replacements to empty tiles, must create one beforehand
}
FTextureID texid = TexMan . CheckForTexture ( filename , ETextureType : : Any , FTextureManager : : TEXMAN_ForceLookup ) ;
if ( ! texid . isValid ( ) )
{
2022-12-31 14:14:29 +00:00
sc . ScriptMessage ( " %s: Replacement for tile %d does not exist or is invalid \n " , filename , tilenum ) ;
2022-12-07 16:10:27 +00:00
return - 1 ;
}
2022-12-31 14:14:29 +00:00
tileSetHightileReplacement ( tilenum , palnum , texid , alphacut , xscale , yscale , specpower , specfactor , indexed ) ;
2022-12-07 16:10:27 +00:00
return 0 ;
}
//==========================================================================
//
// Import a tile from an external image.
// This has been signifcantly altered so it may not cover everything yet.
//
//==========================================================================
2022-12-22 16:33:44 +00:00
static int tileImportFromTexture ( FScanner & sc , const char * fn , int tilenum , int alphacut , int istexture )
2022-12-07 16:10:27 +00:00
{
FTextureID texid = TexMan . CheckForTexture ( fn , ETextureType : : Any , FTextureManager : : TEXMAN_ForceLookup ) ;
if ( ! texid . isValid ( ) ) return - 1 ;
auto tex = TexMan . GetGameTexture ( texid ) ;
int32_t xsiz = tex - > GetTexelWidth ( ) , ysiz = tex - > GetTexelHeight ( ) ;
if ( xsiz < = 0 | | ysiz < = 0 )
return - 2 ;
tbuild - > tile [ tilenum ] . imported = TexMan . GetGameTexture ( texid ) ;
tbuild - > tile [ tilenum ] . tileimage = tbuild - > tile [ tilenum ] . imported - > GetTexture ( ) - > GetImage ( ) ;
if ( istexture )
tileSetHightileReplacement ( sc , tilenum , 0 , fn , ( float ) ( 255 - alphacut ) * ( 1.f / 255.f ) , 1.0f , 1.0f , 1.0 , 1.0 ) ;
return 0 ;
}
2022-12-05 16:14:01 +00:00
//===========================================================================
//
// Internal worker for tileImportTexture
//
//===========================================================================
2022-12-06 17:30:57 +00:00
struct TileImport
{
FString fn ;
int tile = - 1 ;
int alphacut = 128 , flags = 0 ;
int haveextra = 0 ;
int xoffset = INT_MAX , yoffset = INT_MAX ;
int istexture = 0 , extra = INT_MAX ;
int64_t crc32 = INT64_MAX ;
int sizex = INT_MAX , sizey ;
// Blood extensions
int surface = INT_MAX , vox = INT_MAX , shade = INT_MAX ;
} ;
2022-12-22 16:33:44 +00:00
static void processTileImport ( FScanner & sc , const char * cmd , FScriptPosition & pos , TileImport & imp )
2022-12-05 16:14:01 +00:00
{
if ( ! ValidateTilenum ( cmd , imp . tile , pos ) )
return ;
2022-12-07 16:10:27 +00:00
auto tex = tbuild - > tile [ imp . tile ] . tileimage ;
if ( imp . crc32 ! = INT64_MAX & & int ( imp . crc32 ) ! = tileGetCRC32 ( tex ) )
2022-12-05 16:14:01 +00:00
return ;
2022-12-07 16:10:27 +00:00
if ( imp . sizex ! = INT_MAX )
{
// the size must match the previous tile
if ( ! tex )
{
if ( imp . sizex ! = 0 | | imp . sizey ! = 0 ) return ;
}
else
{
if ( tex - > GetWidth ( ) ! = imp . sizex | | tex - > GetHeight ( ) ! = imp . sizey ) return ;
}
}
2022-12-05 16:14:01 +00:00
imp . alphacut = clamp ( imp . alphacut , 0 , 255 ) ;
2023-10-08 07:15:32 +00:00
if ( imp . fn . IsNotEmpty ( ) & & tileImportFromTexture ( sc , imp . fn . GetChars ( ) , imp . tile , imp . alphacut , imp . istexture ) < 0 ) return ;
2022-12-05 16:14:01 +00:00
2022-12-07 16:10:27 +00:00
tbuild - > tile [ imp . tile ] . extinfo . picanm . sf | = imp . flags ;
2022-12-09 15:10:20 +00:00
if ( imp . surface ! = INT_MAX ) tbuild - > tile [ imp . tile ] . extinfo . surftype = imp . surface ;
if ( imp . shade ! = INT_MAX ) tbuild - > tile [ imp . tile ] . extinfo . tileshade = imp . shade ;
2022-12-08 17:26:09 +00:00
2022-12-05 16:14:01 +00:00
// This is not quite the same as originally, for two reasons:
// 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.
2022-12-07 16:10:27 +00:00
auto itex = tbuild - > tile [ imp . tile ] . imported ;
2022-12-06 19:49:49 +00:00
2022-12-07 16:10:27 +00:00
if ( imp . xoffset = = INT_MAX ) imp . xoffset = itex - > GetTexelLeftOffset ( ) ;
2022-12-05 16:14:01 +00:00
else imp . xoffset = clamp ( imp . xoffset , - 128 , 127 ) ;
2022-12-07 16:10:27 +00:00
if ( imp . yoffset = = INT_MAX ) imp . yoffset = itex - > GetTexelTopOffset ( ) ;
2022-12-05 16:14:01 +00:00
else imp . yoffset = clamp ( imp . yoffset , - 128 , 127 ) ;
2022-12-07 16:10:27 +00:00
tbuild - > tile [ imp . tile ] . leftOffset = imp . xoffset ;
tbuild - > tile [ imp . tile ] . topOffset = imp . yoffset ;
if ( imp . extra ! = INT_MAX ) tbuild - > tile [ imp . tile ] . extinfo . picanm . extra = imp . extra ;
2022-12-05 16:14:01 +00:00
}
//===========================================================================
//
// Internal worker for tileSetAnim
//
//===========================================================================
2022-12-06 17:30:57 +00:00
struct SetAnim
{
int tile1 , tile2 , speed , type ;
} ;
2022-12-22 16:33:44 +00:00
static void setAnim ( int tile , int type , int speed , int frames )
2022-12-07 16:10:27 +00:00
{
auto & anm = tbuild - > tile [ tile ] . extinfo . picanm ;
anm . sf & = ~ ( PICANM_ANIMTYPE_MASK | PICANM_ANIMSPEED_MASK ) ;
anm . sf | = clamp ( speed , 0 , 15 ) | ( type < < PICANM_ANIMTYPE_SHIFT ) ;
anm . num = frames ;
}
2022-12-22 16:33:44 +00:00
static void processSetAnim ( const char * cmd , FScriptPosition & pos , SetAnim & imp )
2022-12-05 16:14:01 +00:00
{
if ( ! ValidateTilenum ( cmd , imp . tile1 , pos ) | |
! ValidateTilenum ( cmd , imp . tile2 , pos ) )
return ;
if ( imp . type < 0 | | imp . type > 3 )
{
pos . Message ( MSG_ERROR , " %s: animation type must be 0-3, got %d " , cmd , imp . type ) ;
return ;
}
int count = imp . tile2 - imp . tile1 ;
if ( imp . type = = ( PICANM_ANIMTYPE_BACK > > PICANM_ANIMTYPE_SHIFT ) & & imp . tile1 > imp . tile2 )
count = - count ;
2022-12-07 16:10:27 +00:00
setAnim ( imp . tile1 , imp . type , imp . speed , count ) ;
}
//==========================================================================
//
// Copies a tile into another and optionally translates its palette.
//
//==========================================================================
static void tileCopy ( int tile , int source , int pal , int xoffset , int yoffset , int flags )
{
picanm_t * picanm = nullptr ;
picanm_t * sourceanm = nullptr ;
if ( source = = - 1 ) source = tile ;
auto & tiledesc = tbuild - > tile [ tile ] ;
auto & srcdesc = tbuild - > tile [ source ] ;
tiledesc . extinfo . picanm = { } ;
tiledesc . extinfo . picanm . sf = ( srcdesc . extinfo . picanm . sf & PICANM_MISC_MASK ) | flags ;
tiledesc . tileimage = srcdesc . tileimage ;
tiledesc . imported = srcdesc . imported ;
tiledesc . leftOffset = srcdesc . leftOffset ;
tiledesc . topOffset = srcdesc . topOffset ;
/* todo: create translating image source class.
if ( pal ! = - 1 )
{
tiledesc . tileimage = createTranslatedImage ( tiledesc . tileimage , pal ) ;
}
*/
2022-12-05 16:14:01 +00:00
}
2021-04-07 19:59:47 +00:00
//===========================================================================
//
//
//
//===========================================================================
2021-04-08 18:03:50 +00:00
template < int cnt >
2022-12-22 16:33:44 +00:00
static void parseSkip ( FScanner & sc , FScriptPosition & pos )
2021-04-08 18:03:50 +00:00
{
2021-04-10 14:35:29 +00:00
for ( int i = 0 ; i < cnt ; i + + ) if ( ! sc . GetString ( ) ) return ;
2021-04-08 18:03:50 +00:00
}
2022-12-22 16:33:44 +00:00
static void parseDefine ( FScanner & sc , FScriptPosition & pos )
2021-04-09 17:57:56 +00:00
{
FString name ;
if ( ! sc . GetString ( name ) ) return ;
if ( ! sc . GetNumber ( ) ) return ;
2023-10-08 07:15:32 +00:00
sc . AddSymbol ( name . GetChars ( ) , sc . Number ) ;
2021-04-09 17:57:56 +00:00
}
//===========================================================================
//
//
//
//===========================================================================
2022-12-22 16:33:44 +00:00
static void parseDefineTexture ( FScanner & sc , FScriptPosition & pos )
2021-04-07 22:47:07 +00:00
{
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 ;
2022-12-07 16:10:27 +00:00
tileSetHightileReplacement ( sc , tile , palette , sc . String , - 1.0 , 1.0 , 1.0 , 1.0 , 1.0 ) ;
2021-04-07 22:47:07 +00:00
}
2021-04-07 19:59:47 +00:00
//===========================================================================
//
//
//
//===========================================================================
2021-04-21 15:56:42 +00:00
static void parseTexturePaletteBlock ( FScanner & sc , int tile )
2021-04-10 12:09:08 +00:00
{
FScanner : : SavedPos blockend ;
2021-04-21 15:56:42 +00:00
FScriptPosition pos = sc ;
2021-04-10 12:09:08 +00:00
int pal = - 1 , xsiz = 0 , ysiz = 0 ;
FString fn ;
2021-05-11 23:55:06 +00:00
float alphacut = - 1.0 , xscale = 1.0 , yscale = 1.0 , specpower = 1.0 , specfactor = 1.0 ;
2021-07-13 07:48:14 +00:00
bool indexed = false ;
2021-04-10 12:09:08 +00:00
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 ) ;
2021-07-13 07:48:14 +00:00
else if ( sc . Compare ( " indexed " ) ) indexed = true ;
2021-04-10 12:09:08 +00:00
} ;
if ( ( unsigned ) tile < MAXUSERTILES )
{
2021-04-21 15:56:42 +00:00
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 ) ;
2023-10-08 07:15:32 +00:00
else if ( ! fileSystem . FileExists ( fn . GetChars ( ) ) ) pos . Message ( MSG_ERROR , " texture (%d): file '%s' not found in palette definition " , tile , fn . GetChars ( ) ) ;
2021-04-10 12:09:08 +00:00
else
{
if ( xsiz > 0 & & ysiz > 0 )
{
2022-12-07 16:10:27 +00:00
tbuild - > tile [ tile ] . tileimage = createDummyTile ( xsiz , ysiz ) ;
2021-04-10 12:09:08 +00:00
}
xscale = 1.0f / xscale ;
yscale = 1.0f / yscale ;
2023-10-08 07:15:32 +00:00
tileSetHightileReplacement ( sc , tile , pal , fn . GetChars ( ) , alphacut , xscale , yscale , specpower , specfactor , indexed ) ;
2021-04-10 12:09:08 +00:00
}
}
}
2021-04-21 15:56:42 +00:00
static void parseTextureSpecialBlock ( FScanner & sc , int tile , int pal )
2021-04-10 12:09:08 +00:00
{
FScanner : : SavedPos blockend ;
2021-04-21 15:56:42 +00:00
FScriptPosition pos = sc ;
2021-04-10 12:09:08 +00:00
FString fn ;
2021-05-11 23:55:06 +00:00
float xscale = 1.0 , yscale = 1.0 , specpower = 1.0 , specfactor = 1.0 ;
2021-04-10 12:09:08 +00:00
2021-04-21 15:56:42 +00:00
if ( sc . StartBraces ( & blockend ) ) return ;
2021-04-10 12:09:08 +00:00
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 )
{
2021-04-21 15:56:42 +00:00
if ( fn . IsEmpty ( ) ) pos . Message ( MSG_ERROR , " texture (%d): missing file name for layer definition " , tile ) ;
2023-10-08 07:15:32 +00:00
else if ( ! fileSystem . FileExists ( fn . GetChars ( ) ) ) pos . Message ( MSG_ERROR , " texture (%d): file '%s' not found in layer definition " , tile , fn . GetChars ( ) ) ;
2021-04-10 12:09:08 +00:00
else
{
if ( pal = = DETAILPAL )
{
xscale = 1.0f / xscale ;
yscale = 1.0f / yscale ;
}
2023-10-08 07:15:32 +00:00
tileSetHightileReplacement ( sc , tile , pal , fn . GetChars ( ) , - 1.f , xscale , yscale , specpower , specfactor ) ;
2021-04-10 12:09:08 +00:00
}
}
}
2022-12-22 16:33:44 +00:00
static void parseTexture ( FScanner & sc , FScriptPosition & pos )
2021-04-10 12:09:08 +00:00
{
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 ( ) ;
2021-04-21 15:56:42 +00:00
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 ) ;
2021-04-10 12:09:08 +00:00
}
}
//===========================================================================
//
//
//
//===========================================================================
2022-12-22 16:33:44 +00:00
static void parseUndefTexture ( FScanner & sc , FScriptPosition & pos )
2021-04-13 19:02:14 +00:00
{
if ( ! sc . GetNumber ( true ) ) return ;
if ( ValidateTilenum ( " undeftexture " , sc . Number , pos ) ) tileRemoveReplacement ( sc . Number ) ;
}
//===========================================================================
//
//
//
//===========================================================================
2022-12-22 16:33:44 +00:00
static void parseUndefTextureRange ( FScanner & sc , FScriptPosition & pos )
2021-04-13 19:02:14 +00:00
{
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 ) ;
}
//===========================================================================
//
//
//
//===========================================================================
2022-12-22 16:33:44 +00:00
static void parseTileFromTexture ( FScanner & sc , FScriptPosition & pos )
2021-04-13 16:17:28 +00:00
{
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 ) ;
}
}
}
2022-12-07 16:10:27 +00:00
processTileImport ( sc , " tilefromtexture " , pos , imp ) ;
2021-04-13 16:17:28 +00:00
}
//===========================================================================
//
//
//
//===========================================================================
2022-12-22 16:33:44 +00:00
static void parseCopyTile ( FScanner & sc , FScriptPosition & pos )
2021-04-13 16:17:28 +00:00
{
FScanner : : SavedPos blockend ;
int tile = - 1 , source = - 1 ;
int havetile = 0 , xoffset = - 1024 , yoffset = - 1024 ;
2021-11-14 14:03:50 +00:00
int flags = 0 , temppal = - 1 , tempsource = - 1 ;
2021-04-13 16:17:28 +00:00
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 ;
}
2021-04-21 15:56:42 +00:00
if ( ( unsigned ) temppal > = MAXREALPAL )
2021-04-13 16:17:28 +00:00
{
2021-10-08 17:06:41 +00:00
pos . Message ( MSG_ERROR , " copytile: palette number %d out of range (max=%d) \n " , temppal , MAXREALPAL - 1 ) ;
2021-04-13 16:17:28 +00:00
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 ) ;
}
//===========================================================================
//
//
//
//===========================================================================
2022-12-22 16:33:44 +00:00
static void parseImportTile ( FScanner & sc , FScriptPosition & pos )
2021-04-13 16:17:54 +00:00
{
int tile ;
if ( ! sc . GetNumber ( tile , true ) ) return ;
if ( ! sc . GetString ( ) ) return ;
if ( ! ValidateTilenum ( " importtile " , tile , pos ) ) return ;
2022-12-07 16:10:27 +00:00
int texstatus = tileImportFromTexture ( sc , sc . String , tile , 255 , 0 ) ;
if ( texstatus > = 0 ) tbuild - > tile [ tile ] . extinfo . picanm = { } ;
2021-04-13 16:17:54 +00:00
}
//===========================================================================
//
//
//
//===========================================================================
2022-12-22 16:33:44 +00:00
static void parseDummyTile ( FScanner & sc , FScriptPosition & pos )
2021-04-13 16:17:54 +00:00
{
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 ;
2022-12-07 16:10:27 +00:00
auto & tiled = tbuild - > tile [ tile ] ;
tiled . tileimage = createDummyTile ( xsiz , ysiz ) ;
tiled . imported = nullptr ;
2021-04-13 16:17:54 +00:00
}
//===========================================================================
//
//
//
//===========================================================================
2022-12-22 16:33:44 +00:00
static void parseDummyTileRange ( FScanner & sc , FScriptPosition & pos )
2021-04-13 16:17:54 +00:00
{
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 ;
2022-12-07 16:10:27 +00:00
for ( int i = tile1 ; i < = tile2 ; i + + )
{
auto & tiled = tbuild - > tile [ i ] ;
tiled . tileimage = createDummyTile ( xsiz , ysiz ) ;
tiled . imported = nullptr ;
}
2021-04-13 16:17:54 +00:00
}
//===========================================================================
//
//
//
//===========================================================================
2022-12-22 16:33:44 +00:00
static void parseUndefineTile ( FScanner & sc , FScriptPosition & pos )
2021-04-13 16:17:54 +00:00
{
int tile ;
if ( ! sc . GetNumber ( tile , true ) ) return ;
2022-12-07 16:10:27 +00:00
if ( ValidateTilenum ( " undefinetile " , tile , pos ) )
{
auto & tiled = tbuild - > tile [ tile ] ;
tiled . tileimage = nullptr ;
tiled . imported = nullptr ;
}
2021-04-13 16:17:54 +00:00
}
//===========================================================================
//
//
//
//===========================================================================
2022-12-22 16:33:44 +00:00
static void parseUndefineTileRange ( FScanner & sc , FScriptPosition & pos )
2021-04-13 16:17:54 +00:00
{
int tile1 , tile2 ;
if ( ! sc . GetNumber ( tile1 , true ) ) return ;
if ( ! sc . GetNumber ( tile2 , true ) ) return ;
if ( ! ValidateTileRange ( " undefinetilerange " , tile1 , tile2 , pos ) ) return ;
2022-12-07 16:10:27 +00:00
for ( int i = tile1 ; i < = tile2 ; i + + )
{
auto & tiled = tbuild - > tile [ i ] ;
tiled . tileimage = nullptr ;
tiled . imported = nullptr ;
}
2021-04-13 16:17:54 +00:00
}
//===========================================================================
//
//
//
//===========================================================================
2022-12-22 16:33:44 +00:00
static void parseDefineSkybox ( FScanner & sc , FScriptPosition & pos )
2021-04-07 22:47:07 +00:00
{
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 ) ;
}
2021-04-07 19:59:47 +00:00
//===========================================================================
//
//
//
//===========================================================================
2022-12-22 16:33:44 +00:00
static void parseSkybox ( FScanner & sc , FScriptPosition & pos )
2021-04-07 19:59:47 +00:00
{
2021-04-10 12:09:08 +00:00
FString faces [ 6 ] ;
FScanner : : SavedPos blockend ;
int tile = - 1 , pal = 0 ;
2021-07-13 07:48:14 +00:00
bool indexed = false ;
2021-04-10 12:09:08 +00:00
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 ] ) ;
2021-07-13 07:48:14 +00:00
else if ( sc . Compare ( " indexed " ) ) indexed = true ;
2021-04-07 19:59:47 +00:00
// skip over everything else.
2021-04-10 12:09:08 +00:00
}
2021-04-21 15:56:42 +00:00
if ( tile < 0 ) pos . Message ( MSG_ERROR , " skybox: missing tile number " ) ;
2021-07-13 07:48:14 +00:00
else tileSetSkybox ( tile , pal , faces , indexed ) ;
2021-04-07 19:59:47 +00:00
}
//===========================================================================
//
//
//
//===========================================================================
2022-12-22 16:33:44 +00:00
static void parseSetupTile ( FScanner & sc , FScriptPosition & pos )
2021-04-06 14:22:24 +00:00
{
int tile ;
if ( ! sc . GetNumber ( tile , true ) ) return ;
if ( ! ValidateTilenum ( " setuptile " , tile , pos ) ) return ;
2022-12-07 16:10:27 +00:00
auto & tiled = tbuild - > tile [ tile ] ;
if ( ! sc . GetNumber ( tiled . extinfo . hiofs . xsize , true ) ) return ;
if ( ! sc . GetNumber ( tiled . extinfo . hiofs . ysize , true ) ) return ;
if ( ! sc . GetNumber ( tiled . extinfo . hiofs . xoffs , true ) ) return ;
if ( ! sc . GetNumber ( tiled . extinfo . hiofs . yoffs , true ) ) return ;
2021-04-06 14:22:24 +00:00
}
2021-04-07 19:59:47 +00:00
//===========================================================================
//
//
//
//===========================================================================
2022-12-22 16:33:44 +00:00
static void parseSetupTileRange ( FScanner & sc , FScriptPosition & pos )
2021-04-06 14:22:24 +00:00
{
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 ;
2022-12-07 16:10:27 +00:00
for ( int i = tilestart ; i < = tileend ; i + + )
tbuild - > tile [ i ] . extinfo . hiofs = hiofs ;
2021-04-06 14:22:24 +00:00
}
2021-04-06 13:07:12 +00:00
2021-04-07 19:59:47 +00:00
//===========================================================================
//
//
//
//===========================================================================
2022-12-22 16:33:44 +00:00
static void parseAnimTileRange ( FScanner & sc , FScriptPosition & pos )
2021-04-06 13:07:12 +00:00
{
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 ) ;
}
2021-04-09 17:57:56 +00:00
//===========================================================================
//
//
//
//===========================================================================
2022-12-22 16:33:44 +00:00
static void parseAlphahack ( FScanner & sc , FScriptPosition & pos )
2021-04-09 17:57:56 +00:00
{
int tile ;
if ( ! sc . GetNumber ( tile , true ) ) return ;
if ( ! sc . GetFloat ( true ) ) return ;
2022-12-07 16:10:27 +00:00
if ( ( unsigned ) tile < MAXTILES ) tbuild - > tile [ tile ] . alphathreshold = ( float ) sc . Float ;
2021-04-09 17:57:56 +00:00
}
//===========================================================================
//
//
//
//===========================================================================
2022-12-22 16:33:44 +00:00
static void parseAlphahackRange ( FScanner & sc , FScriptPosition & pos )
2021-04-09 17:57:56 +00:00
{
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 + + )
2022-12-07 16:10:27 +00:00
tbuild - > tile [ i ] . alphathreshold = ( float ) sc . Float ;
2021-04-09 17:57:56 +00:00
}
2021-04-11 16:37:11 +00:00
//===========================================================================
//
//
//
//===========================================================================
static int lastvoxid = - 1 ;
2022-12-22 16:33:44 +00:00
static void parseDefineVoxel ( FScanner & sc , FScriptPosition & pos )
2021-04-11 16:37:11 +00:00
{
sc . MustGetString ( ) ;
2022-12-08 17:42:42 +00:00
if ( tbuild - > nextvoxid = = MAXVOXELS )
2021-04-11 16:37:11 +00:00
{
2021-04-21 15:56:42 +00:00
pos . Message ( MSG_ERROR , " Maximum number of voxels (%d) already defined. " , MAXVOXELS ) ;
2021-04-11 16:37:11 +00:00
return ;
}
2022-12-08 17:42:42 +00:00
if ( voxDefine ( tbuild - > nextvoxid , sc . String ) )
2021-04-11 16:37:11 +00:00
{
2021-04-21 15:56:42 +00:00
pos . Message ( MSG_ERROR , " Unable to load voxel file \" %s \" " , sc . String ) ;
2021-04-11 16:37:11 +00:00
return ;
}
2022-12-08 17:42:42 +00:00
lastvoxid = tbuild - > nextvoxid + + ;
2021-04-11 16:37:11 +00:00
}
//===========================================================================
//
//
//
//===========================================================================
2022-12-22 16:33:44 +00:00
static void parseDefineVoxelTiles ( FScanner & sc , FScriptPosition & pos )
2021-04-11 16:37:11 +00:00
{
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 ;
}
2022-12-08 17:26:09 +00:00
for ( int i = tilestart ; i < = tileend ; i + + ) tbuild - > tile [ i ] . extinfo . tiletovox = lastvoxid ;
2021-04-11 16:37:11 +00:00
}
//===========================================================================
//
//
//
//===========================================================================
2022-12-22 16:33:44 +00:00
static void parseVoxel ( FScanner & sc , FScriptPosition & pos )
2021-04-11 16:37:11 +00:00
{
FScanner : : SavedPos blockend ;
int tile0 = MAXTILES , tile1 = - 1 ;
FString fn ;
2021-05-10 22:48:35 +00:00
bool error = false ;
2021-04-11 16:37:11 +00:00
if ( ! sc . GetString ( fn ) ) return ;
2022-12-08 17:42:42 +00:00
if ( tbuild - > nextvoxid = = MAXVOXELS )
2021-04-11 16:37:11 +00:00
{
2021-04-21 15:56:42 +00:00
pos . Message ( MSG_ERROR , " Maximum number of voxels (%d) already defined. " , MAXVOXELS ) ;
2021-05-10 22:48:35 +00:00
error = true ;
2021-04-11 16:37:11 +00:00
}
2023-10-08 07:15:32 +00:00
else if ( voxDefine ( tbuild - > nextvoxid , fn . GetChars ( ) ) )
2021-04-11 16:37:11 +00:00
{
2021-04-21 15:56:42 +00:00
pos . Message ( MSG_ERROR , " Unable to load voxel file \" %s \" " , fn . GetChars ( ) ) ;
2021-05-10 22:48:35 +00:00
error = true ;
2021-04-11 16:37:11 +00:00
}
2022-12-08 17:42:42 +00:00
int lastvoxid = tbuild - > nextvoxid + + ;
2021-12-30 09:30:21 +00:00
2021-04-11 16:37:11 +00:00
if ( sc . StartBraces ( & blockend ) ) return ;
while ( ! sc . FoundEndBrace ( blockend ) )
{
sc . MustGetString ( ) ;
if ( sc . Compare ( " tile " ) )
{
sc . GetNumber ( true ) ;
2021-05-10 22:48:35 +00:00
if ( ValidateTilenum ( " voxel " , sc . Number , pos ) )
{
2022-12-08 17:26:09 +00:00
if ( ! error ) tbuild - > tile [ sc . Number ] . extinfo . tiletovox = lastvoxid ;
2021-05-10 22:48:35 +00:00
}
2021-04-11 16:37:11 +00:00
}
if ( sc . Compare ( " tile0 " ) ) sc . GetNumber ( tile0 , true ) ;
if ( sc . Compare ( " tile1 " ) )
{
sc . GetNumber ( tile1 , true ) ;
2021-05-10 22:48:35 +00:00
if ( ValidateTileRange ( " voxel " , tile0 , tile1 , pos ) & & ! error )
2021-04-11 16:37:11 +00:00
{
2022-12-08 17:26:09 +00:00
for ( int i = tile0 ; i < = tile1 ; i + + ) tbuild - > tile [ i ] . extinfo . tiletovox = lastvoxid ;
2021-04-11 16:37:11 +00:00
}
}
if ( sc . Compare ( " scale " ) )
{
sc . GetFloat ( true ) ;
2021-05-10 22:48:35 +00:00
if ( ! error ) voxscale [ lastvoxid ] = ( float ) sc . Float ;
2021-04-11 16:37:11 +00:00
}
2021-05-10 22:48:35 +00:00
if ( sc . Compare ( " rotate " ) & & ! error ) voxrotate . Set ( lastvoxid ) ;
2021-04-11 16:37:11 +00:00
}
}
2021-04-09 17:57:56 +00:00
//===========================================================================
//
//
//
//===========================================================================
2022-12-22 16:33:44 +00:00
static void parseDefineTint ( FScanner & sc , FScriptPosition & pos )
2021-04-09 17:57:56 +00:00
{
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 ) ;
}
2021-12-30 09:30:21 +00:00
2021-04-10 14:35:29 +00:00
//===========================================================================
//
//
//
//===========================================================================
2022-12-22 16:33:44 +00:00
static void parseFogpal ( FScanner & sc , FScriptPosition & pos )
2021-04-10 14:35:29 +00:00
{
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 ) ;
}
//===========================================================================
//
//
//
//===========================================================================
2022-12-22 16:33:44 +00:00
static void parseNoFloorpalRange ( FScanner & sc , FScriptPosition & pos )
2021-04-10 14:35:29 +00:00
{
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 ;
}
2021-04-11 13:23:42 +00:00
//===========================================================================
//
//
//
//===========================================================================
2022-12-22 16:33:44 +00:00
static void parseTint ( FScanner & sc , FScriptPosition & pos )
2021-04-11 13:23:42 +00:00
{
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 )
2021-04-21 15:56:42 +00:00
pos . Message ( MSG_ERROR , " tint: palette number missing " ) ;
2021-04-11 13:23:42 +00:00
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 ) ;
}
2021-04-12 22:28:51 +00:00
//===========================================================================
//
//
//
//===========================================================================
2022-12-22 16:33:44 +00:00
static void parseMusic ( FScanner & sc , FScriptPosition & pos )
2021-04-12 22:28:51 +00:00
{
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 ) ;
}
2023-10-08 07:15:32 +00:00
SetMusicReplacement ( id . GetChars ( ) , file . GetChars ( ) ) ;
2021-04-12 22:28:51 +00:00
}
//===========================================================================
//
//
//
//===========================================================================
2022-12-22 16:33:44 +00:00
static void parseMapinfo ( FScanner & sc , FScriptPosition & pos )
2021-04-12 22:28:51 +00:00
{
2022-09-30 17:40:37 +00:00
FString title ;
FString mhkfile ;
uint8_t md4b [ 16 ] { } ;
2021-04-12 22:28:51 +00:00
FScanner : : SavedPos blockend ;
2021-11-28 17:37:57 +00:00
TArray < FString > md4s ;
2021-04-12 22:28:51 +00:00
if ( sc . StartBraces ( & blockend ) ) return ;
while ( ! sc . FoundEndBrace ( blockend ) )
{
sc . MustGetString ( ) ;
if ( sc . Compare ( " mapfile " ) ) sc . GetString ( ) ;
2022-09-30 17:40:37 +00:00
else if ( sc . Compare ( " maptitle " ) ) sc . GetString ( title ) ;
else if ( sc . Compare ( " mhkfile " ) ) sc . GetString ( mhkfile ) ;
2021-04-12 22:28:51 +00:00
else if ( sc . Compare ( " mapmd4 " ) )
{
sc . GetString ( ) ;
2021-11-28 17:37:57 +00:00
md4s . Push ( sc . String ) ;
}
}
for ( auto & md4 : md4s )
{
for ( int i = 0 ; i < 16 ; i + + )
{
char smallbuf [ 3 ] = { md4 [ 2 * i ] , md4 [ 2 * i + 1 ] , 0 } ;
2022-09-30 17:40:37 +00:00
md4b [ i ] = ( uint8_t ) strtol ( smallbuf , nullptr , 16 ) ;
2021-04-12 22:28:51 +00:00
}
2022-09-30 17:40:37 +00:00
AddUserMapHack ( title , mhkfile , md4b ) ;
2021-04-12 22:28:51 +00:00
}
}
//===========================================================================
//
//
//
//===========================================================================
2022-12-22 16:33:44 +00:00
static void parseEcho ( FScanner & sc , FScriptPosition & pos )
2021-04-12 22:28:51 +00:00
{
sc . MustGetString ( ) ;
Printf ( " %s \n " , sc . String ) ;
}
//===========================================================================
//
//
//
//===========================================================================
2022-12-22 16:33:44 +00:00
static void parseRffDefineId ( FScanner & sc , FScriptPosition & pos )
2021-04-13 16:15:12 +00:00
{
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 ( ) ) ;
2023-10-08 07:15:32 +00:00
fileSystem . CreatePathlessCopy ( resName . GetChars ( ) , resID , 0 ) ;
2021-04-13 16:15:12 +00:00
}
//===========================================================================
//
// empty stub
//
//===========================================================================
2022-12-22 16:33:44 +00:00
static void parseEmptyBlock ( FScanner & sc , FScriptPosition & pos )
2021-04-13 16:15:12 +00:00
{
FScanner : : SavedPos blockend ;
if ( sc . StartBraces ( & blockend ) ) return ;
2021-04-13 17:22:28 +00:00
sc . RestorePos ( blockend ) ;
2021-04-20 18:41:04 +00:00
sc . CheckString ( " } " ) ;
2021-04-13 16:15:12 +00:00
}
2022-12-22 16:33:44 +00:00
static void parseEmptyBlockWithParm ( FScanner & sc , FScriptPosition & pos )
2021-04-20 18:41:04 +00:00
{
FScanner : : SavedPos blockend ;
sc . MustGetString ( ) ;
if ( sc . StartBraces ( & blockend ) ) return ;
sc . RestorePos ( blockend ) ;
sc . CheckString ( " } " ) ;
}
2021-04-13 16:15:56 +00:00
//===========================================================================
//
//
//
//===========================================================================
2022-12-22 16:33:44 +00:00
static void parseTexHitscanRange ( FScanner & sc , FScriptPosition & pos )
2021-04-13 16:15:56 +00:00
{
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 + + )
2022-12-07 16:10:27 +00:00
tbuild - > tile [ i ] . extinfo . picanm . sf | = PICANM_TEXHITSCAN_BIT ;
2021-04-13 16:15:56 +00:00
}
//===========================================================================
//
//
//
//===========================================================================
2022-12-22 16:33:44 +00:00
static void parseNoFullbrightRange ( FScanner & sc , FScriptPosition & pos )
2021-04-13 16:15:56 +00:00
{
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 + + )
{
2022-12-07 16:10:27 +00:00
tbuild - > tile [ i ] . extinfo . picanm . sf | = PICANM_NOFULLBRIGHT_BIT ;
2021-04-13 18:51:19 +00:00
}
}
2021-04-20 20:43:02 +00:00
//===========================================================================
//
//
//
//===========================================================================
2022-12-22 16:33:44 +00:00
static void parseHighpalookup ( FScanner & sc , FScriptPosition & pos )
2021-04-20 20:43:02 +00:00
{
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 ) ;
}
2021-04-21 15:56:42 +00:00
else if ( ( unsigned ) pal > = MAXREALPAL )
2021-04-20 20:43:02 +00:00
{
pos . Message ( MSG_ERROR , " highpalookup: invalid palette number %d " , pal ) ;
}
else if ( fn . IsEmpty ( ) )
{
pos . Message ( MSG_ERROR , " highpalookup: missing file name " ) ;
}
2023-10-08 07:15:32 +00:00
else if ( ! fileSystem . FileExists ( fn . GetChars ( ) ) )
2021-04-20 20:43:02 +00:00
{
pos . Message ( MSG_ERROR , " highpalookup: file %s not found " , fn . GetChars ( ) ) ;
}
// todo
}
2021-04-20 21:13:42 +00:00
struct ModelStatics
{
int lastmodelid ;
int modelskin , lastmodelskin ;
int seenframe ;
} mdglobal ;
//===========================================================================
//
//
//
//===========================================================================
2022-12-22 16:33:44 +00:00
static void parseDefineModel ( FScanner & sc , FScriptPosition & pos )
2021-04-20 21:13:42 +00:00
{
FString modelfn ;
double scale ;
int shadeoffs ;
if ( ! sc . GetString ( modelfn ) ) return ;
if ( ! sc . GetFloat ( scale , true ) ) return ;
if ( ! sc . GetNumber ( shadeoffs , true ) ) return ;
2023-10-08 07:15:32 +00:00
mdglobal . lastmodelid = modelManager . LoadModel ( modelfn . GetChars ( ) ) ;
2021-04-20 21:13:42 +00:00
if ( mdglobal . lastmodelid < 0 )
{
2021-04-21 15:56:42 +00:00
pos . Message ( MSG_WARNING , " definemodel: unable to load model file '%s' " , modelfn . GetChars ( ) ) ;
2021-04-20 21:13:42 +00:00
}
else
{
2022-09-30 14:25:21 +00:00
modelManager . SetMisc ( mdglobal . lastmodelid , ( float ) scale , shadeoffs , 0.0 , 0.0 , 0 ) ;
2021-04-20 21:13:42 +00:00
mdglobal . modelskin = mdglobal . lastmodelskin = 0 ;
mdglobal . seenframe = 0 ;
}
}
//===========================================================================
//
//
//
//===========================================================================
2022-12-22 16:33:44 +00:00
static void parseDefineModelFrame ( FScanner & sc , FScriptPosition & pos )
2021-04-20 21:13:42 +00:00
{
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 + + )
{
2023-10-08 07:15:32 +00:00
int err = ( modelManager . DefineFrame ( mdglobal . lastmodelid , framename . GetChars ( ) , i , max ( 0 , mdglobal . modelskin ) , 0.0f , 0 ) ) ;
2021-04-20 21:13:42 +00:00
if ( err < 0 ) ok = false ;
if ( err = = - 2 ) pos . Message ( MSG_ERROR , " Invalid tile number %d " , i ) ;
2021-10-08 17:06:41 +00:00
else if ( err = = - 3 ) pos . Message ( MSG_ERROR , " Invalid frame name '%s' " , framename . GetChars ( ) ) ;
2021-04-20 21:13:42 +00:00
}
mdglobal . seenframe = 1 ;
}
//===========================================================================
//
//
//
//===========================================================================
2022-12-22 16:33:44 +00:00
static void parseDefineModelAnim ( FScanner & sc , FScriptPosition & pos )
2021-04-20 21:13:42 +00:00
{
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 ;
}
2023-10-08 07:15:32 +00:00
int err = ( modelManager . DefineAnimation ( mdglobal . lastmodelid , startframe . GetChars ( ) , endframe . GetChars ( ) , ( int32_t ) ( dfps * ( 65536.0 * .001 ) ) , flags ) ) ;
2021-04-21 15:56:42 +00:00
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 ( ) ) ;
2021-04-20 21:13:42 +00:00
}
//===========================================================================
//
//
//
//===========================================================================
2022-12-22 16:33:44 +00:00
static void parseDefineModelSkin ( FScanner & sc , FScriptPosition & pos )
2021-04-20 21:13:42 +00:00
{
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 ;
2023-10-08 07:15:32 +00:00
if ( ! fileSystem . FileExists ( skinfn . GetChars ( ) ) ) return ;
2021-04-20 21:13:42 +00:00
2023-10-08 07:15:32 +00:00
int err = ( modelManager . DefineSkin ( mdglobal . lastmodelid , skinfn . GetChars ( ) , palnum , max ( 0 , mdglobal . modelskin ) , 0 , 0.0f , 1.0f , 1.0f , 0 ) ) ;
2021-04-20 21:13:42 +00:00
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 ) ;
}
//===========================================================================
//
//
//
//===========================================================================
2022-12-22 16:33:44 +00:00
static void parseSelectModelSkin ( FScanner & sc , FScriptPosition & pos )
2021-04-20 21:13:42 +00:00
{
sc . GetNumber ( mdglobal . modelskin , true ) ;
}
//===========================================================================
//
//
//
//===========================================================================
2022-12-22 16:33:44 +00:00
static void parseUndefModel ( FScanner & sc , FScriptPosition & pos )
2021-04-20 21:13:42 +00:00
{
int tile ;
if ( ! sc . GetNumber ( tile , true ) ) return ;
if ( ! ValidateTilenum ( " undefmodel " , tile , pos ) ) return ;
2022-09-30 14:25:21 +00:00
modelManager . UndefineTile ( tile ) ;
2021-04-20 21:13:42 +00:00
}
2022-12-22 16:33:44 +00:00
static void parseUndefModelRange ( FScanner & sc , FScriptPosition & pos )
2021-04-20 21:13:42 +00:00
{
int start , end ;
if ( ! sc . GetNumber ( start , true ) ) return ;
if ( ! sc . GetNumber ( end , true ) ) return ;
if ( ! ValidateTileRange ( " undefmodel " , start , end , pos ) ) return ;
2022-09-30 14:25:21 +00:00
for ( int i = start ; i < = end ; i + + ) modelManager . UndefineTile ( i ) ;
2021-04-20 21:13:42 +00:00
}
2022-12-22 16:33:44 +00:00
static void parseUndefModelOf ( FScanner & sc , FScriptPosition & pos )
2021-04-20 21:13:42 +00:00
{
int tile ;
if ( ! sc . GetNumber ( tile , true ) ) return ;
if ( ! ValidateTilenum ( " undefmodelof " , tile , pos ) ) return ;
pos . Message ( MSG_WARNING , " undefmodelof: currently non-functional. " ) ;
}
2021-04-20 22:00:14 +00:00
//===========================================================================
//
//
//
//===========================================================================
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 ;
2021-05-11 23:55:06 +00:00
float smoothduration = 0.1f ;
2021-04-20 22:00:14 +00:00
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 ) ;
}
2021-04-21 15:56:42 +00:00
if ( ! ValidateTileRange ( " model/frame " , starttile , endtile , pos ) ) return false ;
2021-04-20 22:00:14 +00:00
if ( smoothduration > 1.0 )
{
pos . Message ( MSG_WARNING , " smoothduration out of range " ) ;
smoothduration = 1.0 ;
}
for ( int i = starttile ; i < = endtile & & ok ; i + + )
{
2023-10-08 07:15:32 +00:00
int res = modelManager . DefineFrame ( mdglobal . lastmodelid , framename . GetChars ( ) , i , max ( 0 , mdglobal . modelskin ) , smoothduration , pal ) ;
2021-04-20 22:00:14 +00:00
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 ;
}
2023-10-08 07:15:32 +00:00
int res = modelManager . DefineAnimation ( mdglobal . lastmodelid , startframe . GetChars ( ) , endframe . GetChars ( ) , ( int ) ( fps * ( 65536.0 * .001 ) ) , flags ) ;
2021-04-20 22:00:14 +00:00
if ( res < 0 )
{
2021-04-21 15:56:42 +00:00
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 ( ) ) ;
2021-04-20 22:00:14 +00:00
return false ;
}
return true ;
}
2021-04-20 22:46:32 +00:00
static bool parseModelSkinBlock ( FScanner & sc , int pal )
2021-04-20 22:00:14 +00:00
{
FScanner : : SavedPos blockend ;
FScriptPosition pos = sc ;
FString filename ;
2021-04-20 22:46:32 +00:00
int surface = 0 ;
2021-05-11 23:55:06 +00:00
float param = 1.0 , specpower = 1.0 , specfactor = 1.0 ;
2021-04-20 22:00:14 +00:00
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 ;
2023-10-08 07:15:32 +00:00
if ( ! fileSystem . FileExists ( filename . GetChars ( ) ) )
2021-04-20 22:00:14 +00:00
{
pos . Message ( MSG_ERROR , " %s: file not found " , filename . GetChars ( ) ) ;
return false ;
}
2021-05-11 23:55:06 +00:00
if ( pal = = DETAILPAL ) param = 1.f / param ;
2023-10-08 07:15:32 +00:00
int res = modelManager . DefineSkin ( mdglobal . lastmodelid , filename . GetChars ( ) , pal , max ( 0 , mdglobal . modelskin ) , surface , param , specpower , specfactor , flags ) ;
2021-04-20 22:00:14 +00:00
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 + + )
{
2021-12-14 08:58:01 +00:00
FVector3 addf = { ( float ) add . X , ( float ) add . Y , ( float ) add . Z } ;
2022-09-30 14:25:21 +00:00
int res = modelManager . DefineHud ( mdglobal . lastmodelid , i , addf , angadd , flags , fov ) ;
2021-04-20 22:00:14 +00:00
if ( res < 0 )
{
if ( res = = - 2 ) pos . Message ( MSG_ERROR , " Invalid tile number %d " , i ) ;
return false ;
}
}
return true ;
}
2022-12-22 16:33:44 +00:00
static void parseModel ( FScanner & sc , FScriptPosition & pos )
2021-04-20 22:00:14 +00:00
{
FScanner : : SavedPos blockend ;
FString modelfn ;
double scale = 1.0 , mzadd = 0.0 , myoffset = 0.0 ;
2021-11-14 14:03:50 +00:00
int32_t shadeoffs = 0 , flags = 0 ;
2021-04-20 22:00:14 +00:00
FixedBitArray < 1024 > usedframes ;
usedframes . Zero ( ) ;
mdglobal . modelskin = mdglobal . lastmodelskin = 0 ;
mdglobal . seenframe = 0 ;
if ( ! sc . GetString ( modelfn ) ) return ;
if ( sc . StartBraces ( & blockend ) ) return ;
2023-10-08 07:15:32 +00:00
mdglobal . lastmodelid = modelManager . LoadModel ( modelfn . GetChars ( ) ) ;
2021-04-20 22:00:14 +00:00
if ( mdglobal . lastmodelid < 0 )
{
2021-04-21 15:56:42 +00:00
pos . Message ( MSG_WARNING , " Unable to load model file '%s' " , modelfn . GetChars ( ) ) ;
2021-04-20 22:00:14 +00:00
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 ) ;
2022-09-30 14:25:21 +00:00
modelManager . UndefineModel ( mdglobal . lastmodelid ) ;
2021-04-20 22:00:14 +00:00
}
}
else
{
2022-09-30 14:25:21 +00:00
modelManager . SetMisc ( mdglobal . lastmodelid , ( float ) scale , shadeoffs , ( float ) mzadd , ( float ) myoffset , flags ) ;
2021-04-20 22:00:14 +00:00
mdglobal . modelskin = mdglobal . lastmodelskin = 0 ;
mdglobal . seenframe = 0 ;
}
}
2021-04-20 22:46:32 +00:00
2021-08-22 23:00:30 +00:00
//===========================================================================
//
//
//
//===========================================================================
2021-11-14 21:52:20 +00:00
static bool parseDefineQAVInterpolateIgnoreBlock ( FScanner & sc , const int res_id , TMap < int , TArray < int > > & ignoredata , const int numframes )
2021-08-22 23:00:30 +00:00
{
FScanner : : SavedPos blockend ;
FScriptPosition pos = sc ;
FString scframes , sctiles ;
TArray < int > framearray , tilearray ;
if ( sc . StartBraces ( & blockend ) )
{
2021-08-26 00:33:24 +00:00
pos . Message ( MSG_ERROR , " defineqav (%d): interpolate: ignore: malformed syntax, unable to continue " , res_id ) ;
2021-08-22 23:00:30 +00:00
return false ;
}
while ( ! sc . FoundEndBrace ( blockend ) )
{
sc . GetString ( ) ;
if ( sc . Compare ( " frames " ) ) sc . GetString ( scframes ) ;
else if ( sc . Compare ( " tiles " ) ) sc . GetString ( sctiles ) ;
}
// Confirm we received something for 'frames' and 'tiles'.
if ( scframes . IsEmpty ( ) | | sctiles . IsEmpty ( ) )
{
2021-08-26 00:33:24 +00:00
pos . Message ( MSG_ERROR , " defineqav (%d): interpolate: ignore: unable to get any values for 'frames' or 'tiles', unable to continue " , res_id ) ;
2021-08-22 23:00:30 +00:00
return false ;
}
2021-11-14 21:52:20 +00:00
auto arraybuilder = [ & ] ( const FString & input , TArray < int > & output , const int maxvalue ) - > bool
2021-08-22 23:00:30 +00:00
{
2021-08-23 05:05:38 +00:00
if ( input . CompareNoCase ( " all " ) = = 0 )
2021-08-22 23:00:30 +00:00
{
2021-08-23 05:05:38 +00:00
// All indices from 0 through to maxvalue are to be added to output array.
output . Push ( 0 ) ;
output . Push ( maxvalue ) ;
}
else if ( input . IndexOf ( " - " ) ! = - 1 )
{
// Input is a range of values, split on the hypthen and add each value to the output array.
2021-08-22 23:00:30 +00:00
auto temparray = input . Split ( " - " ) ;
2021-08-23 05:34:14 +00:00
if ( temparray . Size ( ) = = 2 )
{
// Test if keywords 'first' and 'last' have been used.'
2023-10-08 07:15:32 +00:00
output . Push ( temparray [ 0 ] . CompareNoCase ( " first " ) = = 0 ? 0 : atoi ( temparray [ 0 ] . GetChars ( ) ) ) ;
output . Push ( temparray [ 1 ] . CompareNoCase ( " last " ) = = 0 ? maxvalue : atoi ( temparray [ 1 ] . GetChars ( ) ) ) ;
2021-08-23 05:34:14 +00:00
}
2021-08-22 23:00:30 +00:00
}
else
{
2021-08-23 05:05:38 +00:00
// We just have a number. Convert the string into an int and push it twice to the output array.
2023-10-08 07:15:32 +00:00
auto tempvalue = atoi ( input . GetChars ( ) ) ;
2021-08-22 23:00:30 +00:00
for ( auto i = 0 ; i < 2 ; i + + ) output . Push ( tempvalue ) ;
}
2021-08-23 05:05:38 +00:00
if ( output . Size ( ) ! = 2 | | output [ 0 ] > output [ 1 ] | | output [ 0 ] < 0 | | output [ 1 ] > maxvalue )
2021-08-22 23:00:30 +00:00
{
pos . Message ( MSG_ERROR , " defineqav (%d): interpolate: ignore: value of '%s' is malformed, unable to continue " , res_id , input . GetChars ( ) ) ;
return false ;
}
return true ;
} ;
if ( ! arraybuilder ( scframes , framearray , numframes - 1 ) ) return false ;
if ( ! arraybuilder ( sctiles , tilearray , 7 ) ) return false ;
// Process arrays and add ignored frames as required.
for ( auto i = framearray [ 0 ] ; i < = framearray [ 1 ] ; i + + )
{
auto & frametiles = ignoredata [ i ] ;
for ( auto j = tilearray [ 0 ] ; j < = tilearray [ 1 ] ; j + + )
{
if ( ! frametiles . Contains ( j ) ) frametiles . Push ( j ) ;
}
}
return true ;
}
2021-11-14 21:52:20 +00:00
static bool parseDefineQAVInterpolateBlock ( FScanner & sc , const int res_id , const int numframes )
2021-08-22 23:00:30 +00:00
{
FScanner : : SavedPos blockend ;
FScriptPosition pos = sc ;
FString interptype ;
bool loopable = false ;
TMap < int , TArray < int > > ignoredata ;
if ( sc . StartBraces ( & blockend ) )
{
2021-08-26 00:33:24 +00:00
pos . Message ( MSG_ERROR , " defineqav (%d): interpolate: malformed syntax, unable to continue " , res_id ) ;
2021-08-22 23:00:30 +00:00
return false ;
}
while ( ! sc . FoundEndBrace ( blockend ) )
{
sc . GetString ( ) ;
if ( sc . Compare ( " type " ) )
{
2021-08-23 04:19:33 +00:00
if ( interptype . IsNotEmpty ( ) )
{
2021-08-26 00:33:24 +00:00
pos . Message ( MSG_ERROR , " defineqav (%d): interpolate: more than one interpolation type defined, unable to continue " , res_id ) ;
2021-08-23 04:19:33 +00:00
return false ;
}
2021-08-22 23:00:30 +00:00
sc . GetString ( interptype ) ;
if ( ! gi - > IsQAVInterpTypeValid ( interptype ) )
{
2021-08-26 00:33:24 +00:00
pos . Message ( MSG_ERROR , " defineqav (%d): interpolate: interpolation type not found " , res_id ) ;
2021-08-22 23:00:30 +00:00
return false ;
}
}
else if ( sc . Compare ( " loopable " ) ) loopable = true ;
else if ( sc . Compare ( " ignore " ) ) if ( ! parseDefineQAVInterpolateIgnoreBlock ( sc , res_id , ignoredata , numframes ) ) return false ;
}
// Add interpolation properties to game for processing while drawing.
2021-11-14 21:52:20 +00:00
gi - > AddQAVInterpProps ( res_id , interptype , loopable , std : : move ( ignoredata ) ) ;
2021-08-22 23:00:30 +00:00
return true ;
}
2021-11-14 21:52:20 +00:00
static void parseDefineQAV ( FScanner & sc , FScriptPosition & pos )
2021-08-22 23:00:30 +00:00
{
FScanner : : SavedPos blockend ;
FString fn ;
int res_id = - 1 ;
int numframes = - 1 ;
bool interpolate = false ;
if ( ! sc . GetNumber ( res_id , true ) )
{
pos . Message ( MSG_ERROR , " defineqav: invalid or non-defined resource ID " ) ;
return ;
}
if ( sc . StartBraces ( & blockend ) )
{
pos . Message ( MSG_ERROR , " defineqav (%d): malformed syntax, unable to continue " , res_id ) ;
return ;
}
while ( ! sc . FoundEndBrace ( blockend ) )
{
sc . MustGetString ( ) ;
if ( sc . Compare ( " file " ) )
{
2021-08-23 04:19:33 +00:00
if ( fn . IsNotEmpty ( ) )
{
pos . Message ( MSG_ERROR , " defineqav (%d): more than one file defined, unable to continue " , res_id ) ;
return ;
}
2021-08-22 23:00:30 +00:00
sc . GetString ( fn ) ;
// Test file's validity.
FixPathSeperator ( fn ) ;
2023-10-08 07:15:32 +00:00
auto lump = fileSystem . FindFile ( fn . GetChars ( ) ) ;
2021-08-22 23:00:30 +00:00
if ( lump < 0 )
{
pos . Message ( MSG_ERROR , " defineqav (%d): file '%s' could not be found " , res_id , fn . GetChars ( ) ) ;
return ;
}
// Read file to get number of frames from QAV, skipping first 8 bytes.
auto fr = fileSystem . OpenFileReader ( lump ) ;
fr . ReadUInt64 ( ) ;
numframes = fr . ReadInt32 ( ) ;
}
else if ( sc . Compare ( " interpolate " ) )
{
2021-08-23 04:19:33 +00:00
if ( interpolate )
{
pos . Message ( MSG_ERROR , " defineqav (%d): more than one interpolate block defined, unable to continue " , res_id ) ;
return ;
}
2021-08-22 23:00:30 +00:00
interpolate = true ;
if ( ! parseDefineQAVInterpolateBlock ( sc , res_id , numframes ) ) return ;
}
}
// If we're not interpolating, remove any reference to interpolation data for this res_id.
if ( ! interpolate ) gi - > RemoveQAVInterpProps ( res_id ) ;
// Add new file to filesystem.
2023-10-08 07:15:32 +00:00
fileSystem . CreatePathlessCopy ( fn . GetChars ( ) , res_id , 0 ) ;
2021-08-22 23:00:30 +00:00
}
2021-04-20 22:46:32 +00:00
//===========================================================================
//
//
//
//===========================================================================
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 } ,
{ " texture " , parseTexture } ,
{ " tile " , parseTexture } ,
{ " music " , parseMusic } ,
2021-04-21 15:56:42 +00:00
{ " sound " , parseEmptyBlock } ,
2021-04-20 22:46:32 +00:00
{ " 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 } ,
2021-04-21 16:34:34 +00:00
{ " spritecol " , parseSkip < 3 > } ,
2021-04-20 22:46:32 +00:00
{ " 2dcol " , parseSkip < 4 > } ,
2021-04-21 16:34:34 +00:00
{ " 2dcolidxrange " , parseSkip < 3 > } ,
2021-04-20 22:46:32 +00:00
{ " fogpal " , parseFogpal } ,
{ " loadgrp " , parseSkip < 1 > } ,
2021-04-21 16:34:34 +00:00
{ " dummytile " , parseDummyTile } ,
2021-04-20 22:46:32 +00:00
{ " dummytilerange " , parseDummyTileRange } ,
{ " setuptile " , parseSetupTile } ,
{ " setuptilerange " , parseSetupTileRange } ,
{ " undefinetile " , parseUndefineTile } ,
{ " undefinetilerange " , parseUndefineTileRange } ,
{ " animtilerange " , parseAnimTileRange } ,
2021-04-21 16:34:34 +00:00
{ " cachesize " , parseSkip < 1 > } ,
2021-04-20 22:46:32 +00:00
{ " dummytilefrompic " , parseImportTile } ,
{ " tilefromtexture " , parseTileFromTexture } ,
{ " mapinfo " , parseMapinfo } ,
{ " echo " , parseEcho } ,
{ " globalflags " , parseSkip < 1 > } ,
{ " copytile " , parseCopyTile } ,
{ " globalgameflags " , parseSkip < 1 > } ,
2022-12-09 11:53:16 +00:00
{ " multipsky " , parseEmptyBlockWithParm } ,
2021-04-20 22:46:32 +00:00
{ " undefblendtablerange " , parseSkip < 2 > } ,
{ " shadefactor " , parseSkip < 1 > } ,
{ " newgamechoices " , parseEmptyBlock } ,
{ " rffdefineid " , parseRffDefineId } ,
2021-08-22 23:00:30 +00:00
{ " defineqav " , parseDefineQAV } ,
2022-01-26 22:53:05 +00:00
2021-07-11 07:16:26 +00:00
{ nullptr , nullptr } ,
2021-04-20 22:46:32 +00:00
} ;
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 ) ;
}
}
2022-12-07 16:10:27 +00:00
void loaddefinitionsfile ( TilesetBuildInfo & info , const char * fn , bool cumulative , bool maingame )
2021-04-20 22:46:32 +00:00
{
2022-12-07 16:10:27 +00:00
tbuild = & info ;
2021-04-20 22:46:32 +00:00
bool done = false ;
auto parseit = [ & ] ( int lump )
{
FScanner sc ;
sc . OpenLumpNum ( lump ) ;
defsparser ( sc ) ;
done = true ;
Printf ( PRINT_NONOTIFY , " \n " ) ;
} ;
2021-04-22 08:50:49 +00:00
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 ( ) ) ;
2021-04-22 08:57:41 +00:00
deftimer . Reset ( ) ;
2021-04-22 08:50:49 +00:00
} ;
2021-04-20 22:46:32 +00:00
if ( ! cumulative )
{
int lump = fileSystem . FindFile ( fn ) ;
if ( lump > = 0 )
{
Printf ( PRINT_NONOTIFY , " Loading \" %s \" \n " , fn ) ;
2021-04-22 08:57:41 +00:00
deftimer . Clock ( ) ;
2021-04-20 22:46:32 +00:00
parseit ( lump ) ;
2021-04-22 08:50:49 +00:00
printtimer ( fn ) ;
2021-04-20 22:46:32 +00:00
}
}
else
{
int lump , lastlump = 0 ;
while ( ( lump = fileSystem . FindLumpFullName ( fn , & lastlump ) ) > = 0 )
{
2024-11-24 16:22:47 +00:00
if ( maingame & & fileSystem . GetFileContainer ( lump ) > fileSystem . GetMaxBaseNum ( ) ) break ;
2023-08-20 00:25:12 +00:00
Printf ( PRINT_NONOTIFY , " Loading \" %s \" \n " , fileSystem . GetFileFullPath ( lump ) . c_str ( ) ) ;
2021-04-22 08:57:41 +00:00
deftimer . Clock ( ) ;
2021-04-20 22:46:32 +00:00
parseit ( lump ) ;
2021-04-22 08:50:49 +00:00
printtimer ( fn ) ;
2021-04-20 22:46:32 +00:00
}
}
}