/// \file k_collide.cpp /// \brief SRB2Kart item collision hooks #include "k_collide.h" #include "doomtype.h" #include "p_mobj.h" #include "k_kart.h" #include "p_local.h" #include "s_sound.h" #include "r_main.h" // R_PointToAngle2, R_PointToDist2 #include "hu_stuff.h" // Sink snipe print #include "doomdef.h" // Sink snipe print #include "g_game.h" // Sink snipe print #include "k_objects.h" #include "k_roulette.h" #include "k_podium.h" angle_t K_GetCollideAngle(mobj_t *t1, mobj_t *t2) { fixed_t momux, momuy; angle_t test; if (!(t1->flags & MF_PAPERCOLLISION)) { return R_PointToAngle2(t1->x, t1->y, t2->x, t2->y)+ANGLE_90; } test = R_PointToAngle2(0, 0, t2->momx, t2->momy) + ANGLE_90 - t1->angle; if (test > ANGLE_180) test = t1->angle + ANGLE_180; else test = t1->angle; // intentional way around - sine... momuy = P_AproxDistance(t2->momx, t2->momy); momux = t2->momx - P_ReturnThrustY(t2, test, 2*momuy); momuy = t2->momy - P_ReturnThrustX(t2, test, 2*momuy); return R_PointToAngle2(0, 0, momux, momuy); } boolean K_BananaBallhogCollide(mobj_t *t1, mobj_t *t2) { boolean damageitem = false; if (((t1->target == t2) || (!(t2->flags & (MF_ENEMY|MF_BOSS)) && (t1->target == t2->target))) && (t1->threshold > 0 || (t2->type != MT_PLAYER && t2->threshold > 0))) return true; if (t1->health <= 0 || t2->health <= 0) return true; if (((t1->type == MT_BANANA_SHIELD) && (t2->type == MT_BANANA_SHIELD)) && (t1->target == t2->target)) // Don't hit each other if you have the same target return true; if (t1->type == MT_BALLHOG && t2->type == MT_BALLHOG) return true; // Ballhogs don't collide with eachother if (t2->player) { if (t2->player->flashing > 0 && t2->hitlag == 0) return true; // Banana snipe! if (t1->type == MT_BANANA && t1->health > 1) S_StartSound(t2, sfx_bsnipe); damageitem = true; if (t2->player->flamedash && t2->player->itemtype == KITEM_FLAMESHIELD) { // Melt item S_StartSound(t2, sfx_s3k43); } else if (K_IsRidingFloatingTop(t2->player)) { // Float over silly banana damageitem = false; } else { P_DamageMobj(t2, t1, t1->target, 1, DMG_NORMAL|DMG_WOMBO); } } else if (t2->type == MT_BANANA || t2->type == MT_BANANA_SHIELD || t2->type == MT_ORBINAUT || t2->type == MT_ORBINAUT_SHIELD || t2->type == MT_JAWZ || t2->type == MT_JAWZ_SHIELD || t2->type == MT_BALLHOG || t2->type == MT_GACHABOM) { // Other Item Damage angle_t bounceangle = K_GetCollideAngle(t1, t2); S_StartSound(t2, t2->info->deathsound); P_KillMobj(t2, t1, t1, DMG_NORMAL); P_SetObjectMomZ(t2, 24*FRACUNIT, false); P_InstaThrust(t2, bounceangle, 16*FRACUNIT); P_SpawnMobj(t2->x/2 + t1->x/2, t2->y/2 + t1->y/2, t2->z/2 + t1->z/2, MT_ITEMCLASH); damageitem = true; } else if (t2->type == MT_SSMINE_SHIELD || t2->type == MT_SSMINE || t2->type == MT_LANDMINE) { damageitem = true; // Bomb death P_KillMobj(t2, t1, t1, DMG_NORMAL); } else if (t2->flags & MF_SHOOTABLE) { // Shootable damage P_DamageMobj(t2, t1, t1->target, 1, DMG_NORMAL); damageitem = true; } if (damageitem && P_MobjWasRemoved(t1) == false) { angle_t bounceangle; if (P_MobjWasRemoved(t2) == false) { bounceangle = K_GetCollideAngle(t2, t1); } else { bounceangle = K_MomentumAngle(t1) + ANGLE_90; t2 = NULL; // handles the arguments to P_KillMobj } // This Item Damage S_StartSound(t1, t1->info->deathsound); P_KillMobj(t1, t2, t2, DMG_NORMAL); P_SetObjectMomZ(t1, 24*FRACUNIT, false); P_InstaThrust(t1, bounceangle, 16*FRACUNIT); } return true; } boolean K_EggItemCollide(mobj_t *t1, mobj_t *t2) { // Push fakes out of other item boxes if (t2->type == MT_RANDOMITEM || t2->type == MT_EGGMANITEM) { P_InstaThrust(t1, R_PointToAngle2(t2->x, t2->y, t1->x, t1->y), t2->radius/4); return true; } if (t2->player) { if ((t1->target == t2 || t1->target == t2->target) && (t1->threshold > 0)) return true; if (t1->health <= 0 || t2->health <= 0) return true; if (!P_CanPickupItem(t2->player, 2)) return true; K_DropItems(t2->player); K_StartEggmanRoulette(t2->player); if (t2->player->flamedash && t2->player->itemtype == KITEM_FLAMESHIELD) { // Melt item S_StartSound(t2, sfx_s3k43); P_KillMobj(t1, t2, t2, DMG_NORMAL); return true; } else { Obj_SpawnItemDebrisEffects(t1, t2); #if 0 // Eggbox snipe! if (t1->type == MT_EGGMANITEM && t1->health > 1) S_StartSound(t2, sfx_bsnipe); #endif if (t1->target && t1->target->player) { t2->player->eggmanblame = t1->target->player - players; if (t1->target->hnext == t1) { P_SetTarget(&t1->target->hnext, NULL); t1->target->player->pflags &= ~PF_EGGMANOUT; } } P_RemoveMobj(t1); return true; } } return true; } static mobj_t *grenade; static fixed_t explodedist; static boolean explodespin; static INT32 minehitlag; static inline boolean PIT_SSMineChecks(mobj_t *thing) { if (thing == grenade) // Don't explode yourself! Endless loop! return true; if (thing->health <= 0) return true; if (!(thing->flags & MF_SHOOTABLE) || (thing->flags & MF_SCENERY)) return true; if (thing->player && thing->player->spectator) return true; if (P_AproxDistance(P_AproxDistance(thing->x - grenade->x, thing->y - grenade->y), thing->z - grenade->z) > explodedist) return true; // Too far away if (P_CheckSight(grenade, thing) == false) return true; // Not in sight return false; } static inline BlockItReturn_t PIT_SSMineSearch(mobj_t *thing) { if (grenade == NULL || P_MobjWasRemoved(grenade)) return BMIT_ABORT; // There's the possibility these can chain react onto themselves after they've already died if there are enough all in one spot if (grenade->flags2 & MF2_DEBRIS) // don't explode twice return BMIT_ABORT; if (thing->type != MT_PLAYER) // Don't explode for anything but an actual player. return BMIT_CONTINUE; if (thing == grenade->target && grenade->threshold != 0) // Don't blow up at your owner instantly. return BMIT_CONTINUE; if (PIT_SSMineChecks(thing) == true) return BMIT_CONTINUE; // Explode! P_SetMobjState(grenade, grenade->info->deathstate); return BMIT_ABORT; } void K_DoMineSearch(mobj_t *actor, fixed_t size) { INT32 bx, by, xl, xh, yl, yh; explodedist = FixedMul(size, actor->scale); grenade = actor; yh = (unsigned)(actor->y + (explodedist + MAXRADIUS) - bmaporgy)>>MAPBLOCKSHIFT; yl = (unsigned)(actor->y - (explodedist + MAXRADIUS) - bmaporgy)>>MAPBLOCKSHIFT; xh = (unsigned)(actor->x + (explodedist + MAXRADIUS) - bmaporgx)>>MAPBLOCKSHIFT; xl = (unsigned)(actor->x - (explodedist + MAXRADIUS) - bmaporgx)>>MAPBLOCKSHIFT; BMBOUNDFIX (xl, xh, yl, yh); for (by = yl; by <= yh; by++) for (bx = xl; bx <= xh; bx++) P_BlockThingsIterator(bx, by, PIT_SSMineSearch); } static inline BlockItReturn_t PIT_SSMineExplode(mobj_t *thing) { const INT32 oldhitlag = thing->hitlag; INT32 lagadded; if (grenade == NULL || P_MobjWasRemoved(grenade)) return BMIT_ABORT; // There's the possibility these can chain react onto themselves after they've already died if there are enough all in one spot #if 0 if (grenade->flags2 & MF2_DEBRIS) // don't explode twice return BMIT_ABORT; #endif if (PIT_SSMineChecks(thing) == true) return BMIT_CONTINUE; P_DamageMobj(thing, grenade, grenade->target, 1, (explodespin ? DMG_NORMAL : DMG_EXPLODE)); lagadded = (thing->hitlag - oldhitlag); if (lagadded > 0) { minehitlag = lagadded; } return BMIT_CONTINUE; } tic_t K_MineExplodeAttack(mobj_t *actor, fixed_t size, boolean spin) { INT32 bx, by, xl, xh, yl, yh; explodespin = spin; explodedist = FixedMul(size, actor->scale); grenade = actor; minehitlag = 0; // Use blockmap to check for nearby shootables yh = (unsigned)(actor->y + explodedist - bmaporgy)>>MAPBLOCKSHIFT; yl = (unsigned)(actor->y - explodedist - bmaporgy)>>MAPBLOCKSHIFT; xh = (unsigned)(actor->x + explodedist - bmaporgx)>>MAPBLOCKSHIFT; xl = (unsigned)(actor->x - explodedist - bmaporgx)>>MAPBLOCKSHIFT; BMBOUNDFIX (xl, xh, yl, yh); for (by = yl; by <= yh; by++) for (bx = xl; bx <= xh; bx++) P_BlockThingsIterator(bx, by, PIT_SSMineExplode); // Set this flag to ensure that the inital action won't be triggered twice. actor->flags2 |= MF2_DEBRIS; // Set this flag to ensure the hitbox timer doesn't get extended with every player hit actor->flags |= MF_NOHITLAGFORME; actor->hitlag = 0; // same deal if (!spin) { if (minehitlag == 0) { minehitlag = actor->hitlag; } Obj_SpawnBrolyKi(actor, minehitlag); return minehitlag; } return 0; } boolean K_MineCollide(mobj_t *t1, mobj_t *t2) { if (((t1->target == t2) || (!(t2->flags & (MF_ENEMY|MF_BOSS)) && (t1->target == t2->target))) && (t1->threshold > 0 || (t2->type != MT_PLAYER && t2->threshold > 0))) return true; if (t1->health <= 0 || t2->health <= 0) return true; if (t2->player) { if (t2->player->flashing > 0 && t2->hitlag == 0) return true; // Bomb punting if ((t1->state >= &states[S_SSMINE1] && t1->state <= &states[S_SSMINE4]) || (t1->state >= &states[S_SSMINE_DEPLOY8] && t1->state <= &states[S_SSMINE_EXPLODE2])) { P_KillMobj(t1, t2, t2, DMG_NORMAL); } else { K_PuntMine(t1, t2); } } else if (t2->type == MT_ORBINAUT || t2->type == MT_JAWZ || t2->type == MT_ORBINAUT_SHIELD || t2->type == MT_JAWZ_SHIELD || t2->type == MT_GACHABOM) { // Bomb death angle_t bounceangle = K_GetCollideAngle(t1, t2); P_KillMobj(t1, t2, t2, DMG_NORMAL); // Other Item Damage S_StartSound(t2, t2->info->deathsound); P_KillMobj(t2, t1, t1, DMG_NORMAL); P_SetObjectMomZ(t2, 24*FRACUNIT, false); P_InstaThrust(t2, bounceangle, 16*FRACUNIT); } else if (t2->flags & MF_SHOOTABLE) { // Bomb death P_KillMobj(t1, t2, t2, DMG_NORMAL); // Shootable damage P_DamageMobj(t2, t1, t1->target, 1, DMG_NORMAL); } return true; } boolean K_LandMineCollide(mobj_t *t1, mobj_t *t2) { if (((t1->target == t2) || (!(t2->flags & (MF_ENEMY|MF_BOSS)) && (t1->target == t2->target))) && (t1->threshold > 0 || (t2->type != MT_PLAYER && t2->threshold > 0))) return true; if (t1->health <= 0 || t2->health <= 0) return true; if (t2->player) { const INT32 oldhitlag = t2->hitlag; if (t2->player->flashing) return true; // Banana snipe! if (t1->health > 1) { if (t1->target && t1->target->player) { t1->target->player->roundconditions.landmine_dunk = true; t1->target->player->roundconditions.checkthisframe = true; } S_StartSound(t2, sfx_bsnipe); } if (t2->player->flamedash && t2->player->itemtype == KITEM_FLAMESHIELD) { // Melt item S_StartSound(t2, sfx_s3k43); K_SetHitLagForObjects(t2, t1, 3, false); } else { // Player Damage P_DamageMobj(t2, t1, t1->target, 1, DMG_TUMBLE); } t1->reactiontime = (t2->hitlag - oldhitlag); P_KillMobj(t1, t2, t2, DMG_NORMAL); } else if (t2->type == MT_BANANA || t2->type == MT_BANANA_SHIELD || t2->type == MT_ORBINAUT || t2->type == MT_ORBINAUT_SHIELD || t2->type == MT_JAWZ || t2->type == MT_JAWZ_SHIELD || t2->type == MT_BALLHOG || t2->type == MT_GACHABOM) { // Other Item Damage angle_t bounceangle = K_GetCollideAngle(t1, t2); if (t2->eflags & MFE_VERTICALFLIP) t2->z -= t2->height; else t2->z += t2->height; S_StartSound(t2, t2->info->deathsound); P_KillMobj(t2, t1, t1, DMG_NORMAL); P_SetObjectMomZ(t2, 24*FRACUNIT, false); P_InstaThrust(t2, bounceangle, 16*FRACUNIT); P_SpawnMobj(t2->x/2 + t1->x/2, t2->y/2 + t1->y/2, t2->z/2 + t1->z/2, MT_ITEMCLASH); t1->reactiontime = t2->hitlag; P_KillMobj(t1, t2, t2, DMG_NORMAL); } else if (t2->type == MT_SSMINE_SHIELD || t2->type == MT_SSMINE || t2->type == MT_LANDMINE) { P_KillMobj(t1, t2, t2, DMG_NORMAL); // Bomb death P_KillMobj(t2, t1, t1, DMG_NORMAL); } else if (t2->flags & MF_SHOOTABLE) { // Shootable damage P_DamageMobj(t2, t1, t1->target, 1, DMG_NORMAL); t1->reactiontime = t2->hitlag; P_KillMobj(t1, t2, t2, DMG_NORMAL); } return true; } boolean K_DropTargetCollide(mobj_t *t1, mobj_t *t2) { mobj_t *draggeddroptarget = (t1->type == MT_DROPTARGET_SHIELD) ? t1->target : NULL; UINT8 strength; if (((t1->target == t2) || (t1->target == t2->target)) && ((t1->threshold > 0 && t2->type == MT_PLAYER) || (t2->type != MT_PLAYER && t2->threshold > 0))) return true; if (t1->health <= 0 || t2->health <= 0) return true; if (t2->player && (t2->player->hyudorotimer || t2->player->justbumped)) return true; if (t1->health > 3) // forward thrown { strength = 0; } else if (t1->reactiontime == 0 || draggeddroptarget) { strength = 80; } else { strength = 140; } // Intensify bumps if already spinning... P_Thrust(t1, R_PointToAngle2(t1->x, t1->y, t2->x, t2->y), strength * t1->scale); if (draggeddroptarget) { // "Pass through" the shock of the impact, part 1. t1->momx = t1->target->momx; t1->momy = t1->target->momy; t1->momz = t1->target->momz; } { angle_t t2angle = R_PointToAngle2(t2->momx, t2->momy, 0, 0); angle_t t2deflect; fixed_t t1speed, t2speed; K_KartBouncing(t1, t2); t1speed = FixedHypot(t1->momx, t1->momy); t2speed = FixedHypot(t2->momx, t2->momy); t2deflect = t2angle - R_PointToAngle2(0, 0, t2->momx, t2->momy); if (t2deflect > ANGLE_180) t2deflect = InvAngle(t2deflect); if (t2deflect < ANG10) P_InstaThrust(t2, t2angle, t2speed); t1->angle = t1->old_angle = R_PointToAngle2(0, 0, t1->momx, t1->momy); t1->reactiontime = (7 * (t1speed + t2speed)) / (4 * t1->scale); if (t1->reactiontime < 10) t1->reactiontime = 10; t1->threshold = 10; } t1->renderflags &= ~RF_FULLDARK; // brightest on the bump if (draggeddroptarget) { // "Pass through" the shock of the impact, part 2. draggeddroptarget->momx = t1->momx; draggeddroptarget->momy = t1->momy; draggeddroptarget->momz = t1->momz; // Have the drop target travel between them. t1->momx = (t1->momx + t2->momx)/2; t1->momy = (t1->momy + t2->momy)/2; t1->momz = (t1->momz + t2->momz)/2; K_AddHitLag(t1->target, 6, false); } K_AddHitLag(t1, 6, true); K_AddHitLag(t2, 6, false); { mobj_t *ghost = P_SpawnGhostMobj(t1); UINT8 i; P_SetScale(ghost, 3*ghost->destscale/2); ghost->destscale = 15*ghost->destscale/2; ghost->fuse = 10; ghost->scalespeed = (ghost->destscale - ghost->scale)/ghost->fuse; for (i = 0; i < 2; i++) { mobj_t *blast = P_SpawnMobjFromMobj(t1, 0, 0, FixedDiv(t1->height, t1->scale), MT_BATTLEBUMPER_BLAST); P_SetScale(blast, 5*blast->scale/2); blast->angle = R_PointToAngle2(0, 0, t1->momx, t1->momy) + ANGLE_45; if (i & 1) { blast->angle += ANGLE_90; } blast->destscale *= 10; } } t1->flags |= MF_SHOOTABLE; // The following sets t1->target to t2, so draggeddroptarget keeps it persisting... P_DamageMobj(t1, t2, (t2->target ? t2->target : t2), 1, DMG_NORMAL); switch (t1->health) { case 3: t1->color = SKINCOLOR_LIME; break; case 2: t1->color = SKINCOLOR_GOLD; break; case 1: t1->color = SKINCOLOR_CRIMSON; break; } t1->flags &= ~MF_SHOOTABLE; t1->spritexscale = 3*FRACUNIT; t1->spriteyscale = 3*FRACUNIT/2; if (!t2->player) { t2->angle += ANGLE_180; if (t2->type == MT_JAWZ) P_SetTarget(&t2->tracer, t2->target); // Back to the source! t2->threshold = 10; } if (t1->reactiontime > 1000) { S_StartSound(t2, sfx_kdtrg3); } else if (t1->reactiontime > 500) { S_StartSound(t2, sfx_kdtrg2); } else { S_StartSound(t2, sfx_kdtrg1); } if (draggeddroptarget && draggeddroptarget->player) { // The following removes t1, be warned // (its newly assigned properties are moved across) K_DropHnextList(draggeddroptarget->player); // Do NOT modify or reference t1 after this line // I mean it! Do not even absentmindedly try it } return true; } static mobj_t *lightningSource; static fixed_t lightningDist; static inline BlockItReturn_t PIT_LightningShieldAttack(mobj_t *thing) { if (lightningSource == NULL || P_MobjWasRemoved(lightningSource)) { // Invalid? return BMIT_ABORT; } if (thing == NULL || P_MobjWasRemoved(thing)) { // Invalid? return BMIT_ABORT; } if (thing == lightningSource) { // Don't explode yourself!! return BMIT_CONTINUE; } if (thing->health <= 0) { // Dead return BMIT_CONTINUE; } if (thing->type != MT_SPB) { if (!(thing->flags & MF_SHOOTABLE) || (thing->flags & MF_SCENERY)) { // Not shootable return BMIT_CONTINUE; } } if (thing->player && thing->player->spectator) { // Spectator return BMIT_CONTINUE; } if (P_AproxDistance(thing->x - lightningSource->x, thing->y - lightningSource->y) > lightningDist + thing->radius) { // Too far away return BMIT_CONTINUE; } #if 0 if (P_CheckSight(lightningSource, thing) == false) { // Not in sight return BMIT_CONTINUE; } #endif P_DamageMobj(thing, lightningSource, lightningSource, 1, DMG_VOLTAGE|DMG_CANTHURTSELF|DMG_WOMBO); return BMIT_CONTINUE; } void K_LightningShieldAttack(mobj_t *actor, fixed_t size) { INT32 bx, by, xl, xh, yl, yh; lightningDist = FixedMul(size, actor->scale); lightningSource = actor; // Use blockmap to check for nearby shootables yh = (unsigned)(actor->y + lightningDist - bmaporgy)>>MAPBLOCKSHIFT; yl = (unsigned)(actor->y - lightningDist - bmaporgy)>>MAPBLOCKSHIFT; xh = (unsigned)(actor->x + lightningDist - bmaporgx)>>MAPBLOCKSHIFT; xl = (unsigned)(actor->x - lightningDist - bmaporgx)>>MAPBLOCKSHIFT; BMBOUNDFIX (xl, xh, yl, yh); for (by = yl; by <= yh; by++) for (bx = xl; bx <= xh; bx++) P_BlockThingsIterator(bx, by, PIT_LightningShieldAttack); } boolean K_BubbleShieldCollide(mobj_t *t1, mobj_t *t2) { if (t1->type == MT_PLAYER) { // Bubble Shield already has a hitbox, and it gets // teleported every tic so the Bubble itself will // always make contact with other objects. // // Therefore, we don't need a second, smaller hitbox // on the player. It'll just cause unwanted hitlag. return true; } if (t2->type == MT_PLAYER) { // Counter desyncs /*mobj_t *oldthing = thing; mobj_t *oldtm.thing = tm.thing; P_Thrust(tm.thing, R_PointToAngle2(thing->x, thing->y, tm.thing->x, tm.thing->y), 4*thing->scale); thing = oldthing; P_SetTarget(&tm.thing, oldtm.thing);*/ if (P_PlayerInPain(t2->player) || t2->player->flashing || t2->player->hyudorotimer || t2->player->justbumped || K_IsBigger(t2, t1)) { return true; } // Player Damage P_DamageMobj(t2, t1->target, t1, 1, DMG_NORMAL|DMG_WOMBO); if (t2->player->timeshit > t2->player->timeshitprev) { // Don't play from t1 else it gets cut out... for some reason. S_StartSound(t2, sfx_s3k44); } } else { if (!t2->threshold || t2->type == MT_DROPTARGET) { if (!t2->momx && !t2->momy) { t2->momz += (24*t2->scale) * P_MobjFlip(t2); } else { t2->momx = -4*t2->momx; t2->momy = -4*t2->momy; t2->momz = -4*t2->momz; t2->angle += ANGLE_180; } if (t2->type == MT_JAWZ) P_SetTarget(&t2->tracer, t2->target); // Back to the source! t2->threshold = 10; S_StartSound(t1, sfx_s3k44); } } // no interaction return true; } boolean K_KitchenSinkCollide(mobj_t *t1, mobj_t *t2) { if (((t1->target == t2) || (!(t2->flags & (MF_ENEMY|MF_BOSS)) && (t1->target == t2->target))) && (t1->threshold > 0 || (t2->type != MT_PLAYER && t2->threshold > 0))) return true; if (t2->player) { if (t2->player->flashing > 0 && t2->hitlag == 0) return true; S_StartSound(NULL, sfx_bsnipe); // let all players hear it. HU_SetCEchoFlags(0); HU_SetCEchoDuration(5); HU_DoCEcho(va("%s\\was hit by a kitchen sink.\\\\\\\\", player_names[t2->player-players])); I_OutputMsg("%s was hit by a kitchen sink.\n", player_names[t2->player-players]); P_DamageMobj(t2, t1, t1->target, 1, DMG_INSTAKILL); P_KillMobj(t1, t2, t2, DMG_NORMAL); } else if (t2->flags & MF_SHOOTABLE) { // Shootable damage P_KillMobj(t2, t2, t1->target, DMG_NORMAL); // This item damage P_KillMobj(t1, t2, t2, DMG_NORMAL); } return true; } boolean K_FallingRockCollide(mobj_t *t1, mobj_t *t2) { if (t2->player || t2->type == MT_FALLINGROCK) K_KartBouncing(t2, t1); return true; } boolean K_SMKIceBlockCollide(mobj_t *t1, mobj_t *t2) { if (!(t2->flags & MF_SOLID || t2->flags & MF_SHOOTABLE)) return true; if (!(t2->health)) return true; if (t2->type == MT_BANANA || t2->type == MT_BANANA_SHIELD || t2->type == MT_EGGMANITEM || t2->type == MT_EGGMANITEM_SHIELD || t2->type == MT_SSMINE || t2->type == MT_SSMINE_SHIELD || t2->type == MT_DROPTARGET_SHIELD || t2->type == MT_ORBINAUT_SHIELD || t2->type == MT_JAWZ_SHIELD) return false; if (t1->health) P_KillMobj(t1, t2, t2, DMG_NORMAL); /* if (t2->player && (t2->player->invincibilitytimer > 0 || K_IsBigger(t2, t1) == true)) return true; */ K_KartSolidBounce(t1, t2); return true; } boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2) { boolean t1Condition = false; boolean t2Condition = false; boolean stungT1 = false; boolean stungT2 = false; if (K_PodiumSequence() == true) { // Always regular bumps, no ring toss. return false; } // Clash instead of damage if both parties have any of these conditions t1Condition = (K_IsBigger(t1, t2) == true) || (t1->player->invincibilitytimer > 0) || (t1->player->flamedash > 0 && t1->player->itemtype == KITEM_FLAMESHIELD) || (t1->player->curshield == KSHIELD_TOP && !K_IsHoldingDownTop(t1->player)); t2Condition = (K_IsBigger(t2, t1) == true) || (t2->player->invincibilitytimer > 0) || (t2->player->flamedash > 0 && t2->player->itemtype == KITEM_FLAMESHIELD) || (t2->player->curshield == KSHIELD_TOP && !K_IsHoldingDownTop(t2->player)); if (t1Condition == true && t2Condition == true) { K_DoPowerClash(t1->player, t2->player); return false; } // Cause tumble on invincibility t1Condition = (t1->player->invincibilitytimer > 0); t2Condition = (t2->player->invincibilitytimer > 0); if (t1Condition == true && t2Condition == false) { P_DamageMobj(t2, t1, t1, 1, DMG_TUMBLE); return true; } else if (t1Condition == false && t2Condition == true) { P_DamageMobj(t1, t2, t2, 1, DMG_TUMBLE); return true; } // Flame Shield dash damage t1Condition = (t1->player->flamedash > 0 && t1->player->itemtype == KITEM_FLAMESHIELD); t2Condition = (t2->player->flamedash > 0 && t2->player->itemtype == KITEM_FLAMESHIELD); if (t1Condition == true && t2Condition == false) { P_DamageMobj(t2, t1, t1, 1, DMG_WIPEOUT|DMG_WOMBO); return true; } else if (t1Condition == false && t2Condition == true) { P_DamageMobj(t1, t2, t2, 1, DMG_WIPEOUT|DMG_WOMBO); return true; } // Battle Mode Sneaker damage // (Pogo Spring damage is handled in head-stomping code) if (gametyperules & GTR_BUMPERS) { t1Condition = ((t1->player->sneakertimer > 0) && !P_PlayerInPain(t1->player) && (t1->player->flashing == 0)); t2Condition = ((t2->player->sneakertimer > 0) && !P_PlayerInPain(t2->player) && (t2->player->flashing == 0)); if (t1Condition == true && t2Condition == false) { P_DamageMobj(t2, t1, t1, 1, DMG_WIPEOUT|DMG_STEAL|DMG_WOMBO); return true; } else if (t1Condition == false && t2Condition == true) { P_DamageMobj(t1, t2, t2, 1, DMG_WIPEOUT|DMG_STEAL|DMG_WOMBO); return true; } } // Cause stumble on scale difference t1Condition = K_IsBigger(t1, t2); t2Condition = K_IsBigger(t2, t1); if (t1Condition == true && t2Condition == false) { K_StumblePlayer(t2->player); return true; } else if (t1Condition == false && t2Condition == true) { K_StumblePlayer(t1->player); return true; } // Ring sting, this is a bit more unique t1Condition = (K_GetShieldFromItem(t2->player->itemtype) == KSHIELD_NONE); t2Condition = (K_GetShieldFromItem(t1->player->itemtype) == KSHIELD_NONE); if (t1Condition == true) { if (t2->player->rings <= 0) { P_DamageMobj(t2, t1, t1, 1, DMG_STING|DMG_WOMBO); stungT2 = true; } P_PlayerRingBurst(t2->player, 1); } if (t2Condition == true) { if (t1->player->rings <= 0) { P_DamageMobj(t1, t2, t2, 1, DMG_STING|DMG_WOMBO); stungT1 = true; } P_PlayerRingBurst(t1->player, 1); } // No damage hitlag for stinging. if (stungT1 == true && stungT2 == false) { t2->eflags &= ~MFE_DAMAGEHITLAG; } else if (stungT2 == true && stungT1 == false) { t1->eflags &= ~MFE_DAMAGEHITLAG; } return (stungT1 || stungT2); }