Merge branch 'next' into text-prompt-features

This commit is contained in:
Lactozilla 2024-01-17 23:17:22 -03:00
commit f24cf804f4
37 changed files with 1427 additions and 194 deletions

View file

@ -72,6 +72,7 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32
r_things.c
r_bbox.c
r_textures.c
r_translation.c
r_patch.c
r_patchrotation.c
r_picformats.c

View file

@ -66,6 +66,7 @@ r_splats.c
r_things.c
r_bbox.c
r_textures.c
r_translation.c
r_patch.c
r_patchrotation.c
r_picformats.c

View file

@ -1429,8 +1429,8 @@ void CV_RegisterVar(consvar_t *variable)
#ifdef PARANOIA
if ((variable->flags & CV_NOINIT) && !(variable->flags & CV_CALL))
I_Error("variable %s has CV_NOINIT without CV_CALL\n", variable->name);
if ((variable->flags & CV_CALL) && !variable->func)
I_Error("variable %s has CV_CALL without a function\n", variable->name);
if ((variable->flags & CV_CALL) && !(variable->func || variable->can_change))
I_Error("variable %s has CV_CALL without any callbacks\n", variable->name);
#endif
if (variable->flags & CV_NOINIT)
@ -1496,12 +1496,35 @@ static void Setvalue(consvar_t *var, const char *valstr, boolean stealth)
boolean override = false;
INT32 overrideval = 0;
// If we want messages informing us if cheats have been enabled or disabled,
// we need to rework the consvars a little bit. This call crashes the game
// on load because not all variables will be registered at that time.
/* boolean prevcheats = false;
if (var->flags & CV_CHEAT)
prevcheats = CV_CheatsEnabled(); */
// raise 'can change' code
LUA_CVarChanged(var); // let consolelib know what cvar this is.
if (var->flags & CV_CALL && var->can_change && !stealth)
{
if (!var->can_change(valstr))
{
// The callback refused the default value on register. How naughty...
// So we just use some fallback value.
if (var->string == NULL)
{
if (var->PossibleValue)
{
// Use PossibleValue
valstr = var->PossibleValue[0].strvalue;
}
else
{
// Else, use an empty string
valstr = "";
}
}
else
{
// Callback returned false, and the game is not registering this variable,
// so we can return safely.
return;
}
}
}
if (var->PossibleValue)
{
@ -1663,16 +1686,6 @@ found:
}
finish:
// See the note above.
/* if (var->flags & CV_CHEAT)
{
boolean newcheats = CV_CheatsEnabled();
if (!prevcheats && newcheats)
CONS_Printf(M_GetText("Cheats have been enabled.\n"));
else if (prevcheats && !newcheats)
CONS_Printf(M_GetText("Cheats have been disabled.\n"));
} */
if (var->flags & CV_SHOWMODIFONETIME || var->flags & CV_SHOWMODIF)
{
@ -1685,8 +1698,7 @@ finish:
}
var->flags |= CV_MODIFIED;
// raise 'on change' code
LUA_CVarChanged(var); // let consolelib know what cvar this is.
if (var->flags & CV_CALL && !stealth)
if (var->flags & CV_CALL && var->func && !stealth)
var->func();
return;

View file

@ -136,6 +136,7 @@ typedef struct consvar_s //NULL, NULL, 0, NULL, NULL |, 0, NULL, NULL, 0, 0, NUL
INT32 flags; // flags see cvflags_t above
CV_PossibleValue_t *PossibleValue; // table of possible values
void (*func)(void); // called on change, if CV_CALL set
boolean (*can_change)(const char*); // called before change, if CV_CALL set
INT32 value; // for INT32 and fixed_t
const char *string; // value in string
char *zstring; // Either NULL or same as string.
@ -158,6 +159,9 @@ typedef struct consvar_s //NULL, NULL, 0, NULL, NULL |, 0, NULL, NULL, 0, 0, NUL
/* name, defaultvalue, flags, PossibleValue, func */
#define CVAR_INIT( ... ) \
{ __VA_ARGS__, NULL, 0, NULL, NULL, {0, {NULL}}, 0U, (char)0, NULL }
#define CVAR_INIT_WITH_CALLBACKS( ... ) \
{ __VA_ARGS__, 0, NULL, NULL, {0, {NULL}}, 0U, (char)0, NULL }
#ifdef OLD22DEMOCOMPAT

View file

@ -51,6 +51,7 @@
#include "usdf.h"
#include "r_main.h"
#include "r_local.h"
#include "r_translation.h"
#include "s_sound.h"
#include "st_stuff.h"
#include "v_video.h"
@ -1471,6 +1472,8 @@ void D_SRB2Main(void)
// setup loading screen
SCR_Startup();
PaletteRemap_Init();
HU_Init();
CON_Init();

View file

@ -573,7 +573,8 @@ static int ScanConstants(lua_State *L, boolean mathlib, const char *word)
if (mathlib) return luaL_error(L, "NiGHTS grade '%s' could not be found.\n", word);
return 0;
}
else if (fastncmp("MN_",word,3)) {
else if (fastncmp("MN_",word,3))
{
p = word+3;
for (i = 0; i < NUMMENUTYPES; i++)
if (fastcmp(p, MENUTYPES_LIST[i])) {
@ -583,6 +584,17 @@ static int ScanConstants(lua_State *L, boolean mathlib, const char *word)
if (mathlib) return luaL_error(L, "menutype '%s' could not be found.\n", word);
return 0;
}
else if (mathlib && fastncmp("TRANSLATION_",word,12))
{
p = word+12;
int id = R_FindCustomTranslation_CaseInsensitive(p);
if (id != -1)
{
lua_pushinteger(L, id);
return 1;
}
return luaL_error(L, "translation '%s' could not be found.\n", word);
}
// TODO: 2.3: Delete this alias
if (fastcmp(word, "BT_USE"))

View file

@ -20,6 +20,7 @@
#include "m_misc.h"
#include "p_local.h"
#include "st_stuff.h"
#include "r_translation.h"
#include "fastcmp.h"
#include "lua_script.h"
#include "lua_libs.h"

View file

@ -219,6 +219,7 @@ actionpointer_t actionpointers[] =
{{A_ChangeColorRelative}, "A_CHANGECOLORRELATIVE"},
{{A_ChangeColorAbsolute}, "A_CHANGECOLORABSOLUTE"},
{{A_Dye}, "A_DYE"},
{{A_SetTranslation}, "A_SETTRANSLATION"},
{{A_MoveRelative}, "A_MOVERELATIVE"},
{{A_MoveAbsolute}, "A_MOVEABSOLUTE"},
{{A_Thrust}, "A_THRUST"},

View file

@ -555,6 +555,7 @@ void M_UnGetToken(void);
void M_TokenizerOpen(const char *inputString);
void M_TokenizerClose(void);
const char *M_TokenizerRead(UINT32 i);
const char *M_TokenizerReadZDoom(UINT32 i);
UINT32 M_TokenizerGetEndPos(void);
void M_TokenizerSetEndPos(UINT32 newPos);
char *sizeu1(size_t num);

View file

@ -74,8 +74,6 @@ typedef struct gl_vissprite_s
float spritexscale, spriteyscale;
float spritexoffset, spriteyoffset;
skincolornum_t color;
UINT32 renderflags;
UINT8 rotateflags;

View file

@ -39,6 +39,7 @@
#include "../m_cheat.h"
#include "../f_finale.h"
#include "../r_things.h" // R_GetShadowZ
#include "../r_translation.h"
#include "../d_main.h"
#include "../p_slopes.h"
#include "hw_md2.h"
@ -1532,7 +1533,7 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom
wallVerts[3].t += (gl_frontsector->ceilingheight - worldtop) * yscale * grTex->scaleY;
wallVerts[2].t += (gl_frontsector->ceilingheight - worldtopslope) * yscale * grTex->scaleY;
wallVerts[0].t += (gl_frontsector->floorheight - worldbottom) * yscale * grTex->scaleY;
wallVerts[1].t += (gl_frontsector->floorheight - worldbottomslope) * yscale * yscale;
wallVerts[1].t += (gl_frontsector->floorheight - worldbottomslope) * yscale * grTex->scaleY;
} else if (gl_linedef->flags & ML_DONTPEGBOTTOM) {
wallVerts[3].t = wallVerts[0].t + ((worldbottom - worldtop) * yscale) * grTex->scaleY;
wallVerts[2].t = wallVerts[1].t + ((worldbottomslope - worldtopslope) * yscale) * grTex->scaleY;
@ -5064,6 +5065,8 @@ static void HWR_ProjectSprite(mobj_t *thing)
boolean vflip = (!(thing->eflags & MFE_VERTICALFLIP) != !R_ThingVerticallyFlipped(thing));
boolean mirrored = thing->mirrored;
boolean hflip = (!R_ThingHorizontallyFlipped(thing) != !mirrored);
skincolornum_t color;
UINT16 translation;
INT32 dispoffset;
angle_t ang;
@ -5495,45 +5498,19 @@ static void HWR_ProjectSprite(mobj_t *thing)
vis->gpatch = (patch_t *)W_CachePatchNum(sprframe->lumppat[rot], PU_SPRITE);
vis->mobj = thing;
if ((thing->flags2 & MF2_LINKDRAW) && thing->tracer && thing->color == SKINCOLOR_NONE)
vis->color = thing->tracer->color;
color = thing->tracer->color;
else
vis->color = thing->color;
color = thing->color;
if ((thing->flags2 & MF2_LINKDRAW) && thing->tracer && thing->translation == 0)
translation = thing->tracer->translation;
else
translation = thing->translation;
//Hurdler: 25/04/2000: now support colormap in hardware mode
if ((vis->mobj->flags & (MF_ENEMY|MF_BOSS)) && (vis->mobj->flags2 & MF2_FRET) && !(vis->mobj->flags & MF_GRENADEBOUNCE) && (leveltime & 1)) // Bosses "flash"
{
if (vis->mobj->type == MT_CYBRAKDEMON || vis->mobj->colorized)
vis->colormap = R_GetTranslationColormap(TC_ALLWHITE, 0, GTC_CACHE);
else if (vis->mobj->type == MT_METALSONIC_BATTLE)
vis->colormap = R_GetTranslationColormap(TC_METALSONIC, 0, GTC_CACHE);
else
vis->colormap = R_GetTranslationColormap(TC_BOSS, vis->color, GTC_CACHE);
}
else if (vis->color)
{
// New colormap stuff for skins Tails 06-07-2002
if (thing->colorized)
vis->colormap = R_GetTranslationColormap(TC_RAINBOW, vis->color, GTC_CACHE);
else if (thing->player && thing->player->dashmode >= DASHMODE_THRESHOLD
&& (thing->player->charflags & SF_DASHMODE)
&& ((leveltime/2) & 1))
{
if (thing->player->charflags & SF_MACHINE)
vis->colormap = R_GetTranslationColormap(TC_DASHMODE, 0, GTC_CACHE);
else
vis->colormap = R_GetTranslationColormap(TC_RAINBOW, vis->color, GTC_CACHE);
}
else if (thing->skin && thing->sprite == SPR_PLAY) // This thing is a player!
{
UINT8 skinnum = ((skin_t*)thing->skin)->skinnum;
vis->colormap = R_GetTranslationColormap(skinnum, vis->color, GTC_CACHE);
}
else
vis->colormap = R_GetTranslationColormap(TC_DEFAULT, vis->color ? vis->color : SKINCOLOR_CYAN, GTC_CACHE);
}
else
vis->colormap = NULL;
vis->colormap = R_GetTranslationForThing(vis->mobj, color, translation);
// set top/bottom coords
vis->gzt = gzt;
@ -5659,7 +5636,6 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing)
vis->gpatch = (patch_t *)W_CachePatchNum(sprframe->lumppat[rot], PU_SPRITE);
vis->flip = flip;
vis->mobj = (mobj_t *)thing;
vis->color = SKINCOLOR_NONE;
vis->colormap = NULL;

View file

@ -173,6 +173,7 @@ enum actionnum
A_CHANGECOLORRELATIVE,
A_CHANGECOLORABSOLUTE,
A_DYE,
A_SETTRANSLATION,
A_MOVERELATIVE,
A_MOVEABSOLUTE,
A_THRUST,
@ -445,6 +446,7 @@ void A_SetRandomTics();
void A_ChangeColorRelative();
void A_ChangeColorAbsolute();
void A_Dye();
void A_SetTranslation();
void A_MoveRelative();
void A_MoveAbsolute();
void A_Thrust();

View file

@ -300,6 +300,30 @@ static void Lua_OnChange(void)
lua_remove(gL, 1); // remove LUA_GetErrorMessage
}
static boolean Lua_CanChange(const char *valstr)
{
lua_pushcfunction(gL, LUA_GetErrorMessage);
lua_insert(gL, 1); // Because LUA_Call wants it at index 1.
// From CV_CanChange registry field, get the function for this cvar by name.
lua_getfield(gL, LUA_REGISTRYINDEX, "CV_CanChange");
I_Assert(lua_istable(gL, -1));
lua_pushlightuserdata(gL, this_cvar);
lua_rawget(gL, -2); // get function
LUA_RawPushUserdata(gL, this_cvar);
lua_pushstring(gL, valstr);
boolean result;
LUA_Call(gL, 2, 1, 1); // call function(cvar, valstr)
result = lua_toboolean(gL, -1);
lua_pop(gL, 1); // pop CV_CanChange table
lua_remove(gL, 1); // remove LUA_GetErrorMessage
return result;
}
static int lib_cvRegisterVar(lua_State *L)
{
const char *k;
@ -458,6 +482,20 @@ static int lib_cvRegisterVar(lua_State *L)
lua_pop(L, 1);
cvar->func = Lua_OnChange;
}
else if (cvar->flags & CV_CALL && (k && fasticmp(k, "can_change")))
{
if (!lua_isfunction(L, 4))
{
TYPEERROR("func", LUA_TFUNCTION)
}
lua_getfield(L, LUA_REGISTRYINDEX, "CV_CanChange");
I_Assert(lua_istable(L, 5));
lua_pushlightuserdata(L, cvar);
lua_pushvalue(L, 4);
lua_rawset(L, 5);
lua_pop(L, 1);
cvar->can_change = Lua_CanChange;
}
lua_pop(L, 1);
}
@ -479,9 +517,9 @@ static int lib_cvRegisterVar(lua_State *L)
return luaL_error(L, M_GetText("Variable %s has CV_NOINIT without CV_CALL"), cvar->name);
}
if ((cvar->flags & CV_CALL) && !cvar->func)
if ((cvar->flags & CV_CALL) && !(cvar->func || cvar->can_change))
{
return luaL_error(L, M_GetText("Variable %s has CV_CALL without a function"), cvar->name);
return luaL_error(L, M_GetText("Variable %s has CV_CALL without any callbacks"), cvar->name);
}
cvar->flags |= CV_ALLOWLUA;
@ -672,6 +710,8 @@ int LUA_ConsoleLib(lua_State *L)
lua_setfield(L, LUA_REGISTRYINDEX, "CV_PossibleValue");
lua_newtable(L);
lua_setfield(L, LUA_REGISTRYINDEX, "CV_OnChange");
lua_newtable(L);
lua_setfield(L, LUA_REGISTRYINDEX, "CV_CanChange");
// Push opaque CV_PossibleValue pointers
// Because I don't care enough to bother.

View file

@ -22,6 +22,7 @@
#include "r_patch.h"
#include "r_picformats.h"
#include "r_things.h"
#include "r_translation.h"
#include "r_draw.h" // R_GetColorByName
#include "doomstat.h" // luabanks[]
@ -1899,6 +1900,32 @@ static int colorramp_len(lua_State *L)
return 1;
}
//////////////////////
// TRANSLATION INFO //
//////////////////////
// Arbitrary translations[] table index -> colormap_t *
static int lib_getTranslation(lua_State *L)
{
lua_remove(L, 1);
const char *name = luaL_checkstring(L, 1);
remaptable_t *tr = R_GetTranslationByID(R_FindCustomTranslation(name));
if (tr)
LUA_PushUserdata(L, &tr->remap, META_COLORMAP);
else
lua_pushnil(L);
return 1;
}
// #translations -> R_NumCustomTranslations()
static int lib_translationslen(lua_State *L)
{
lua_pushinteger(L, R_NumCustomTranslations());
return 1;
}
//////////////////////////////
//
// Now push all these functions into the Lua state!
@ -1931,6 +1958,7 @@ int LUA_InfoLib(lua_State *L)
LUA_RegisterGlobalUserdata(L, "spr2defaults", lib_getSpr2default, lib_setSpr2default, lib_spr2namelen);
LUA_RegisterGlobalUserdata(L, "states", lib_getState, lib_setState, lib_statelen);
LUA_RegisterGlobalUserdata(L, "mobjinfo", lib_getMobjInfo, lib_setMobjInfo, lib_mobjinfolen);
LUA_RegisterGlobalUserdata(L, "translations", lib_getTranslation, NULL, lib_translationslen);
LUA_RegisterGlobalUserdata(L, "skincolors", lib_getSkinColor, lib_setSkinColor, lib_skincolorslen);
LUA_RegisterGlobalUserdata(L, "spriteinfo", lib_getSpriteInfo, lib_setSpriteInfo, lib_spriteinfolen);
LUA_RegisterGlobalUserdata(L, "sfxinfo", lib_getSfxInfo, lib_setSfxInfo, lib_sfxlen);

View file

@ -14,6 +14,7 @@
#include "fastcmp.h"
#include "r_data.h"
#include "r_skins.h"
#include "r_translation.h"
#include "p_local.h"
#include "g_game.h"
#include "p_setup.h"
@ -66,6 +67,7 @@ enum mobj_e {
mobj_renderflags,
mobj_skin,
mobj_color,
mobj_translation,
mobj_blendmode,
mobj_bnext,
mobj_bprev,
@ -146,6 +148,7 @@ static const char *const mobj_opt[] = {
"renderflags",
"skin",
"color",
"translation",
"blendmode",
"bnext",
"bprev",
@ -338,6 +341,16 @@ static int mobj_get(lua_State *L)
case mobj_color:
lua_pushinteger(L, mo->color);
break;
case mobj_translation:
if (mo->translation)
{
const char *name = R_GetCustomTranslationName(mo->translation);
if (name)
lua_pushstring(L, name);
break;
}
lua_pushnil(L);
break;
case mobj_blendmode:
lua_pushinteger(L, mo->blendmode);
break;
@ -689,12 +702,26 @@ static int mobj_set(lua_State *L)
}
case mobj_color:
{
UINT16 newcolor = (UINT16)luaL_checkinteger(L,3);
UINT16 newcolor = (UINT16)luaL_checkinteger(L, 3);
if (newcolor >= numskincolors)
return luaL_error(L, "mobj.color %d out of range (0 - %d).", newcolor, numskincolors-1);
mo->color = newcolor;
break;
}
case mobj_translation:
{
if (!lua_isnil(L, 3)) {
const char *tr = luaL_checkstring(L, 3);
int id = R_FindCustomTranslation(tr);
if (id != -1)
mo->translation = id;
else
return luaL_error(L, "invalid translation '%s'.", tr);
}
else
mo->translation = 0;
break;
}
case mobj_blendmode:
{
INT32 blendmode = (INT32)luaL_checkinteger(L, 3);

View file

@ -54,7 +54,8 @@ enum skin {
skin_contspeed,
skin_contangle,
skin_soundsid,
skin_sprites
skin_sprites,
skin_natkcolor
};
static const char *const skin_opt[] = {
@ -94,6 +95,7 @@ static const char *const skin_opt[] = {
"contangle",
"soundsid",
"sprites",
"natkcolor",
NULL};
#define UNIMPLEMENTED luaL_error(L, LUA_QL("skin_t") " field " LUA_QS " is not implemented for Lua and cannot be accessed.", skin_opt[field])
@ -218,6 +220,9 @@ static int skin_get(lua_State *L)
case skin_sprites:
LUA_PushUserdata(L, skin->sprites, META_SKINSPRITES);
break;
case skin_natkcolor:
lua_pushinteger(L, skin->natkcolor);
break;
}
return 1;
}

View file

@ -5979,16 +5979,6 @@ static void M_DrawNightsAttackBackground(void)
if (ntsatkdrawtimer < 0) ntsatkdrawtimer = 0;
}
// NiGHTS Attack floating Super Sonic.
static patch_t *ntssupersonic[2];
static void M_DrawNightsAttackSuperSonic(void)
{
const UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_YELLOW, GTC_CACHE);
INT32 timer = FixedInt(ntsatkdrawtimer/4) % 2;
angle_t fa = (FixedAngle((FixedInt(ntsatkdrawtimer * 4) % 360)<<FRACBITS)>>ANGLETOFINESHIFT) & FINEMASK;
V_DrawFixedPatch(235<<FRACBITS, (120<<FRACBITS) - (8*FINESINE(fa)), FRACUNIT, 0, ntssupersonic[timer], colormap);
}
static void M_DrawLevelPlatterMenu(void)
{
UINT8 iter = lsrow, sizeselect = (lswide(lsrow) ? 1 : 0);
@ -10345,8 +10335,38 @@ void M_DrawNightsAttackMenu(void)
V_DrawString(104-72, 180, V_TRANSLUCENT, M_GetText("Press ESC to exit"));
}
// Super Sonic
M_DrawNightsAttackSuperSonic();
// Draw selected character's NiGHTS sprite
patch_t *natksprite; //The patch for the sprite itself
INT32 spritetimer; //Timer for animating NiGHTS sprite
INT32 skinnumber; //Number for skin
UINT16 color; //natkcolor
if (skins[cv_chooseskin.value-1]->sprites[SPR2_NFLY].numframes == 0) //If we don't have NiGHTS sprites
skinnumber = 0; //Default to Sonic
else
skinnumber = (cv_chooseskin.value-1);
spritedef_t *sprdef = &skins[skinnumber]->sprites[SPR2_NFLY]; //Make our patch the selected character's NFLY sprite
spritetimer = FixedInt(ntsatkdrawtimer/2) % skins[skinnumber]->sprites[SPR2_NFLY].numframes; //Make the sprite timer cycle though all the frames at 2 tics per frame
spriteframe_t *sprframe = &sprdef->spriteframes[spritetimer]; //Our animation frame is equal to the number on the timer
natksprite = W_CachePatchNum(sprframe->lumppat[6], PU_PATCH); //Draw the right facing angle
if (skins[skinnumber]->natkcolor) //If you set natkcolor use it
color = skins[skinnumber]->natkcolor;
else if ((skins[skinnumber]->flags & SF_SUPER) && !(skins[skinnumber]->flags & SF_NONIGHTSSUPER)) //If you go super in NiGHTS, use supercolor
color = skins[skinnumber]->supercolor+4;
else //If you don't go super in NiGHTS or at all, use prefcolor
color = skins[skinnumber]->prefcolor;
angle_t fa = (FixedAngle(((FixedInt(ntsatkdrawtimer * 4)) % 360)<<FRACBITS)>>ANGLETOFINESHIFT) & FINEMASK;
V_DrawFixedPatch(270<<FRACBITS, (186<<FRACBITS) - 8*FINESINE(fa),
FixedDiv(skins[skinnumber]->highresscale, skins[skinnumber]->shieldscale),
(sprframe->flip & 1<<6) ? V_FLIP : 0,
natksprite,
R_GetTranslationColormap(TC_BLINK, color, GTC_CACHE));
//if (P_HasGrades(cv_nextmap.value, 0))
// V_DrawScaledPatch(235 - (((ngradeletters[bestoverall])->width)*3)/2, 135, 0, ngradeletters[bestoverall]);
@ -10438,9 +10458,6 @@ static void M_NightsAttack(INT32 choice)
// This is really just to make sure Sonic is the played character, just in case
M_PatchSkinNameTable();
ntssupersonic[0] = W_CachePatchName("NTSSONC1", PU_PATCH);
ntssupersonic[1] = W_CachePatchName("NTSSONC2", PU_PATCH);
G_SetGamestate(GS_TIMEATTACK); // do this before M_SetupNextMenu so that menu meta state knows that we're switching
titlemapinaction = TITLEMAP_OFF; // Nope don't give us HOMs please
M_SetupNextMenu(&SP_NightsAttackDef);

View file

