From 0b98417d070a57b774850ea43de4443ce2461b85 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 15 Sep 2022 07:26:08 -0400 Subject: [PATCH] Terrain overlays Customizable state to display when standing / moving on terrain. Intended to pair with floor clipping. --- src/k_terrain.c | 410 +++++++++++++++++++++++++++++++++++++++++++++++- src/k_terrain.h | 104 +++++++++++- src/p_mobj.c | 2 + src/p_mobj.h | 2 + src/p_saveg.c | 10 +- 5 files changed, 524 insertions(+), 4 deletions(-) diff --git a/src/k_terrain.c b/src/k_terrain.c index e44c0f0cf..993253504 100644 --- a/src/k_terrain.c +++ b/src/k_terrain.c @@ -35,6 +35,9 @@ static size_t numSplashDefs = 0; static t_footstep_t *footstepDefs = NULL; static size_t numFootstepDefs = 0; +static t_overlay_t *overlayDefs = NULL; +static size_t numOverlayDefs = 0; + static terrain_t *terrainDefs = NULL; static size_t numTerrainDefs = 0; @@ -182,6 +185,75 @@ t_footstep_t *K_GetFootstepByName(const char *checkName) return NULL; } +/*-------------------------------------------------- + size_t K_GetOverlayHeapIndex(t_overlay_t *overlay) + + See header file for description. +--------------------------------------------------*/ +size_t K_GetOverlayHeapIndex(t_overlay_t *overlay) +{ + if (overlay == NULL) + { + return SIZE_MAX; + } + + return (overlay - overlayDefs); +} + +/*-------------------------------------------------- + size_t K_GetNumOverlayDefs(void) + + See header file for description. +--------------------------------------------------*/ +size_t K_GetNumOverlayDefs(void) +{ + return numOverlayDefs; +} + +/*-------------------------------------------------- + t_overlay_t *K_GetOverlayByIndex(size_t checkIndex) + + See header file for description. +--------------------------------------------------*/ +t_overlay_t *K_GetOverlayByIndex(size_t checkIndex) +{ + if (checkIndex >= numOverlayDefs) + { + return NULL; + } + + return &overlayDefs[checkIndex]; +} + +/*-------------------------------------------------- + t_overlay_t *K_GetOverlayByName(const char *checkName) + + See header file for description. +--------------------------------------------------*/ +t_overlay_t *K_GetOverlayByName(const char *checkName) +{ + UINT32 checkHash = quickncasehash(checkName, TERRAIN_NAME_LEN); + size_t i; + + if (numOverlayDefs == 0) + { + return NULL; + } + + for (i = 0; i < numOverlayDefs; i++) + { + t_overlay_t *o = &overlayDefs[i]; + + if (checkHash == o->hash && !strncmp(checkName, o->name, TERRAIN_NAME_LEN)) + { + // Name matches. + return o; + } + } + + return NULL; +} + /*-------------------------------------------------- size_t K_GetTerrainHeapIndex(terrain_t *terrain) @@ -521,7 +593,16 @@ void K_SetDefaultFriction(mobj_t *mo) /*-------------------------------------------------- static void K_SpawnSplashParticles(mobj_t *mo, t_splash_t *s, fixed_t impact) - See header file for description. + Creates all of the splash particles for an object + from a splash definition. + + Input Arguments:- + mo - The object to spawn the splash particles for. + s - The splash definition to use. + impact - How hard the object hit the surface. + + Return:- + N/A --------------------------------------------------*/ static void K_SpawnSplashParticles(mobj_t *mo, t_splash_t *s, fixed_t impact) { @@ -648,7 +729,16 @@ void K_SpawnSplashForMobj(mobj_t *mo, fixed_t impact) /*-------------------------------------------------- static void K_SpawnFootstepParticle(mobj_t *mo, t_footstep_t *fs) - See header file for description. + Creates a new footstep particle for an object + from a footstep definition. + + Input Arguments:- + mo - The object to spawn the footstep particle for. + fs - The footstep definition to use. + timer - Spawning frequency timer. + + Return:- + N/A --------------------------------------------------*/ static void K_SpawnFootstepParticle(mobj_t *mo, t_footstep_t *fs, tic_t timer) { @@ -798,6 +888,192 @@ void K_HandleFootstepParticles(mobj_t *mo) K_SpawnFootstepParticle(mo, fs, timer); } +/*-------------------------------------------------- + static void K_CleanupTerrainOverlay(mobj_t *mo) + + Removes an object's terrain overlay. + + Input Arguments:- + mo - The object to remove the overlay from. + + Return:- + N/A +--------------------------------------------------*/ +static void K_CleanupTerrainOverlay(mobj_t *mo) +{ + if (mo->terrainOverlay != NULL && P_MobjWasRemoved(mo->terrainOverlay) == false) + { + P_RemoveMobj(mo->terrainOverlay); + } +} + +/*-------------------------------------------------- + static boolean K_InitTerrainOverlay(mobj_t *mo) + + Creates a new terrain overlay for an object. + + Input Arguments:- + mo - The object to give an overlay to. + + Return:- + true if successful, otherwise false. +--------------------------------------------------*/ +static boolean K_InitTerrainOverlay(mobj_t *mo) +{ + mobj_t *new = P_SpawnMobjFromMobj(mo, 0, 0, 0, MT_OVERLAY); + + // Tells the overlay that we haven't set up a state yet. + new->extravalue1 = TOV_UNDEFINED; + + // Set up our pointers. + P_SetTarget(&new->target, mo); + P_SetTarget(&mo->terrainOverlay, new); + + return true; +} + +/*-------------------------------------------------- + static t_overlay_state_t K_DesiredTerrainOverlayAction(mobj_t *mo) + + Figures out the overlay action to use for an object. + + Input Arguments:- + mo - The object + st - The terrain overlay state. + + Return:- + The overlay action enum to use for the object. +--------------------------------------------------*/ +static t_overlay_action_t K_DesiredTerrainOverlayAction(mobj_t *mo) +{ + const boolean moving = (P_AproxDistance(mo->momx, mo->momy) >= (mo->scale >> 1)); + + if (moving == true) + { + return TOV_MOVING; + } + + return TOV_STILL; +} + +/*-------------------------------------------------- + static statenum_t K_GetTerrainOverlayState(t_overlay_t *o, t_overlay_action_t act) + + Converts our overlay's action enum into an actual state ID. + + Input Arguments:- + o - The overlay properties. + act - The terrain overlay action. + + Return:- + The actual state ID, for use with P_SetMobjState. +--------------------------------------------------*/ +static statenum_t K_GetTerrainOverlayState(t_overlay_t *o, t_overlay_action_t act) +{ + if (act >= 0 && act < TOV__MAX) + { + return o->states[act]; + } + + return S_NULL; +} + +/*-------------------------------------------------- + static void K_SetTerrainOverlayState(mobj_t *mo, t_overlay_action_t act, statenum_t st) + + Updates our overlay's current state. + + Input Arguments:- + o - The overlay properties. + act - The terrain overlay action. + st - The new object's state. + + Return:- + N/A +--------------------------------------------------*/ +static void K_SetTerrainOverlayState(mobj_t *mo, t_overlay_action_t act, statenum_t st) +{ + if (act == mo->terrainOverlay->extravalue1) + { + // Already set the state, so leave it alone. + return; + } + + P_SetMobjState(mo->terrainOverlay, st); + mo->terrainOverlay->extravalue1 = act; +} + +/*-------------------------------------------------- + static void K_UpdateTerrainOverlay(mobj_t *mo) + + See header file for description. +--------------------------------------------------*/ +void K_UpdateTerrainOverlay(mobj_t *mo) +{ + t_overlay_t *o = NULL; + t_overlay_action_t act = TOV_UNDEFINED; + statenum_t st = S_NULL; + + if (mo == NULL || P_MobjWasRemoved(mo) == true) + { + // Invalid object. + return; + } + + if (!(mo->flags & MF_APPLYTERRAIN)) + { + // No TERRAIN effects for this object. + K_CleanupTerrainOverlay(mo); + return; + } + + if (mo->terrain == NULL || mo->terrain->overlayID == SIZE_MAX) + { + // No overlay for this terrain type. + K_CleanupTerrainOverlay(mo); + return; + } + else + { + o = K_GetOverlayByIndex(mo->terrain->overlayID); + } + + if (o == NULL) + { + // No overlay to use. + K_CleanupTerrainOverlay(mo); + return; + } + + // Determine the state to use. We want to do this before creating + // the overlay, so that we keep it despawned if the state is S_NULL. + act = K_DesiredTerrainOverlayAction(mo); + st = K_GetTerrainOverlayState(o, act); + + if (st == S_NULL) + { + // No state to use for this action. + K_CleanupTerrainOverlay(mo); + return; + } + + if (mo->terrainOverlay == NULL || P_MobjWasRemoved(mo->terrainOverlay) == true) + { + // Doesn't exist currently, so try to create + // a new terrain overlay. + + if (K_InitTerrainOverlay(mo) == false) + { + // We were unsuccessful, get out of here. + return; + } + } + + mo->terrainOverlay->color = o->color; + + K_SetTerrainOverlayState(mo, act, st); +} + /*-------------------------------------------------- static void K_FlagBoolean(UINT32 *inputFlags, UINT32 newFlag, char *val) @@ -1034,6 +1310,89 @@ static void K_ParseFootstepParameter(size_t i, char *param, char *val) } } +/*-------------------------------------------------- + static void K_OverlayDefaults(t_overlay_t *overlay) + + Sets the defaults for a new Overlay block. + + Input Arguments:- + overlay - Terrain Overlay structure to default. + + Return:- + None +--------------------------------------------------*/ +static void K_OverlayDefaults(t_overlay_t *overlay) +{ + size_t i; + + for (i = 0; i < TOV__MAX; i++) + { + overlay->states[i] = S_NULL; + } + + overlay->scale = FRACUNIT; + overlay->color = SKINCOLOR_NONE; + overlay->speed = 0; +} + +/*-------------------------------------------------- + static void K_NewOverlayDefs(void) + + Increases the size of overlayDefs by 1, and + sets the new struct's values to their defaults. + + Input Arguments:- + None + + Return:- + None +--------------------------------------------------*/ +static void K_NewOverlayDefs(void) +{ + numOverlayDefs++; + overlayDefs = (t_overlay_t *)Z_Realloc(overlayDefs, sizeof(t_overlay_t) * (numOverlayDefs + 1), PU_STATIC, NULL); + K_OverlayDefaults( &overlayDefs[numOverlayDefs - 1] ); +} + +/*-------------------------------------------------- + static void K_ParseOverlayParameter(size_t i, char *param, char *val) + + Parser function for Overlay blocks. + + Input Arguments:- + i - Struct ID + param - Parameter string + val - Value string + + Return:- + None +--------------------------------------------------*/ +static void K_ParseOverlayParameter(size_t i, char *param, char *val) +{ + t_overlay_t *overlay = &overlayDefs[i]; + + if (stricmp(param, "stillState") == 0) + { + overlay->states[TOV_STILL] = get_number(val); + } + else if (stricmp(param, "movingState") == 0) + { + overlay->states[TOV_MOVING] = get_number(val); + } + else if (stricmp(param, "scale") == 0) + { + overlay->scale = FLOAT_TO_FIXED(atof(val)); + } + else if (stricmp(param, "color") == 0) + { + overlay->color = get_number(val); + } + else if (stricmp(param, "speed") == 0) + { + overlay->speed = FLOAT_TO_FIXED(atof(val)); + } +} + /*-------------------------------------------------- static void K_TerrainDefaults(terrain_t *terrain) @@ -1049,6 +1408,7 @@ static void K_TerrainDefaults(terrain_t *terrain) { terrain->splashID = SIZE_MAX; terrain->footstepID = SIZE_MAX; + terrain->overlayID = SIZE_MAX; terrain->friction = 0; terrain->offroad = 0; @@ -1103,6 +1463,11 @@ static void K_ParseTerrainParameter(size_t i, char *param, char *val) t_footstep_t *footstep = K_GetFootstepByName(val); terrain->footstepID = K_GetFootstepHeapIndex(footstep); } + else if (stricmp(param, "overlay") == 0) + { + t_overlay_t *overlay = K_GetOverlayByName(val); + terrain->overlayID = K_GetOverlayHeapIndex(overlay); + } else if (stricmp(param, "friction") == 0) { terrain->friction = FLOAT_TO_FIXED(atof(val)); @@ -1319,6 +1684,47 @@ static boolean K_TERRAINLumpParser(UINT8 *data, size_t size) valid = false; } } + else if (stricmp(tkn, "overlay") == 0) + { + Z_Free(tkn); + tkn = M_GetToken(NULL); + pos = M_GetTokenPos(); + + if (tkn && pos < size) + { + t_overlay_t *o = NULL; + + tknHash = quickncasehash(tkn, TERRAIN_NAME_LEN); + + for (i = 0; i < numOverlayDefs; i++) + { + o = &overlayDefs[i]; + + if (tknHash == o->hash && !strncmp(tkn, o->name, TERRAIN_NAME_LEN)) + { + break; + } + } + + if (i == numOverlayDefs) + { + K_NewOverlayDefs(); + o = &overlayDefs[i]; + + strncpy(o->name, tkn, TERRAIN_NAME_LEN); + o->hash = tknHash; + + CONS_Printf("Created new Overlay type '%s'\n", o->name); + } + + valid = K_DoTERRAINLumpParse(i, K_ParseOverlayParameter); + } + else + { + CONS_Alert(CONS_ERROR, "No Overlay type name.\n"); + valid = false; + } + } else if (stricmp(tkn, "terrain") == 0) { Z_Free(tkn); diff --git a/src/k_terrain.h b/src/k_terrain.h index 016fde820..29ce0873e 100644 --- a/src/k_terrain.h +++ b/src/k_terrain.h @@ -66,10 +66,33 @@ typedef struct t_footstep_s fixed_t requiredSpeed; // Speed percentage you need to be at to trigger the particles. } t_footstep_t; +typedef enum +{ + // Overlay actions. + TOV_UNDEFINED = -1, + TOV_STILL, + TOV_MOVING, + TOV__MAX +} t_overlay_action_t; + +typedef struct t_overlay_s +{ + // Overlay definition. + // These are sprites displayed on top of the base object. + + char name[TERRAIN_NAME_LEN]; // Lookup name. + UINT32 hash; // Lookup name's hash. + + UINT16 states[TOV__MAX]; // State to use when the object is still. + fixed_t scale; // Thing scale multiplier. + UINT16 color; // Colorize effect. SKINCOLOR_NONE has no colorize. + fixed_t speed; // Speed-up based on object speed. 0 plays the animation at a constant rate. +} t_overlay_t; + typedef enum { // Terrain flag values. - TRF_LIQUID = 1, // Texture water properties (wavy, slippery, etc) + TRF_LIQUID = 1, // Texture has water properties (wavy, slippery, etc) TRF_SNEAKERPANEL = 1<<1, // Texture is a booster TRF_STAIRJANK = 1<<2, // Texture is bumpy road TRF_TRIPWIRE = 1<<3 // Texture is a tripwire when used as a midtexture @@ -85,6 +108,7 @@ typedef struct terrain_s size_t splashID; // Splash defintion ID. size_t footstepID; // Footstep defintion ID. + size_t overlayID; // Overlay defintion ID. fixed_t friction; // The default friction of this texture. UINT8 offroad; // The default offroad level of this texture. @@ -227,6 +251,67 @@ t_footstep_t *K_GetFootstepByIndex(size_t checkIndex); t_footstep_t *K_GetFootstepByName(const char *checkName); +/*-------------------------------------------------- + size_t K_GetOverlayHeapIndex(t_overlay_t *overlay); + + Returns an overlay defintion's index in the + overlay definition heap. + + Input Arguments:- + overlay - The overlay definition to return the index of. + + Return:- + The overlay heap index, SIZE_MAX if the overlay was invalid. +--------------------------------------------------*/ + +size_t K_GetOverlayHeapIndex(t_overlay_t *overlay); + + +/*-------------------------------------------------- + size_t K_GetNumOverlayDefs(void); + + Returns the number of overlay definitions. + + Input Arguments:- + None + + Return:- + Length of overlayDefs. +--------------------------------------------------*/ + +size_t K_GetNumOverlayDefs(void); + + +/*-------------------------------------------------- + t_overlay_t *K_GetOverlayByIndex(size_t checkIndex); + + Retrieves an overlay definition by its heap index. + + Input Arguments:- + checkIndex - The heap index to retrieve. + + Return:- + The overlay definition, NULL if it didn't exist. +--------------------------------------------------*/ + +t_overlay_t *K_GetOverlayByIndex(size_t checkIndex); + + +/*-------------------------------------------------- + t_overlay_t *K_GetOverlayByName(const char *checkName); + + Retrieves an overlay definition by its lookup name. + + Input Arguments:- + checkName - The lookup name to retrieve. + + Return:- + The overlay definition, NULL if it didn't exist. +--------------------------------------------------*/ + +t_overlay_t *K_GetOverlayByName(const char *checkName); + + /*-------------------------------------------------- size_t K_GetTerrainHeapIndex(terrain_t *terrain); @@ -444,6 +529,23 @@ void K_SpawnSplashForMobj(mobj_t *mo, fixed_t impact); void K_HandleFootstepParticles(mobj_t *mo); +/*-------------------------------------------------- + void K_UpdateTerrainOverlay(mobj_t *mo); + + Updates an object's terrainOverlay pointer, + depending on the terrain type. Intended to be + called every tic. + + Input Arguments:- + mo - The object to update the overlay for. + + Return:- + None +--------------------------------------------------*/ + +void K_UpdateTerrainOverlay(mobj_t *mo); + + /*-------------------------------------------------- void K_InitTerrain(UINT16 wadNum); diff --git a/src/p_mobj.c b/src/p_mobj.c index 378143bab..a7bd7769f 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -3784,6 +3784,7 @@ static void P_PlayerMobjThinker(mobj_t *mobj) } P_SquishThink(mobj); + K_UpdateTerrainOverlay(mobj); animonly: P_CyclePlayerMobjState(mobj); @@ -9539,6 +9540,7 @@ void P_MobjThinker(mobj_t *mobj) } P_SquishThink(mobj); + K_UpdateTerrainOverlay(mobj); if (mobj->flags & (MF_ENEMY|MF_BOSS) && mobj->health && P_CheckDeathPitCollide(mobj)) // extra pit check in case these didn't have momz diff --git a/src/p_mobj.h b/src/p_mobj.h index 49f1a3b71..4cdacea6d 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -406,6 +406,8 @@ typedef struct mobj_s fixed_t sprxoff, spryoff, sprzoff; // Sprite offsets in real space, does NOT affect position or collision struct terrain_s *terrain; // Terrain definition of the floor this object last hit. NULL when in the air. + struct mobj_s *terrainOverlay; // Overlay sprite object for terrain + INT32 hitlag; // Sal-style hit lag, straight from Captain Fetch's jowls INT32 dispoffset; diff --git a/src/p_saveg.c b/src/p_saveg.c index be02b13c6..5d802be53 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -1857,7 +1857,7 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type) diff2 |= MD2_ITNEXT; if (mobj->lastmomz) diff2 |= MD2_LASTMOMZ; - if (mobj->terrain != NULL) + if (mobj->terrain != NULL || mobj->terrainOverlay != NULL) diff2 |= MD2_TERRAIN; if (diff2 != 0) @@ -3177,6 +3177,7 @@ static thinker_t* LoadMobjThinker(actionf_p1 thinker) if (diff2 & MD2_TERRAIN) { mobj->terrain = (terrain_t *)(size_t)READUINT32(save_p); + mobj->terrainOverlay = (mobj_t *)(size_t)READUINT32(save_p); } else { @@ -4219,6 +4220,13 @@ static void P_RelinkPointers(void) CONS_Debug(DBG_GAMELOGIC, "terrain not found on %d\n", mobj->type); } } + if (mobj->terrainOverlay) + { + temp = (UINT32)(size_t)mobj->terrainOverlay; + mobj->terrainOverlay = NULL; + if (!P_SetTarget(&mobj->terrainOverlay, P_FindNewPosition(temp))) + CONS_Debug(DBG_GAMELOGIC, "terrainOverlay not found on %d\n", mobj->type); + } if (mobj->player) { if ( mobj->player->skybox.viewpoint)