diff --git a/src/info.c b/src/info.c index 943c0bbb9..839debb3b 100644 --- a/src/info.c +++ b/src/info.c @@ -16003,7 +16003,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_SPB_DEAD, // deathstate S_NULL, // xdeathstate sfx_s3k5d, // deathsound - 96*FRACUNIT, // speed + 64*FRACUNIT, // speed 24*FRACUNIT, // radius 48*FRACUNIT, // height 0, // display offset diff --git a/src/k_kart.c b/src/k_kart.c index 42067f333..5c9dd7d8d 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2109,7 +2109,7 @@ void K_RespawnChecker(player_t *player) { while (lasersteps) { - + stepha = R_PointToAngle2(laserx, lasery, destx, desty); stepva = R_PointToAngle2(0, laserz, P_AproxDistance(laserx - destx, lasery - desty), destz); @@ -4207,6 +4207,7 @@ void K_DoSneaker(player_t *player, INT32 type) static void K_DoShrink(player_t *user) { INT32 i; + mobj_t *mobj, *next; S_StartSound(user->mo, sfx_kc46); // Sound the BANG! user->pflags |= PF_ATTACKDOWN; @@ -4244,6 +4245,43 @@ static void K_DoShrink(player_t *user) } } } + + // kill everything in the kitem list while we're at it: + for (mobj = kitemcap; mobj; mobj = next) + { + next = mobj->itnext; + + // check if the item is being held by a player behind us before removing it. + // check if the item is a "shield" first, bc i'm p sure thrown items keep the player that threw em as target anyway + + if (mobj->type == MT_BANANA_SHIELD || mobj->type == MT_JAWZ_SHIELD || + mobj->type == MT_SSMINE_SHIELD || mobj->type == MT_EGGMANITEM_SHIELD || + mobj->type == MT_SINK_SHIELD || mobj->type == MT_ORBINAUT_SHIELD) + { + if (mobj->target && mobj->target->player) + { + if (mobj->target->player->kartstuff[k_position] > user->kartstuff[k_position]) + continue; // this guy's behind us, don't take his stuff away! + } + } + + // @TODO: This should probably go into the P_KillMobj code for items? + + if (mobj->eflags & MFE_VERTICALFLIP) + mobj->z -= mobj->height; + else + mobj->z += mobj->height; + + S_StartSound(mobj, mobj->info->deathsound); + P_KillMobj(mobj, user->mo, user->mo); + + P_SetObjectMomZ(mobj, 8*FRACUNIT, false); + P_InstaThrust(mobj, (angle_t)P_RandomRange(0, 359)*ANG1, 16*FRACUNIT); + + if (mobj->type == MT_SPB) + spbplace = -1; + + } } @@ -4416,6 +4454,7 @@ void K_DropHnextList(player_t *player) dropwork = P_SpawnMobj(work->x, work->y, work->z, type); P_SetTarget(&dropwork->target, player->mo); + P_AddKartItem(dropwork); // needs to be called here so shrink can bust items off players in front of the user. dropwork->angle = work->angle; dropwork->flags2 = work->flags2; dropwork->flags |= MF_NOCLIPTHING; @@ -5744,7 +5783,7 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player, boolean closest) R_PointToAngle2(player->mo->x, player->mo->y, waypoint->mobj->x, waypoint->mobj->y); angle_t angledelta = ANGLE_MAX; - if (player->mo->momx != 0 || player->mo->momy != 0) + if (player->mo->momx != 0 || player->mo->momy != 0) { // Default to facing angle if you're not moving, but use momentum angle otherwise. playerangle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); @@ -9489,7 +9528,8 @@ static void K_drawKartMinimap(void) UINT8 skin = 0; UINT8 *colormap = NULL; SINT8 localplayers[4]; - SINT8 numlocalplayers = 0; + SINT8 numlocalplayers = 0; + mobj_t *mobj, *next; // for SPB drawing (or any other item(s) we may wanna draw, I dunno!) // Draw the HUD only when playing in a level. // hu_stuff needs this, unlike st_stuff. @@ -9674,7 +9714,16 @@ static void K_drawKartMinimap(void) if ((G_RaceGametype() && players[localplayers[i]].kartstuff[k_position] == spbplace) || (G_BattleGametype() && K_IsPlayerWanted(&players[localplayers[i]]))) K_drawKartMinimapIcon(players[localplayers[i]].mo->x, players[localplayers[i]].mo->y, x, y, splitflags, kp_wantedreticle, NULL, AutomapPic); - } + } + + // draw SPB(s?) + for (mobj = kitemcap; mobj; mobj = next) + { + next = mobj->itnext; + if (mobj->type == MT_SPB) + K_drawKartMinimapIcon(mobj->x, mobj->y, x, y, splitflags, kp_ringspblocksmall[14 + leveltime%4 /2], NULL, AutomapPic); + } + } static void K_drawKartStartCountdown(void) diff --git a/src/p_enemy.c b/src/p_enemy.c index 86e615ee8..ff57b0ffa 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -8495,11 +8495,15 @@ static void SpawnSPBTrailRings(mobj_t *actor) void A_SPBChase(mobj_t *actor) { player_t *player = NULL; + player_t *scplayer = NULL; // secondary target for seeking UINT8 i; UINT8 bestrank = UINT8_MAX; fixed_t dist; angle_t hang, vang; fixed_t wspeed, xyspeed, zspeed; + fixed_t pdist = 1536<lastlook = -1; // Just make sure this is reset @@ -8732,13 +8737,22 @@ void A_SPBChase(mobj_t *actor) dist = P_AproxDistance(P_AproxDistance(actor->x-actor->tracer->x, actor->y-actor->tracer->y), actor->z-actor->tracer->z); // Move along the waypoints until you get close enough - if (actor->cusval > -1) + if (actor->cusval > -1 && actor->extravalue2 > 0) { // Previously set nextwaypoint lastwaypoint = K_GetWaypointFromIndex((size_t)actor->cusval); - } + tempwaypoint = K_GetBestWaypointTouchingMobj(actor); + // check if the tempwaypoint corresponds to lastwaypoint's next ID at least; + // This is to avoid situations where the SPB decides to suicide jump down a bridge because it found a COMPLETELY unrelated waypoint down there. - bestwaypoint = K_GetBestWaypointTouchingMobj(actor); + if (K_GetWaypointID(tempwaypoint) == K_GetWaypointNextID(lastwaypoint) || K_GetWaypointID(tempwaypoint) == K_GetWaypointID(lastwaypoint)) + // either our previous or curr waypoint ID, sure, take it + bestwaypoint = tempwaypoint; + else + bestwaypoint = K_GetWaypointFromIndex((size_t)actor->extravalue2); // keep going from the PREVIOUS wp. + } + else + bestwaypoint = K_GetBestWaypointTouchingMobj(actor); if (bestwaypoint == NULL && lastwaypoint == NULL) { @@ -8767,6 +8781,7 @@ void A_SPBChase(mobj_t *actor) nextwaypoint = lastwaypoint; } + if (nextwaypoint != NULL) { const fixed_t xywaypointdist = P_AproxDistance( @@ -8776,6 +8791,7 @@ void A_SPBChase(mobj_t *actor) vang = R_PointToAngle2(0, actor->z, xywaypointdist, nextwaypoint->mobj->z); actor->cusval = (INT32)K_GetWaypointHeapIndex(nextwaypoint); + actor->extravalue2 = (INT32)K_GetWaypointHeapIndex(bestwaypoint); // save our last best, used above. } else { @@ -8816,10 +8832,36 @@ void A_SPBChase(mobj_t *actor) actor->movedir += input; } + actor->momx = FixedMul(FixedMul(xyspeed, FINECOSINE(actor->angle>>ANGLETOFINESHIFT)), FINECOSINE(actor->movedir>>ANGLETOFINESHIFT)); actor->momy = FixedMul(FixedMul(xyspeed, FINESINE(actor->angle>>ANGLETOFINESHIFT)), FINECOSINE(actor->movedir>>ANGLETOFINESHIFT)); actor->momz = FixedMul(zspeed, FINESINE(actor->movedir>>ANGLETOFINESHIFT)); + // see if a player is near us, if they are, try to hit them by slightly thrusting towards them, otherwise, bleh! + for (i=0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator || players[i].exiting) + continue; // not in-game + + if (R_PointToDist2(actor->x, actor->y, players[i].mo->x, players[i].mo->y) < pdist) + { + pdist = R_PointToDist2(actor->x, actor->y, players[i].mo->x, players[i].mo->y); + scplayer = &players[i]; // it doesn't matter if we override this guy now. + } + } + + // different player from our main target, try and ram into em~! + if (scplayer && scplayer != player) + { + pangle = actor->angle - R_PointToAngle2(actor->x, actor->y,scplayer->mo->x, scplayer->mo->y); + // check if the angle wouldn't make us LOSE speed... + if ((INT32)pangle/ANG1 >= -80 && (INT32)pangle/ANG1 <= 80) // allow for around 80 degrees + { + // Thrust us towards the guy, try to screw em up! + P_Thrust(actor, R_PointToAngle2(actor->x, actor->y, scplayer->mo->x, scplayer->mo->y), actor->movefactor/4); // not too fast though. + } + } + // Spawn a trail of rings behind the SPB! SpawnSPBTrailRings(actor); @@ -8832,6 +8874,13 @@ void A_SPBChase(mobj_t *actor) } } + // Finally, no matter what, the spb should not be able to be under the ground, or above the ceiling; + if (actor->z < actor->floorz) + actor->z = actor->floorz; + else if (actor->z > actor->ceilingz - actor->height) + actor->z = actor->ceilingz - actor->height; + + return; } diff --git a/src/p_mobj.c b/src/p_mobj.c index b04bf717a..5ed7b7447 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -48,6 +48,7 @@ actioncache_t actioncachehead; static mobj_t *overlaycap = NULL; static mobj_t *shadowcap = NULL; +mobj_t *kitemcap = NULL; // Used for Kart offensive items (the ones that can get removed by sizedown) mobj_t *waypointcap = NULL; void P_InitCachedActions(void) @@ -6101,6 +6102,71 @@ static boolean P_AddShield(mobj_t *thing) return true; }*/ + +// Kartitem stuff. +boolean P_IsKartItem(INT32 type) +{ + if (type == MT_EGGMANITEM || type == MT_EGGMANITEM_SHIELD || + type == MT_BANANA || type == MT_BANANA_SHIELD || + type == MT_ORBINAUT || type == MT_ORBINAUT_SHIELD || + type == MT_JAWZ || type == MT_JAWZ_DUD || type == MT_JAWZ_SHIELD || + type == MT_SSMINE || type == MT_SSMINE_SHIELD || + type == MT_SINK || type == MT_SINK_SHIELD || + type == MT_FLOATINGITEM || type == MT_SPB) + return true; + else + return false; +} + +// Called when a kart item "thinks" +void P_AddKartItem(mobj_t *thing) +{ + I_Assert(thing != NULL); + + if (kitemcap == NULL) + P_SetTarget(&kitemcap, thing); + else { + mobj_t *mo; + for (mo = kitemcap; mo && mo->itnext; mo = mo->itnext) + ; + + I_Assert(mo != NULL); + I_Assert(mo->itnext == NULL); + + P_SetTarget(&mo->itnext, thing); + } + P_SetTarget(&thing->itnext, NULL); +} + +// Called only when a kart item is removed +// Keeps the hnext list from corrupting. +static void P_RemoveKartItem(mobj_t *thing) +{ + mobj_t *mo; + for (mo = kitemcap; mo; mo = mo->itnext) + if (mo->itnext == thing) + { + P_SetTarget(&mo->itnext, thing->itnext); + P_SetTarget(&thing->itnext, NULL); + return; + } +} + +// Doesn't actually do anything since items have their own thinkers, +// but this is necessary for the sole purpose of updating kitemcap +void P_RunKartItems(void) +{ + mobj_t *mobj, *next; + + for (mobj = kitemcap; mobj; mobj = next) + { + next = mobj->itnext; + P_SetTarget(&mobj->itnext, NULL); + } + P_SetTarget(&kitemcap, NULL); +} + + void P_RunOverlays(void) { // run overlays @@ -9961,6 +10027,12 @@ for (i = ((mobj->flags2 & MF2_STRONGBOX) ? strongboxamt : weakboxamt); i; --i) s } } + if (P_MobjWasRemoved(mobj)) + return; // obligatory paranoia check + + if (P_IsKartItem(mobj->type)) // mobj is a kart item we want on the list: + P_AddKartItem(mobj); // add to kitem list + // Can end up here if a player dies. if (mobj->player) P_CyclePlayerMobjState(mobj); @@ -10887,6 +10959,9 @@ void P_RemoveMobj(mobj_t *mobj) if (mobj->type == MT_SPB) spbplace = -1; + if (P_IsKartItem(mobj->type)) + P_RemoveKartItem(mobj); + mobj->health = 0; // Just because // unlink from sector and block lists diff --git a/src/p_mobj.h b/src/p_mobj.h index aec2ed951..3ade78aa4 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -321,6 +321,9 @@ typedef struct mobj_s struct mobj_s *hnext; struct mobj_s *hprev; + // One last pointer for kart item lists + struct mobj_s *itnext; + mobjtype_t type; const mobjinfo_t *info; // &mobjinfo[mobj->type] @@ -436,12 +439,18 @@ typedef struct actioncache_s extern actioncache_t actioncachehead; +extern mobj_t *kitemcap; extern mobj_t *waypointcap; void P_InitCachedActions(void); void P_RunCachedActions(void); void P_AddCachedAction(mobj_t *mobj, INT32 statenum); +// kartitem stuff: Returns true if the specified 'type' is one of the kart item constants we want in the kitemcap list +boolean P_IsKartItem(INT32 type); +void P_AddKartItem(mobj_t *thing); // needs to be called in k_kart.c +void P_RunKartItems(void); + // check mobj against water content, before movement code void P_MobjCheckWater(mobj_t *mobj); diff --git a/src/p_saveg.c b/src/p_saveg.c index d51240521..4767b09d1 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -957,9 +957,11 @@ typedef enum MD2_HNEXT = 1<<7, MD2_HPREV = 1<<8, MD2_COLORIZED = 1<<9, - MD2_WAYPOINTCAP = 1<<10 + MD2_WAYPOINTCAP = 1<<10, + MD2_KITEMCAP = 1<<11, + MD2_ITNEXT = 1<<12 #ifdef ESLOPE - , MD2_SLOPE = 1<<11 + , MD2_SLOPE = 1<<13 #endif } mobj_diff2_t; @@ -1151,6 +1153,8 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type) diff2 |= MD2_HNEXT; if (mobj->hprev) diff2 |= MD2_HPREV; + if (mobj->itnext) + diff2 |= MD2_ITNEXT; #ifdef ESLOPE if (mobj->standingslope) diff2 |= MD2_SLOPE; @@ -1159,6 +1163,8 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type) diff2 |= MD2_COLORIZED; if (mobj == waypointcap) diff2 |= MD2_WAYPOINTCAP; + if (mobj == kitemcap) + diff2 |= MD2_KITEMCAP; if (diff2 != 0) diff |= MD_MORE; @@ -1274,6 +1280,8 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type) WRITEUINT32(save_p, mobj->hnext->mobjnum); if (diff2 & MD2_HPREV) WRITEUINT32(save_p, mobj->hprev->mobjnum); + if (diff2 & MD2_ITNEXT) + WRITEUINT32(save_p, mobj->itnext->mobjnum); #ifdef ESLOPE if (diff2 & MD2_SLOPE) WRITEUINT16(save_p, mobj->standingslope->id); @@ -2151,6 +2159,8 @@ static void LoadMobjThinker(actionf_p1 thinker) mobj->hnext = (mobj_t *)(size_t)READUINT32(save_p); if (diff2 & MD2_HPREV) mobj->hprev = (mobj_t *)(size_t)READUINT32(save_p); + if (diff2 & MD2_ITNEXT) + mobj->itnext = (mobj_t *)(size_t)READUINT32(save_p); #ifdef ESLOPE if (diff2 & MD2_SLOPE) { @@ -2192,6 +2202,9 @@ static void LoadMobjThinker(actionf_p1 thinker) if (diff2 & MD2_WAYPOINTCAP) P_SetTarget(&waypointcap, mobj); + if (diff2 & MD2_KITEMCAP) + P_SetTarget(&kitemcap, mobj); + mobj->info = (mobjinfo_t *)next; // temporarily, set when leave this function } @@ -3044,6 +3057,13 @@ static void P_RelinkPointers(void) if (!(mobj->hprev = P_FindNewPosition(temp))) CONS_Debug(DBG_GAMELOGIC, "hprev not found on %d\n", mobj->type); } + if (mobj->itnext) + { + temp = (UINT32)(size_t)mobj->itnext; + mobj->itnext = NULL; + if (!(mobj->itnext = P_FindNewPosition(temp))) + CONS_Debug(DBG_GAMELOGIC, "itnext not found on %d\n", mobj->type); + } if (mobj->player && mobj->player->capsule) { temp = (UINT32)(size_t)mobj->player->capsule; @@ -3324,7 +3344,7 @@ static void P_NetArchiveMisc(void) WRITEUINT32(save_p, hyubgone); WRITEUINT32(save_p, mapreset); - for (i = 0; i < MAXPLAYERS; i++) + for (i = 0; i < MAXPLAYERS; i++) WRITEINT16(save_p, nospectategrief[i]); WRITEUINT8(save_p, thwompsactive); @@ -3447,7 +3467,7 @@ static inline boolean P_NetUnArchiveMisc(void) hyubgone = READUINT32(save_p); mapreset = READUINT32(save_p); - for (i = 0; i < MAXPLAYERS; i++) + for (i = 0; i < MAXPLAYERS; i++) nospectategrief[i] = READINT16(save_p); thwompsactive = (boolean)READUINT8(save_p); diff --git a/src/p_tick.c b/src/p_tick.c index 2e3b17b69..33cc32602 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -183,6 +183,7 @@ void P_InitThinkers(void) { thinkercap.prev = thinkercap.next = &thinkercap; waypointcap = NULL; + kitemcap = NULL; } // @@ -639,6 +640,9 @@ void P_Ticker(boolean run) if (runemeraldmanager) P_EmeraldManager(); // Power stone mode*/ + // formality so kitemcap gets updated properly each frame. + P_RunKartItems(); + if (run) { P_RunThinkers();