diff --git a/src/dehacked.c b/src/dehacked.c index c7561cb7c..c546f4431 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -2406,6 +2406,7 @@ static actionpointer_t actionpointers[] = {{A_SnapperSpawn}, "A_SNAPPERSPAWN"}, {{A_SnapperThinker}, "A_SNAPPERTHINKER"}, {{A_SaloonDoorSpawn}, "A_SALOONDOORSPAWN"}, + {{A_MinecartSparkThink}, "A_MINECARTSPARKTHINK"}, {{NULL}, "NONE"}, // This NULL entry must be the last in the list @@ -5786,6 +5787,18 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_ARIDDUST2", "S_ARIDDUST3", + // Minecart + "S_MINECART_IDLE", + "S_MINECART_DTH1", + "S_MINECARTEND", + "S_MINECARTSEG_FRONT", + "S_MINECARTSEG_BACK", + "S_MINECARTSEG_LEFT", + "S_MINECARTSEG_RIGHT", + "S_MINECARTSIDEMARK1", + "S_MINECARTSIDEMARK2", + "S_MINECARTSPARK", + // Saloon door "S_SALOONDOOR", "S_SALOONDOORTHINKER", @@ -7503,12 +7516,19 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_DUSTDEVIL", "MT_DUSTLAYER", "MT_ARIDDUST", + "MT_MINECART", + "MT_MINECARTSPAWNER", + "MT_MINECARTEND", + "MT_MINECARTENDSOLID", + "MT_MINECARTSIDEMARK", + "MT_MINECARTSPARK", "MT_SALOONDOOR", "MT_SALOONDOORTHINKER", "MT_TRAINCAMEOSPAWNER", "MT_TRAINSEG", "MT_TRAINDUSTSPAWNER", "MT_TRAINSTEAMSPAWNER", + "MT_MINECARTSWITCHPOINT", // Red Volcano Scenery "MT_FLAMEJET", diff --git a/src/hardware/hw_light.c b/src/hardware/hw_light.c index 83fe8f16a..8baa547af 100644 --- a/src/hardware/hw_light.c +++ b/src/hardware/hw_light.c @@ -361,6 +361,8 @@ light_t *t_lspr[NUMSPRITES] = &lspr[NOLIGHT], // SPR_REMT &lspr[NOLIGHT], // SPR_TAZD &lspr[NOLIGHT], // SPR_ADST + &lspr[NOLIGHT], // SPR_MCRT + &lspr[NOLIGHT], // SPR_MCSP &lspr[NOLIGHT], // SPR_NON2 &lspr[NOLIGHT], // SPR_SALD &lspr[NOLIGHT], // SPR_TRAE diff --git a/src/info.c b/src/info.c index 2716baddb..eb170a1fe 100644 --- a/src/info.c +++ b/src/info.c @@ -256,6 +256,8 @@ char sprnames[NUMSPRITES + 1][5] = "REMT", // TNT proximity shell "TAZD", // Dust devil "ADST", // Arid dust + "MCRT", // Minecart + "MCSP", // Minecart spark "NON2", // Saloon door thinker "SALD", // Saloon door "TRAE", // Train cameo locomotive @@ -2425,6 +2427,18 @@ state_t states[NUMSTATES] = {SPR_ADST, 3|FF_ANIMATE, 24, {NULL}, 3, 8, S_NULL}, // S_ARIDDUST2 {SPR_ADST, 6|FF_ANIMATE, 24, {NULL}, 3, 8, S_NULL}, // S_ARIDDUST3 + // Minecart + {SPR_NULL, 0, 1, {NULL}, 0, 0, S_MINECART_IDLE}, // S_MINECART_IDLE + {SPR_NULL, 0, 45, {NULL}, 0, 0, S_NULL}, // S_MINECART_DTH1 + {SPR_MCRT, 8|FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_MINECARTEND + {SPR_MCRT, 0|FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_MINECARTSEG_FRONT + {SPR_MCRT, 1|FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_MINECARTSEG_BACK + {SPR_MCRT, 2|FF_PAPERSPRITE, -1, {NULL}, 2, 3, S_NULL}, // S_MINECARTSEG_LEFT + {SPR_MCRT, 5|FF_PAPERSPRITE, -1, {NULL}, 2, 3, S_NULL}, // S_MINECARTSEG_RIGHT + {SPR_LCKN, 2|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL}, // S_MINECARTSIDEMARK1 + {SPR_LCKN, 0|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL}, // S_MINECARTSIDEMARK2 + {SPR_MCSP, FF_FULLBRIGHT, 1, {A_MinecartSparkThink}, 0, 0, S_MINECARTSPARK}, // S_MINECARTSPARK + // Saloon door {SPR_SALD, 0|FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_SALOONDOOR {SPR_NON2, 0, -1, {A_SaloonDoorSpawn}, 0, 0, S_NULL}, // S_SALONDOORTHINKER @@ -11840,6 +11854,168 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_MINECART + -1, // doomednum + S_MINECART_IDLE,// spawnstate + 1, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 24*FRACUNIT, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_statu2, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_MINECART_DTH1,// deathstate + S_NULL, // xdeathstate + sfx_s3k59, // deathsound + 20*FRACUNIT, // speed + 22*FRACUNIT, // radius + 32*FRACUNIT, // height + 0, // display offset + 100, // mass + 0, // damage + sfx_s3k76, // activesound + MF_PUSHABLE, // flags + MT_MINECARTSIDEMARK // raisestate + }, + + { // MT_MINECARTSPAWNER + 1219, // 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 + 22*FRACUNIT, // radius + 16*FRACUNIT, // height + 0, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MINECARTEND + 1220, // doomednum + S_MINECARTEND, // 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 + 32*FRACUNIT, // radius + 160*FRACUNIT, // height + 0, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MINECARTENDSOLID + -1, // 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 + 32*FRACUNIT, // radius + 32*FRACUNIT, // height + 0, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_PAPERCOLLISION, // flags + S_NULL // raisestate + }, + + { // MT_MINECARTSIDEMARK + -1, // doomednum + S_MINECARTSIDEMARK1, // spawnstate + 1, // spawnhealth + S_MINECARTSIDEMARK2, // 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 + 22*FRACUNIT, // radius + 32*FRACUNIT, // height + 0, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MINECARTSPARK + -1, // doomednum + S_MINECARTSPARK,// 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 + 2*FRACUNIT, // radius + 2*FRACUNIT, // height + 0, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_BOUNCE|MF_NOCLIPTHING|MF_GRENADEBOUNCE, // flags + S_NULL // raisestate + }, + { // MT_SALOONDOOR -1, // doomednum S_SALOONDOOR, // spawnstate @@ -12002,6 +12178,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_MINECARTSWITCHPOINT + 1229, // 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 + FRACUNIT, // radius + 160*FRACUNIT, // height + 0, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_NOCLIPHEIGHT|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + { // MT_FLAMEJET 1300, // doomednum S_FLAMEJETSTND, // spawnstate diff --git a/src/info.h b/src/info.h index 460f65ae0..717f659c9 100644 --- a/src/info.h +++ b/src/info.h @@ -264,6 +264,7 @@ void A_KillSegments(); void A_SnapperSpawn(); void A_SnapperThinker(); void A_SaloonDoorSpawn(); +void A_MinecartSparkThink(); // ratio of states to sprites to mobj types is roughly 6 : 1 : 1 #define NUMMOBJFREESLOTS 256 @@ -500,6 +501,8 @@ typedef enum sprite SPR_REMT, // TNT proximity shell SPR_TAZD, // Dust devil SPR_ADST, // Arid dust + SPR_MCRT, // Minecart + SPR_MCSP, // Minecart spark SPR_NON2, // Saloon door thinker SPR_SALD, // Saloon door SPR_TRAE, // Train cameo locomotive @@ -2549,6 +2552,18 @@ typedef enum state S_ARIDDUST2, S_ARIDDUST3, + // Minecart + S_MINECART_IDLE, + S_MINECART_DTH1, + S_MINECARTEND, + S_MINECARTSEG_FRONT, + S_MINECARTSEG_BACK, + S_MINECARTSEG_LEFT, + S_MINECARTSEG_RIGHT, + S_MINECARTSIDEMARK1, + S_MINECARTSIDEMARK2, + S_MINECARTSPARK, + // Saloon door S_SALOONDOOR, S_SALOONDOORTHINKER, @@ -4286,12 +4301,19 @@ typedef enum mobj_type MT_DUSTDEVIL, MT_DUSTLAYER, MT_ARIDDUST, + MT_MINECART, + MT_MINECARTSPAWNER, + MT_MINECARTEND, + MT_MINECARTENDSOLID, + MT_MINECARTSIDEMARK, + MT_MINECARTSPARK, MT_SALOONDOOR, MT_SALOONDOORTHINKER, MT_TRAINCAMEOSPAWNER, MT_TRAINSEG, MT_TRAINDUSTSPAWNER, MT_TRAINSTEAMSPAWNER, + MT_MINECARTSWITCHPOINT, // Red Volcano Scenery MT_FLAMEJET, diff --git a/src/p_enemy.c b/src/p_enemy.c index 61c9f7d7d..3c91ef393 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -293,6 +293,7 @@ void A_KillSegments(mobj_t *actor); void A_SnapperSpawn(mobj_t *actor); void A_SnapperThinker(mobj_t *actor); void A_SaloonDoorSpawn(mobj_t *actor); +void A_MinecartSparkThink(mobj_t *actor); //for p_enemy.c // @@ -13512,4 +13513,41 @@ void A_SaloonDoorSpawn(mobj_t *actor) // Origin door door->tracer = actor; +} + +// Function: A_MinecartSparkThink +// +// Description: Thinker for the minecart spark. +// +// var1 = unused +// var2 = unused +// +void A_MinecartSparkThink(mobj_t *actor) +{ + fixed_t dx = actor->momx; + fixed_t dy = actor->momy; + fixed_t dz, dm; + UINT8 i; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_MinecartSparkThink", actor)) + return; +#endif + + if (actor->momz == 0 && P_IsObjectOnGround(actor)) + actor->momz = P_RandomRange(2, 4)*FRACUNIT; + + dz = actor->momz; + dm = FixedHypot(FixedHypot(dx, dy), dz); + dx = FixedDiv(dx, dm); + dy = FixedDiv(dy, dm); + dz = FixedDiv(dz, dm); + + for (i = 1; i <= 8; i++) + { + mobj_t *trail = P_SpawnMobj(actor->x - dx*i, actor->y - dy*i, actor->z - dz*i, MT_PARTICLE); + trail->tics = 2; + trail->sprite = actor->sprite; + P_SetScale(trail, trail->scale/4); + } } \ No newline at end of file diff --git a/src/p_inter.c b/src/p_inter.c index aed63b208..b474c1a66 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1709,6 +1709,53 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) special->momz = toucher->momz; return; + case MT_MINECARTSPAWNER: + if (!special->fuse || player->powers[pw_carry] != CR_MINECART) + { + mobj_t *mcart = P_SpawnMobj(special->x, special->y, special->z, MT_MINECART); + P_SetTarget(&mcart->target, toucher); + mcart->angle = toucher->angle = player->drawangle = special->angle; + mcart->friction = FRACUNIT; + + P_ResetPlayer(player); + player->powers[pw_carry] = CR_MINECART; + toucher->player->pflags &= ~PF_APPLYAUTOBRAKE; + P_SetTarget(&toucher->tracer, mcart); + toucher->momx = toucher->momy = toucher->momz = 0; + + special->fuse = 3*TICRATE; + special->flags2 |= MF2_DONTDRAW; + } + return; + + case MT_MINECARTEND: + if (player->powers[pw_carry] == CR_MINECART && toucher->tracer && !P_MobjWasRemoved(toucher->tracer) && toucher->tracer->health) + { + fixed_t maxz = max(toucher->z, special->z + 35*special->scale); + + toucher->momx = toucher->tracer->momx/2; + toucher->momy = toucher->tracer->momy/2; + toucher->momz = toucher->tracer->momz + P_AproxDistance(toucher->tracer->momx, toucher->tracer->momy)/2; + P_ResetPlayer(player); + player->pflags &= ~PF_APPLYAUTOBRAKE; + P_SetMobjState(toucher, S_PLAY_FALL); + P_SetTarget(&toucher->tracer->target, NULL); + P_KillMobj(toucher->tracer, toucher, special, 0); + P_SetTarget(&toucher->tracer, NULL); + player->powers[pw_carry] = CR_NONE; + P_TeleportMove(toucher, special->x, special->y, maxz); + } + return; + + case MT_MINECARTSWITCHPOINT: + if (player->powers[pw_carry] == CR_MINECART && toucher->tracer && !P_MobjWasRemoved(toucher->tracer) && toucher->tracer->health) + { + boolean destambush = special->flags & MF2_AMBUSH; + if ((toucher->tracer->angle - special->angle + ANGLE_90) >= ANGLE_180) + destambush ^= MF2_AMBUSH; + toucher->tracer->flags2 = (toucher->tracer->flags2 & ~MF2_AMBUSH) | destambush; + } + return; default: // SOC or script pickup if (player->bot) return; @@ -2556,6 +2603,13 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget target->fuse = TICRATE*2; break; + case MT_MINECART: + A_KillSegments(target); // found in green snapper's code - the minecart segments need hardcode-side support to flash while they have a nonzero fuse + A_Scream(target); + P_SetMobjState(target, S_TNTBARREL_EXPL3); + target->momx = target->momy = target->momz = 0; + break; + case MT_PLAYER: { target->fuse = TICRATE*3; // timer before mobj disappears from view (even if not an actual player) diff --git a/src/p_map.c b/src/p_map.c index bb98f8fdb..5015e6a79 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -818,6 +818,22 @@ static boolean PIT_CheckThing(mobj_t *thing) } #endif + if (tmthing->type == MT_MINECART) + { + //height check + if (tmthing->z > thing->z + thing->height || thing->z > tmthing->z + tmthing->height || !(thing->health)) + return true; + + if (thing->type == MT_TNTBARREL) + P_KillMobj(thing, tmthing, tmthing->target, 0); + else if ((thing->flags & MF_MONITOR) || (thing->flags & MF_ENEMY)) + { + P_KillMobj(thing, tmthing, tmthing->target, 0); + if (tmthing->momz*P_MobjFlip(tmthing) < 0) + tmthing->momz = abs(tmthing->momz)*P_MobjFlip(tmthing); + } + } + if (thing->type == MT_SALOONDOOR && tmthing->player) { if ((tmthing->player->powers[pw_carry] == CR_MINECART && tmthing->player->mo->tracer && !P_MobjWasRemoved(tmthing->player->mo->tracer))) @@ -1456,7 +1472,7 @@ static boolean PIT_CheckThing(mobj_t *thing) if (thing->type == MT_FAN || thing->type == MT_STEAM) P_DoFanAndGasJet(thing, tmthing); - else if (thing->flags & MF_SPRING) + else if (thing->flags & MF_SPRING && tmthing->player->powers[pw_carry] != CR_MINECART) { if ( thing->z <= tmthing->z + tmthing->height && tmthing->z <= thing->z + thing->height) diff --git a/src/p_mobj.c b/src/p_mobj.c index 214de7a11..6beec3684 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -8495,6 +8495,16 @@ void P_MobjThinker(mobj_t *mobj) P_TeleportMove(mobj, x + c0 + c, y + s0 + s, z); break; } + case MT_MINECARTSPAWNER: + if (!mobj->fuse || mobj->fuse > TICRATE) + break; + if (mobj->fuse == 2) + { + mobj->fuse = 0; + break; + } + mobj->flags2 ^= MF2_DONTDRAW; + break; case MT_SPINFIRE: if (mobj->flags & MF_NOGRAVITY) { @@ -9245,6 +9255,10 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) case MT_TNTBARREL: mobj->momx = 1; //stack hack break; + case MT_MINECARTEND: + mobj->tracer = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_MINECARTENDSOLID); + mobj->tracer->angle = mobj->angle + ANGLE_90; + break; default: break; } diff --git a/src/p_user.c b/src/p_user.c index ca14c64d4..e5d3f4835 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -963,7 +963,7 @@ void P_ResetPlayer(player_t *player) { player->pflags &= ~(PF_SPINNING|PF_STARTDASH|PF_STARTJUMP|PF_JUMPED|PF_NOJUMPDAMAGE|PF_GLIDING|PF_THOKKED|PF_CANCARRY|PF_SHIELDABILITY|PF_BOUNCING); - if (!(player->powers[pw_carry] == CR_NIGHTSMODE || player->powers[pw_carry] == CR_NIGHTSFALL || player->powers[pw_carry] == CR_BRAKGOOP)) + if (!(player->powers[pw_carry] == CR_NIGHTSMODE || player->powers[pw_carry] == CR_NIGHTSFALL || player->powers[pw_carry] == CR_BRAKGOOP || player->powers[pw_carry] == CR_MINECART)) player->powers[pw_carry] = CR_NONE; player->secondjump = 0; diff --git a/src/sounds.c b/src/sounds.c index a707e4428..56de4a9c5 100644 --- a/src/sounds.c +++ b/src/sounds.c @@ -319,7 +319,7 @@ sfxinfo_t S_sfx[NUMSFX] = {"s3k4e", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Big explosion"}, {"s3k4f", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Flamethrower"}, {"s3k50", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Siren"}, - {"s3k51", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Falling hazard"}, + {"s3k51", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Falling"}, {"s3k52", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Spike"}, {"s3k53", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Powering up"}, {"s3k54", false, 64, 64, -1, NULL, 0, -1, -1, LUMPERROR, "Firing"}, // MetalSonic shot fire @@ -388,7 +388,7 @@ sfxinfo_t S_sfx[NUMSFX] = {"s3k93", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Gas release"}, {"s3k94", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Spike"}, {"s3k95", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Lava burst"}, - {"s3k96", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Falling object"}, + {"s3k96", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Landing"}, {"s3k97", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Wind"}, {"s3k98", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Falling spike"}, {"s3k99", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Bounce"},