mirror of
https://git.do.srb2.org/STJr/SRB2.git
synced 2024-11-24 21:31:46 +00:00
Allow custom characters to define their own states for standard player animations
This commit is contained in:
parent
37a2507bfd
commit
351759ef14
11 changed files with 1334 additions and 14 deletions
|
@ -6378,7 +6378,7 @@ void A_UnidusBall(mobj_t *actor)
|
|||
else if (locvar1 == 2)
|
||||
{
|
||||
boolean skull = (actor->target->flags2 & MF2_SKULLFLY) == MF2_SKULLFLY;
|
||||
if (actor->target->state == &states[actor->target->info->painstate])
|
||||
if (P_IsMobjInPainState(actor->target))
|
||||
{
|
||||
P_KillMobj(actor, NULL, NULL, 0);
|
||||
return;
|
||||
|
|
|
@ -412,7 +412,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
{
|
||||
if (special->type == MT_STEAM)
|
||||
{
|
||||
if (player && player->mo->state == &states[player->mo->info->painstate]) // can't use gas jets when player is in pain!
|
||||
if (player && P_IsPlayerInState(player, S_PLAY_PAIN)) // can't use gas jets when player is in pain!
|
||||
return;
|
||||
|
||||
fixed_t speed = special->info->mass; // gas jets use this for the vertical thrust
|
||||
|
|
|
@ -135,6 +135,7 @@ boolean P_TryCameraMove(fixed_t x, fixed_t y, camera_t *thiscam);
|
|||
void P_SlideCameraMove(camera_t *thiscam);
|
||||
boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcalled);
|
||||
pflags_t P_GetJumpFlags(player_t *player);
|
||||
statenum_t P_GetCanonicalPlayerState(player_t *player, statenum_t state);
|
||||
boolean P_IsPlayerInState(player_t *player, statenum_t state);
|
||||
boolean P_IsPlayerInSuperTransformationState(player_t *player);
|
||||
boolean P_IsPlayerInNightsTransformationState(player_t *player);
|
||||
|
@ -556,5 +557,6 @@ void P_DoSuperDetransformation(player_t *player);
|
|||
void P_ExplodeMissile(mobj_t *mo);
|
||||
void P_CheckGravity(mobj_t *mo, boolean affect);
|
||||
void P_SetPitchRollFromSlope(mobj_t *mo, pslope_t *slope);
|
||||
boolean P_IsMobjInPainState(mobj_t *mobj);
|
||||
|
||||
#endif // __P_LOCAL__
|
||||
|
|
|
@ -509,7 +509,7 @@ static void P_DoFan(mobj_t *fan, mobj_t *object)
|
|||
fixed_t speed = fan->info->mass; // fans use this for the vertical thrust
|
||||
SINT8 flipval = P_MobjFlip(fan); // virtually everything here centers around the thruster's gravity, not the object's!
|
||||
|
||||
if (p && object->state == &states[object->info->painstate]) // can't use fans when player is in pain!
|
||||
if (p && P_IsPlayerInState(p, S_PLAY_PAIN)) // can't use fans when player is in pain!
|
||||
return;
|
||||
|
||||
// is object's top below thruster's position? if not, calculate distance between their bottoms
|
||||
|
|
25
src/p_mobj.c
25
src/p_mobj.c
|
@ -36,6 +36,7 @@
|
|||
#include "p_slopes.h"
|
||||
#include "f_finale.h"
|
||||
#include "m_cond.h"
|
||||
#include "simple_hashmap.h"
|
||||
#include "netcode/net_command.h"
|
||||
|
||||
static CV_PossibleValue_t CV_BobSpeed[] = {{0, "MIN"}, {4*FRACUNIT, "MAX"}, {0, NULL}};
|
||||
|
@ -195,9 +196,9 @@ static void P_CyclePlayerMobjState(mobj_t *mobj)
|
|||
}
|
||||
}
|
||||
|
||||
static panim_t GetPlayerAnimationFromState(statenum_t state)
|
||||
static panim_t GetPlayerAnimationFromState(player_t *player, statenum_t state)
|
||||
{
|
||||
switch(state)
|
||||
switch(P_GetCanonicalPlayerState(player, state))
|
||||
{
|
||||
case S_PLAY_STND:
|
||||
case S_PLAY_WAIT:
|
||||
|
@ -273,6 +274,12 @@ static boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
|
|||
I_Error("P_SetPlayerMobjState used for non-player mobj. Use P_SetMobjState instead!\n(Mobj type: %d, State: %d)", mobj->type, state);
|
||||
#endif
|
||||
|
||||
// If the state has been overriden for this skin, use the replacement instead
|
||||
statenum_t customskinstate;
|
||||
SIMPLEHASH_FIND_INT(skins[player->skin]->defaulttocustomstate, hashentry_int32_int32_t, state, S_NULL, customskinstate)
|
||||
if (customskinstate)
|
||||
state = customskinstate;
|
||||
|
||||
// Catch falling for nojumpspin
|
||||
if ((state == S_PLAY_JUMP) && (player->charflags & SF_NOJUMPSPIN) && (P_MobjFlip(mobj)*mobj->momz < 0))
|
||||
return P_SetPlayerMobjState(mobj, S_PLAY_FALL);
|
||||
|
@ -302,14 +309,16 @@ static boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
|
|||
return true;
|
||||
}
|
||||
// You were in pain state after taking a hit, and you're moving out of pain state now?
|
||||
else if (mobj->state == &states[mobj->info->painstate] && player->powers[pw_flashing] == flashingtics && state != mobj->info->painstate)
|
||||
else if (P_IsPlayerInState(player, S_PLAY_PAIN)
|
||||
&& player->powers[pw_flashing] == flashingtics
|
||||
&& P_GetCanonicalPlayerState(player, state) != S_PLAY_PAIN)
|
||||
{
|
||||
// Start flashing, since you've landed.
|
||||
player->powers[pw_flashing] = flashingtics-1;
|
||||
P_DoPityCheck(player);
|
||||
}
|
||||
|
||||
player->panim = GetPlayerAnimationFromState(state);
|
||||
player->panim = GetPlayerAnimationFromState(player, state);
|
||||
|
||||
if (recursion++) // if recursion detected,
|
||||
memset(seenstate = tempstate, 0, sizeof tempstate); // clear state table
|
||||
|
@ -14339,3 +14348,11 @@ mobj_t *P_SpawnMobjFromMobj(mobj_t *mobj, fixed_t xofs, fixed_t yofs, fixed_t zo
|
|||
|
||||
return newmobj;
|
||||
}
|
||||
|
||||
boolean P_IsMobjInPainState(mobj_t *mobj)
|
||||
{
|
||||
if (mobj->player)
|
||||
return P_IsPlayerInState(mobj->player, S_PLAY_PAIN);
|
||||
else
|
||||
return (mobj->state == &states[mobj->info->painstate]);
|
||||
}
|
||||
|
|
|
@ -4758,7 +4758,7 @@ static void P_ProcessRopeHang(player_t *player, mtag_t sectag)
|
|||
if (player->cmd.buttons & BT_SPIN)
|
||||
return;
|
||||
|
||||
if (!(player->pflags & PF_SLIDING) && player->mo->state == &states[player->mo->info->painstate])
|
||||
if (!(player->pflags & PF_SLIDING) && P_IsPlayerInState(player, S_PLAY_PAIN))
|
||||
return;
|
||||
|
||||
if (player->exiting)
|
||||
|
@ -8805,7 +8805,7 @@ void T_Pusher(pusher_t *p)
|
|||
if (thing->player && thing->player->powers[pw_carry] == CR_ROPEHANG)
|
||||
continue;
|
||||
|
||||
if (thing->player && (thing->state == &states[thing->info->painstate]) && (thing->player->powers[pw_flashing] > (flashingtics/4)*3 && thing->player->powers[pw_flashing] <= flashingtics))
|
||||
if (thing->player && P_IsPlayerInState(thing->player, S_PLAY_PAIN) && (thing->player->powers[pw_flashing] > (flashingtics/4)*3 && thing->player->powers[pw_flashing] <= flashingtics))
|
||||
continue;
|
||||
|
||||
inFOF = touching = moved = false;
|
||||
|
|
20
src/p_user.c
20
src/p_user.c
|
@ -47,6 +47,7 @@
|
|||
#include "m_cheat.h"
|
||||
// Thok camera snap (ctrl-f "chalupa")
|
||||
#include "g_input.h"
|
||||
#include "simple_hashmap.h"
|
||||
|
||||
#ifdef HW3SOUND
|
||||
#include "hardware/hw3sound.h"
|
||||
|
@ -967,9 +968,20 @@ pflags_t P_GetJumpFlags(player_t *player)
|
|||
return (PF_JUMPED|PF_NOJUMPDAMAGE);
|
||||
return PF_JUMPED;
|
||||
}
|
||||
|
||||
// If the state is a custom state for the player's skin, retrieve its "canonical" state
|
||||
// e.g. S_SKIN_BIGTHECAT_WALK => S_PLAY_WALK
|
||||
statenum_t P_GetCanonicalPlayerState(player_t *player, statenum_t state)
|
||||
{
|
||||
skin_t *skin = skins[player->skin];
|
||||
statenum_t mappedstate;
|
||||
SIMPLEHASH_FIND_INT(skin->customtodefaultstate, hashentry_int32_int32_t, state, state, mappedstate)
|
||||
return mappedstate;
|
||||
}
|
||||
|
||||
boolean P_IsPlayerInState(player_t *player, statenum_t state)
|
||||
{
|
||||
return (player->mo->state == &states[state]);
|
||||
return (P_GetCanonicalPlayerState(player, player->mo->state - states) == state);
|
||||
}
|
||||
|
||||
boolean P_IsPlayerInSuperTransformationState(player_t *player)
|
||||
|
@ -1020,7 +1032,7 @@ void P_DoPlayerPain(player_t *player, mobj_t *source, mobj_t *inflictor)
|
|||
fixed_t fallbackspeed;
|
||||
|
||||
P_ResetPlayer(player);
|
||||
P_SetMobjState(player->mo, player->mo->info->painstate);
|
||||
P_SetMobjState(player->mo, S_PLAY_PAIN);
|
||||
|
||||
if (player->mo->eflags & MFE_VERTICALFLIP)
|
||||
player->mo->z--;
|
||||
|
@ -12796,7 +12808,7 @@ void P_PlayerAfterThink(player_t *player)
|
|||
S_StartSound(NULL, sfx_wepchg);
|
||||
|
||||
if ((player->pflags & PF_SLIDING) && ((player->pflags & (PF_JUMPED|PF_NOJUMPDAMAGE)) != PF_JUMPED))
|
||||
P_SetMobjState(player->mo, player->mo->info->painstate);
|
||||
P_SetMobjState(player->mo, S_PLAY_PAIN);
|
||||
|
||||
/* if (player->powers[pw_carry] == CR_NONE && player->mo->tracer && !player->homing)
|
||||
P_SetTarget(&player->mo->tracer, NULL);
|
||||
|
@ -13235,7 +13247,7 @@ boolean P_PlayerCanEnterSpinGaps(player_t *player)
|
|||
boolean P_PlayerShouldUseSpinHeight(player_t *player)
|
||||
{
|
||||
return ((player->pflags & (PF_SPINNING|PF_SLIDING|PF_GLIDING))
|
||||
|| (player->mo->state == &states[player->mo->info->painstate])
|
||||
|| P_IsPlayerInState(player, S_PLAY_PAIN)
|
||||
|| (player->panim == PA_ROLL)
|
||||
|| ((player->powers[pw_tailsfly] || (player->charability == CA_FLY && P_IsPlayerInState(player, S_PLAY_FLY_TIRED)))
|
||||
&& !(player->charflags & SF_NOJUMPSPIN))
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "p_local.h"
|
||||
#include "dehacked.h" // get_number (for thok)
|
||||
#include "m_cond.h"
|
||||
#include "deh_tables.h"
|
||||
#ifdef HWRENDER
|
||||
#include "hardware/hw_md2.h"
|
||||
#endif
|
||||
|
@ -773,6 +774,90 @@ static boolean R_ProcessPatchableFields(skin_t *skin, char *stoken, char *value)
|
|||
return true;
|
||||
}
|
||||
|
||||
// e.g. "RUN" => S_PLAY_RUN, "RUN1" => S_PLAY_RUN, "RUN8" => S_PLAY_RUN
|
||||
static statenum_t GetCanonicalPlayerStateNumByName(const char *name)
|
||||
{
|
||||
if (startswith(name, "STAND")) return S_PLAY_STND;
|
||||
else if (startswith(name, "STND")) return S_PLAY_STND;
|
||||
else if (startswith(name, "WAIT")) return S_PLAY_WAIT;
|
||||
else if (startswith(name, "WALK")) return S_PLAY_WALK;
|
||||
else if (startswith(name, "SKID")) return S_PLAY_SKID;
|
||||
else if (startswith(name, "RUN")) return S_PLAY_RUN;
|
||||
else if (startswith(name, "DASH")) return S_PLAY_DASH;
|
||||
else if (startswith(name, "PAIN")) return S_PLAY_PAIN;
|
||||
else if (startswith(name, "STUN")) return S_PLAY_STUN;
|
||||
else if (startswith(name, "DEAD")) return S_PLAY_DEAD;
|
||||
else if (startswith(name, "DRWN")) return S_PLAY_DRWN;
|
||||
else if (startswith(name, "ROLL")) return S_PLAY_ROLL;
|
||||
else if (startswith(name, "GASP")) return S_PLAY_GASP;
|
||||
else if (startswith(name, "JUMP")) return S_PLAY_JUMP;
|
||||
else if (startswith(name, "SPRING")) return S_PLAY_SPRING;
|
||||
else if (startswith(name, "FALL")) return S_PLAY_FALL;
|
||||
else if (startswith(name, "EDGE")) return S_PLAY_EDGE;
|
||||
else if (startswith(name, "RIDE")) return S_PLAY_RIDE;
|
||||
else if (startswith(name, "SPINDASH")) return S_PLAY_SPINDASH;
|
||||
else if (startswith(name, "FLY")) return S_PLAY_FLY;
|
||||
else if (startswith(name, "SWIM")) return S_PLAY_SWIM;
|
||||
else if (startswith(name, "FLY_TIRED")) return S_PLAY_FLY_TIRED;
|
||||
else if (startswith(name, "GLIDE")) return S_PLAY_GLIDE;
|
||||
else if (startswith(name, "GLIDE_LANDING")) return S_PLAY_GLIDE_LANDING;
|
||||
else if (startswith(name, "CLING")) return S_PLAY_CLING;
|
||||
else if (startswith(name, "CLIMB")) return S_PLAY_CLIMB;
|
||||
else if (startswith(name, "FLOAT")) return S_PLAY_FLOAT;
|
||||
else if (startswith(name, "FLOAT_RUN")) return S_PLAY_FLOAT_RUN;
|
||||
else if (startswith(name, "BOUNCE")) return S_PLAY_BOUNCE;
|
||||
else if (startswith(name, "BOUNCE_LANDING")) return S_PLAY_BOUNCE_LANDING;
|
||||
else if (startswith(name, "FIRE")) return S_PLAY_FIRE;
|
||||
else if (startswith(name, "FIRE_FINISH")) return S_PLAY_FIRE_FINISH;
|
||||
else if (startswith(name, "TWINSPIN")) return S_PLAY_TWINSPIN;
|
||||
else if (startswith(name, "MELEE")) return S_PLAY_MELEE;
|
||||
else if (startswith(name, "MELEE_FINISH")) return S_PLAY_MELEE_FINISH;
|
||||
else if (startswith(name, "MELEE_LANDING")) return S_PLAY_MELEE_LANDING;
|
||||
else if (startswith(name, "NIGHTS_STAND")) return S_PLAY_NIGHTS_STAND;
|
||||
else if (startswith(name, "NIGHTS_FLOAT")) return S_PLAY_NIGHTS_FLOAT;
|
||||
else if (startswith(name, "NIGHTS_FLY")) return S_PLAY_NIGHTS_FLY;
|
||||
else if (startswith(name, "NIGHTS_DRILL")) return S_PLAY_NIGHTS_DRILL;
|
||||
else if (startswith(name, "NIGHTS_STUN")) return S_PLAY_NIGHTS_STUN;
|
||||
else if (startswith(name, "NIGHTS_PULL")) return S_PLAY_NIGHTS_PULL;
|
||||
else if (startswith(name, "NIGHTS_ATTACK")) return S_PLAY_NIGHTS_ATTACK;
|
||||
else return S_NULL;
|
||||
}
|
||||
|
||||
static void CacheCustomSkinStates(skin_t *skin)
|
||||
{
|
||||
SIMPLEHASH_CLEAR(skin->defaulttocustomstate, hashentry_int32_int32_t)
|
||||
SIMPLEHASH_CLEAR(skin->customtodefaultstate, hashentry_int32_int32_t)
|
||||
|
||||
char *skinstateprefix = va("SKIN_%s_", skin->name);
|
||||
strupr(skinstateprefix);
|
||||
size_t skinstateprefixlen = strlen(skinstateprefix);
|
||||
|
||||
for (INT32 state = S_FIRSTFREESLOT; state <= S_LASTFREESLOT; state++)
|
||||
{
|
||||
const char *statename = FREE_STATES[state - S_FIRSTFREESLOT];
|
||||
|
||||
if (!statename)
|
||||
continue;
|
||||
if (strncmp(statename, skinstateprefix, skinstateprefixlen))
|
||||
continue;
|
||||
|
||||
statenum_t defaultstate = GetCanonicalPlayerStateNumByName(&statename[skinstateprefixlen]);
|
||||
if (defaultstate)
|
||||
{
|
||||
CONS_Printf("custom skin state: %s => %s\n", &statename[skinstateprefixlen], statename);
|
||||
|
||||
// If a default state is overriden by multiple custom states, use the first one as reference
|
||||
// e.g. WALK+WALK2+WALK3+WALK4 instead of just WALK
|
||||
statenum_t alreadyoverriden;
|
||||
SIMPLEHASH_FIND_INT(skin->defaulttocustomstate, hashentry_int32_int32_t, defaultstate, S_NULL, alreadyoverriden)
|
||||
if (!alreadyoverriden)
|
||||
SIMPLEHASH_REPLACE_INT(skin->defaulttocustomstate, hashentry_int32_int32_t, defaultstate, state)
|
||||
|
||||
SIMPLEHASH_REPLACE_INT(skin->customtodefaultstate, hashentry_int32_int32_t, state, defaultstate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Find skin sprites, sounds & optional status bar face, & add them
|
||||
//
|
||||
|
@ -927,6 +1012,8 @@ next_token:
|
|||
|
||||
R_FlushTranslationColormapCache();
|
||||
|
||||
CacheCustomSkinStates(skin);
|
||||
|
||||
if (mainfile == false)
|
||||
CONS_Printf(M_GetText("Added skin '%s'\n"), skin->name);
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "r_patch.h"
|
||||
#include "r_picformats.h" // spriteinfo_t
|
||||
#include "r_defs.h" // spritedef_t
|
||||
#include "simple_hashmap.h"
|
||||
|
||||
/// Defaults
|
||||
#define SKINNAMESIZE 16
|
||||
|
@ -88,6 +89,9 @@ typedef struct
|
|||
spritedef_t sprites[NUMPLAYERSPRITES];
|
||||
spriteinfo_t sprinfo[NUMPLAYERSPRITES];
|
||||
} super;
|
||||
|
||||
hashentry_int32_int32_t *defaulttocustomstate; // e.g. S_PLAY_WALK => S_SKIN_BIGTHECAT_WALK
|
||||
hashentry_int32_int32_t *customtodefaultstate; // e.g. S_SKIN_BIGTHECAT_WALK => S_PLAY_WALK
|
||||
} skin_t;
|
||||
|
||||
/// Externs
|
||||
|
|
58
src/simple_hashmap.h
Normal file
58
src/simple_hashmap.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
// SONIC ROBO BLAST 2
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2024 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 simplehash.h
|
||||
/// \brief Macros for handling basic hashmap types
|
||||
|
||||
#ifndef __SIMPLEHASH__
|
||||
#define __SIMPLEHASH__
|
||||
|
||||
#include "uthash.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
INT32 k;
|
||||
INT32 v;
|
||||
UT_hash_handle hh;
|
||||
} hashentry_int32_int32_t;
|
||||
|
||||
// hashmap<type>[key] = value;
|
||||
#define SIMPLEHASH_REPLACE_INT(hashmap, type, key, value) \
|
||||
{ \
|
||||
type *entry = malloc(sizeof(type)); \
|
||||
if (!entry) \
|
||||
I_Error("%s:%d: Out of memory\n", __func__, __LINE__); \
|
||||
entry->k = (key); \
|
||||
entry->v = (value); \
|
||||
\
|
||||
type *oldentry; \
|
||||
HASH_REPLACE_INT((hashmap), k, entry, oldentry); \
|
||||
if (oldentry) \
|
||||
free(oldentry); \
|
||||
}
|
||||
|
||||
#define SIMPLEHASH_CLEAR(hashmap, type) \
|
||||
{ \
|
||||
type *entry, *tmpentry; \
|
||||
HASH_ITER(hh, (hashmap), entry, tmpentry) \
|
||||
{ \
|
||||
HASH_DEL((hashmap), entry); \
|
||||
free(entry); \
|
||||
} \
|
||||
}
|
||||
|
||||
// value = hashmap<type>[key] or fallback;
|
||||
#define SIMPLEHASH_FIND_INT(hashmap, type, key, fallback, value) \
|
||||
{ \
|
||||
int tmpkey = (key); \
|
||||
type *entry; \
|
||||
HASH_FIND_INT((hashmap), &tmpkey, entry); \
|
||||
(value) = entry ? entry->v : (int)(fallback); \
|
||||
}
|
||||
|
||||
#endif //__SIMPLEHASH__
|
1140
src/uthash.h
Normal file
1140
src/uthash.h
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue