2020-03-08 19:32:07 +00:00
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2020 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file r_skins.c
/// \brief Loading skins
# include "doomdef.h"
# include "console.h"
# include "g_game.h"
# include "r_local.h"
# include "st_stuff.h"
# include "w_wad.h"
# include "z_zone.h"
# include "m_misc.h"
# include "info.h" // spr2names
# include "i_video.h" // rendermode
# include "i_system.h"
# include "r_things.h"
# include "r_skins.h"
# include "p_local.h"
# include "dehacked.h" // get_number (for thok)
# include "m_cond.h"
# ifdef HWRENDER
# include "hardware/hw_md2.h"
# endif
INT32 numskins = 0 ;
skin_t skins [ MAXSKINS ] ;
// FIXTHIS: don't work because it must be inistilised before the config load
//#define SKINVALUES
# ifdef SKINVALUES
CV_PossibleValue_t skin_cons_t [ MAXSKINS + 1 ] ;
# endif
//
// P_GetSkinSprite2
// For non-super players, tries each sprite2's immediate predecessor until it finds one with a number of frames or ends up at standing.
// For super players, does the same as above - but tries the super equivalent for each sprite2 before the non-super version.
//
UINT8 P_GetSkinSprite2 ( skin_t * skin , UINT8 spr2 , player_t * player )
{
UINT8 super = 0 , i = 0 ;
if ( ! skin )
return 0 ;
if ( ( playersprite_t ) ( spr2 & ~ FF_SPR2SUPER ) > = free_spr2 )
return 0 ;
while ( ! skin - > sprites [ spr2 ] . numframes
& & spr2 ! = SPR2_STND
& & + + i < 32 ) // recursion limiter
{
if ( spr2 & FF_SPR2SUPER )
{
super = FF_SPR2SUPER ;
spr2 & = ~ FF_SPR2SUPER ;
continue ;
}
switch ( spr2 )
{
// Normal special cases.
case SPR2_JUMP :
spr2 = ( ( player
? player - > charflags
: skin - > flags )
& SF_NOJUMPSPIN ) ? SPR2_SPNG : SPR2_ROLL ;
break ;
case SPR2_TIRE :
spr2 = ( ( player
? player - > charability
: skin - > ability )
= = CA_SWIM ) ? SPR2_SWIM : SPR2_FLY ;
break ;
// Use the handy list, that's what it's there for!
default :
spr2 = spr2defaults [ spr2 ] ;
break ;
}
spr2 | = super ;
}
if ( i > = 32 ) // probably an infinite loop...
return 0 ;
return spr2 ;
}
static void Sk_SetDefaultValue ( skin_t * skin )
{
INT32 i ;
//
// set default skin values
//
memset ( skin , 0 , sizeof ( skin_t ) ) ;
snprintf ( skin - > name ,
sizeof skin - > name , " skin %u " , ( UINT32 ) ( skin - skins ) ) ;
skin - > name [ sizeof skin - > name - 1 ] = ' \0 ' ;
skin - > wadnum = INT16_MAX ;
skin - > flags = 0 ;
strcpy ( skin - > realname , " Someone " ) ;
strcpy ( skin - > hudname , " ??? " ) ;
skin - > starttranscolor = 96 ;
skin - > prefcolor = SKINCOLOR_GREEN ;
skin - > supercolor = SKINCOLOR_SUPERGOLD1 ;
skin - > prefoppositecolor = 0 ; // use tables
skin - > normalspeed = 36 < < FRACBITS ;
skin - > runspeed = 28 < < FRACBITS ;
skin - > thrustfactor = 5 ;
skin - > accelstart = 96 ;
skin - > acceleration = 40 ;
skin - > ability = CA_NONE ;
skin - > ability2 = CA2_SPINDASH ;
skin - > jumpfactor = FRACUNIT ;
skin - > actionspd = 30 < < FRACBITS ;
skin - > mindash = 15 < < FRACBITS ;
skin - > maxdash = 70 < < FRACBITS ;
skin - > radius = mobjinfo [ MT_PLAYER ] . radius ;
skin - > height = mobjinfo [ MT_PLAYER ] . height ;
skin - > spinheight = FixedMul ( skin - > height , 2 * FRACUNIT / 3 ) ;
skin - > shieldscale = FRACUNIT ;
skin - > camerascale = FRACUNIT ;
skin - > thokitem = - 1 ;
skin - > spinitem = - 1 ;
skin - > revitem = - 1 ;
skin - > followitem = 0 ;
skin - > highresscale = FRACUNIT ;
skin - > contspeed = 17 ;
skin - > contangle = 0 ;
skin - > availability = 0 ;
for ( i = 0 ; i < sfx_skinsoundslot0 ; i + + )
if ( S_sfx [ i ] . skinsound ! = - 1 )
skin - > soundsid [ S_sfx [ i ] . skinsound ] = i ;
}
//
// Initialize the basic skins
//
void R_InitSkins ( void )
{
# ifdef SKINVALUES
INT32 i ;
for ( i = 0 ; i < = MAXSKINS ; i + + )
{
skin_cons_t [ i ] . value = 0 ;
skin_cons_t [ i ] . strvalue = NULL ;
}
# endif
// no default skin!
numskins = 0 ;
}
UINT32 R_GetSkinAvailabilities ( void )
{
INT32 s ;
UINT32 response = 0 ;
for ( s = 0 ; s < MAXSKINS ; s + + )
{
if ( skins [ s ] . availability & & unlockables [ skins [ s ] . availability - 1 ] . unlocked )
response | = ( 1 < < s ) ;
}
return response ;
}
// returns true if available in circumstances, otherwise nope
// warning don't use with an invalid skinnum other than -1 which always returns true
boolean R_SkinUsable ( INT32 playernum , INT32 skinnum )
{
return ( ( skinnum = = - 1 ) // Simplifies things elsewhere, since there's already plenty of checks for less-than-0...
| | ( ! skins [ skinnum ] . availability )
| | ( ( ( netgame | | multiplayer ) & & playernum ! = - 1 ) ? ( players [ playernum ] . availabilities & ( 1 < < skinnum ) ) : ( unlockables [ skins [ skinnum ] . availability - 1 ] . unlocked ) )
| | ( modeattacking ) // If you have someone else's run you might as well take a look
| | ( Playing ( ) & & ( R_SkinAvailable ( mapheaderinfo [ gamemap - 1 ] - > forcecharacter ) = = skinnum ) ) // Force 1.
| | ( netgame & & ( cv_forceskin . value = = skinnum ) ) // Force 2.
| | ( metalrecording & & skinnum = = 5 ) // Force 3.
) ;
}
// returns true if the skin name is found (loaded from pwad)
// warning return -1 if not found
INT32 R_SkinAvailable ( const char * name )
{
INT32 i ;
for ( i = 0 ; i < numskins ; i + + )
{
// search in the skin list
if ( stricmp ( skins [ i ] . name , name ) = = 0 )
return i ;
}
return - 1 ;
}
// network code calls this when a 'skin change' is received
void SetPlayerSkin ( INT32 playernum , const char * skinname )
{
INT32 i = R_SkinAvailable ( skinname ) ;
player_t * player = & players [ playernum ] ;
if ( ( i ! = - 1 ) & & R_SkinUsable ( playernum , i ) )
{
SetPlayerSkinByNum ( playernum , i ) ;
return ;
}
if ( P_IsLocalPlayer ( player ) )
CONS_Alert ( CONS_WARNING , M_GetText ( " Skin '%s' not found. \n " ) , skinname ) ;
else if ( server | | IsPlayerAdmin ( consoleplayer ) )
CONS_Alert ( CONS_WARNING , M_GetText ( " Player %d (%s) skin '%s' not found \n " ) , playernum , player_names [ playernum ] , skinname ) ;
SetPlayerSkinByNum ( playernum , 0 ) ;
}
// Same as SetPlayerSkin, but uses the skin #.
// network code calls this when a 'skin change' is received
void SetPlayerSkinByNum ( INT32 playernum , INT32 skinnum )
{
player_t * player = & players [ playernum ] ;
skin_t * skin = & skins [ skinnum ] ;
2020-05-24 00:29:07 +00:00
UINT16 newcolor = 0 ;
2020-03-08 19:32:07 +00:00
if ( skinnum > = 0 & & skinnum < numskins & & R_SkinUsable ( playernum , skinnum ) ) // Make sure it exists!
{
player - > skin = skinnum ;
player - > camerascale = skin - > camerascale ;
player - > shieldscale = skin - > shieldscale ;
player - > charability = ( UINT8 ) skin - > ability ;
player - > charability2 = ( UINT8 ) skin - > ability2 ;
player - > charflags = ( UINT32 ) skin - > flags ;
player - > thokitem = skin - > thokitem < 0 ? ( UINT32 ) mobjinfo [ MT_PLAYER ] . painchance : ( UINT32 ) skin - > thokitem ;
player - > spinitem = skin - > spinitem < 0 ? ( UINT32 ) mobjinfo [ MT_PLAYER ] . damage : ( UINT32 ) skin - > spinitem ;
player - > revitem = skin - > revitem < 0 ? ( mobjtype_t ) mobjinfo [ MT_PLAYER ] . raisestate : ( UINT32 ) skin - > revitem ;
player - > followitem = skin - > followitem ;
if ( ( ( player - > powers [ pw_shield ] & SH_NOSTACK ) = = SH_PINK ) & & ( player - > revitem = = MT_LHRT | | player - > spinitem = = MT_LHRT | | player - > thokitem = = MT_LHRT ) ) // Healers can't keep their buff.
player - > powers [ pw_shield ] & = SH_STACK ;
player - > actionspd = skin - > actionspd ;
player - > mindash = skin - > mindash ;
player - > maxdash = skin - > maxdash ;
player - > normalspeed = skin - > normalspeed ;
player - > runspeed = skin - > runspeed ;
player - > thrustfactor = skin - > thrustfactor ;
player - > accelstart = skin - > accelstart ;
player - > acceleration = skin - > acceleration ;
player - > jumpfactor = skin - > jumpfactor ;
player - > height = skin - > height ;
player - > spinheight = skin - > spinheight ;
if ( ! ( cv_debug | | devparm ) & & ! ( netgame | | multiplayer | | demoplayback ) )
{
if ( playernum = = consoleplayer )
CV_StealthSetValue ( & cv_playercolor , skin - > prefcolor ) ;
else if ( playernum = = secondarydisplayplayer )
CV_StealthSetValue ( & cv_playercolor2 , skin - > prefcolor ) ;
player - > skincolor = newcolor = skin - > prefcolor ;
}
if ( player - > followmobj )
{
P_RemoveMobj ( player - > followmobj ) ;
P_SetTarget ( & player - > followmobj , NULL ) ;
}
if ( player - > mo )
{
fixed_t radius = FixedMul ( skin - > radius , player - > mo - > scale ) ;
if ( ( player - > powers [ pw_carry ] = = CR_NIGHTSMODE ) & & ( skin - > sprites [ SPR2_NFLY ] . numframes = = 0 ) ) // If you don't have a sprite for flying horizontally, use the default NiGHTS skin.
{
skin = & skins [ DEFAULTNIGHTSSKIN ] ;
player - > followitem = skin - > followitem ;
if ( ! ( cv_debug | | devparm ) & & ! ( netgame | | multiplayer | | demoplayback ) )
newcolor = skin - > prefcolor ; // will be updated in thinker to flashing
}
player - > mo - > skin = skin ;
if ( newcolor )
player - > mo - > color = newcolor ;
P_SetScale ( player - > mo , player - > mo - > scale ) ;
player - > mo - > radius = radius ;
P_SetPlayerMobjState ( player - > mo , player - > mo - > state - states ) ; // Prevent visual errors when switching between skins with differing number of frames
}
return ;
}
if ( P_IsLocalPlayer ( player ) )
CONS_Alert ( CONS_WARNING , M_GetText ( " Requested skin %d not found \n " ) , skinnum ) ;
else if ( server | | IsPlayerAdmin ( consoleplayer ) )
CONS_Alert ( CONS_WARNING , " Player %d (%s) skin %d not found \n " , playernum , player_names [ playernum ] , skinnum ) ;
SetPlayerSkinByNum ( playernum , 0 ) ; // not found put the sonic skin
}
//
// Add skins from a pwad, each skin preceded by 'S_SKIN' marker
//
// Does the same is in w_wad, but check only for
// the first 6 characters (this is so we can have S_SKIN1, S_SKIN2..
// for wad editors that don't like multiple resources of the same name)
//
static UINT16 W_CheckForSkinMarkerInPwad ( UINT16 wadid , UINT16 startlump )
{
UINT16 i ;
const char * S_SKIN = " S_SKIN " ;
lumpinfo_t * lump_p ;
// scan forward, start at <startlump>
if ( startlump < wadfiles [ wadid ] - > numlumps )
{
lump_p = wadfiles [ wadid ] - > lumpinfo + startlump ;
for ( i = startlump ; i < wadfiles [ wadid ] - > numlumps ; i + + , lump_p + + )
if ( memcmp ( lump_p - > name , S_SKIN , 6 ) = = 0 )
return i ;
}
return INT16_MAX ; // not found
}
# define HUDNAMEWRITE(value) STRBUFCPY(skin->hudname, value)
// turn _ into spaces and . into katana dot
# define SYMBOLCONVERT(name) for (value = name; *value; value++)\
{ \
if ( * value = = ' _ ' ) * value = ' ' ; \
else if ( * value = = ' . ' ) * value = ' \x1E ' ; \
}
//
// Patch skins from a pwad, each skin preceded by 'P_SKIN' marker
//
// Does the same is in w_wad, but check only for
// the first 6 characters (this is so we can have P_SKIN1, P_SKIN2..
// for wad editors that don't like multiple resources of the same name)
//
static UINT16 W_CheckForPatchSkinMarkerInPwad ( UINT16 wadid , UINT16 startlump )
{
UINT16 i ;
const char * P_SKIN = " P_SKIN " ;
lumpinfo_t * lump_p ;
// scan forward, start at <startlump>
if ( startlump < wadfiles [ wadid ] - > numlumps )
{
lump_p = wadfiles [ wadid ] - > lumpinfo + startlump ;
for ( i = startlump ; i < wadfiles [ wadid ] - > numlumps ; i + + , lump_p + + )
if ( memcmp ( lump_p - > name , P_SKIN , 6 ) = = 0 )
return i ;
}
return INT16_MAX ; // not found
}
static void R_LoadSkinSprites ( UINT16 wadnum , UINT16 * lump , UINT16 * lastlump , skin_t * skin )
{
UINT16 newlastlump ;
UINT8 sprite2 ;
* lump + = 1 ; // start after S_SKIN
* lastlump = W_CheckNumForNamePwad ( " S_END " , wadnum , * lump ) ; // stop at S_END
// old wadding practices die hard -- stop at S_SKIN (or P_SKIN) or S_START if they come before S_END.
newlastlump = W_CheckForSkinMarkerInPwad ( wadnum , * lump ) ;
if ( newlastlump < * lastlump ) * lastlump = newlastlump ;
newlastlump = W_CheckForPatchSkinMarkerInPwad ( wadnum , * lump ) ;
if ( newlastlump < * lastlump ) * lastlump = newlastlump ;
newlastlump = W_CheckNumForNamePwad ( " S_START " , wadnum , * lump ) ;
if ( newlastlump < * lastlump ) * lastlump = newlastlump ;
// ...and let's handle super, too
newlastlump = W_CheckNumForNamePwad ( " S_SUPER " , wadnum , * lump ) ;
if ( newlastlump < * lastlump )
{
newlastlump + + ;
// load all sprite sets we are aware of... for super!
for ( sprite2 = 0 ; sprite2 < free_spr2 ; sprite2 + + )
2020-03-08 19:51:18 +00:00
R_AddSingleSpriteDef ( spr2names [ sprite2 ] , & skin - > sprites [ FF_SPR2SUPER | sprite2 ] , wadnum , newlastlump , * lastlump ) ;
2020-03-08 19:32:07 +00:00
newlastlump - - ;
* lastlump = newlastlump ; // okay, make the normal sprite set loading end there
}
// load all sprite sets we are aware of... for normal stuff.
for ( sprite2 = 0 ; sprite2 < free_spr2 ; sprite2 + + )
2020-03-08 19:51:18 +00:00
R_AddSingleSpriteDef ( spr2names [ sprite2 ] , & skin - > sprites [ sprite2 ] , wadnum , * lump , * lastlump ) ;
2020-03-08 19:32:07 +00:00
if ( skin - > sprites [ 0 ] . numframes = = 0 )
I_Error ( " R_LoadSkinSprites: no frames found for sprite SPR2_%s \n " , spr2names [ 0 ] ) ;
}
// returns whether found appropriate property
static boolean R_ProcessPatchableFields ( skin_t * skin , char * stoken , char * value )
{
// custom translation table
if ( ! stricmp ( stoken , " startcolor " ) )
skin - > starttranscolor = atoi ( value ) ;
# define FULLPROCESS(field) else if (!stricmp(stoken, #field)) skin->field = get_number(value);
// character type identification
FULLPROCESS ( flags )
FULLPROCESS ( ability )
FULLPROCESS ( ability2 )
FULLPROCESS ( thokitem )
FULLPROCESS ( spinitem )
FULLPROCESS ( revitem )
FULLPROCESS ( followitem )
# undef FULLPROCESS
# define GETFRACBITS(field) else if (!stricmp(stoken, #field)) skin->field = atoi(value)<<FRACBITS;
GETFRACBITS ( normalspeed )
GETFRACBITS ( runspeed )
GETFRACBITS ( mindash )
GETFRACBITS ( maxdash )
GETFRACBITS ( actionspd )
GETFRACBITS ( radius )
GETFRACBITS ( height )
GETFRACBITS ( spinheight )
# undef GETFRACBITS
# define GETINT(field) else if (!stricmp(stoken, #field)) skin->field = atoi(value);
GETINT ( thrustfactor )
GETINT ( accelstart )
GETINT ( acceleration )
GETINT ( contspeed )
GETINT ( contangle )
# undef GETINT
2020-07-12 11:39:52 +00:00
# define GETSKINCOLOR(field) else if (!stricmp(stoken, #field)) \
{ \
UINT16 color = R_GetColorByName ( value ) ; \
skin - > field = ( color ? color : SKINCOLOR_GREEN ) ; \
}
2020-03-08 19:32:07 +00:00
GETSKINCOLOR ( prefcolor )
GETSKINCOLOR ( prefoppositecolor )
# undef GETSKINCOLOR
else if ( ! stricmp ( stoken , " supercolor " ) )
2020-07-12 11:39:52 +00:00
{
UINT16 color = R_GetSuperColorByName ( value ) ;
skin - > supercolor = ( color ? color : SKINCOLOR_SUPERGOLD1 ) ;
}
2020-03-08 19:32:07 +00:00
# define GETFLOAT(field) else if (!stricmp(stoken, #field)) skin->field = FLOAT_TO_FIXED(atof(value));
GETFLOAT ( jumpfactor )
GETFLOAT ( highresscale )
GETFLOAT ( shieldscale )
GETFLOAT ( camerascale )
# undef GETFLOAT
# define GETFLAG(field) else if (!stricmp(stoken, #field)) { \
strupr ( value ) ; \
if ( atoi ( value ) | | value [ 0 ] = = ' T ' | | value [ 0 ] = = ' Y ' ) \
skin - > flags | = ( SF_ # # field ) ; \
else \
skin - > flags & = ~ ( SF_ # # field ) ; \
}
// parameters for individual character flags
// these are uppercase so they can be concatenated with SF_
// 1, true, yes are all valid values
GETFLAG ( SUPER )
GETFLAG ( NOSUPERSPIN )
GETFLAG ( NOSPINDASHDUST )
GETFLAG ( HIRES )
GETFLAG ( NOSKID )
GETFLAG ( NOSPEEDADJUST )
GETFLAG ( RUNONWATER )
GETFLAG ( NOJUMPSPIN )
GETFLAG ( NOJUMPDAMAGE )
GETFLAG ( STOMPDAMAGE )
GETFLAG ( MARIODAMAGE )
GETFLAG ( MACHINE )
GETFLAG ( DASHMODE )
GETFLAG ( FASTEDGE )
GETFLAG ( MULTIABILITY )
GETFLAG ( NONIGHTSROTATION )
2020-04-09 23:47:52 +00:00
GETFLAG ( NONIGHTSSUPER )
2020-03-08 19:32:07 +00:00
# undef GETFLAG
else // let's check if it's a sound, otherwise error out
{
boolean found = false ;
sfxenum_t i ;
size_t stokenadjust ;
// Remove the prefix. (We need to affect an adjusting variable so that we can print error messages if it's not actually a sound.)
if ( ( stoken [ 0 ] = = ' D ' | | stoken [ 0 ] = = ' d ' ) & & ( stoken [ 1 ] = = ' S ' | | stoken [ 1 ] = = ' s ' ) ) // DS*
stokenadjust = 2 ;
else // sfx_*
stokenadjust = 4 ;
// Remove the prefix. (We can affect this directly since we're not going to use it again.)
if ( ( value [ 0 ] = = ' D ' | | value [ 0 ] = = ' d ' ) & & ( value [ 1 ] = = ' S ' | | value [ 1 ] = = ' s ' ) ) // DS*
value + = 2 ;
else // sfx_*
value + = 4 ;
// copy name of sounds that are remapped
// for this skin
for ( i = 0 ; i < sfx_skinsoundslot0 ; i + + )
{
if ( ! S_sfx [ i ] . name )
continue ;
if ( S_sfx [ i ] . skinsound ! = - 1
& & ! stricmp ( S_sfx [ i ] . name ,
stoken + stokenadjust ) )
{
skin - > soundsid [ S_sfx [ i ] . skinsound ] =
S_AddSoundFx ( value , S_sfx [ i ] . singularity , S_sfx [ i ] . pitch , true ) ;
found = true ;
}
}
return found ;
}
return true ;
}
//
// Find skin sprites, sounds & optional status bar face, & add them
//
void R_AddSkins ( UINT16 wadnum )
{
UINT16 lump , lastlump = 0 ;
char * buf ;
char * buf2 ;
char * stoken ;
char * value ;
size_t size ;
skin_t * skin ;
boolean hudname , realname ;
//
// search for all skin markers in pwad
//
while ( ( lump = W_CheckForSkinMarkerInPwad ( wadnum , lastlump ) ) ! = INT16_MAX )
{
// advance by default
lastlump = lump + 1 ;
if ( numskins > = MAXSKINS )
{
CONS_Debug ( DBG_RENDER , " ignored skin (%d skins maximum) \n " , MAXSKINS ) ;
continue ; // so we know how many skins couldn't be added
}
buf = W_CacheLumpNumPwad ( wadnum , lump , PU_CACHE ) ;
size = W_LumpLengthPwad ( wadnum , lump ) ;
// for strtok
buf2 = malloc ( size + 1 ) ;
if ( ! buf2 )
I_Error ( " R_AddSkins: No more free memory \n " ) ;
M_Memcpy ( buf2 , buf , size ) ;
buf2 [ size ] = ' \0 ' ;
// set defaults
skin = & skins [ numskins ] ;
Sk_SetDefaultValue ( skin ) ;
skin - > wadnum = wadnum ;
hudname = realname = false ;
// parse
stoken = strtok ( buf2 , " \r \n = " ) ;
while ( stoken )
{
if ( ( stoken [ 0 ] = = ' / ' & & stoken [ 1 ] = = ' / ' )
| | ( stoken [ 0 ] = = ' # ' ) ) // skip comments
{
stoken = strtok ( NULL , " \r \n " ) ; // skip end of line
goto next_token ; // find the real next token
}
value = strtok ( NULL , " \r \n = " ) ;
if ( ! value )
I_Error ( " R_AddSkins: syntax error in S_SKIN lump# %d(%s) in WAD %s \n " , lump , W_CheckNameForNumPwad ( wadnum , lump ) , wadfiles [ wadnum ] - > filename ) ;
// Some of these can't go in R_ProcessPatchableFields because they have side effects for future lines.
// Others can't go in there because we don't want them to be patchable.
if ( ! stricmp ( stoken , " name " ) )
{
INT32 skinnum = R_SkinAvailable ( value ) ;
strlwr ( value ) ;
if ( skinnum = = - 1 )
STRBUFCPY ( skin - > name , value ) ;
// the skin name must uniquely identify a single skin
// if the name is already used I make the name 'namex'
// using the default skin name's number set above
else
{
const size_t stringspace =
strlen ( value ) + sizeof ( numskins ) + 1 ;
char * value2 = Z_Malloc ( stringspace , PU_STATIC , NULL ) ;
snprintf ( value2 , stringspace ,
" %s%d " , value , numskins ) ;
value2 [ stringspace - 1 ] = ' \0 ' ;
if ( R_SkinAvailable ( value2 ) = = - 1 )
// I'm lazy so if NEW name is already used I leave the 'skin x'
// default skin name set in Sk_SetDefaultValue
STRBUFCPY ( skin - > name , value2 ) ;
Z_Free ( value2 ) ;
}
// copy to hudname and fullname as a default.
if ( ! realname )
{
STRBUFCPY ( skin - > realname , skin - > name ) ;
for ( value = skin - > realname ; * value ; value + + )
{
if ( * value = = ' _ ' ) * value = ' ' ; // turn _ into spaces.
else if ( * value = = ' . ' ) * value = ' \x1E ' ; // turn . into katana dot.
}
}
if ( ! hudname )
{
HUDNAMEWRITE ( skin - > name ) ;
strupr ( skin - > hudname ) ;
SYMBOLCONVERT ( skin - > hudname )
}
}
else if ( ! stricmp ( stoken , " realname " ) )
{ // Display name (eg. "Knuckles")
realname = true ;
STRBUFCPY ( skin - > realname , value ) ;
SYMBOLCONVERT ( skin - > realname )
if ( ! hudname )
HUDNAMEWRITE ( skin - > realname ) ;
}
else if ( ! stricmp ( stoken , " hudname " ) )
{ // Life icon name (eg. "K.T.E")
hudname = true ;
HUDNAMEWRITE ( value ) ;
SYMBOLCONVERT ( skin - > hudname )
if ( ! realname )
STRBUFCPY ( skin - > realname , skin - > hudname ) ;
}
else if ( ! stricmp ( stoken , " availability " ) )
{
skin - > availability = atoi ( value ) ;
if ( skin - > availability > = MAXUNLOCKABLES )
skin - > availability = 0 ;
}
else if ( ! R_ProcessPatchableFields ( skin , stoken , value ) )
CONS_Debug ( DBG_SETUP , " R_AddSkins: Unknown keyword '%s' in S_SKIN lump #%d (WAD %s) \n " , stoken , lump , wadfiles [ wadnum ] - > filename ) ;
next_token :
stoken = strtok ( NULL , " \r \n = " ) ;
}
free ( buf2 ) ;
// Add sprites
R_LoadSkinSprites ( wadnum , & lump , & lastlump , skin ) ;
//ST_LoadFaceGraphics(numskins); -- nah let's do this elsewhere
R_FlushTranslationColormapCache ( ) ;
if ( ! skin - > availability ) // Safe to print...
CONS_Printf ( M_GetText ( " Added skin '%s' \n " ) , skin - > name ) ;
# ifdef SKINVALUES
skin_cons_t [ numskins ] . value = numskins ;
skin_cons_t [ numskins ] . strvalue = skin - > name ;
# endif
# ifdef HWRENDER
if ( rendermode = = render_opengl )
HWR_AddPlayerModel ( numskins ) ;
# endif
numskins + + ;
}
return ;
}
//
// Patch skin sprites
//
void R_PatchSkins ( UINT16 wadnum )
{
UINT16 lump , lastlump = 0 ;
char * buf ;
char * buf2 ;
char * stoken ;
char * value ;
size_t size ;
skin_t * skin ;
boolean noskincomplain , realname , hudname ;
//
// search for all skin patch markers in pwad
//
while ( ( lump = W_CheckForPatchSkinMarkerInPwad ( wadnum , lastlump ) ) ! = INT16_MAX )
{
INT32 skinnum = 0 ;
// advance by default
lastlump = lump + 1 ;
buf = W_CacheLumpNumPwad ( wadnum , lump , PU_CACHE ) ;
size = W_LumpLengthPwad ( wadnum , lump ) ;
// for strtok
buf2 = malloc ( size + 1 ) ;
if ( ! buf2 )
I_Error ( " R_PatchSkins: No more free memory \n " ) ;
M_Memcpy ( buf2 , buf , size ) ;
buf2 [ size ] = ' \0 ' ;
skin = NULL ;
noskincomplain = realname = hudname = false ;
/*
Parse . Has more phases than the parser in R_AddSkins because it needs to have the patching name first ( no default skin name is acceptible for patching , unlike skin creation )
*/
stoken = strtok ( buf2 , " \r \n = " ) ;
while ( stoken )
{
if ( ( stoken [ 0 ] = = ' / ' & & stoken [ 1 ] = = ' / ' )
| | ( stoken [ 0 ] = = ' # ' ) ) // skip comments
{
stoken = strtok ( NULL , " \r \n " ) ; // skip end of line
goto next_token ; // find the real next token
}
value = strtok ( NULL , " \r \n = " ) ;
if ( ! value )
I_Error ( " R_PatchSkins: syntax error in P_SKIN lump# %d(%s) in WAD %s \n " , lump , W_CheckNameForNumPwad ( wadnum , lump ) , wadfiles [ wadnum ] - > filename ) ;
if ( ! skin ) // Get the name!
{
if ( ! stricmp ( stoken , " name " ) )
{
strlwr ( value ) ;
skinnum = R_SkinAvailable ( value ) ;
if ( skinnum ! = - 1 )
skin = & skins [ skinnum ] ;
else
{
CONS_Debug ( DBG_SETUP , " R_PatchSkins: unknown skin name in P_SKIN lump# %d(%s) in WAD %s \n " , lump , W_CheckNameForNumPwad ( wadnum , lump ) , wadfiles [ wadnum ] - > filename ) ;
noskincomplain = true ;
}
}
}
else // Get the properties!
{
// Some of these can't go in R_ProcessPatchableFields because they have side effects for future lines.
if ( ! stricmp ( stoken , " realname " ) )
{ // Display name (eg. "Knuckles")
realname = true ;
STRBUFCPY ( skin - > realname , value ) ;
SYMBOLCONVERT ( skin - > realname )
if ( ! hudname )
HUDNAMEWRITE ( skin - > realname ) ;
}
else if ( ! stricmp ( stoken , " hudname " ) )
{ // Life icon name (eg. "K.T.E")
hudname = true ;
HUDNAMEWRITE ( value ) ;
SYMBOLCONVERT ( skin - > hudname )
if ( ! realname )
STRBUFCPY ( skin - > realname , skin - > hudname ) ;
}
else if ( ! R_ProcessPatchableFields ( skin , stoken , value ) )
CONS_Debug ( DBG_SETUP , " R_PatchSkins: Unknown keyword '%s' in P_SKIN lump #%d (WAD %s) \n " , stoken , lump , wadfiles [ wadnum ] - > filename ) ;
}
if ( ! skin )
break ;
next_token :
stoken = strtok ( NULL , " \r \n = " ) ;
}
free ( buf2 ) ;
if ( ! skin ) // Didn't include a name parameter? What a waste.
{
if ( ! noskincomplain )
CONS_Debug ( DBG_SETUP , " R_PatchSkins: no skin name given in P_SKIN lump #%d (WAD %s) \n " , lump , wadfiles [ wadnum ] - > filename ) ;
continue ;
}
// Patch sprites
R_LoadSkinSprites ( wadnum , & lump , & lastlump , skin ) ;
//ST_LoadFaceGraphics(skinnum); -- nah let's do this elsewhere
R_FlushTranslationColormapCache ( ) ;
if ( ! skin - > availability ) // Safe to print...
CONS_Printf ( M_GetText ( " Patched skin '%s' \n " ) , skin - > name ) ;
}
return ;
}
# undef HUDNAMEWRITE
# undef SYMBOLCONVERT