/// <summary> /// Returns true if the traverser function returns true for all lines. /// </summary> private bool TraverseIntercepts(Func <Intercept, bool> func, Fixed maxFrac) { var count = this.interceptCount; Intercept intercept = null; while (count-- > 0) { var dist = Fixed.MaxValue; for (var i = 0; i < this.interceptCount; i++) { if (this.intercepts[i].Frac < dist) { dist = this.intercepts[i].Frac; intercept = this.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> /// 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 = this.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 = this.currentRange * intercept.Frac; if (line.FrontSector.FloorHeight != line.BackSector.FloorHeight) { var slope = (mc.OpenBottom - this.currentShooterZ) / dist; if (slope > this.bottomSlope) { this.bottomSlope = slope; } } if (line.FrontSector.CeilingHeight != line.BackSector.CeilingHeight) { var slope = (mc.OpenTop - this.currentShooterZ) / dist; if (slope < this.topSlope) { this.topSlope = slope; } } if (this.topSlope <= this.bottomSlope) { // Stop. return(false); } // Shot continues. return(true); } // Shoot a thing. var thing = intercept.Thing; if (thing == this.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 = this.currentRange * intercept.Frac; var thingTopSlope = (thing.Z + thing.Height - this.currentShooterZ) / dist; if (thingTopSlope < this.bottomSlope) { // Shot over the thing. return(true); } var thingBottomSlope = (thing.Z - this.currentShooterZ) / dist; if (thingBottomSlope > this.topSlope) { // Shot under the thing. return(true); } // This thing can be hit! if (thingTopSlope > this.topSlope) { thingTopSlope = this.topSlope; } if (thingBottomSlope < this.bottomSlope) { thingBottomSlope = this.bottomSlope; } this.currentAimSlope = (thingTopSlope + thingBottomSlope) / 2; this.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 = this.world.MapInteraction; var pt = this.world.PathTraversal; if (intercept.Line != null) { var line = intercept.Line; if (line.Special != 0) { mi.ShootSpecialLine(this.currentShooter, line); } if ((line.Flags & LineFlags.TwoSided) == 0) { goto hitLine; } var mc = this.world.MapCollision; // Crosses a two sided line. mc.LineOpening(line); var dist = this.currentRange * intercept.Frac; if (line.FrontSector.FloorHeight != line.BackSector.FloorHeight) { var slope = (mc.OpenBottom - this.currentShooterZ) / dist; if (slope > this.currentAimSlope) { goto hitLine; } } if (line.FrontSector.CeilingHeight != line.BackSector.CeilingHeight) { var slope = (mc.OpenTop - this.currentShooterZ) / dist; if (slope < this.currentAimSlope) { goto hitLine; } } // Shot continues. return(true); // Hit line. hitLine: // Position a bit closer. var frac = intercept.Frac - Fixed.FromInt(4) / this.currentRange; var x = pt.Trace.X + pt.Trace.Dx * frac; var y = pt.Trace.Y + pt.Trace.Dy * frac; var z = this.currentShooterZ + this.currentAimSlope * (frac * this.currentRange); if (line.FrontSector.CeilingFlat == this.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 == this.world.Map.SkyFlatNumber) { return(false); } } // Spawn bullet puffs. this.SpawnPuff(x, y, z); // Don't go any farther. return(false); } { // Shoot a thing. var thing = intercept.Thing; if (thing == this.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 = this.currentRange * intercept.Frac; var thingTopSlope = (thing.Z + thing.Height - this.currentShooterZ) / dist; if (thingTopSlope < this.currentAimSlope) { // Shot over the thing. return(true); } var thingBottomSlope = (thing.Z - this.currentShooterZ) / dist; if (thingBottomSlope > this.currentAimSlope) { // Shot under the thing. return(true); } // Hit thing. // Position a bit closer. var frac = intercept.Frac - Fixed.FromInt(10) / this.currentRange; var x = pt.Trace.X + pt.Trace.Dx * frac; var y = pt.Trace.Y + pt.Trace.Dy * frac; var z = this.currentShooterZ + this.currentAimSlope * (frac * this.currentRange); // Spawn bullet puffs or blod spots, depending on target type. if ((intercept.Thing.Flags & MobjFlags.NoBlood) != 0) { this.SpawnPuff(x, y, z); } else { this.SpawnBlood(x, y, z, this.currentDamage); } if (this.currentDamage != 0) { this.world.ThingInteraction.DamageMobj(thing, this.currentShooter, this.currentShooter, this.currentDamage); } // Don't go any farther. return(false); } }