mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
Merge branch 'hardcode-frozen-production' into 'master'
Hardcode Frozen Production Frost Throwers See merge request KartKrew/Kart!1659
This commit is contained in:
commit
d90f0b5eed
20 changed files with 1058 additions and 43 deletions
|
|
@ -551,6 +551,16 @@ struct powerupvars_t {
|
|||
mobj_t *barrier;
|
||||
};
|
||||
|
||||
// player_t struct for Frozen Production ice cube state
|
||||
struct icecubevars_t {
|
||||
tic_t hitat; // last tic player properly touched frost
|
||||
|
||||
boolean frozen; // frozen in an ice cube
|
||||
UINT8 wiggle; // number of times player wiggled so far
|
||||
tic_t frozenat; // tic that player was frozen
|
||||
UINT8 shaketimer; // while it counts down, ice cube shakes
|
||||
};
|
||||
|
||||
// player_t struct for all alternative viewpoint variables
|
||||
struct altview_t
|
||||
{
|
||||
|
|
@ -965,6 +975,7 @@ struct player_t
|
|||
sonicloopvars_t loop;
|
||||
roundconditions_t roundconditions;
|
||||
powerupvars_t powerup;
|
||||
icecubevars_t icecube;
|
||||
|
||||
level_tally_t tally;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4795,6 +4795,39 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi
|
|||
"S_GPZ_TREETHING_B",
|
||||
"S_GPZ_TREETHING_M",
|
||||
"S_GPZ_TREETHING_S",
|
||||
|
||||
// MT_GGZFREEZETHRUSTER
|
||||
"S_GGZFREEZETHRUSTER",
|
||||
|
||||
// MT_GGZICEDUST
|
||||
"S_GGZICEDUST1",
|
||||
"S_GGZICEDUST2",
|
||||
"S_GGZICEDUST3",
|
||||
"S_GGZICEDUST4",
|
||||
"S_GGZICEDUST5",
|
||||
"S_GGZICEDUST6",
|
||||
"S_GGZICEDUST7",
|
||||
"S_GGZICEDUST8",
|
||||
"S_GGZICEDUST9",
|
||||
"S_GGZICEDUST10",
|
||||
"S_GGZICEDUST11",
|
||||
"S_GGZPARTICLE11",
|
||||
"S_GGZPARTICLE12",
|
||||
"S_GGZPARTICLE13",
|
||||
"S_GGZPARTICLE14",
|
||||
"S_GGZPARTICLE15",
|
||||
"S_GGZPARTICLE16",
|
||||
"S_GGZPARTICLE17",
|
||||
"S_GGZPARTICLE18",
|
||||
"S_GGZPARTICLE21",
|
||||
"S_GGZPARTICLE22",
|
||||
"S_GGZPARTICLE23",
|
||||
"S_GGZPARTICLE24",
|
||||
|
||||
"S_GGZICECUBE",
|
||||
|
||||
// MT_THRUSTERPART
|
||||
"S_THRUSTERPART",
|
||||
};
|
||||
|
||||
// RegEx to generate this from info.h: ^\tMT_([^,]+), --> \t"MT_\1",
|
||||
|
|
@ -6010,6 +6043,13 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t
|
|||
"MT_GPZ_TREETHING_B",
|
||||
"MT_GPZ_TREETHING_M",
|
||||
"MT_GPZ_TREETHING_S",
|
||||
|
||||
"MT_GGZFREEZETHRUSTER",
|
||||
"MT_GGZICEDUST",
|
||||
"MT_GGZICECUBE",
|
||||
"MT_GGZICESHATTER",
|
||||
"MT_SIDEWAYSFREEZETHRUSTER",
|
||||
"MT_THRUSTERPART",
|
||||
};
|
||||
|
||||
const char *const MOBJFLAG_LIST[] = {
|
||||
|
|
|
|||
199
src/info.c
199
src/info.c
|
|
@ -977,6 +977,15 @@ char sprnames[NUMSPRITES + 1][5] =
|
|||
"GPTM",
|
||||
"GPTS",
|
||||
|
||||
"GGZ1",
|
||||
"GGZ2",
|
||||
"GGZ3",
|
||||
"GGZ6",
|
||||
"GGZ7",
|
||||
"GGZ8",
|
||||
"FBTN",
|
||||
"SFTR",
|
||||
|
||||
// First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later
|
||||
"VIEW",
|
||||
};
|
||||
|
|
@ -5632,6 +5641,39 @@ state_t states[NUMSTATES] =
|
|||
{SPR_GPTB, 0, -1, {A_SetScale}, 4*FRACUNIT, 0, S_NULL}, // S_GPZ_TREETHING_B,
|
||||
{SPR_GPTM, 0, -1, {A_SetScale}, 4*FRACUNIT, 0, S_NULL}, // S_GPZ_TREETHING_M,
|
||||
{SPR_GPTS, 0, -1, {A_SetScale}, 4*FRACUNIT, 0, S_NULL}, // S_GPZ_TREETHING_S,
|
||||
|
||||
// MT_GGZFREEZETHRUSTER
|
||||
{SPR_GGZ6, 0, -1, {NULL}, 0, 0, S_GGZFREEZETHRUSTER}, // S_GGZFREEZETHRUSTER
|
||||
|
||||
// MT_GGZICEDUST
|
||||
{SPR_GGZ7, 0|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_GGZICEDUST2}, // S_GGZICEDUST1
|
||||
{SPR_GGZ7, 1|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_GGZICEDUST3}, // S_GGZICEDUST2
|
||||
{SPR_GGZ7, 2|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_GGZICEDUST4}, // S_GGZICEDUST3
|
||||
{SPR_GGZ7, 3|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_GGZICEDUST5}, // S_GGZICEDUST4
|
||||
{SPR_GGZ7, 4|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_GGZICEDUST6}, // S_GGZICEDUST5
|
||||
{SPR_GGZ7, 5|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_GGZICEDUST7}, // S_GGZICEDUST6
|
||||
{SPR_GGZ7, 6|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_GGZICEDUST8}, // S_GGZICEDUST7
|
||||
{SPR_GGZ7, 7|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_GGZICEDUST9}, // S_GGZICEDUST8
|
||||
{SPR_GGZ7, 8|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_GGZICEDUST10}, // S_GGZICEDUST9
|
||||
{SPR_GGZ7, 9|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_GGZICEDUST11}, // S_GGZICEDUST10
|
||||
{SPR_GGZ7, 10|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_NULL}, // S_GGZICEDUST11
|
||||
{SPR_GGZ1, 0|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_GGZPARTICLE12}, // S_GGZPARTICLE11
|
||||
{SPR_GGZ1, 1|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_GGZPARTICLE13}, // S_GGZPARTICLE12
|
||||
{SPR_GGZ1, 2|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_GGZPARTICLE14}, // S_GGZPARTICLE13
|
||||
{SPR_GGZ1, 3|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_GGZPARTICLE15}, // S_GGZPARTICLE14
|
||||
{SPR_GGZ1, 4|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_GGZPARTICLE16}, // S_GGZPARTICLE15
|
||||
{SPR_GGZ1, 5|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_GGZPARTICLE17}, // S_GGZPARTICLE16
|
||||
{SPR_GGZ1, 6|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_GGZPARTICLE18}, // S_GGZPARTICLE17
|
||||
{SPR_GGZ1, 7|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_GGZPARTICLE11}, // S_GGZPARTICLE18
|
||||
{SPR_GGZ2, 0|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_GGZPARTICLE22}, // S_GGZPARTICLE21
|
||||
{SPR_GGZ2, 1|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_GGZPARTICLE23}, // S_GGZPARTICLE22
|
||||
{SPR_GGZ2, 2|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_GGZPARTICLE24}, // S_GGZPARTICLE23
|
||||
{SPR_GGZ2, 3|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_GGZPARTICLE21}, // S_GGZPARTICLE24
|
||||
|
||||
{SPR_GGZ8, FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_GGZICECUBE
|
||||
|
||||
// MT_THRUSTERPART
|
||||
{SPR_SFTR, 0|FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_THRUSTERPART}, // S_THRUSTERPART
|
||||
};
|
||||
|
||||
mobjinfo_t mobjinfo[NUMMOBJTYPES] =
|
||||
|
|
@ -31919,6 +31961,163 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
|
|||
MF_NOGRAVITY|MF_RUNSPAWNFUNC, // flags
|
||||
S_NULL // raisestate
|
||||
},
|
||||
|
||||
{ // MT_GGZFREEZETHRUSTER
|
||||
691, // doomednum
|
||||
S_GGZFREEZETHRUSTER, // spawnstate
|
||||
1000, // spawnhealth
|
||||
S_NULL, // seestate
|
||||
sfx_None, // seesound
|
||||
0, // reactiontime
|
||||
sfx_None, // attacksound
|
||||
S_NULL, // painstate
|
||||
0, // painchance
|
||||
sfx_None, // painsound
|
||||
S_NULL, // meleestate
|
||||
S_NULL, // missilestate
|
||||
S_NULL, // deathstate
|
||||
S_NULL, // xdeathstate
|
||||
sfx_None, // deathsound
|
||||
0, // speed
|
||||
32*FRACUNIT, // radius
|
||||
48*FRACUNIT, // height
|
||||
0, // dispoffset
|
||||
0, // mass
|
||||
0, // damage
|
||||
sfx_None, // activesound
|
||||
MF_NOCLIPTHING|MF_NOGRAVITY, // flags
|
||||
S_NULL // raisestate
|
||||
},
|
||||
{ // MT_GGZICEDUST
|
||||
-1, // doomednum
|
||||
S_GGZICEDUST1, // spawnstate
|
||||
2, // spawnhealth
|
||||
S_NULL, // seestate
|
||||
sfx_None, // seesound
|
||||
0, // reactiontime
|
||||
sfx_None, // attacksound
|
||||
S_NULL, // painstate
|
||||
0, // painchance
|
||||
sfx_None, // painsound
|
||||
S_NULL, // meleestate
|
||||
S_NULL, // missilestate
|
||||
S_NULL, // deathstate
|
||||
S_NULL, // xdeathstate
|
||||
sfx_None, // deathsound
|
||||
0, // speed
|
||||
32*FRACUNIT, // radius
|
||||
16*FRACUNIT, // height
|
||||
0, // dispoffset
|
||||
0, // mass
|
||||
0, // damage
|
||||
sfx_None, // activesound
|
||||
MF_SPECIAL|MF_NOSQUISH|MF_NOGRAVITY, // flags
|
||||
S_NULL // raisestate
|
||||
},
|
||||
{ // MT_GGZICECUBE
|
||||
-1, // doomednum
|
||||
S_GGZICECUBE, // spawnstate
|
||||
1000, // spawnhealth
|
||||
S_NULL, // seestate
|
||||
sfx_None, // seesound
|
||||
0, // reactiontime
|
||||
sfx_None, // attacksound
|
||||
S_NULL, // painstate
|
||||
0, // painchance
|
||||
sfx_None, // painsound
|
||||
S_NULL, // meleestate
|
||||
S_NULL, // missilestate
|
||||
S_NULL, // deathstate
|
||||
S_NULL, // xdeathstate
|
||||
sfx_None, // deathsound
|
||||
0, // speed
|
||||
16*FRACUNIT, // radius
|
||||
16*FRACUNIT, // height
|
||||
0, // dispoffset
|
||||
0, // mass
|
||||
0, // damage
|
||||
sfx_None, // activesound
|
||||
MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPTHING|MF_NOCLIPHEIGHT|MF_SCENERY|MF_NOSQUISH, // flags
|
||||
S_NULL // raisestate
|
||||
},
|
||||
{ // MT_GGZICESHATTER
|
||||
-1, // doomednum
|
||||
S_INVISIBLE, // spawnstate
|
||||
1000, // spawnhealth
|
||||
S_NULL, // seestate
|
||||
sfx_None, // seesound
|
||||
0, // reactiontime
|
||||
sfx_None, // attacksound
|
||||
S_NULL, // painstate
|
||||
0, // painchance
|
||||
sfx_None, // painsound
|
||||
S_NULL, // meleestate
|
||||
S_NULL, // missilestate
|
||||
S_NULL, // deathstate
|
||||
S_NULL, // xdeathstate
|
||||
sfx_None, // deathsound
|
||||
0, // speed
|
||||
32*FRACUNIT, // radius
|
||||
64*FRACUNIT, // height
|
||||
0, // dispoffset
|
||||
0, // mass
|
||||
0, // damage
|
||||
sfx_None, // activesound
|
||||
MF_NOCLIPHEIGHT|MF_NOSQUISH, // flags
|
||||
S_NULL // raisestate
|
||||
},
|
||||
{ // MT_SIDEWAYSFREEZETHRUSTER
|
||||
693, // doomednum
|
||||
S_INVISIBLE, // spawnstate
|
||||
1, // spawnhealth
|
||||
S_NULL, // seestate
|
||||
sfx_None, // seesound
|
||||
0, // reactiontime
|
||||
sfx_None, // attacksound
|
||||
S_NULL, // painstate
|
||||
0, // painchance
|
||||
sfx_None, // painsound
|
||||
S_NULL, // meleestate
|
||||
S_NULL, // missilestate
|
||||
S_NULL, // deathstate
|
||||
S_NULL, // xdeathstate
|
||||
sfx_None, // deathsound
|
||||
0, // speed
|
||||
16*FRACUNIT, // radius
|
||||
32*FRACUNIT, // height
|
||||
0, // dispoffset
|
||||
0, // mass
|
||||
0, // damage
|
||||
sfx_None, // activesound
|
||||
MF_NOGRAVITY, // flags
|
||||
S_NULL // raisestate
|
||||
},
|
||||
{ // MT_THRUSTERPART
|
||||
-1, // doomednum
|
||||
S_THRUSTERPART, // spawnstate
|
||||
1, // spawnhealth
|
||||
S_NULL, // seestate
|
||||
sfx_None, // seesound
|
||||
0, // reactiontime
|
||||
sfx_None, // attacksound
|
||||
S_NULL, // painstate
|
||||
0, // painchance
|
||||
sfx_None, // painsound
|
||||
S_NULL, // meleestate
|
||||
S_NULL, // missilestate
|
||||
S_NULL, // deathstate
|
||||
S_NULL, // xdeathstate
|
||||
sfx_None, // deathsound
|
||||
0, // speed
|
||||
16*FRACUNIT, // radius
|
||||
32*FRACUNIT, // height
|
||||
0, // dispoffset
|
||||
0, // mass
|
||||
0, // damage
|
||||
sfx_None, // activesound
|
||||
MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_SCENERY|MF_NOCLIPTHING, // flags
|
||||
S_NULL // raisestate
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
49
src/info.h
49
src/info.h
|
|
@ -1531,6 +1531,15 @@ typedef enum sprite
|
|||
SPR_GPTM,
|
||||
SPR_GPTS,
|
||||
|
||||
SPR_GGZ1,
|
||||
SPR_GGZ2,
|
||||
SPR_GGZ3,
|
||||
SPR_GGZ6,
|
||||
SPR_GGZ7,
|
||||
SPR_GGZ8,
|
||||
SPR_FBTN,
|
||||
SPR_SFTR,
|
||||
|
||||
// First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later
|
||||
SPR_VIEW,
|
||||
|
||||
|
|
@ -6057,6 +6066,39 @@ typedef enum state
|
|||
S_GPZ_TREETHING_M,
|
||||
S_GPZ_TREETHING_S,
|
||||
|
||||
// MT_GGZFREEZETHRUSTER
|
||||
S_GGZFREEZETHRUSTER,
|
||||
|
||||
// MT_GGZICEDUST
|
||||
S_GGZICEDUST1,
|
||||
S_GGZICEDUST2,
|
||||
S_GGZICEDUST3,
|
||||
S_GGZICEDUST4,
|
||||
S_GGZICEDUST5,
|
||||
S_GGZICEDUST6,
|
||||
S_GGZICEDUST7,
|
||||
S_GGZICEDUST8,
|
||||
S_GGZICEDUST9,
|
||||
S_GGZICEDUST10,
|
||||
S_GGZICEDUST11,
|
||||
S_GGZPARTICLE11,
|
||||
S_GGZPARTICLE12,
|
||||
S_GGZPARTICLE13,
|
||||
S_GGZPARTICLE14,
|
||||
S_GGZPARTICLE15,
|
||||
S_GGZPARTICLE16,
|
||||
S_GGZPARTICLE17,
|
||||
S_GGZPARTICLE18,
|
||||
S_GGZPARTICLE21,
|
||||
S_GGZPARTICLE22,
|
||||
S_GGZPARTICLE23,
|
||||
S_GGZPARTICLE24,
|
||||
|
||||
S_GGZICECUBE,
|
||||
|
||||
// MT_THRUSTERPART
|
||||
S_THRUSTERPART,
|
||||
|
||||
S_FIRSTFREESLOT,
|
||||
S_LASTFREESLOT = S_FIRSTFREESLOT + NUMSTATEFREESLOTS - 1,
|
||||
NUMSTATES
|
||||
|
|
@ -7292,6 +7334,13 @@ typedef enum mobj_type
|
|||
MT_GPZ_TREETHING_M,
|
||||
MT_GPZ_TREETHING_S,
|
||||
|
||||
MT_GGZFREEZETHRUSTER,
|
||||
MT_GGZICEDUST,
|
||||
MT_GGZICECUBE,
|
||||
MT_GGZICESHATTER,
|
||||
MT_SIDEWAYSFREEZETHRUSTER,
|
||||
MT_THRUSTERPART,
|
||||
|
||||
MT_FIRSTFREESLOT,
|
||||
MT_LASTFREESLOT = MT_FIRSTFREESLOT + NUMMOBJFREESLOTS - 1,
|
||||
NUMMOBJTYPES
|
||||
|
|
|
|||
34
src/k_kart.c
34
src/k_kart.c
|
|
@ -1168,7 +1168,8 @@ static void K_UpdateOffroad(player_t *player)
|
|||
fixed_t offroadstrength = 0;
|
||||
|
||||
// If tiregrease is active, don't
|
||||
if (player->tiregrease == 0)
|
||||
// If inside an ice cube, don't
|
||||
if (player->tiregrease == 0 && player->icecube.frozen == false)
|
||||
{
|
||||
// TODO: Make this use actual special touch code.
|
||||
if (terrain != NULL && terrain->offroad > 0)
|
||||
|
|
@ -3599,6 +3600,7 @@ SINT8 K_GetForwardMove(player_t *player)
|
|||
}
|
||||
|
||||
if (player->spinouttimer != 0
|
||||
|| player->icecube.frozen
|
||||
|| K_PressingEBrake(player) == true
|
||||
|| K_PlayerEBrake(player) == true)
|
||||
{
|
||||
|
|
@ -8865,6 +8867,15 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
|
|||
K_HandleDelayedHitByEm(player);
|
||||
|
||||
player->pflags &= ~PF_POINTME;
|
||||
|
||||
if (player->icecube.frozen && player->icecube.shaketimer)
|
||||
{
|
||||
player->mo->sprxoff += P_RandomRange(PR_DECORATION, -4, 4) * player->mo->scale;
|
||||
player->mo->spryoff += P_RandomRange(PR_DECORATION, -4, 4) * player->mo->scale;
|
||||
player->mo->sprzoff += P_RandomRange(PR_DECORATION, -4, 4) * player->mo->scale;
|
||||
|
||||
player->icecube.shaketimer--;
|
||||
}
|
||||
}
|
||||
|
||||
void K_KartResetPlayerColor(player_t *player)
|
||||
|
|
@ -8953,12 +8964,17 @@ void K_KartResetPlayerColor(player_t *player)
|
|||
fullbright = true;
|
||||
goto finalise;
|
||||
}
|
||||
else
|
||||
|
||||
if (player->icecube.frozen)
|
||||
{
|
||||
player->mo->colorized = (player->dye != 0);
|
||||
player->mo->color = player->dye ? player->dye : player->skincolor;
|
||||
player->mo->colorized = true;
|
||||
player->mo->color = SKINCOLOR_CYAN;
|
||||
goto finalise;
|
||||
}
|
||||
|
||||
player->mo->colorized = (player->dye != 0);
|
||||
player->mo->color = player->dye ? player->dye : player->skincolor;
|
||||
|
||||
finalise:
|
||||
|
||||
if (player->curshield && player->curshield != KSHIELD_TOP)
|
||||
|
|
@ -11233,6 +11249,11 @@ void K_AdjustPlayerFriction(player_t *player)
|
|||
player->mo->friction -= 9824;
|
||||
}
|
||||
|
||||
if (player->icecube.frozen)
|
||||
{
|
||||
player->mo->friction = FRACUNIT;
|
||||
}
|
||||
|
||||
// Cap between intended values
|
||||
if (player->mo->friction > FRACUNIT)
|
||||
player->mo->friction = FRACUNIT;
|
||||
|
|
@ -12643,6 +12664,11 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
else
|
||||
player->turbine--; // acts as a cooldown
|
||||
}
|
||||
|
||||
if (player->icecube.frozen)
|
||||
{
|
||||
Obj_IceCubeInput(player);
|
||||
}
|
||||
}
|
||||
|
||||
void K_CheckSpectateStatus(boolean considermapreset)
|
||||
|
|
|
|||
|
|
@ -303,6 +303,16 @@ void Obj_GPZSeasawSpawn(mobj_t *mo);
|
|||
void Obj_GPZSeasawThink(mobj_t *mo);
|
||||
void Obj_GPZSeasawCollide(mobj_t *mo, mobj_t *mo2);
|
||||
|
||||
/* Frozen Production Frost Thrower */
|
||||
void Obj_FreezeThrusterInit(mobj_t *mobj);
|
||||
void Obj_FreezeThrusterThink(mobj_t *mobj);
|
||||
void Obj_IceDustCollide(mobj_t *t1, mobj_t *t2);
|
||||
boolean Obj_IceCubeThink(mobj_t *mobj);
|
||||
void Obj_IceCubeInput(player_t *player);
|
||||
void Obj_IceCubeBurst(player_t *player);
|
||||
void Obj_SidewaysFreezeThrusterInit(mobj_t *mobj);
|
||||
void Obj_SidewaysFreezeThrusterThink(mobj_t *mobj);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -83,6 +83,8 @@ typedef enum
|
|||
|
||||
PR_FUZZ, // Stability testing
|
||||
|
||||
PR_FROSTTHROWERS,
|
||||
|
||||
PRNUMSYNCED,
|
||||
|
||||
PR_INTERPHUDRANDOM = PRNUMSYNCED, // Interpolation-accomodating HUD randomisation
|
||||
|
|
|
|||
195
src/mobj.hpp
Normal file
195
src/mobj.hpp
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
// DR. ROBOTNIK'S RING RACERS
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2023 by James Robert Roman
|
||||
//
|
||||
// This program is free software distributed under the
|
||||
// terms of the GNU General Public License, version 2.
|
||||
// See the 'LICENSE' file for more details.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef mobj_hpp
|
||||
#define mobj_hpp
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "math/fixed.hpp"
|
||||
#include "math/vec.hpp"
|
||||
|
||||
#include "info.h"
|
||||
#include "p_local.h"
|
||||
#include "p_mobj.h"
|
||||
#include "s_sound.h"
|
||||
#include "sounds.h"
|
||||
#include "typedef.h"
|
||||
|
||||
namespace srb2
|
||||
{
|
||||
|
||||
struct Mobj : mobj_t
|
||||
{
|
||||
// TODO: Vec3 would be nice
|
||||
struct PosArg
|
||||
{
|
||||
math::Fixed x, y, z;
|
||||
|
||||
PosArg() : PosArg(0, 0, 0) {}
|
||||
PosArg(fixed_t x_, fixed_t y_, fixed_t z_) : x(x_), y(y_), z(z_) {}
|
||||
|
||||
template <typename T>
|
||||
PosArg(math::Vec2<T> p, fixed_t z) : PosArg(p.x, p.y, z) {}
|
||||
|
||||
PosArg(const mobj_t* mobj) : PosArg(mobj->x, mobj->y, mobj->z) {}
|
||||
};
|
||||
|
||||
// ManagedPtr(mobj_t::target); wrapper around a reference
|
||||
// counted mobj pointer. Assigning the wrapper updates
|
||||
// the original pointer and performs reference counting.
|
||||
struct ManagedPtr
|
||||
{
|
||||
ManagedPtr(mobj_t*& ref) : ref_(ref) {}
|
||||
ManagedPtr& operator=(mobj_t* ptr)
|
||||
{
|
||||
P_SetTarget(&ref_, ptr);
|
||||
return *this;
|
||||
}
|
||||
operator mobj_t*() const { return ref_; }
|
||||
|
||||
private:
|
||||
mobj_t*& ref_;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Validity checks
|
||||
//
|
||||
|
||||
static bool valid(const Mobj* mobj) { return !P_MobjWasRemoved(mobj); } // safe for nullptr
|
||||
bool valid() const { return Mobj::valid(this); }
|
||||
|
||||
void remove() { P_RemoveMobj(this); }
|
||||
|
||||
|
||||
//
|
||||
// Spawning
|
||||
//
|
||||
|
||||
// Mobj_t::spawn; spawn Mobj at position. Mobj inherits map defaults (scale, gravity).
|
||||
template <typename T>
|
||||
static T* spawn(const PosArg& p, mobjtype_t type) { return static_cast<T*>(P_SpawnMobj(p.x, p.y, p.z, type)); }
|
||||
|
||||
// this->spawn_from; spawn Mobj relative to parent (this->pos + p). Mobj inherits parent scale, gravity.
|
||||
template <typename T>
|
||||
T* spawn_from(const PosArg& p, mobjtype_t type)
|
||||
{
|
||||
return static_cast<T*>(P_SpawnMobjFromMobjUnscaled(this, p.x, p.y, p.z, type));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* spawn_from(mobjtype_t type) { return spawn_from<T>({}, type); }
|
||||
|
||||
// TODO: ghosts have unique properties, add Ghost class
|
||||
Mobj* spawn_ghost() { return static_cast<Mobj*>(P_SpawnGhostMobj(this)); }
|
||||
|
||||
|
||||
//
|
||||
// Position
|
||||
//
|
||||
|
||||
PosArg center() const { return {x, y, z + (height / 2)}; }
|
||||
PosArg pos() const { return {x, y, z}; }
|
||||
math::Vec2<math::Fixed> pos2d() const { return {x, y}; }
|
||||
math::Fixed top() const { return z + height; }
|
||||
|
||||
bool is_flipped() const { return eflags & MFE_VERTICALFLIP; }
|
||||
math::Fixed flip(fixed_t x) const { return x * P_MobjFlip(this); }
|
||||
|
||||
// Collision helper
|
||||
bool z_overlaps(const Mobj* b) const { return z < b->top() && b->z < top(); }
|
||||
|
||||
void move_origin(const PosArg& p) { P_MoveOrigin(this, p.x, p.y, p.z); }
|
||||
void set_origin(const PosArg& p) { P_SetOrigin(this, p.x, p.y, p.z); }
|
||||
void instathrust(angle_t angle, fixed_t speed) { P_InstaThrust(this, angle, speed); }
|
||||
void thrust(angle_t angle, fixed_t speed) { P_Thrust(this, angle, speed); }
|
||||
|
||||
|
||||
//
|
||||
// Mobj pointers
|
||||
//
|
||||
|
||||
#define MOBJ_PTR_METHOD(member) \
|
||||
template <typename T = Mobj>\
|
||||
T* member() const { return static_cast<T*>(mobj_t::member); }\
|
||||
template <typename T>\
|
||||
void member(T* n) { ManagedPtr(this->mobj_t::member) = n; }
|
||||
|
||||
MOBJ_PTR_METHOD(hnext)
|
||||
MOBJ_PTR_METHOD(hprev)
|
||||
MOBJ_PTR_METHOD(itnext)
|
||||
MOBJ_PTR_METHOD(target)
|
||||
MOBJ_PTR_METHOD(tracer)
|
||||
MOBJ_PTR_METHOD(punt_ref)
|
||||
MOBJ_PTR_METHOD(owner)
|
||||
|
||||
#undef MOBJ_PTR_METHOD
|
||||
|
||||
|
||||
//
|
||||
// State
|
||||
//
|
||||
|
||||
struct State : state_t
|
||||
{
|
||||
statenum_t num() const { return static_cast<statenum_t>(static_cast<const state_t*>(this) - states); }
|
||||
};
|
||||
|
||||
void state(statenum_t state) { P_SetMobjState(this, state); }
|
||||
const State* state() const { return static_cast<const State*>(mobj_t::state); }
|
||||
|
||||
|
||||
//
|
||||
// Scale
|
||||
//
|
||||
|
||||
math::Fixed scale() const { return mobj_t::scale; }
|
||||
|
||||
void scale(fixed_t n)
|
||||
{
|
||||
mobj_t::scale = n;
|
||||
mobj_t::destscale = n;
|
||||
}
|
||||
|
||||
void scale_to(fixed_t stop, std::optional<fixed_t> speed = {})
|
||||
{
|
||||
mobj_t::destscale = stop;
|
||||
|
||||
if (speed)
|
||||
{
|
||||
mobj_t::scalespeed = *speed;
|
||||
}
|
||||
}
|
||||
|
||||
void scale_between(fixed_t start, fixed_t stop, std::optional<fixed_t> speed = {})
|
||||
{
|
||||
mobj_t::scale = start;
|
||||
scale_to(stop, speed);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Sound
|
||||
//
|
||||
|
||||
void voice(sfxenum_t sfx, int volume = 255) const { S_StartSoundAtVolume(this, sfx, volume); }
|
||||
bool voice_playing(sfxenum_t sfx) const { return S_SoundPlaying(this, sfx); }
|
||||
void voice_loop(sfxenum_t sfx, int volume = 255) const
|
||||
{
|
||||
if (!voice_playing(sfx))
|
||||
{
|
||||
voice(sfx, volume);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}; // namespace srb2
|
||||
|
||||
#endif/*mobj_hpp*/
|
||||
|
|
@ -42,6 +42,7 @@ target_sources(SRB2SDL2 PRIVATE
|
|||
ball-switch.cpp
|
||||
charge.c
|
||||
mega-barrier.cpp
|
||||
frost-thrower.cpp
|
||||
)
|
||||
|
||||
add_subdirectory(versus)
|
||||
|
|
|
|||
451
src/objects/frost-thrower.cpp
Normal file
451
src/objects/frost-thrower.cpp
Normal file
|
|
@ -0,0 +1,451 @@
|
|||
// DR. ROBOTNIK'S RING RACERS
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2023 by Kart Krew.
|
||||
//
|
||||
// This program is free software distributed under the
|
||||
// terms of the GNU General Public License, version 2.
|
||||
// See the 'LICENSE' file for more details.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Original Lua script by Lat
|
||||
// Hardcoded by jartha
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "../math/fixed.hpp"
|
||||
#include "../math/vec.hpp"
|
||||
#include "../mobj.hpp"
|
||||
|
||||
#include "../d_player.h"
|
||||
#include "../doomdef.h"
|
||||
#include "../doomstat.h"
|
||||
#include "../k_bot.h" // FIXME
|
||||
#include "../k_kart.h"
|
||||
#include "../k_objects.h"
|
||||
#include "../m_random.h"
|
||||
#include "../sounds.h"
|
||||
#include "../tables.h"
|
||||
|
||||
using srb2::Mobj;
|
||||
using srb2::math::Fixed;
|
||||
using srb2::math::Vec2;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
Vec2<Fixed> angle_vector(angle_t x)
|
||||
{
|
||||
return Vec2<Fixed> {FCOS(x), FSIN(x)};
|
||||
}
|
||||
|
||||
struct IceCube : Mobj
|
||||
{
|
||||
static constexpr int kRadius = 33;
|
||||
|
||||
void extravalue1() = delete;
|
||||
angle_t angle_offset() const { return mobj_t::extravalue1; }
|
||||
void angle_offset(angle_t n) { mobj_t::extravalue1 = n; }
|
||||
|
||||
static void spawn_cube(Mobj* source)
|
||||
{
|
||||
angle_t offset = 0;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
spawn_side(source, offset);
|
||||
offset += ANGLE_90;
|
||||
}
|
||||
}
|
||||
|
||||
bool valid() const { return Mobj::valid() && Mobj::valid(owner()) && player() && player()->icecube.frozen; }
|
||||
player_t* player() const { return owner()->player; }
|
||||
|
||||
bool think()
|
||||
{
|
||||
if (!valid())
|
||||
{
|
||||
remove();
|
||||
return false;
|
||||
}
|
||||
|
||||
angle_t facing = player()->drawangle + angle_offset();
|
||||
|
||||
scale(owner()->scale());
|
||||
move_origin({owner()->pos2d() + (vector(facing) * scale()), owner()->z});
|
||||
frame = state()->frame + player()->icecube.wiggle;
|
||||
angle = facing + ANGLE_90;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
static Vec2<Fixed> vector(angle_t x) { return angle_vector(x) * kRadius; }
|
||||
|
||||
static IceCube* spawn_side(Mobj* source, angle_t offset)
|
||||
{
|
||||
angle_t facing = source->angle + offset;
|
||||
IceCube* cube = source->spawn_from<IceCube>({vector(facing) * source->scale(), 0}, MT_GGZICECUBE);
|
||||
cube->angle_offset(offset);
|
||||
cube->angle = facing + ANGLE_90;
|
||||
cube->owner(source);
|
||||
return cube;
|
||||
}
|
||||
};
|
||||
|
||||
struct Frost : Mobj
|
||||
{
|
||||
void extravalue1() = delete;
|
||||
bool skip_invincible_checks() const { return mobj_t::extravalue1; }
|
||||
void skip_invincible_checks(bool n) { mobj_t::extravalue1 = n; }
|
||||
|
||||
static Frost* spawn(Mobj* source, const Mobj::PosArg& p)
|
||||
{
|
||||
Frost* frost = source->spawn_from<Frost>(p, MT_GGZICEDUST);
|
||||
|
||||
auto rng = [] { return P_RandomRange(PR_FROSTTHROWERS, -2, 2) * (4 * mapobjectscale); };
|
||||
|
||||
frost->momx = rng();
|
||||
frost->momy = rng();
|
||||
frost->scale_between(mapobjectscale / 2, mapobjectscale * 2, mapobjectscale / 6);
|
||||
|
||||
return frost;
|
||||
}
|
||||
|
||||
void collide(Mobj* mo) const
|
||||
{
|
||||
player_t* p = mo->player;
|
||||
icecubevars_t& vars = p->icecube;
|
||||
|
||||
if (vars.hitat < leveltime && leveltime - vars.hitat < TICRATE)
|
||||
{
|
||||
// avoid spamming instashield
|
||||
return;
|
||||
}
|
||||
|
||||
vars.hitat = leveltime;
|
||||
|
||||
if (!skip_invincible_checks())
|
||||
{
|
||||
if (p->flashing || p->growshrinktimer > 0 || p->invincibilitytimer || p->hyudorotimer)
|
||||
{
|
||||
K_DoInstashield(p);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (vars.frozen)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
vars.frozen = true;
|
||||
vars.wiggle = 0;
|
||||
vars.frozenat = leveltime;
|
||||
vars.shaketimer = 0;
|
||||
|
||||
IceCube::spawn_cube(mo);
|
||||
}
|
||||
};
|
||||
|
||||
struct FreezeThruster : Mobj
|
||||
{
|
||||
static constexpr tic_t kFlashRate = TICRATE*3/2;
|
||||
|
||||
void extravalue1() = delete;
|
||||
bool frosting() const { return mobj_t::extravalue1; }
|
||||
void frosting(bool n) { mobj_t::extravalue1 = n; }
|
||||
|
||||
void extravalue2() = delete;
|
||||
tic_t default_timer() const { return mobj_t::extravalue2; }
|
||||
void default_timer(tic_t n) { mobj_t::extravalue2 = n; }
|
||||
|
||||
void threshold() = delete;
|
||||
tic_t timer() const { return mobj_t::threshold; }
|
||||
void timer(tic_t n) { mobj_t::threshold = n; }
|
||||
|
||||
void thing_args() = delete;
|
||||
bool double_speed() const { return mobj_t::thing_args[0]; }
|
||||
bool skip_invincible_checks() const { return mobj_t::thing_args[1]; }
|
||||
bool default_frosting() const { return !mobj_t::thing_args[2]; }
|
||||
|
||||
void init()
|
||||
{
|
||||
// FIXME: this should use a thing arg, not angle!
|
||||
if (spawnpoint)
|
||||
{
|
||||
// Object Angle = Tics Between Activations
|
||||
default_timer(spawnpoint->angle);
|
||||
}
|
||||
|
||||
frosting(default_timer() ? default_frosting() : true);
|
||||
timer(default_timer());
|
||||
}
|
||||
|
||||
void think()
|
||||
{
|
||||
if (timer())
|
||||
{
|
||||
timer(timer() - 1);
|
||||
|
||||
if (timer() < 1)
|
||||
{
|
||||
frosting(!frosting());
|
||||
timer(default_timer());
|
||||
}
|
||||
}
|
||||
|
||||
// flash before turning on
|
||||
if (!frosting() && timer() < kFlashRate && (leveltime % 4) < 2)
|
||||
{
|
||||
colorized = true;
|
||||
color = SKINCOLOR_RED;
|
||||
}
|
||||
else
|
||||
{
|
||||
colorized = false;
|
||||
}
|
||||
|
||||
spew();
|
||||
}
|
||||
|
||||
private:
|
||||
void spew()
|
||||
{
|
||||
if (!frosting())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Object Height = Frostthrower Speed
|
||||
fixed_t mz = is_flipped() ? floorz + 1 : ceilingz;
|
||||
fixed_t frostspeed = std::abs(z - mz) / 10;
|
||||
|
||||
if (double_speed())
|
||||
{
|
||||
frostspeed *= 2;
|
||||
}
|
||||
|
||||
if (leveltime % 3 == 0)
|
||||
{
|
||||
Frost* frost = Frost::spawn(this, {});
|
||||
|
||||
if (is_flipped())
|
||||
{
|
||||
frost->momz = -frostspeed;
|
||||
}
|
||||
else
|
||||
{
|
||||
frost->momz = frostspeed;
|
||||
frost->flags2 |= MF2_OBJECTFLIP;
|
||||
}
|
||||
|
||||
frost->skip_invincible_checks(skip_invincible_checks());
|
||||
}
|
||||
|
||||
voice_loop(sfx_s3k7f);
|
||||
}
|
||||
};
|
||||
|
||||
struct SidewaysFreezeThruster : Mobj
|
||||
{
|
||||
static constexpr int kBaseRadius = 12;
|
||||
|
||||
bool skip_invincible_checks() const { return mobj_t::thing_args[0]; }
|
||||
|
||||
void init()
|
||||
{
|
||||
scale(scale()*3/2);
|
||||
|
||||
const Vec2<Fixed> base_vector = vector(angle) * kBaseRadius;
|
||||
|
||||
angle_t an = angle + ANGLE_90;
|
||||
|
||||
z += flip(24 * mapobjectscale);
|
||||
|
||||
auto spawn_piece = [&](const Mobj::PosArg& p, angle_t angle, int frame)
|
||||
{
|
||||
Mobj* part = spawn_from<Mobj>(p, MT_THRUSTERPART);
|
||||
part->angle = angle;
|
||||
part->frame += frame;
|
||||
return part;
|
||||
};
|
||||
|
||||
spawn_piece({}, an, 0); // base
|
||||
spawn_piece({vector(angle) * 24, 0}, an, 1); // cannon
|
||||
|
||||
Vec2<Fixed> h_vector = angle_vector(an);
|
||||
|
||||
// spawn the pipes:
|
||||
auto spawn_pipe = [&](int i)
|
||||
{
|
||||
angle_t v_an = ANGLE_45 + (ANGLE_90 * i);
|
||||
Vec2<Fixed> v_vector = vector(v_an) * 32;
|
||||
spawn_piece({h_vector * v_vector.y, v_vector.x}, angle, 2);
|
||||
};
|
||||
|
||||
// This is unrolled because when it was a for loop,
|
||||
// it ran infinitely, but only under MinGW.
|
||||
// Tested: gcc.exe (Rev2, Built by MSYS2 project) 13.2.0 (32-bit version)
|
||||
spawn_pipe(0);
|
||||
spawn_pipe(1);
|
||||
spawn_pipe(2);
|
||||
spawn_pipe(3);
|
||||
|
||||
// spawn the icons:
|
||||
Vec2<Fixed> v = vector(an) * 32;
|
||||
spawn_piece({base_vector + v, 0}, angle, 3);
|
||||
spawn_piece({base_vector - v, 0}, angle, 3);
|
||||
}
|
||||
|
||||
void think()
|
||||
{
|
||||
if (leveltime % 2 == 0)
|
||||
{
|
||||
spew();
|
||||
spew();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Vec2<Fixed> vector(angle_t angle) const { return angle_vector(angle) * scale(); }
|
||||
|
||||
void spew()
|
||||
{
|
||||
Frost* frost = Frost::spawn(this, {vector(angle) * kBaseRadius, 0});
|
||||
auto rng = [](int x, int y) { return P_RandomRange(PR_FROSTTHROWERS, x, y); };
|
||||
frost->angle = angle + (rng(-8, 8) * ANG1);
|
||||
frost->instathrust(frost->angle, mapobjectscale * 64);
|
||||
frost->momz = rng(-5, 5) * mapobjectscale;
|
||||
frost->scale_between(mapobjectscale * 3 / 4, mapobjectscale * 2, mapobjectscale / 6);
|
||||
frost->skip_invincible_checks(skip_invincible_checks());
|
||||
}
|
||||
};
|
||||
|
||||
struct Shatter : Mobj
|
||||
{
|
||||
static Shatter* spawn(Mobj* source)
|
||||
{
|
||||
auto rng = [source](int x, int y) { return P_RandomRange(PR_DECORATION, x, y) * source->scale(); };
|
||||
Shatter* part = source->spawn_from<Shatter>({rng(-64, 64), rng(-64, 64), rng(0, 64)}, MT_GGZICESHATTER);
|
||||
part->state(P_RandomRange(PR_DECORATION, 0, 1) ? S_GGZPARTICLE11 : S_GGZPARTICLE21);
|
||||
part->fuse = TICRATE * 4;
|
||||
return part;
|
||||
}
|
||||
};
|
||||
|
||||
}; // namespace
|
||||
|
||||
void Obj_FreezeThrusterInit(mobj_t* mo)
|
||||
{
|
||||
static_cast<FreezeThruster*>(mo)->init();
|
||||
}
|
||||
|
||||
void Obj_FreezeThrusterThink(mobj_t* mo)
|
||||
{
|
||||
static_cast<FreezeThruster*>(mo)->think();
|
||||
}
|
||||
|
||||
void Obj_IceDustCollide(mobj_t* t1, mobj_t* t2)
|
||||
{
|
||||
static_cast<Frost*>(t1)->collide(static_cast<Mobj*>(t2));
|
||||
}
|
||||
|
||||
boolean Obj_IceCubeThink(mobj_t* mo)
|
||||
{
|
||||
return static_cast<IceCube*>(mo)->think();
|
||||
}
|
||||
|
||||
void Obj_IceCubeInput(player_t* player)
|
||||
{
|
||||
// FIXME: this should be in the bot ticcmd code
|
||||
auto bot_hack = [player]
|
||||
{
|
||||
if (!K_PlayerUsesBotMovement(player))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (leveltime % 7)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (player->sneakertimer)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!P_IsObjectOnGround(player->mo))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// Must be mashing some buttons
|
||||
auto press = [player](buttoncode_t bt) { return (player->cmd.buttons & ~player->oldcmd.buttons) & bt; };
|
||||
if (!(press(BT_ACCELERATE) || press(BT_ATTACK) || press(BT_DRIFT) || bot_hack()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (leveltime - std::min(player->icecube.frozenat, leveltime) < TICRATE*2/3)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Mobj* mo = static_cast<Mobj*>(player->mo);
|
||||
|
||||
player->icecube.wiggle++;
|
||||
player->icecube.shaketimer = 10;
|
||||
|
||||
mo->voice(sfx_s3k94);
|
||||
|
||||
auto rng = [mo](int x, int y) { return P_RandomRange(PR_DECORATION, x, y) * mo->scale(); };
|
||||
|
||||
for (int i = 0; i < 6; ++i)
|
||||
{
|
||||
Shatter* part = Shatter::spawn(mo);
|
||||
part->scale_between(mo->scale() * 3, 1);
|
||||
part->momx = mo->momx + rng(-14, 14);
|
||||
part->momy = mo->momy + rng(-14, 14);
|
||||
part->momz = mo->momz + rng(0, 14);
|
||||
}
|
||||
|
||||
if (player->icecube.wiggle > 4)
|
||||
{
|
||||
Obj_IceCubeBurst(player);
|
||||
player->flashing = (TICRATE * 3) - 1;
|
||||
}
|
||||
}
|
||||
|
||||
void Obj_IceCubeBurst(player_t* player)
|
||||
{
|
||||
Mobj* mo = static_cast<Mobj*>(player->mo);
|
||||
|
||||
auto rng = [mo](int x, int y) { return P_RandomRange(PR_DECORATION, x, y) * mo->scale(); };
|
||||
|
||||
for (int i = 0; i < 20; ++i)
|
||||
{
|
||||
Shatter* part = Shatter::spawn(mo);
|
||||
part->scale_between(mo->scale() * 5, 1);
|
||||
part->momx = (mo->momx * 2) + rng(-64, 64);
|
||||
part->momy = (mo->momy * 2) + rng(-64, 64);
|
||||
part->momz = (mo->momz * 2) + rng(0, 20);
|
||||
}
|
||||
|
||||
player->icecube.frozen = false;
|
||||
|
||||
mo->voice(sfx_glgz1);
|
||||
}
|
||||
|
||||
void Obj_SidewaysFreezeThrusterInit(mobj_t* mo)
|
||||
{
|
||||
static_cast<SidewaysFreezeThruster*>(mo)->init();
|
||||
}
|
||||
|
||||
void Obj_SidewaysFreezeThrusterThink(mobj_t* mo)
|
||||
{
|
||||
static_cast<SidewaysFreezeThruster*>(mo)->think();
|
||||
}
|
||||
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
#include <cstddef>
|
||||
|
||||
#include "../mobj.hpp"
|
||||
|
||||
#include "../doomdef.h"
|
||||
#include "../d_player.h"
|
||||
#include "../g_game.h"
|
||||
|
|
@ -23,45 +25,13 @@
|
|||
|
||||
#define barrier_player(o) ((o)->extravalue1)
|
||||
|
||||
using srb2::Mobj;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
struct Barrier;
|
||||
|
||||
// TODO: header
|
||||
struct Mobj : mobj_t
|
||||
{
|
||||
struct PosArg
|
||||
{
|
||||
fixed_t x, y, z;
|
||||
|
||||
PosArg(fixed_t x_, fixed_t y_, fixed_t z_) : x(x_), y(y_), z(z_) {}
|
||||
PosArg(const mobj_t* mobj) : x(mobj->x), y(mobj->y), z(mobj->z) {}
|
||||
};
|
||||
|
||||
static bool valid(const Mobj* mobj) { return !P_MobjWasRemoved(mobj); }
|
||||
|
||||
PosArg center() const { return {x, y, z + (height / 2)}; }
|
||||
|
||||
template <typename T>
|
||||
T* spawn_offset(mobjtype_t type) { return static_cast<T*>(P_SpawnMobjFromMobj(this, 0, 0, 0, type)); }
|
||||
|
||||
void state(statenum_t state) { P_SetMobjState(this, state); }
|
||||
statenum_t statenum() const { return static_cast<statenum_t>(mobj_t::state - states); }
|
||||
|
||||
fixed_t scale() const { return mobj_t::scale; }
|
||||
|
||||
void scale(fixed_t n)
|
||||
{
|
||||
mobj_t::scale = n;
|
||||
mobj_t::destscale = n;
|
||||
}
|
||||
|
||||
void move_origin(const PosArg& p) { P_MoveOrigin(this, p.x, p.y, p.z); }
|
||||
|
||||
void remove() { P_RemoveMobj(this); }
|
||||
};
|
||||
|
||||
struct Player : player_t
|
||||
{
|
||||
struct Powerups : powerupvars_t
|
||||
|
|
@ -87,7 +57,7 @@ struct Barrier : Mobj
|
|||
|
||||
static Barrier* spawn(Player* player, statenum_t state, int idx)
|
||||
{
|
||||
Barrier* child = player->mobj()->spawn_offset<Barrier>(MT_MEGABARRIER);
|
||||
Barrier* child = player->mobj()->spawn_from<Barrier>(MT_MEGABARRIER);
|
||||
|
||||
child->angle = player->mobj()->angle + (idx * kSpinGap);
|
||||
child->player(player);
|
||||
|
|
|
|||
|
|
@ -968,6 +968,12 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
break;
|
||||
}
|
||||
|
||||
case MT_GGZICEDUST:
|
||||
{
|
||||
Obj_IceDustCollide(special, toucher);
|
||||
return;
|
||||
}
|
||||
|
||||
default: // SOC or script pickup
|
||||
P_SetTarget(&special->target, toucher);
|
||||
break;
|
||||
|
|
|
|||
28
src/p_mobj.c
28
src/p_mobj.c
|
|
@ -6817,6 +6817,14 @@ static void P_MobjSceneryThink(mobj_t *mobj)
|
|||
}
|
||||
break;
|
||||
}
|
||||
case MT_GGZICECUBE:
|
||||
{
|
||||
if (!Obj_IceCubeThink(mobj))
|
||||
{
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MT_VWREF:
|
||||
case MT_VWREB:
|
||||
{
|
||||
|
|
@ -10204,6 +10212,16 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
|
|||
}
|
||||
break;
|
||||
}
|
||||
case MT_GGZFREEZETHRUSTER:
|
||||
{
|
||||
Obj_FreezeThrusterThink(mobj);
|
||||
break;
|
||||
}
|
||||
case MT_SIDEWAYSFREEZETHRUSTER:
|
||||
{
|
||||
Obj_SidewaysFreezeThrusterThink(mobj);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
// check mobj against possible water content, before movement code
|
||||
|
|
@ -14421,6 +14439,16 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj)
|
|||
Obj_LinkCheckpoint(mobj);
|
||||
break;
|
||||
}
|
||||
case MT_GGZFREEZETHRUSTER:
|
||||
{
|
||||
Obj_FreezeThrusterInit(mobj);
|
||||
break;
|
||||
}
|
||||
case MT_SIDEWAYSFREEZETHRUSTER:
|
||||
{
|
||||
Obj_SidewaysFreezeThrusterInit(mobj);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -771,6 +771,13 @@ static void P_NetArchivePlayers(savebuffer_t *save)
|
|||
WRITEUINT8(save->p, players[i].tally.showGrade);
|
||||
WRITEUINT8(save->p, players[i].tally.done);
|
||||
}
|
||||
|
||||
// icecubevars_t
|
||||
WRITEUINT32(save->p, players[i].icecube.hitat);
|
||||
WRITEUINT8(save->p, players[i].icecube.frozen);
|
||||
WRITEUINT8(save->p, players[i].icecube.wiggle);
|
||||
WRITEUINT32(save->p, players[i].icecube.frozenat);
|
||||
WRITEUINT8(save->p, players[i].icecube.shaketimer);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1329,6 +1336,13 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
|
|||
players[i].tally.done = (boolean)READUINT8(save->p);
|
||||
}
|
||||
|
||||
// icecubevars_t
|
||||
players[i].icecube.hitat = READUINT32(save->p);
|
||||
players[i].icecube.frozen = READUINT8(save->p);
|
||||
players[i].icecube.wiggle = READUINT8(save->p);
|
||||
players[i].icecube.frozenat = READUINT32(save->p);
|
||||
players[i].icecube.shaketimer = READUINT8(save->p);
|
||||
|
||||
//players[i].viewheight = P_GetPlayerViewHeight(players[i]); // scale cannot be factored in at this point
|
||||
}
|
||||
}
|
||||
|
|
|
|||
11
src/p_user.c
11
src/p_user.c
|
|
@ -461,7 +461,7 @@ UINT8 P_FindHighestLap(void)
|
|||
//
|
||||
boolean P_PlayerInPain(player_t *player)
|
||||
{
|
||||
if (player->spinouttimer || (player->tumbleBounces > 0) || (player->pflags & PF_FAULT))
|
||||
if (player->spinouttimer || (player->tumbleBounces > 0) || (player->pflags & PF_FAULT) || player->icecube.frozen)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
|
|
@ -2420,7 +2420,14 @@ void P_MovePlayer(player_t *player)
|
|||
}
|
||||
|
||||
// Kart frames
|
||||
if (player->tumbleBounces > 0)
|
||||
if (player->icecube.frozen)
|
||||
{
|
||||
INT32 spd = FixedMul(player->mo->scale, FixedHypot(player->mo->momx, player->mo->momy)) / FRACUNIT;
|
||||
P_SetPlayerMobjState(player->mo, S_KART_SPINOUT);
|
||||
player->drawangle -= max(2, spd / 6) * ANG1;
|
||||
P_ResetPitchRoll(player->mo);
|
||||
}
|
||||
else if (player->tumbleBounces > 0)
|
||||
{
|
||||
fixed_t playerSpeed = P_AproxDistance(player->mo->momx, player->mo->momy); // maybe momz too?
|
||||
|
||||
|
|
|
|||
|
|
@ -1090,7 +1090,7 @@ INT32 S_IdPlaying(sfxenum_t id)
|
|||
|
||||
// Searches through the channels and checks for
|
||||
// origin x playing sound id y.
|
||||
INT32 S_SoundPlaying(void *origin, sfxenum_t id)
|
||||
INT32 S_SoundPlaying(const void *origin, sfxenum_t id)
|
||||
{
|
||||
INT32 cnum;
|
||||
if (!origin)
|
||||
|
|
|
|||
|
|
@ -248,7 +248,7 @@ void S_SetMusicVolume(void);
|
|||
|
||||
INT32 S_OriginPlaying(void *origin);
|
||||
INT32 S_IdPlaying(sfxenum_t id);
|
||||
INT32 S_SoundPlaying(void *origin, sfxenum_t id);
|
||||
INT32 S_SoundPlaying(const void *origin, sfxenum_t id);
|
||||
|
||||
void S_StartSoundName(void *mo, const char *soundname);
|
||||
|
||||
|
|
|
|||
|
|
@ -1247,6 +1247,8 @@ sfxinfo_t S_sfx[NUMSFX] =
|
|||
{"befan1", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Whisking"}, // Blend Eye whisk startup
|
||||
{"befan2", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Whisking"}, // Blend Eye whisk
|
||||
|
||||
{"glgz1", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Ice Cube shatters"},
|
||||
|
||||
// Damage sounds
|
||||
{"dmga1", false, 255, 8, -1, NULL, 0, -1, -1, LUMPERROR, "Damaged"},
|
||||
{"dmga2", false, 255, 8, -1, NULL, 0, -1, -1, LUMPERROR, "Damaged"},
|
||||
|
|
|
|||
|
|
@ -1317,6 +1317,9 @@ typedef enum
|
|||
sfx_befan1,
|
||||
sfx_befan2,
|
||||
|
||||
// Ice Cube
|
||||
sfx_glgz1,
|
||||
|
||||
// Damage sounds
|
||||
sfx_dmga1,
|
||||
sfx_dmga2,
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ TYPEDEF (roundconditions_t);
|
|||
TYPEDEF (skybox_t);
|
||||
TYPEDEF (itemroulette_t);
|
||||
TYPEDEF (powerupvars_t);
|
||||
TYPEDEF (icecubevars_t);
|
||||
TYPEDEF (altview_t);
|
||||
TYPEDEF (player_t);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue