public Mobj SpawnMissile(Mobj source, Mobj dest, MobjType type) { var missile = SpawnMobj( source.X, source.Y, source.Z + Fixed.FromInt(32), type); if (missile.Info.SeeSound != 0) { world.StartSound(missile, missile.Info.SeeSound); } // Where it came from? missile.Target = source; var angle = Geometry.PointToAngle( source.X, source.Y, dest.X, dest.Y); // Fuzzy player. if ((dest.Flags & MobjFlags.Shadow) != 0) { var random = world.Random; angle += new Angle((random.Next() - random.Next()) << 20); } var speed = GetMissileSpeed(missile.Type); missile.Angle = angle; missile.MomX = new Fixed(speed) * Trig.Cos(angle); missile.MomY = new Fixed(speed) * Trig.Sin(angle); var dist = Geometry.AproxDistance( dest.X - source.X, dest.Y - source.Y); var num = (dest.Z - source.Z).Data; var den = (dist / speed).Data; if (den < 1) { den = 1; } missile.MomZ = new Fixed(num / den); CheckMissileSpawn(missile); return(missile); }
/// <summary> /// Looks for special lines in front of the player to activate. /// </summary> public void UseLines(Player player) { var pt = world.PathTraversal; useThing = player.Mobj; var angle = player.Mobj.Angle; var x1 = player.Mobj.X; var y1 = player.Mobj.Y; var x2 = x1 + useRange.ToIntFloor() * Trig.Cos(angle); var y2 = y1 + useRange.ToIntFloor() * Trig.Sin(angle); pt.PathTraverse(x1, y1, x2, y2, PathTraverseFlags.AddLines, useTraverseFunc); }
// // P_UseLines // Looks for special lines in front of the player to activate. // public void UseLines(Player player) { var pt = world.PathTraversal; usething = player.Mobj; var angle = player.Mobj.Angle; // >> ANGLETOFINESHIFT; var x1 = player.Mobj.X; var y1 = player.Mobj.Y; var x2 = x1 + (World.USERANGE.Data >> Fixed.FracBits) * Trig.Cos(angle); // finecosine[angle]; var y2 = y1 + (World.USERANGE.Data >> Fixed.FracBits) * Trig.Sin(angle); // finesine[angle]; pt.PathTraverse(x1, y1, x2, y2, PathTraverseFlags.AddLines, ic => PTR_UseTraverse(ic)); }
/// <summary> /// Fire a hitscan bullet. /// If damage == 0, it is just a test trace that will leave linetarget set. /// </summary> public void LineAttack(Mobj shooter, Angle angle, Fixed range, Fixed slope, int damage) { currentShooter = shooter; currentShooterZ = shooter.Z + (shooter.Height >> 1) + Fixed.FromInt(8); currentRange = range; currentAimSlope = slope; currentDamage = damage; var targetX = shooter.X + range.ToIntFloor() * Trig.Cos(angle); var targetY = shooter.Y + range.ToIntFloor() * Trig.Sin(angle); world.PathTraversal.PathTraverse( shooter.X, shooter.Y, targetX, targetY, PathTraverseFlags.AddLines | PathTraverseFlags.AddThings, shootTraverseFunc); }
public void SpawnPlayerMissile(Mobj source, MobjType type) { var hs = world.Hitscan; // See which target is to be aimed at. var angle = source.Angle; var slope = hs.AimLineAttack(source, angle, Fixed.FromInt(16 * 64)); if (hs.LineTarget == null) { angle += new Angle(1 << 26); slope = hs.AimLineAttack(source, angle, Fixed.FromInt(16 * 64)); if (hs.LineTarget == null) { angle -= new Angle(2 << 26); slope = hs.AimLineAttack(source, angle, Fixed.FromInt(16 * 64)); } if (hs.LineTarget == null) { angle = source.Angle; slope = Fixed.Zero; } } var x = source.X; var y = source.Y; var z = source.Z + Fixed.FromInt(32); var missile = SpawnMobj(x, y, z, type); if (missile.Info.SeeSound != 0) { world.StartSound(missile, missile.Info.SeeSound); } missile.Target = source; missile.Angle = angle; missile.MomX = new Fixed(missile.Info.Speed) * Trig.Cos(angle); missile.MomY = new Fixed(missile.Info.Speed) * Trig.Sin(angle); missile.MomZ = new Fixed(missile.Info.Speed) * slope; CheckMissileSpawn(missile); }
public void FatAttack2(Mobj actor) { FaceTarget(actor); var ta = world.ThingAllocation; // Now here choose opposite deviation. actor.Angle -= fatSpread; ta.SpawnMissile(actor, actor.Target, MobjType.Fatshot); var missile = ta.SpawnMissile(actor, actor.Target, MobjType.Fatshot); missile.Angle -= fatSpread * 2; var angle = missile.Angle; missile.MomX = new Fixed(missile.Info.Speed) * Trig.Cos(angle); missile.MomY = new Fixed(missile.Info.Speed) * Trig.Sin(angle); }
public void FatAttack1(Mobj actor) { FaceTarget(actor); var ta = world.ThingAllocation; // Change direction to... actor.Angle += fatSpread; ta.SpawnMissile(actor, actor.Target, MobjType.Fatshot); var missile = ta.SpawnMissile(actor, actor.Target, MobjType.Fatshot); missile.Angle += fatSpread; var angle = missile.Angle; missile.MomX = new Fixed(missile.Info.Speed) * Trig.Cos(angle); missile.MomY = new Fixed(missile.Info.Speed) * Trig.Sin(angle); }
public void PainShootSkull(Mobj actor, Angle angle) { // Count total number of skull currently on the level. var count = 0; foreach (var thinker in world.Thinkers) { var mobj = thinker as Mobj; if (mobj != null && mobj.Type == MobjType.Skull) { count++; } } // If there are allready 20 skulls on the level, // don't spit another one. if (count > 20) { return; } // Okay, there's playe for another one. var preStep = Fixed.FromInt(4) + 3 * (actor.Info.Radius + DoomInfo.MobjInfos[(int)MobjType.Skull].Radius) / 2; var x = actor.X + preStep * Trig.Cos(angle); var y = actor.Y + preStep * Trig.Sin(angle); var z = actor.Z + Fixed.FromInt(8); var skull = world.ThingAllocation.SpawnMobj(x, y, z, MobjType.Skull); // Check for movements. if (!world.ThingMovement.TryMove(skull, skull.X, skull.Y)) { // Kill it immediately. world.ThingInteraction.DamageMobj(skull, actor, actor, 10000); return; } skull.Target = actor.Target; SkullAttack(skull); }
public void FatAttack3(Mobj actor) { FaceTarget(actor); var ta = world.ThingAllocation; var missile1 = ta.SpawnMissile(actor, actor.Target, MobjType.Fatshot); missile1.Angle -= fatSpread / 2; var angle1 = missile1.Angle; missile1.MomX = new Fixed(missile1.Info.Speed) * Trig.Cos(angle1); missile1.MomY = new Fixed(missile1.Info.Speed) * Trig.Sin(angle1); var missile2 = ta.SpawnMissile(actor, actor.Target, MobjType.Fatshot); missile2.Angle += fatSpread / 2; var angle2 = missile2.Angle; missile2.MomX = new Fixed(missile2.Info.Speed) * Trig.Cos(angle2); missile2.MomY = new Fixed(missile2.Info.Speed) * Trig.Sin(angle2); }
/// <summary> /// Adjusts the x and y movement so that the next move will /// slide along the wall. /// </summary> private void HitSlideLine(LineDef line) { if (line.SlopeType == SlopeType.Horizontal) { slideMoveY = Fixed.Zero; return; } if (line.SlopeType == SlopeType.Vertical) { slideMoveX = Fixed.Zero; return; } var side = Geometry.PointOnLineSide(slideThing.X, slideThing.Y, line); var lineAngle = Geometry.PointToAngle(Fixed.Zero, Fixed.Zero, line.Dx, line.Dy); if (side == 1) { lineAngle += Angle.Ang180; } var moveAngle = Geometry.PointToAngle(Fixed.Zero, Fixed.Zero, slideMoveX, slideMoveY); var deltaAngle = moveAngle - lineAngle; if (deltaAngle > Angle.Ang180) { deltaAngle += Angle.Ang180; } var moveDist = Geometry.AproxDistance(slideMoveX, slideMoveY); var newDist = moveDist * Trig.Cos(deltaAngle); slideMoveX = newDist * Trig.Cos(lineAngle); slideMoveY = newDist * Trig.Sin(lineAngle); }
// // G_CheckSpot // Returns false if the player cannot be respawned // at the given mapthing_t spot // because something is occupying it // public bool G_CheckSpot(int playernum, MapThing mthing) { if (Players[playernum].Mobj == null) { // first spawn of level, before corpses for (var i = 0; i < playernum; i++) { if (Players[i].Mobj.X == mthing.X && Players[i].Mobj.Y == mthing.Y) { return(false); } } return(true); } var x = mthing.X; var y = mthing.Y; if (!thingMovement.CheckPosition(Players[playernum].Mobj, x, y)) { return(false); } // flush an old corpse if needed if (bodyqueslot >= BODYQUESIZE) { thingAllocation.RemoveMobj(bodyque[bodyqueslot % BODYQUESIZE]); } bodyque[bodyqueslot % BODYQUESIZE] = Players[playernum].Mobj; bodyqueslot++; // spawn a teleport fog var ss = Geometry.PointInSubsector(x, y, map); var an = (Angle.Ang45.Data >> Trig.AngleToFineShift) * ((int)Math.Round(mthing.Angle.ToDegree()) / 45); Fixed xa; Fixed ya; switch (an) { case 4096: // -4096: xa = Trig.Tan(2048); // finecosine[-4096] ya = Trig.Tan(0); // finesine[-4096] break; case 5120: // -3072: xa = Trig.Tan(3072); // finecosine[-3072] ya = Trig.Tan(1024); // finesine[-3072] break; case 6144: // -2048: xa = Trig.Sin(0); // finecosine[-2048] ya = Trig.Tan(2048); // finesine[-2048] break; case 7168: // -1024: xa = Trig.Sin(1024); // finecosine[-1024] ya = Trig.Tan(3072); // finesine[-1024] break; case 0: case 1024: case 2048: case 3072: xa = Trig.Cos((int)an); ya = Trig.Sin((int)an); break; default: throw new Exception("G_CheckSpot: unexpected angle " + an); } var mo = thingAllocation.SpawnMobj( x + 20 * xa, y + 20 * ya, ss.Sector.FloorHeight, MobjType.Tfog); if (Players[Options.ConsolePlayer].ViewZ != new Fixed(1)) { // don't start sound on first frame StartSound(mo, Sfx.TELEPT); } return(true); }
/// <summary> /// Calculate the angle of the line passing through the two points. /// </summary> public static Angle PointToAngle(Fixed fromX, Fixed fromY, Fixed toX, Fixed toY) { var x = toX - fromX; var y = toY - fromY; if (x == Fixed.Zero && y == Fixed.Zero) { return(Angle.Ang0); } if (x >= Fixed.Zero) { // x >= 0 if (y >= Fixed.Zero) { // y >= 0 if (x > y) { // octant 0 return(Trig.TanToAngle(SlopeDiv(y, x))); } else { // octant 1 return(new Angle(Angle.Ang90.Data - 1) - Trig.TanToAngle(SlopeDiv(x, y))); } } else { // y < 0 y = -y; if (x > y) { // octant 8 return(-Trig.TanToAngle(SlopeDiv(y, x))); } else { // octant 7 return(Angle.Ang270 + Trig.TanToAngle(SlopeDiv(x, y))); } } } else { // x < 0 x = -x; if (y >= Fixed.Zero) { // y >= 0 if (x > y) { // octant 3 return(new Angle(Angle.Ang180.Data - 1) - Trig.TanToAngle(SlopeDiv(y, x))); } else { // octant 2 return(Angle.Ang90 + Trig.TanToAngle(SlopeDiv(x, y))); } } else { // y < 0 y = -y; if (x > y) { // octant 4 return(Angle.Ang180 + Trig.TanToAngle(SlopeDiv(y, x))); } else { // octant 5 return(new Angle(Angle.Ang270.Data - 1) - Trig.TanToAngle(SlopeDiv(x, y))); } } } }
/// <summary> /// Calculate the walking / running height adjustment. /// </summary> public void CalcHeight(Player player) { // Regular movement bobbing. // It needs to be calculated for gun swing even if not on ground. player.Bob = player.Mobj.MomX * player.Mobj.MomX + player.Mobj.MomY * player.Mobj.MomY; player.Bob >>= 2; if (player.Bob > maxBob) { player.Bob = maxBob; } if ((player.Cheats & CheatFlags.NoMomentum) != 0 || !onGround) { player.ViewZ = player.Mobj.Z + Player.NormalViewHeight; if (player.ViewZ > player.Mobj.CeilingZ - Fixed.FromInt(4)) { player.ViewZ = player.Mobj.CeilingZ - Fixed.FromInt(4); } player.ViewZ = player.Mobj.Z + player.ViewHeight; return; } var angle = (Trig.FineAngleCount / 20 * world.LevelTime) & Trig.FineMask; var bob = (player.Bob / 2) * Trig.Sin(angle); // Move viewheight. if (player.PlayerState == PlayerState.Live) { player.ViewHeight += player.DeltaViewHeight; if (player.ViewHeight > Player.NormalViewHeight) { player.ViewHeight = Player.NormalViewHeight; player.DeltaViewHeight = Fixed.Zero; } if (player.ViewHeight < Player.NormalViewHeight / 2) { player.ViewHeight = Player.NormalViewHeight / 2; if (player.DeltaViewHeight <= Fixed.Zero) { player.DeltaViewHeight = new Fixed(1); } } if (player.DeltaViewHeight != Fixed.Zero) { player.DeltaViewHeight += Fixed.One / 4; if (player.DeltaViewHeight == Fixed.Zero) { player.DeltaViewHeight = new Fixed(1); } } } player.ViewZ = player.Mobj.Z + player.ViewHeight + bob; if (player.ViewZ > player.Mobj.CeilingZ - Fixed.FromInt(4)) { player.ViewZ = player.Mobj.CeilingZ - Fixed.FromInt(4); } }
/// <summary> /// Damages both enemies and players. /// "inflictor" is the thing that caused the damage creature /// or missile, can be null (slime, etc). /// "source" is the thing to target after taking damage creature /// or null. /// Source and inflictor are the same for melee attacks. /// Source can be null for slime, barrel explosions and other /// environmental stuff. /// </summary> public void DamageMobj(Mobj target, Mobj inflictor, Mobj source, int damage) { if ((target.Flags & MobjFlags.Shootable) == 0) { // Shouldn't happen... return; } if (target.Health <= 0) { return; } if ((target.Flags & MobjFlags.SkullFly) != 0) { target.MomX = target.MomY = target.MomZ = Fixed.Zero; } var player = target.Player; if (player != null && world.Options.Skill == GameSkill.Baby) { // Take half damage in trainer mode. damage >>= 1; } // Some close combat weapons should not inflict thrust and // push the victim out of reach, thus kick away unless using the chainsaw. var notChainsawAttack = source == null || source.Player == null || source.Player.ReadyWeapon != WeaponType.Chainsaw; if (inflictor != null && (target.Flags & MobjFlags.NoClip) == 0 && notChainsawAttack) { var ang = Geometry.PointToAngle( inflictor.X, inflictor.Y, target.X, target.Y); var thrust = new Fixed(damage * (Fixed.FracUnit >> 3) * 100 / target.Info.Mass); // Make fall forwards sometimes. if (damage < 40 && damage > target.Health && target.Z - inflictor.Z > Fixed.FromInt(64) && (world.Random.Next() & 1) != 0) { ang += Angle.Ang180; thrust *= 4; } target.MomX += thrust * Trig.Cos(ang); target.MomY += thrust * Trig.Sin(ang); } // Player specific. if (player != null) { // End of game hell hack. if (target.Subsector.Sector.Special == (SectorSpecial)11 && damage >= target.Health) { damage = target.Health - 1; } // Below certain threshold, ignore damage in GOD mode, or with INVUL power. if (damage < 1000 && ((player.Cheats & CheatFlags.GodMode) != 0 || player.Powers[(int)PowerType.Invulnerability] > 0)) { return; } int saved; if (player.ArmorType != 0) { if (player.ArmorType == 1) { saved = damage / 3; } else { saved = damage / 2; } if (player.ArmorPoints <= saved) { // Armor is used up. saved = player.ArmorPoints; player.ArmorType = 0; } player.ArmorPoints -= saved; damage -= saved; } // Mirror mobj health here for Dave. player.Health -= damage; if (player.Health < 0) { player.Health = 0; } player.Attacker = source; // Add damage after armor / invuln. player.DamageCount += damage; if (player.DamageCount > 100) { // Teleport stomp does 10k points... player.DamageCount = 100; } } // Do the damage. target.Health -= damage; if (target.Health <= 0) { KillMobj(source, target); return; } if ((world.Random.Next() < target.Info.PainChance) && (target.Flags & MobjFlags.SkullFly) == 0) { // Fight back! target.Flags |= MobjFlags.JustHit; target.SetState(target.Info.PainState); } // We're awake now... target.ReactionTime = 0; if ((target.Threshold == 0 || target.Type == MobjType.Vile) && source != null && source != target && source.Type != MobjType.Vile) { // If not intent on another player, chase after this one. target.Target = source; target.Threshold = baseThreshold; if (target.State == DoomInfo.States[(int)target.Info.SpawnState] && target.Info.SeeState != MobjState.Null) { target.SetState(target.Info.SeeState); } } }
/// <summary> /// Moves the given origin along a given angle. /// </summary> public void Thrust(Player player, Angle angle, Fixed move) { player.Mobj.MomX += move * Trig.Cos(angle); player.Mobj.MomY += move * Trig.Sin(angle); }
public void Tracer(Mobj actor) { if ((world.Options.GameTic & 3) != 0) { return; } // Spawn a puff of smoke behind the rocket. world.Hitscan.SpawnPuff(actor.X, actor.Y, actor.Z); var smoke = world.ThingAllocation.SpawnMobj( actor.X - actor.MomX, actor.Y - actor.MomY, actor.Z, MobjType.Smoke); smoke.MomZ = Fixed.One; smoke.Tics -= world.Random.Next() & 3; if (smoke.Tics < 1) { smoke.Tics = 1; } // Adjust direction. var dest = actor.Tracer; if (dest == null || dest.Health <= 0) { return; } // Change angle. var exact = Geometry.PointToAngle( actor.X, actor.Y, dest.X, dest.Y); if (exact != actor.Angle) { if (exact - actor.Angle > Angle.Ang180) { actor.Angle -= traceAngle; if (exact - actor.Angle < Angle.Ang180) { actor.Angle = exact; } } else { actor.Angle += traceAngle; if (exact - actor.Angle > Angle.Ang180) { actor.Angle = exact; } } } exact = actor.Angle; actor.MomX = new Fixed(actor.Info.Speed) * Trig.Cos(exact); actor.MomY = new Fixed(actor.Info.Speed) * Trig.Sin(exact); // Change slope. var dist = Geometry.AproxDistance( dest.X - actor.X, dest.Y - actor.Y); var num = (dest.Z + Fixed.FromInt(40) - actor.Z).Data; var den = dist.Data / actor.Info.Speed; if (den < 1) { den = 1; } var slope = new Fixed(num / den); if (slope < actor.MomZ) { actor.MomZ -= Fixed.One / 8; } else { actor.MomZ += Fixed.One / 8; } }
/// <summary> /// Returns false if the player cannot be respawned at the given /// mapthing spot because something is occupying it. /// </summary> public bool CheckSpot(int playernum, MapThing mthing) { var players = world.Options.Players; if (players[playernum].Mobj == null) { // First spawn of level, before corpses. for (var i = 0; i < playernum; i++) { if (players[i].Mobj.X == mthing.X && players[i].Mobj.Y == mthing.Y) { return(false); } } return(true); } var x = mthing.X; var y = mthing.Y; if (!world.ThingMovement.CheckPosition(players[playernum].Mobj, x, y)) { return(false); } // Flush an old corpse if needed. if (bodyQueSlot >= bodyQueSize) { RemoveMobj(bodyQue[bodyQueSlot % bodyQueSize]); } bodyQue[bodyQueSlot % bodyQueSize] = players[playernum].Mobj; bodyQueSlot++; // Spawn a teleport fog. var subsector = Geometry.PointInSubsector(x, y, world.Map); var angle = (Angle.Ang45.Data >> Trig.AngleToFineShift) * ((int)Math.Round(mthing.Angle.ToDegree()) / 45); // // The code below to reproduce respawn fog bug in deathmath // is based on Chocolate Doom's implementation. // Fixed xa; Fixed ya; switch (angle) { case 4096: // -4096: xa = Trig.Tan(2048); // finecosine[-4096] ya = Trig.Tan(0); // finesine[-4096] break; case 5120: // -3072: xa = Trig.Tan(3072); // finecosine[-3072] ya = Trig.Tan(1024); // finesine[-3072] break; case 6144: // -2048: xa = Trig.Sin(0); // finecosine[-2048] ya = Trig.Tan(2048); // finesine[-2048] break; case 7168: // -1024: xa = Trig.Sin(1024); // finecosine[-1024] ya = Trig.Tan(3072); // finesine[-1024] break; case 0: case 1024: case 2048: case 3072: xa = Trig.Cos((int)angle); ya = Trig.Sin((int)angle); break; default: throw new Exception("Unexpected angle: " + angle); } var mo = SpawnMobj( x + 20 * xa, y + 20 * ya, subsector.Sector.FloorHeight, MobjType.Tfog); if (!world.FirstTicIsNotYetDone) { // Don't start sound on first frame. world.StartSound(mo, Sfx.TELEPT, SfxType.Misc); } return(true); }