private bool TraverseIntercepts(Func <Intercept, bool> func, Fixed maxFrac) { var count = interceptCount; Intercept intercept = null; while (count-- > 0) { var dist = Fixed.MaxValue; for (var i = 0; i < interceptCount; i++) { if (intercepts[i].Frac < dist) { dist = intercepts[i].Frac; intercept = intercepts[i]; } } if (dist > maxFrac) { // Checked everything in range. return(true); } if (!func(intercept)) { // Don't bother going farther. return(false); } intercept.Frac = Fixed.MaxValue; } // Everything was traversed. return(true); }
/// <summary> /// Fire a hitscan bullet along the aiming line. /// </summary> private bool ShootTraverse(Intercept intercept) { var mi = world.MapInteraction; var pt = world.PathTraversal; if (intercept.Line != null) { var line = intercept.Line; if (line.Special != 0) { mi.ShootSpecialLine(currentShooter, line); } if ((line.Flags & LineFlags.TwoSided) == 0) { goto hitLine; } var mc = world.MapCollision; // Crosses a two sided line. mc.LineOpening(line); var dist = currentRange * intercept.Frac; if (line.FrontSector.FloorHeight != line.BackSector.FloorHeight) { var slope = (mc.OpenBottom - currentShooterZ) / dist; if (slope > currentAimSlope) { goto hitLine; } } if (line.FrontSector.CeilingHeight != line.BackSector.CeilingHeight) { var slope = (mc.OpenTop - currentShooterZ) / dist; if (slope < currentAimSlope) { goto hitLine; } } // Shot continues. return(true); // Hit line. hitLine: // Position a bit closer. var frac = intercept.Frac - Fixed.FromInt(4) / currentRange; var x = pt.Trace.X + pt.Trace.Dx * frac; var y = pt.Trace.Y + pt.Trace.Dy * frac; var z = currentShooterZ + currentAimSlope * (frac * currentRange); if (line.FrontSector.CeilingFlat == world.Map.SkyFlatNumber) { // Don't shoot the sky! if (z > line.FrontSector.CeilingHeight) { return(false); } // It's a sky hack wall. if (line.BackSector != null && line.BackSector.CeilingFlat == world.Map.SkyFlatNumber) { return(false); } } // Spawn bullet puffs. SpawnPuff(x, y, z); // Don't go any farther. return(false); } { // Shoot a thing. var thing = intercept.Thing; if (thing == currentShooter) { // Can't shoot self. return(true); } if ((thing.Flags & MobjFlags.Shootable) == 0) { // Corpse or something. return(true); } // Check angles to see if the thing can be aimed at. var dist = currentRange * intercept.Frac; var thingTopSlope = (thing.Z + thing.Height - currentShooterZ) / dist; if (thingTopSlope < currentAimSlope) { // Shot over the thing. return(true); } var thingBottomSlope = (thing.Z - currentShooterZ) / dist; if (thingBottomSlope > currentAimSlope) { // Shot under the thing. return(true); } // Hit thing. // Position a bit closer. var frac = intercept.Frac - Fixed.FromInt(10) / currentRange; var x = pt.Trace.X + pt.Trace.Dx * frac; var y = pt.Trace.Y + pt.Trace.Dy * frac; var z = currentShooterZ + currentAimSlope * (frac * currentRange); // Spawn bullet puffs or blod spots, depending on target type. if ((intercept.Thing.Flags & MobjFlags.NoBlood) != 0) { SpawnPuff(x, y, z); } else { SpawnBlood(x, y, z, currentDamage); } if (currentDamage != 0) { world.ThingInteraction.DamageMobj(thing, currentShooter, currentShooter, currentDamage); } // Don't go any farther. return(false); } }
/// <summary> /// Find a thing or wall which is on the aiming line. /// Sets lineTaget and aimSlope when a target is aimed at. /// </summary> private bool AimTraverse(Intercept intercept) { if (intercept.Line != null) { var line = intercept.Line; if ((line.Flags & LineFlags.TwoSided) == 0) { // Stop. return(false); } var mc = world.MapCollision; // Crosses a two sided line. // A two sided line will restrict the possible target ranges. mc.LineOpening(line); if (mc.OpenBottom >= mc.OpenTop) { // Stop. return(false); } var dist = currentRange * intercept.Frac; if (line.FrontSector.FloorHeight != line.BackSector.FloorHeight) { var slope = (mc.OpenBottom - currentShooterZ) / dist; if (slope > bottomSlope) { bottomSlope = slope; } } if (line.FrontSector.CeilingHeight != line.BackSector.CeilingHeight) { var slope = (mc.OpenTop - currentShooterZ) / dist; if (slope < topSlope) { topSlope = slope; } } if (topSlope <= bottomSlope) { // Stop. return(false); } // Shot continues. return(true); } // Shoot a thing. var thing = intercept.Thing; if (thing == currentShooter) { // Can't shoot self. return(true); } { if ((thing.Flags & MobjFlags.Shootable) == 0) { // Corpse or something. return(true); } // Check angles to see if the thing can be aimed at. var dist = currentRange * intercept.Frac; var thingTopSlope = (thing.Z + thing.Height - currentShooterZ) / dist; if (thingTopSlope < bottomSlope) { // Shot over the thing. return(true); } var thingBottomSlope = (thing.Z - currentShooterZ) / dist; if (thingBottomSlope > topSlope) { // Shot under the thing. return(true); } // This thing can be hit! if (thingTopSlope > topSlope) { thingTopSlope = topSlope; } if (thingBottomSlope < bottomSlope) { thingBottomSlope = bottomSlope; } currentAimSlope = (thingTopSlope + thingBottomSlope) / 2; lineTarget = thing; // Don't go any farther. return(false); } }
/// <summary> /// Fire a hitscan bullet along the aiming line. /// </summary> private bool ShootTraverse(Intercept intercept) { var mi = world.MapInteraction; var pt = world.PathTraversal; if (intercept.Line != null) { var line = intercept.Line; if (line.Special != 0) { mi.ShootSpecialLine(currentShooter, line); } if ((line.Flags & LineFlags.TwoSided) == 0) { goto hitLine; } var mc = world.MapCollision; // Crosses a two sided line. mc.LineOpening(line); var dist = currentRange * intercept.Frac; // Similar to AimTraverse, the code below is imported from Chocolate Doom. if (line.BackSector == null) { { var slope = (mc.OpenBottom - currentShooterZ) / dist; if (slope > currentAimSlope) { goto hitLine; } } { var slope = (mc.OpenTop - currentShooterZ) / dist; if (slope < currentAimSlope) { goto hitLine; } } } else { if (line.FrontSector.FloorHeight != line.BackSector.FloorHeight) { var slope = (mc.OpenBottom - currentShooterZ) / dist; if (slope > currentAimSlope) { goto hitLine; } } if (line.FrontSector.CeilingHeight != line.BackSector.CeilingHeight) { var slope = (mc.OpenTop - currentShooterZ) / dist; if (slope < currentAimSlope) { goto hitLine; } } } // Shot continues. return(true); // Hit line. hitLine: // Position a bit closer. var frac = intercept.Frac - Fixed.FromInt(4) / currentRange; var x = pt.Trace.X + pt.Trace.Dx * frac; var y = pt.Trace.Y + pt.Trace.Dy * frac; var z = currentShooterZ + currentAimSlope * (frac * currentRange); if (line.FrontSector.CeilingFlat == world.Map.SkyFlatNumber) { // Don't shoot the sky! if (z > line.FrontSector.CeilingHeight) { return(false); } // It's a sky hack wall. if (line.BackSector != null && line.BackSector.CeilingFlat == world.Map.SkyFlatNumber) { return(false); } } // Spawn bullet puffs. SpawnPuff(x, y, z); // Don't go any farther. return(false); } { // Shoot a thing. var thing = intercept.Thing; if (thing == currentShooter) { // Can't shoot self. return(true); } if ((thing.Flags & MobjFlags.Shootable) == 0) { // Corpse or something. return(true); } // Check angles to see if the thing can be aimed at. var dist = currentRange * intercept.Frac; var thingTopSlope = (thing.Z + thing.Height - currentShooterZ) / dist; if (thingTopSlope < currentAimSlope) { // Shot over the thing. return(true); } var thingBottomSlope = (thing.Z - currentShooterZ) / dist; if (thingBottomSlope > currentAimSlope) { // Shot under the thing. return(true); } // Hit thing. // Position a bit closer. var frac = intercept.Frac - Fixed.FromInt(10) / currentRange; var x = pt.Trace.X + pt.Trace.Dx * frac; var y = pt.Trace.Y + pt.Trace.Dy * frac; var z = currentShooterZ + currentAimSlope * (frac * currentRange); //* implement if(PuffType == MT_BLASTERPUFF1) // { // Make blaster big puff // mo = P_SpawnMobj(x, y, z, MT_BLASTERPUFF2); // S_StartSound(mo, sfx_blshit); // } // else // { SpawnPuff(x, y, z); // } if (currentDamage != 0) { //* implement if(!(in->d.thing->flags&MF_NOBLOOD) && P_Random() < 192) // { // P_BloodSplatter(x, y, z, in->d.thing); // } world.ThingInteraction.DamageMobj(thing, currentShooter, currentShooter, currentDamage); } // Don't go any farther. return(false); } }