@ -112,6 +112,9 @@ static void Color2_OnChange(void);
static void DummyConsvar_OnChange(void);
static void SoundTest_OnChange(void);
static boolean Skin_CanChange(const char *valstr);
static boolean Skin2_CanChange(const char *valstr);
#ifdef NETGAME_DEVMODE
static void Fishcake_OnChange(void);
#endif
@ -231,7 +234,6 @@ consvar_t cv_seenames = CVAR_INIT ("seenames", "Ally/Foe", CV_SAVE|CV_ALLOWLUA,
consvar_t cv_allowseenames = CVAR_INIT ("allowseenames", "Yes", CV_SAVE|CV_NETVAR|CV_ALLOWLUA, CV_YesNo, NULL);
// names
static char *lastskinnames[2];
consvar_t cv_playername = CVAR_INIT ("name", "Sonic", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Name_OnChange);
consvar_t cv_playername2 = CVAR_INIT ("name2", "Tails", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Name2_OnChange);
// player colors
@ -239,8 +241,8 @@ UINT16 lastgoodcolor = SKINCOLOR_BLUE, lastgoodcolor2 = SKINCOLOR_BLUE;
consvar_t cv_playercolor = CVAR_INIT ("color", "Blue", CV_CALL|CV_NOINIT|CV_ALLOWLUA, Color_cons_t, Color_OnChange);
consvar_t cv_playercolor2 = CVAR_INIT ("color2", "Orange", CV_CALL|CV_NOINIT|CV_ALLOWLUA, Color_cons_t, Color2_OnChange);
// player's skin, saved for commodity, when using a favorite skins wad..
consvar_t cv_skin = CVAR_INIT ("skin", DEFAULTSKIN, CV_CALL|CV_NOINIT|CV_ALLOWLUA, NULL, Skin_OnChange);
consvar_t cv_skin2 = CVAR_INIT ("skin2", DEFAULTSKIN2, CV_CALL|CV_NOINIT|CV_ALLOWLUA, NULL, Skin2_OnChange);
consvar_t cv_skin = CVAR_INIT_WITH_CALLBACKS ("skin", DEFAULTSKIN, CV_CALL|CV_NOINIT|CV_ALLOWLUA, NULL, Skin_OnChange, Skin_CanChange);
consvar_t cv_skin2 = CVAR_INIT_WITH_CALLBACKS ("skin2", DEFAULTSKIN2, CV_CALL|CV_NOINIT|CV_ALLOWLUA, NULL, Skin2_OnChange, Skin2_CanChange);
// saved versions of the above six
consvar_t cv_defaultplayercolor = CVAR_INIT ("defaultcolor", "Blue", CV_SAVE, Color_cons_t, NULL);
@ -4849,6 +4851,41 @@ static void Name2_OnChange(void)
SendNameAndColor2();
}
static boolean Skin_CanChange(const char *valstr)
{
(void)valstr;
if (!Playing())
return true; // do whatever you want
if (!(multiplayer || netgame)) // In single player.
return true;
if (CanChangeSkin(consoleplayer) && !P_PlayerMoving(consoleplayer))
return true;
else
{
CONS_Alert(CONS_NOTICE, M_GetText("You can't change your skin at the moment.\n"));
return false;
}
}
static boolean Skin2_CanChange(const char *valstr)
{
(void)valstr;
if (!Playing() || !splitscreen)
return true; // do whatever you want
if (CanChangeSkin(secondarydisplayplayer) && !P_PlayerMoving(secondarydisplayplayer))
return true;
else
{
CONS_Alert(CONS_NOTICE, M_GetText("You can't change your skin at the moment.\n"));
return false;
}
}
/** Sends a skin change for the console player, unless that player is moving.
* \sa cv_skin, Skin2_OnChange, Color_OnChange
* \author Graue <graue@oceanbase.org>
@ -4856,10 +4893,7 @@ static void Name2_OnChange(void)
static void Skin_OnChange(void)
{
if (!Playing())
return; // do whatever you want
if (lastskinnames[0] == NULL)
lastskinnames[0] = Z_StrDup(cv_skin.string);
return;
if (!(multiplayer || netgame)) // In single player.
{
@ -4875,17 +4909,7 @@ static void Skin_OnChange(void)
return;
}
if (CanChangeSkin(consoleplayer) && !P_PlayerMoving(consoleplayer))
{
SendNameAndColor();
Z_Free(lastskinnames[0]);
lastskinnames[0] = Z_StrDup(cv_skin.string);
}
else
{
CONS_Alert(CONS_NOTICE, M_GetText("You can't change your skin at the moment.\n"));
CV_StealthSet(&cv_skin, lastskinnames[0]);
}
SendNameAndColor();
}
/** Sends a skin change for the secondary splitscreen player, unless that
@ -4896,22 +4920,9 @@ static void Skin_OnChange(void)
static void Skin2_OnChange(void)
{
if (!Playing() || !splitscreen)
return; // do whatever you want
return;
if (lastskinnames[1] == NULL)
lastskinnames[1] = Z_StrDup(cv_skin2.string);
if (CanChangeSkin(secondarydisplayplayer) && !P_PlayerMoving(secondarydisplayplayer))
{
SendNameAndColor2();
Z_Free(lastskinnames[1]);
lastskinnames[1] = Z_StrDup(cv_skin.string);
}
else
{
CONS_Alert(CONS_NOTICE, M_GetText("You can't change your skin at the moment.\n"));
CV_StealthSet(&cv_skin2, lastskinnames[1]);
}
SendNameAndColor2();
}
/** Sends a color change for the console player, unless that player is moving.

View file

@ -424,7 +424,7 @@ static boolean SOCK_cmpipv6(mysockaddr_t *a, mysockaddr_t *b, UINT8 mask)
{
UINT8 bitmask;
I_Assert(mask <= 128);
if (memcmp(&a->ip6.sin6_addr, &b->ip6.sin6_addr, mask / 8) != 0)
if (memcmp(&a->ip6.sin6_addr.s6_addr, &b->ip6.sin6_addr.s6_addr, mask / 8) != 0)
return false;
if (mask % 8 == 0)
return true;
@ -437,6 +437,9 @@ static boolean SOCK_cmpaddr(mysockaddr_t *a, mysockaddr_t *b, UINT8 mask)
{
UINT32 bitmask = INADDR_NONE;
if (a->any.sa_family != b->any.sa_family)
return false;
if (mask && mask < 32)
bitmask = htonl((UINT32)(-1) << (32 - mask));

View file

@ -23,6 +23,7 @@
#include "m_random.h"
#include "m_misc.h"
#include "r_skins.h"
#include "r_translation.h"
#include "i_video.h"
#include "z_zone.h"
#include "lua_hook.h"
@ -196,6 +197,7 @@ void A_SetRandomTics(mobj_t *actor);
void A_ChangeColorRelative(mobj_t *actor);
void A_ChangeColorAbsolute(mobj_t *actor);
void A_Dye(mobj_t *actor);
void A_SetTranslation(mobj_t *actor);
void A_MoveRelative(mobj_t *actor);
void A_MoveAbsolute(mobj_t *actor);
void A_Thrust(mobj_t *actor);
@ -9215,6 +9217,26 @@ void A_Dye(mobj_t *actor)
}
}
// Function: A_SetTranslation
//
// Description: Changes the translation of an actor.
//
// var1 = translation ID
// var2 = unused
//
void A_SetTranslation(mobj_t *actor)
{
INT32 locvar1 = var1;
if (LUA_CallAction(A_SETTRANSLATION, actor))
return;
if (R_TranslationIsValid(locvar1))
actor->translation = (UINT32)locvar1;
else
actor->translation = 0;
}
// Function: A_MoveRelative
//
// Description: Moves an object (wrapper for P_Thrust)

View file

@ -331,9 +331,14 @@ typedef struct mobj_s
UINT16 eflags; // extra flags
void *skin; // overrides 'sprite' when non-NULL (for player bodies to 'remember' the skin)
// Player and mobj sprites in multiplayer modes are modified
// using an internal color lookup table for re-indexing.
UINT16 color; // This replaces MF_TRANSLATION. Use 0 for default (no translation).
UINT16 color;
// This replaces MF_TRANSLATION. Use 0 for default (no translation).
UINT16 translation;
struct player_s *drawonlyforplayer; // If set, hides the mobj for everyone except this player and their spectators
struct mobj_s *dontdrawforviewmobj; // If set, hides the mobj if dontdrawforviewmobj is the current camera (first-person player or awayviewmobj)

View file

@ -1764,7 +1764,8 @@ typedef enum
MD2_FLOORSPRITESLOPE = 1<<22,
MD2_DISPOFFSET = 1<<23,
MD2_DRAWONLYFORPLAYER = 1<<24,
MD2_DONTDRAWFORVIEWMOBJ = 1<<25
MD2_DONTDRAWFORVIEWMOBJ = 1<<25,
MD2_TRANSLATION = 1<<26
} mobj_diff2_t;
typedef enum
@ -1953,6 +1954,8 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type)
diff2 |= MD2_CVMEM;
if (mobj->color)
diff2 |= MD2_COLOR;
if (mobj->translation)
diff2 |= MD2_TRANSLATION;
if (mobj->skin)
diff2 |= MD2_SKIN;
if (mobj->extravalue1)
@ -2186,6 +2189,8 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type)
WRITEUINT32(save_p, mobj->dontdrawforviewmobj->mobjnum);
if (diff2 & MD2_DISPOFFSET)
WRITEINT32(save_p, mobj->dispoffset);
if (diff2 & MD2_TRANSLATION)
WRITEUINT16(save_p, mobj->translation);
WRITEUINT32(save_p, mobj->mobjnum);
}
@ -3251,6 +3256,8 @@ static thinker_t* LoadMobjThinker(actionf_p1 thinker)
mobj->dispoffset = READINT32(save_p);
else
mobj->dispoffset = mobj->info->dispoffset;
if (diff2 & MD2_TRANSLATION)
mobj->translation = READUINT16(save_p);
if (diff & MD_REDFLAG)
{

View file

@ -33,6 +33,7 @@
#include "r_data.h"
#include "r_things.h" // for R_AddSpriteDefs
#include "r_textures.h"
#include "r_translation.h"
#include "r_patch.h"
#include "r_picformats.h"
#include "r_sky.h"
@ -8246,6 +8247,8 @@ static boolean P_LoadAddon(UINT16 numlumps)
HWR_ClearAllTextures();
#endif
R_LoadParsedTranslations();
//
// search for sprite replacements
//

View file

@ -2047,6 +2047,7 @@ mobj_t *P_SpawnGhostMobj(mobj_t *mobj)
}
ghost->color = mobj->color;
ghost->translation = mobj->translation;
ghost->colorized = mobj->colorized; // alternatively, "true" for sonic advance style colourisation
ghost->angle = (mobj->player ? mobj->player->drawangle : mobj->angle);

View file

@ -20,6 +20,7 @@
#include "m_misc.h"
#include "r_data.h"
#include "r_textures.h"
#include "r_translation.h"
#include "r_patch.h"
#include "r_picformats.h"
#include "w_wad.h"
@ -1217,6 +1218,9 @@ void R_InitData(void)
R_Init8to16();
}
CONS_Printf("R_LoadParsedTranslations()...\n");
R_LoadParsedTranslations();
CONS_Printf("R_LoadTextures()...\n");
R_LoadTextures();

View file

@ -24,12 +24,11 @@
#include "screen.h" // MAXVIDWIDTH, MAXVIDHEIGHT
#ifdef HWRENDER
#include "m_aatree.h"
#endif
#include "taglist.h"
// Amount of colors in the palette
#define NUM_PALETTE_ENTRIES 256
//
// ClipWallSegment
// Clips the given range of columns

View file

@ -18,6 +18,7 @@
#include "doomdef.h"
#include "doomstat.h"
#include "r_local.h"
#include "r_translation.h"
#include "st_stuff.h" // need ST_HEIGHT
#include "i_video.h"
#include "v_video.h"
@ -452,8 +453,14 @@ static void R_GenerateTranslationColormap(UINT8 *dest_colormap, INT32 translatio
switch (translation)
{
case TC_ALLWHITE:
memset(dest_colormap, 0, NUM_PALETTE_ENTRIES * sizeof(UINT8));
return;
case TC_DASHMODE:
remaptable_t *tr = R_GetBuiltInTranslation((SINT8)translation);
if (tr)
{
memcpy(dest_colormap, tr->remap, NUM_PALETTE_ENTRIES);
return;
}
break;
case TC_RAINBOW:
if (color >= numskincolors)
I_Error("Invalid skin color #%hu", (UINT16)color);
@ -501,40 +508,6 @@ static void R_GenerateTranslationColormap(UINT8 *dest_colormap, INT32 translatio
for (i = 0; i < COLORRAMPSIZE; i++)
dest_colormap[96+i] = dest_colormap[skincolors[SKINCOLOR_COBALT].ramp[i]];
}
else if (translation == TC_DASHMODE) // This is a long one, because MotorRoach basically hand-picked the indices
{
// greens -> ketchups
dest_colormap[96] = dest_colormap[97] = 48;
dest_colormap[98] = 49;
dest_colormap[99] = 51;
dest_colormap[100] = 52;
dest_colormap[101] = dest_colormap[102] = 54;
dest_colormap[103] = 34;
dest_colormap[104] = 37;
dest_colormap[105] = 39;
dest_colormap[106] = 41;
for (i = 0; i < 5; i++)
dest_colormap[107 + i] = 43 + i;
// reds -> steel blues
dest_colormap[32] = 146;
dest_colormap[33] = 147;
dest_colormap[34] = dest_colormap[35] = 170;
dest_colormap[36] = 171;
dest_colormap[37] = dest_colormap[38] = 172;
dest_colormap[39] = dest_colormap[40] = dest_colormap[41] = 173;
dest_colormap[42] = dest_colormap[43] = dest_colormap[44] = 174;
dest_colormap[45] = dest_colormap[46] = dest_colormap[47] = 175;
dest_colormap[71] = 139;
// steel blues -> oranges
dest_colormap[170] = 52;
dest_colormap[171] = 54;
dest_colormap[172] = 56;
dest_colormap[173] = 42;
dest_colormap[174] = 45;
dest_colormap[175] = 47;
}
return;
}
else if (color == SKINCOLOR_NONE)

View file

@ -144,6 +144,8 @@ static void Sk_SetDefaultValue(skin_t *skin)
skin->contspeed = 17;
skin->contangle = 0;
skin->natkcolor = SKINCOLOR_NONE;
for (i = 0; i < sfx_skinsoundslot0; i++)
if (S_sfx[i].skinsound != -1)
skin->soundsid[S_sfx[i].skinsound] = i;
@ -588,7 +590,6 @@ static boolean R_ProcessPatchableFields(skin_t *skin, char *stoken, char *value)
UINT16 color = R_GetSuperColorByName(value);
skin->supercolor = (color ? color : SKINCOLOR_SUPERGOLD1);
}
#define GETFLOAT(field) else if (!stricmp(stoken, #field)) skin->field = FLOAT_TO_FIXED(atof(value));
GETFLOAT(jumpfactor)
GETFLOAT(highresscale)
@ -629,6 +630,9 @@ static boolean R_ProcessPatchableFields(skin_t *skin, char *stoken, char *value)
GETFLAG(NOSHIELDABILITY)
#undef GETFLAG
else if (!stricmp(stoken, "natkcolor"))
skin->natkcolor = R_GetColorByName(value); // SKINCOLOR_NONE is allowed here
else // let's check if it's a sound, otherwise error out
{
boolean found = false;

View file

@ -71,6 +71,7 @@ typedef struct
UINT16 prefcolor;
UINT16 supercolor;
UINT16 prefoppositecolor; // if 0 use tables instead
UINT16 natkcolor; //Color for Nights Attack Menu
fixed_t highresscale; // scale of highres, default is 0.5
UINT8 contspeed; // continue screen animation speed

View file

@ -404,7 +404,7 @@ static void R_RasterizeFloorSplat(floorsplat_t *pSplat, vector2_t *verts, visspr
}
ds_colormap = vis->colormap;
ds_translation = R_GetSpriteTranslation(vis);
ds_translation = R_GetTranslationForThing(vis->mobj, vis->color, vis->translation);
if (ds_translation == NULL)
ds_translation = colormaps;

View file

@ -25,6 +25,7 @@
#include "i_system.h"
#include "r_fps.h"
#include "r_things.h"
#include "r_translation.h"
#include "r_patch.h"
#include "r_patchrotation.h"
#include "r_picformats.h"
@ -761,50 +762,46 @@ void R_DrawFlippedMaskedColumn(column_t *column)
dc_texturemid = basetexturemid;
}
boolean R_SpriteIsFlashing(vissprite_t *vis)
UINT8 *R_GetTranslationForThing(mobj_t *mobj, skincolornum_t color, UINT16 translation)
{
return (!(vis->cut & SC_PRECIP)
&& (vis->mobj->flags & (MF_ENEMY|MF_BOSS))
&& (vis->mobj->flags2 & MF2_FRET)
&& !(vis->mobj->flags & MF_GRENADEBOUNCE)
&& (leveltime & 1));
}
UINT8 *R_GetSpriteTranslation(vissprite_t *vis)
{
if (R_SpriteIsFlashing(vis)) // Bosses "flash"
if (R_ThingIsFlashing(mobj)) // Bosses "flash"
{
if (vis->mobj->type == MT_CYBRAKDEMON || vis->mobj->colorized)
if (mobj->type == MT_CYBRAKDEMON || mobj->colorized)
return R_GetTranslationColormap(TC_ALLWHITE, 0, GTC_CACHE);
else if (vis->mobj->type == MT_METALSONIC_BATTLE)
else if (mobj->type == MT_METALSONIC_BATTLE)
return R_GetTranslationColormap(TC_METALSONIC, 0, GTC_CACHE);
else
return R_GetTranslationColormap(TC_BOSS, vis->color, GTC_CACHE);
return R_GetTranslationColormap(TC_BOSS, color, GTC_CACHE);
}
else if (vis->color)
else if (translation != 0)
{
remaptable_t *tr = R_GetTranslationByID(translation);
if (tr != NULL)
return tr->remap;
}
else if (color != SKINCOLOR_NONE)
{
// New colormap stuff for skins Tails 06-07-2002
if (!(vis->cut & SC_PRECIP) && vis->mobj->colorized)
return R_GetTranslationColormap(TC_RAINBOW, vis->color, GTC_CACHE);
else if (!(vis->cut & SC_PRECIP)
&& vis->mobj->player && vis->mobj->player->dashmode >= DASHMODE_THRESHOLD
&& (vis->mobj->player->charflags & SF_DASHMODE)
if (mobj->colorized)
return R_GetTranslationColormap(TC_RAINBOW, color, GTC_CACHE);
else if (mobj->player && mobj->player->dashmode >= DASHMODE_THRESHOLD
&& (mobj->player->charflags & SF_DASHMODE)
&& ((leveltime/2) & 1))
{
if (vis->mobj->player->charflags & SF_MACHINE)
if (mobj->player->charflags & SF_MACHINE)
return R_GetTranslationColormap(TC_DASHMODE, 0, GTC_CACHE);
else
return R_GetTranslationColormap(TC_RAINBOW, vis->color, GTC_CACHE);
return R_GetTranslationColormap(TC_RAINBOW, color, GTC_CACHE);
}
else if (!(vis->cut & SC_PRECIP) && vis->mobj->skin && vis->mobj->sprite == SPR_PLAY) // This thing is a player!
else if (mobj->skin && mobj->sprite == SPR_PLAY) // This thing is a player!
{
UINT8 skinnum = ((skin_t*)vis->mobj->skin)->skinnum;
return R_GetTranslationColormap(skinnum, vis->color, GTC_CACHE);
UINT8 skinnum = ((skin_t*)mobj->skin)->skinnum;
return R_GetTranslationColormap(skinnum, color, GTC_CACHE);
}
else // Use the defaults
return R_GetTranslationColormap(TC_DEFAULT, vis->color, GTC_CACHE);
return R_GetTranslationColormap(TC_DEFAULT, color, GTC_CACHE);
}
else if (vis->mobj->sprite == SPR_PLAY) // Looks like a player, but doesn't have a color? Get rid of green sonic syndrome.
else if (mobj->sprite == SPR_PLAY) // Looks like a player, but doesn't have a color? Get rid of green sonic syndrome.
return R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_BLUE, GTC_CACHE);
return NULL;
@ -852,11 +849,11 @@ static void R_DrawVisSprite(vissprite_t *vis)
colfunc = colfuncs[BASEDRAWFUNC]; // hack: this isn't resetting properly somewhere.
dc_colormap = vis->colormap;
dc_translation = R_GetSpriteTranslation(vis);
dc_translation = R_GetTranslationForThing(vis->mobj, vis->color, vis->translation);
if (R_SpriteIsFlashing(vis)) // Bosses "flash"
if (R_ThingIsFlashing(vis->mobj)) // Bosses "flash"
colfunc = colfuncs[COLDRAWFUNC_TRANS]; // translate certain pixels to white
else if (vis->color && vis->transmap) // Color mapping
else if (dc_translation && vis->transmap) // Color mapping
{
colfunc = colfuncs[COLDRAWFUNC_TRANSTRANS];
dc_transmap = vis->transmap;
@ -866,9 +863,7 @@ static void R_DrawVisSprite(vissprite_t *vis)
colfunc = colfuncs[COLDRAWFUNC_FUZZY];
dc_transmap = vis->transmap; //Fab : 29-04-98: translucency table
}
else if (vis->color) // translate green skin to another color
colfunc = colfuncs[COLDRAWFUNC_TRANS];
else if (vis->mobj->sprite == SPR_PLAY) // Looks like a player, but doesn't have a color? Get rid of green sonic syndrome.
else if (dc_translation) // translate green skin to another color
colfunc = colfuncs[COLDRAWFUNC_TRANS];
// Hack: Use a special column function for drop shadows that bypasses
@ -1403,6 +1398,7 @@ static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale,
shadow->mobj = thing; // Easy access! Tails 06-07-2002
shadow->color = thing->color;
shadow->translation = 0;
shadow->x1 = x1 < portalclipstart ? portalclipstart : x1;
shadow->x2 = x2 >= portalclipend ? portalclipend-1 : x2;
@ -2200,6 +2196,11 @@ static void R_ProjectSprite(mobj_t *thing)
else
vis->color = oldthing->color;
if ((oldthing->flags2 & MF2_LINKDRAW) && oldthing->tracer && oldthing->translation == 0)
vis->translation = oldthing->tracer->translation;
else
vis->translation = oldthing->translation;
vis->x1 = x1 < portalclipstart ? portalclipstart : x1;
vis->x2 = x2 >= portalclipend ? portalclipend-1 : x2;
@ -2470,6 +2471,7 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing)
vis->extra_colormap = thing->subsector->sector->extra_colormap;
vis->heightsec = thing->subsector->sector->heightsec;
vis->color = SKINCOLOR_NONE;
vis->translation = 0;
// Fullbright
vis->colormap = colormaps;
@ -3570,6 +3572,14 @@ boolean R_ThingIsFullDark(mobj_t *thing)
return ((thing->frame & FF_BRIGHTMASK) == FF_FULLDARK || (thing->renderflags & RF_BRIGHTMASK) == RF_FULLDARK);
}
boolean R_ThingIsFlashing(mobj_t *thing)
{
if (thing == NULL)
return false;
return (thing->flags & (MF_ENEMY|MF_BOSS)) && (thing->flags2 & MF2_FRET) && !(thing->flags & MF_GRENADEBOUNCE) && (leveltime & 1);
}
// Offsets MT_OVERLAY towards the camera at render-time - Works in splitscreen!
// The &x and &y arguments should be pre-interpolated, and will be modified
void R_ThingOffsetOverlay(mobj_t *thing, fixed_t *x, fixed_t *y)

View file

@ -88,6 +88,10 @@ boolean R_ThingIsFullBright (mobj_t *thing);
boolean R_ThingIsSemiBright (mobj_t *thing);
boolean R_ThingIsFullDark (mobj_t *thing);
boolean R_ThingIsFlashing (mobj_t *thing);
UINT8 *R_GetTranslationForThing(mobj_t *mobj, skincolornum_t color, UINT16 translation);
void R_ThingOffsetOverlay (mobj_t *thing, fixed_t *outx, fixed_t *outy);
// --------------
@ -216,6 +220,7 @@ typedef struct vissprite_s
fixed_t shadowscale;
skincolornum_t color;
UINT16 translation;
INT16 clipbot[MAXVIDWIDTH], cliptop[MAXVIDWIDTH];
@ -226,12 +231,8 @@ extern UINT32 visspritecount, numvisiblesprites;
void R_ClipSprites(drawseg_t* dsstart, portal_t* portal);
boolean R_SpriteIsFlashing(vissprite_t *vis);
void R_DrawThingBoundingBox(vissprite_t *spr);
UINT8 *R_GetSpriteTranslation(vissprite_t *vis);
// ----------
// DRAW NODES
// ----------

993
src/r_translation.c Normal file
View file

@ -0,0 +1,993 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2006 by Randy Heit.
// Copyright (C) 2023 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_translation.c
/// \brief Translation table handling
#include "r_translation.h"
#include "r_data.h"
#include "r_draw.h"
#include "v_video.h" // pMasterPalette
#include "z_zone.h"
#include "w_wad.h"
#include "m_tokenizer.h"
#include <errno.h>
static remaptable_t **paletteremaps = NULL;
static unsigned numpaletteremaps = 0;
static int allWhiteRemap = 0;
static int dashModeRemap = 0;
static void MakeDashModeRemap(void);
static boolean PaletteRemap_AddIndexRange(remaptable_t *tr, int start, int end, int pal1, int pal2);
static boolean PaletteRemap_AddColorRange(remaptable_t *tr, int start, int end, int r1i, int g1i, int b1i, int r2i, int g2i, int b2i);
static boolean PaletteRemap_AddDesaturation(remaptable_t *tr, int start, int end, double r1, double g1, double b1, double r2, double g2, double b2);
static boolean PaletteRemap_AddColourisation(remaptable_t *tr, int start, int end, int r, int g, int b);
static boolean PaletteRemap_AddTint(remaptable_t *tr, int start, int end, int r, int g, int b, int amount);
static boolean PaletteRemap_AddInvert(remaptable_t *tr, int start, int end);
enum PaletteRemapType
{
REMAP_ADD_INDEXRANGE,
REMAP_ADD_COLORRANGE,
REMAP_ADD_COLOURISATION,
REMAP_ADD_DESATURATION,
REMAP_ADD_TINT
};
struct PaletteRemapParseResult
{
int start, end;
enum PaletteRemapType type;
union
{
struct
{
int pal1, pal2;
} indexRange;
struct
{
int r1, g1, b1;
int r2, g2, b2;
} colorRange;
struct
{
double r1, g1, b1;
double r2, g2, b2;
} desaturation;
struct
{
int r, g, b;
} colourisation;
struct
{
int r, g, b, amount;
} tint;
};
boolean has_error;
char error[4096];
};
void PaletteRemap_Init(void)
{
// First translation must be the identity one.
remaptable_t *base = PaletteRemap_New();
PaletteRemap_SetIdentity(base);
PaletteRemap_Add(base);
// Grayscale translation
remaptable_t *grayscale = PaletteRemap_New();
PaletteRemap_SetIdentity(grayscale);
PaletteRemap_AddDesaturation(grayscale, 0, 255, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0);
R_AddCustomTranslation("Grayscale", PaletteRemap_Add(grayscale));
// All white (TC_ALLWHITE)
remaptable_t *allWhite = PaletteRemap_New();
memset(allWhite->remap, 0, NUM_PALETTE_ENTRIES * sizeof(UINT8));
allWhiteRemap = PaletteRemap_Add(allWhite);
R_AddCustomTranslation("AllWhite", allWhiteRemap);
// All black
remaptable_t *allBlack = PaletteRemap_New();
memset(allBlack->remap, 31, NUM_PALETTE_ENTRIES * sizeof(UINT8));
R_AddCustomTranslation("AllBlack", PaletteRemap_Add(allBlack));
// Invert
remaptable_t *invertRemap = PaletteRemap_New();
PaletteRemap_SetIdentity(invertRemap);
PaletteRemap_AddInvert(invertRemap, 0, 255);
R_AddCustomTranslation("Invert", PaletteRemap_Add(invertRemap));
// Dash mode (TC_DASHMODE)
MakeDashModeRemap();
}
remaptable_t *PaletteRemap_New(void)
{
remaptable_t *tr = Z_Calloc(sizeof(remaptable_t), PU_STATIC, NULL);
tr->num_entries = NUM_PALETTE_ENTRIES;
return tr;
}
remaptable_t *PaletteRemap_Copy(remaptable_t *tr)
{
remaptable_t *copy = Z_Malloc(sizeof(remaptable_t), PU_STATIC, NULL);
memcpy(copy, tr, sizeof(remaptable_t));
return copy;
}
boolean PaletteRemap_Equal(remaptable_t *a, remaptable_t *b)
{
if (a->num_entries != b->num_entries)
return false;
return memcmp(a->remap, b->remap, a->num_entries) == 0;
}
void PaletteRemap_SetIdentity(remaptable_t *tr)
{
for (unsigned i = 0; i < tr->num_entries; i++)
{
tr->remap[i] = i;
}
}
boolean PaletteRemap_IsIdentity(remaptable_t *tr)
{
for (unsigned i = 0; i < NUM_PALETTE_ENTRIES; i++)
{
if (tr->remap[i] != i)
return false;
}
return true;
}
unsigned PaletteRemap_Add(remaptable_t *tr)
{
#if 0
for (unsigned i = 0; i < numpaletteremaps; i++)
{
if (PaletteRemap_Equal(tr, paletteremaps[i]))
return i;
}
#endif
numpaletteremaps++;
paletteremaps = Z_Realloc(paletteremaps, sizeof(remaptable_t *) * numpaletteremaps, PU_STATIC, NULL);
paletteremaps[numpaletteremaps - 1] = tr;
return numpaletteremaps - 1;
}
// This is a long one, because MotorRoach basically hand-picked the indices
static void MakeDashModeRemap(void)
{
remaptable_t *dashmode = PaletteRemap_New();
PaletteRemap_SetIdentity(dashmode);
UINT8 *dest_colormap = dashmode->remap;
// greens -> ketchups
dest_colormap[96] = dest_colormap[97] = 48;
dest_colormap[98] = 49;
dest_colormap[99] = 51;
dest_colormap[100] = 52;
dest_colormap[101] = dest_colormap[102] = 54;
dest_colormap[103] = 34;
dest_colormap[104] = 37;
dest_colormap[105] = 39;
dest_colormap[106] = 41;
for (unsigned i = 0; i < 5; i++)
dest_colormap[107 + i] = 43 + i;
// reds -> steel blues
dest_colormap[32] = 146;
dest_colormap[33] = 147;
dest_colormap[34] = dest_colormap[35] = 170;
dest_colormap[36] = 171;
dest_colormap[37] = dest_colormap[38] = 172;
dest_colormap[39] = dest_colormap[40] = dest_colormap[41] = 173;
dest_colormap[42] = dest_colormap[43] = dest_colormap[44] = 174;
dest_colormap[45] = dest_colormap[46] = dest_colormap[47] = 175;
dest_colormap[71] = 139;
// steel blues -> oranges
dest_colormap[170] = 52;
dest_colormap[171] = 54;
dest_colormap[172] = 56;
dest_colormap[173] = 42;
dest_colormap[174] = 45;
dest_colormap[175] = 47;
dashModeRemap = PaletteRemap_Add(dashmode);
R_AddCustomTranslation("DashMode", dashModeRemap);
}
static boolean PalIndexOutOfRange(int color)
{
return color < 0 || color > 255;
}
static boolean IndicesOutOfRange(int start, int end)
{
return PalIndexOutOfRange(start) || PalIndexOutOfRange(end);
}
static boolean IndicesOutOfRange2(int start1, int end1, int start2, int end2)
{
return IndicesOutOfRange(start1, end1) || IndicesOutOfRange(start2, end2);
}
#define SWAP(a, b, t) { \
t swap = a; \
a = b; \
b = swap; \
}
static boolean PaletteRemap_AddIndexRange(remaptable_t *tr, int start, int end, int pal1, int pal2)
{
if (IndicesOutOfRange2(start, end, pal1, pal2))
return false;
if (start > end)
{
SWAP(start, end, int);
SWAP(pal1, pal2, int);
}
else if (start == end)
{
tr->remap[start] = pal1;
return true;
}
double palcol = pal1;
double palstep = (pal2 - palcol) / (end - start);
for (int i = start; i <= end; palcol += palstep, ++i)
{
double idx = round(palcol);
tr->remap[i] = (int)idx;
}
return true;
}
static boolean PaletteRemap_AddColorRange(remaptable_t *tr, int start, int end, int r1i, int g1i, int b1i, int r2i, int g2i, int b2i)
{
if (IndicesOutOfRange(start, end))
return false;
double r1 = r1i;
double g1 = g1i;
double b1 = b1i;
double r2 = r2i;
double g2 = g2i;
double b2 = b2i;
double r, g, b;
double rs, gs, bs;
if (start > end)
{
SWAP(start, end, int);
r = r2;
g = g2;
b = b2;
rs = r1 - r2;
gs = g1 - g2;
bs = b1 - b2;
}
else
{
r = r1;
g = g1;
b = b1;
rs = r2 - r1;
gs = g2 - g1;
bs = b2 - b1;
}
if (start == end)
{
tr->remap[start] = NearestColor(r, g, b);
}
else
{
rs /= (end - start);
gs /= (end - start);
bs /= (end - start);
for (int i = start; i <= end; ++i)
{
tr->remap[i] = NearestColor(r, g, b);
r += rs;
g += gs;
b += bs;
}
}
return true;
}
#define CLAMP(val, minval, maxval) max(min(val, maxval), minval)
static boolean PaletteRemap_AddDesaturation(remaptable_t *tr, int start, int end, double r1, double g1, double b1, double r2, double g2, double b2)
{
if (IndicesOutOfRange(start, end))
return false;
r1 = CLAMP(r1, 0.0, 2.0);
g1 = CLAMP(g1, 0.0, 2.0);
b1 = CLAMP(b1, 0.0, 2.0);
r2 = CLAMP(r2, 0.0, 2.0);
g2 = CLAMP(g2, 0.0, 2.0);
b2 = CLAMP(b2, 0.0, 2.0);
if (start > end)
{
SWAP(start, end, int);
SWAP(r1, r2, double);
SWAP(g1, g2, double);
SWAP(b1, b2, double);
}
r2 -= r1;
g2 -= g1;
b2 -= b1;
r1 *= 255;
g1 *= 255;
b1 *= 255;
for (int c = start; c <= end; c++)
{
double intensity = (pMasterPalette[c].s.red * 77 + pMasterPalette[c].s.green * 143 + pMasterPalette[c].s.blue * 37) / 255.0;
tr->remap[c] = NearestColor(
min(255, max(0, (int)(r1 + intensity*r2))),
min(255, max(0, (int)(g1 + intensity*g2))),
min(255, max(0, (int)(b1 + intensity*b2)))
);
}
return true;
}
#undef CLAMP
#undef SWAP
static boolean PaletteRemap_AddColourisation(remaptable_t *tr, int start, int end, int r, int g, int b)
{
if (IndicesOutOfRange(start, end))
return false;
for (int i = start; i < end; ++i)
{
double br = pMasterPalette[i].s.red;
double bg = pMasterPalette[i].s.green;
double bb = pMasterPalette[i].s.blue;
double grey = (br * 0.299 + bg * 0.587 + bb * 0.114) / 255.0f;
if (grey > 1.0)
grey = 1.0;
br = r * grey;
bg = g * grey;
bb = b * grey;
tr->remap[i] = NearestColor(
(int)br,
(int)bg,
(int)bb
);
}
return true;
}
static boolean PaletteRemap_AddTint(remaptable_t *tr, int start, int end, int r, int g, int b, int amount)
{
if (IndicesOutOfRange(start, end))
return false;
for (int i = start; i < end; ++i)
{
float br = pMasterPalette[i].s.red;
float bg = pMasterPalette[i].s.green;
float bb = pMasterPalette[i].s.blue;
float a = amount * 0.01f;
float ia = 1.0f - a;
br = br * ia + r * a;
bg = bg * ia + g * a;
bb = bb * ia + b * a;
tr->remap[i] = NearestColor(
(int)br,
(int)bg,
(int)bb
);
}
return true;
}
static boolean PaletteRemap_AddInvert(remaptable_t *tr, int start, int end)
{
if (IndicesOutOfRange(start, end))
return false;
for (int i = start; i < end; ++i)
{
tr->remap[i] = NearestColor(
255 - tr->remap[pMasterPalette[i].s.red],
255 - tr->remap[pMasterPalette[i].s.green],
255 - tr->remap[pMasterPalette[i].s.blue]
);
}
return true;
}
struct ParsedTranslation
{
struct ParsedTranslation *next;
remaptable_t *remap;
remaptable_t *baseTranslation;
struct PaletteRemapParseResult *data;
};
static struct ParsedTranslation *parsedTranslationListHead = NULL;
static struct ParsedTranslation *parsedTranslationListTail = NULL;
static void AddParsedTranslation(unsigned id, int base_translation, struct PaletteRemapParseResult *data)
{
struct ParsedTranslation *node = Z_Calloc(sizeof(struct ParsedTranslation), PU_STATIC, NULL);
node->remap = paletteremaps[id];
node->data = data;
if (base_translation != -1)
node->baseTranslation = paletteremaps[base_translation];
if (parsedTranslationListHead == NULL)
parsedTranslationListHead = parsedTranslationListTail = node;
else
{
parsedTranslationListTail->next = node;
parsedTranslationListTail = node;
}
}
static void PaletteRemap_ApplyResult(remaptable_t *tr, struct PaletteRemapParseResult *data)
{
int start = data->start;
int end = data->end;
switch (data->type)
{
case REMAP_ADD_INDEXRANGE:
PaletteRemap_AddIndexRange(tr, start, end, data->indexRange.pal1, data->indexRange.pal2);
break;
case REMAP_ADD_COLORRANGE:
PaletteRemap_AddColorRange(tr, start, end,
data->colorRange.r1, data->colorRange.g1, data->colorRange.b1,
data->colorRange.r2, data->colorRange.g2, data->colorRange.b2);
break;
case REMAP_ADD_COLOURISATION:
PaletteRemap_AddColourisation(tr, start, end,
data->colourisation.r, data->colourisation.g, data->colourisation.b);
break;
case REMAP_ADD_DESATURATION:
PaletteRemap_AddDesaturation(tr, start, end,
data->desaturation.r1, data->desaturation.g1, data->desaturation.b1,
data->desaturation.r2, data->desaturation.g2, data->desaturation.b2);
break;
case REMAP_ADD_TINT:
PaletteRemap_AddTint(tr, start, end, data->tint.r, data->tint.g, data->tint.b, data->tint.amount);
break;
}
}
void R_LoadParsedTranslations(void)
{
struct ParsedTranslation *node = parsedTranslationListHead;
while (node)
{
struct PaletteRemapParseResult *result = node->data;
struct ParsedTranslation *next = node->next;
remaptable_t *tr = node->remap;
PaletteRemap_SetIdentity(tr);
if (node->baseTranslation)
memcpy(tr, node->baseTranslation, sizeof(remaptable_t));
PaletteRemap_ApplyResult(tr, result);
Z_Free(result);
Z_Free(node);
node = next;
}
parsedTranslationListHead = parsedTranslationListTail = NULL;
}
static boolean ExpectToken(tokenizer_t *sc, const char *expect)
{
return strcmp(sc->get(sc, 0), expect) == 0;
}
static boolean StringToNumber(const char *tkn, int *out)
{
char *endPos = NULL;
errno = 0;
int result = strtol(tkn, &endPos, 10);
if (endPos == tkn || *endPos != '\0')
return false;
if (errno == ERANGE)
return false;
*out = result;
return true;
}
static boolean ParseNumber(tokenizer_t *sc, int *out)
{
return StringToNumber(sc->get(sc, 0), out);
}
static boolean ParseDecimal(tokenizer_t *sc, double *out)
{
const char *tkn = sc->get(sc, 0);
char *endPos = NULL;
errno = 0;
double result = strtod(tkn, &endPos);
if (endPos == tkn || *endPos != '\0')
return false;
if (errno == ERANGE)
return false;
*out = result;
return true;
}
static struct PaletteRemapParseResult *ThrowError(const char *format, ...)
{
struct PaletteRemapParseResult *err = Z_Calloc(sizeof(struct PaletteRemapParseResult), PU_STATIC, NULL);
va_list argptr;
va_start(argptr, format);
vsnprintf(err->error, sizeof err->error, format, argptr);
va_end(argptr);
err->has_error = true;
return err;
}
static struct PaletteRemapParseResult *MakeResult(enum PaletteRemapType type, int start, int end)
{
struct PaletteRemapParseResult *tr = Z_Calloc(sizeof(struct PaletteRemapParseResult), PU_STATIC, NULL);
tr->type = type;
tr->start = start;
tr->end = end;
return tr;
}
static struct PaletteRemapParseResult *PaletteRemap_ParseString(tokenizer_t *sc)
{
int start, end;
if (!ParseNumber(sc, &start))
return ThrowError("expected a number for start range");
if (!ExpectToken(sc, ":"))
return ThrowError("expected ':'");
if (!ParseNumber(sc, &end))
return ThrowError("expected a number for end range");
if (start < 0 || start > 255 || end < 0 || end > 255)
return ThrowError("palette indices out of range");
if (!ExpectToken(sc, "="))
return ThrowError("expected '='");
const char *tkn = sc->get(sc, 0);
if (strcmp(tkn, "[") == 0)
{
// translation using RGB values
int r1, g1, b1;
int r2, g2, b2;
// start
if (!ParseNumber(sc, &r1))
return ThrowError("expected a number for starting red");
if (!ExpectToken(sc, ","))
return ThrowError("expected ','");
if (!ParseNumber(sc, &g1))
return ThrowError("expected a number for starting green");
if (!ExpectToken(sc, ","))
return ThrowError("expected ','");
if (!ParseNumber(sc, &b1))
return ThrowError("expected a number for starting blue");
if (!ExpectToken(sc, ","))
return ThrowError("expected ','");
if (!ExpectToken(sc, "]"))
return ThrowError("expected ']'");
if (!ExpectToken(sc, ":"))
return ThrowError("expected ':'");
if (!ExpectToken(sc, "["))
return ThrowError("expected '[");
// end
if (!ParseNumber(sc, &r2))
return ThrowError("expected a number for ending red");
if (!ExpectToken(sc, ","))
return ThrowError("expected ','");
if (!ParseNumber(sc, &g2))
return ThrowError("expected a number for ending green");
if (!ExpectToken(sc, ","))
return ThrowError("expected ','");
if (!ParseNumber(sc, &b2))
return ThrowError("expected a number for ending blue");
if (!ExpectToken(sc, "]"))
return ThrowError("expected ']'");
struct PaletteRemapParseResult *tr = MakeResult(REMAP_ADD_COLORRANGE, start, end);
tr->colorRange.r1 = r1;
tr->colorRange.g1 = g1;
tr->colorRange.b1 = b1;
tr->colorRange.r2 = r2;
tr->colorRange.g2 = g2;
tr->colorRange.b2 = b2;
return tr;
}
else if (strcmp(tkn, "%") == 0)
{
// translation using RGB values (desaturation)
double r1, g1, b1;
double r2, g2, b2;
if (!ExpectToken(sc, "["))
return ThrowError("expected '[");
// start
if (!ParseDecimal(sc, &r1))
return ThrowError("expected a number for starting red");
if (!ExpectToken(sc, ","))
return ThrowError("expected ','");
if (!ParseDecimal(sc, &g1))
return ThrowError("expected a number for starting green");
if (!ExpectToken(sc, ","))
return ThrowError("expected ','");
if (!ParseDecimal(sc, &b1))
return ThrowError("expected a number for starting blue");
if (!ExpectToken(sc, "]"))
return ThrowError("expected ']'");
if (!ExpectToken(sc, ":"))
return ThrowError("expected ':'");
if (!ExpectToken(sc, "["))
return ThrowError("expected '[");
// end
if (!ParseDecimal(sc, &r2))
return ThrowError("expected a number for ending red");
if (!ExpectToken(sc, ","))
return ThrowError("expected ','");
if (!ParseDecimal(sc, &g2))
return ThrowError("expected a number for ending green");
if (!ExpectToken(sc, ","))
return ThrowError("expected ','");
if (!ParseDecimal(sc, &b2))
return ThrowError("expected a number for ending blue");
if (!ExpectToken(sc, "]"))
return ThrowError("expected ']'");
struct PaletteRemapParseResult *tr = MakeResult(REMAP_ADD_DESATURATION, start, end);
tr->desaturation.r1 = r1;
tr->desaturation.g1 = g1;
tr->desaturation.b1 = b1;
tr->desaturation.r2 = r2;
tr->desaturation.g2 = g2;
tr->desaturation.b2 = b2;
return tr;
}
else if (strcmp(tkn, "#") == 0)
{
// Colourise translation
int r, g, b;
if (!ExpectToken(sc, "["))
return ThrowError("expected '[");
if (!ParseNumber(sc, &r))
return ThrowError("expected a number for red");
if (!ExpectToken(sc, ","))
return ThrowError("expected ','");
if (!ParseNumber(sc, &g))
return ThrowError("expected a number for green");
if (!ExpectToken(sc, ","))
return ThrowError("expected ','");
if (!ParseNumber(sc, &b))
return ThrowError("expected a number for blue");
if (!ExpectToken(sc, "]"))
return ThrowError("expected ']'");
struct PaletteRemapParseResult *tr = MakeResult(REMAP_ADD_COLOURISATION, start, end);
tr->colourisation.r = r;
tr->colourisation.g = g;
tr->colourisation.b = b;
return tr;
}
else if (strcmp(tkn, "@") == 0)
{
// Tint translation
int a, r, g, b;
if (!ExpectToken(sc, "["))
return ThrowError("expected '[");
if (!ParseNumber(sc, &a))
return ThrowError("expected a number for amount");
if (!ExpectToken(sc, ","))
return ThrowError("expected ','");
if (!ParseNumber(sc, &r))
return ThrowError("expected a number for red");
if (!ExpectToken(sc, ","))
return ThrowError("expected ','");
if (!ParseNumber(sc, &g))
return ThrowError("expected a number for green");
if (!ExpectToken(sc, ","))
return ThrowError("expected ','");
if (!ParseNumber(sc, &b))
return ThrowError("expected a number for blue");
if (!ExpectToken(sc, "]"))
return ThrowError("expected ']'");
struct PaletteRemapParseResult *tr = MakeResult(REMAP_ADD_TINT, start, end);
tr->tint.r = r;
tr->tint.g = g;
tr->tint.b = b;
tr->tint.amount = a;
return tr;
}
else
{
int pal1, pal2;
if (!StringToNumber(tkn, &pal1))
return ThrowError("expected a number for starting index");
if (!ExpectToken(sc, ":"))
return ThrowError("expected ':'");
if (!ParseNumber(sc, &pal2))
return ThrowError("expected a number for ending index");
struct PaletteRemapParseResult *tr = MakeResult(REMAP_ADD_INDEXRANGE, start, end);
tr->indexRange.pal1 = pal1;
tr->indexRange.pal2 = pal2;
return tr;
}
return NULL;
}
static struct PaletteRemapParseResult *PaletteRemap_ParseTranslation(const char *translation)
{
tokenizer_t *sc = Tokenizer_Open(translation, 1);
struct PaletteRemapParseResult *result = PaletteRemap_ParseString(sc);
Tokenizer_Close(sc);
return result;
}
void R_ParseTrnslate(INT32 wadNum, UINT16 lumpnum)
{
char *lumpData = (char *)W_CacheLumpNumPwad(wadNum, lumpnum, PU_STATIC);
size_t lumpLength = W_LumpLengthPwad(wadNum, lumpnum);
char *text = (char *)Z_Malloc((lumpLength + 1), PU_STATIC, NULL);
memmove(text, lumpData, lumpLength);
text[lumpLength] = '\0';
Z_Free(lumpData);
tokenizer_t *sc = Tokenizer_Open(text, 1);
const char *tkn = sc->get(sc, 0);
while (tkn != NULL)
{
int base_translation = -1;
char *name = Z_StrDup(tkn);
tkn = sc->get(sc, 0);
if (strcmp(tkn, ":") == 0)
{
tkn = sc->get(sc, 0);
base_translation = R_FindCustomTranslation(tkn);
if (base_translation == -1)
{
CONS_Alert(CONS_ERROR, "Error parsing translation '%s': No translation named '%s'\n", name, tkn);
goto fail;
}
tkn = sc->get(sc, 0);
}
if (strcmp(tkn, "=") != 0)
{
CONS_Alert(CONS_ERROR, "Error parsing translation '%s': Expected '=', got '%s'\n", name, tkn);
goto fail;
}
tkn = sc->get(sc, 0);
struct PaletteRemapParseResult *result = NULL;
do {
result = PaletteRemap_ParseTranslation(tkn);
if (result->has_error)
{
CONS_Alert(CONS_ERROR, "Error parsing translation '%s': %s\n", name, result->error);
Z_Free(result);
goto fail;
}
tkn = sc->get(sc, 0);
if (!tkn)
break;
if (strcmp(tkn, ",") != 0)
break;
tkn = sc->get(sc, 0);
} while (true);
// Allocate it and register it
remaptable_t *tr = PaletteRemap_New();
unsigned id = PaletteRemap_Add(tr);
R_AddCustomTranslation(name, id);
// Free this, since it's no longer needed
Z_Free(name);
// The translation is not generated until later, because the palette may not have been loaded.
// We store the result for when it's needed.
AddParsedTranslation(id, base_translation, result);
}
fail:
Tokenizer_Close(sc);
Z_Free(text);
}
typedef struct CustomTranslation
{
char *name;
unsigned id;
UINT32 hash;
} customtranslation_t;
static customtranslation_t *customtranslations = NULL;
static unsigned numcustomtranslations = 0;
int R_FindCustomTranslation(const char *name)
{
UINT32 hash = quickncasehash(name, strlen(name));
for (unsigned i = 0; i < numcustomtranslations; i++)
{
if (hash == customtranslations[i].hash && strcmp(name, customtranslations[i].name) == 0)
return (int)customtranslations[i].id;
}
return -1;
}
// This is needed for SOC (which is case insensitive)
int R_FindCustomTranslation_CaseInsensitive(const char *name)
{
for (unsigned i = 0; i < numcustomtranslations; i++)
{
if (stricmp(name, customtranslations[i].name) == 0)
return (int)customtranslations[i].id;
}
return -1;
}
void R_AddCustomTranslation(const char *name, int trnum)
{
customtranslation_t *tr = NULL;
UINT32 hash = quickncasehash(name, strlen(name));
for (unsigned i = 0; i < numcustomtranslations; i++)
{
customtranslation_t *lookup = &customtranslations[i];
if (hash == lookup->hash && strcmp(name, lookup->name) == 0)
{
tr = lookup;
break;
}
}
if (tr == NULL)
{
numcustomtranslations++;
customtranslations = Z_Realloc(customtranslations, sizeof(customtranslation_t) * numcustomtranslations, PU_STATIC, NULL);
tr = &customtranslations[numcustomtranslations - 1];
}
tr->id = trnum;
tr->name = Z_StrDup(name);
tr->hash = quickncasehash(name, strlen(name));
}
const char *R_GetCustomTranslationName(unsigned id)
{
for (unsigned i = 0; i < numcustomtranslations; i++)
{
if (id == customtranslations[i].id)
return customtranslations[i].name;
}
return NULL;
}
unsigned R_NumCustomTranslations(void)
{
return numcustomtranslations;
}
remaptable_t *R_GetTranslationByID(int id)
{
if (!R_TranslationIsValid(id))
return NULL;
return paletteremaps[id];
}
boolean R_TranslationIsValid(int id)
{
if (id < 0 || id >= (signed)numpaletteremaps)
return false;
return true;
}
remaptable_t *R_GetBuiltInTranslation(SINT8 tc)
{
switch (tc)
{
case TC_ALLWHITE:
return R_GetTranslationByID(allWhiteRemap);
case TC_DASHMODE:
return R_GetTranslationByID(dashModeRemap);
}
return NULL;
}

45
src/r_translation.h Normal file
View file

@ -0,0 +1,45 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2006 by Randy Heit.
// Copyright (C) 2023 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_translation.h
/// \brief Translation table handling
#ifndef __R_TRANSLATION__
#define __R_TRANSLATION__
#include "doomdef.h"
typedef struct
{
UINT8 remap[256];
unsigned num_entries;
} remaptable_t;
void PaletteRemap_Init(void);
remaptable_t *PaletteRemap_New(void);
remaptable_t *PaletteRemap_Copy(remaptable_t *tr);
boolean PaletteRemap_Equal(remaptable_t *a, remaptable_t *b);
void PaletteRemap_SetIdentity(remaptable_t *tr);
boolean PaletteRemap_IsIdentity(remaptable_t *tr);
unsigned PaletteRemap_Add(remaptable_t *tr);
int R_FindCustomTranslation(const char *name);
int R_FindCustomTranslation_CaseInsensitive(const char *name);
void R_AddCustomTranslation(const char *name, int trnum);
const char *R_GetCustomTranslationName(unsigned id);
unsigned R_NumCustomTranslations(void);
remaptable_t *R_GetTranslationByID(int id);
boolean R_TranslationIsValid(int id);
void R_ParseTrnslate(INT32 wadNum, UINT16 lumpnum);
void R_LoadParsedTranslations(void);
remaptable_t *R_GetBuiltInTranslation(SINT8 tc);
#endif

View file

@ -344,6 +344,7 @@
<ClInclude Include="..\r_state.h" />
<ClInclude Include="..\r_textures.h" />
<ClInclude Include="..\r_things.h" />
<ClInclude Include="..\r_translation.h" />
<ClInclude Include="..\screen.h" />
<ClInclude Include="..\snake.h" />
<ClInclude Include="..\sounds.h" />
@ -535,6 +536,7 @@
<ClCompile Include="..\r_splats.c" />
<ClCompile Include="..\r_textures.c" />
<ClCompile Include="..\r_things.c" />
<ClCompile Include="..\r_translation.c" />
<ClCompile Include="..\screen.c" />
<ClCompile Include="..\snake.c" />
<ClCompile Include="..\sounds.c" />

View file

@ -543,6 +543,9 @@
<ClInclude Include="..\r_textures.h">
<Filter>R_Rend</Filter>
</ClInclude>
<ClInclude Include="..\r_translation.h">
<Filter>R_Rend</Filter>
</ClInclude>
<ClInclude Include="..\r_portal.h">
<Filter>R_Rend</Filter>
</ClInclude>
@ -1099,6 +1102,9 @@
<ClCompile Include="..\r_textures.c">
<Filter>R_Rend</Filter>
</ClCompile>
<ClCompile Include="..\r_translation.c">
<Filter>R_Rend</Filter>
</ClCompile>
<ClCompile Include="..\r_portal.c">
<Filter>R_Rend</Filter>
</ClCompile>

View file

@ -59,6 +59,7 @@
#include "r_textures.h"
#include "r_patch.h"
#include "r_picformats.h"
#include "r_translation.h"
#include "i_time.h"
#include "i_system.h"
#include "i_video.h" // rendermode
@ -831,6 +832,16 @@ static void W_ReadFileShaders(wadfile_t *wadfile)
#endif
}
static void W_LoadTrnslateLumps(UINT16 w)
{
UINT16 lump = W_CheckNumForNamePwad("TRNSLATE", w, 0);
while (lump != INT16_MAX)
{
R_ParseTrnslate(w, lump);
lump = W_CheckNumForNamePwad("TRNSLATE", (UINT16)w, lump + 1);
}
}
// Allocate a wadfile, setup the lumpinfo (directory) and
// lumpcache, add the wadfile to the current active wadfiles
//
@ -981,6 +992,9 @@ UINT16 W_InitFile(const char *filename, boolean mainfile, boolean startup)
// Read shaders from file
W_ReadFileShaders(wadfile);
// The below hack makes me load this here.
W_LoadTrnslateLumps(numwadfiles - 1);
// TODO: HACK ALERT - Load Lua & SOC stuff right here. I feel like this should be out of this place, but... Let's stick with this for now.
switch (wadfile->type)
{