/// <summary> /// Called when the missile hits something (wall or thing). /// </summary> public void ExplodeMissile(Mobj thing) { thing.MomX = thing.MomY = thing.MomZ = Fixed.Zero; thing.SetState(DoomInfo.MobjInfos[(int)thing.Type].DeathState); thing.Tics -= this.world.Random.Next() & 3; if (thing.Tics < 1) { thing.Tics = 1; } thing.Flags &= ~MobjFlags.Missile; if (thing.Info.DeathSound != 0) { this.world.StartSound(thing, thing.Info.DeathSound, SfxType.Misc); } }
/// <summary> /// Called when the target is killed. /// </summary> public void KillMobj(Mobj source, Mobj target) { target.Flags &= ~(MobjFlags.Shootable | MobjFlags.Float | MobjFlags.SkullFly); if (target.Type != MobjType.Skull) { target.Flags &= ~MobjFlags.NoGravity; } target.Flags |= MobjFlags.Corpse | MobjFlags.DropOff; target.Height = new Fixed(target.Height.Data >> 2); if (source != null && source.Player != null) { // Count for intermission. if ((target.Flags & MobjFlags.CountKill) != 0) { source.Player.KillCount++; } if (target.Player != null) { source.Player.Frags[target.Player.Number]++; } } else if (!this.world.Options.NetGame && (target.Flags & MobjFlags.CountKill) != 0) { // Count all monster deaths, even those caused by other monsters. this.world.Options.Players[0].KillCount++; } if (target.Player != null) { // Count environment kills against you. if (source == null) { target.Player.Frags[target.Player.Number]++; } target.Flags &= ~MobjFlags.Solid; target.Player.PlayerState = PlayerState.Dead; this.world.PlayerBehavior.DropWeapon(target.Player); var am = this.world.AutoMap; if (target.Player.Number == this.world.Options.ConsolePlayer && am.Visible) { // Don't die in auto map, switch view prior to dying. am.Close(); } } if (target.Health < -target.Info.SpawnHealth && target.Info.XdeathState != 0) { target.SetState(target.Info.XdeathState); } else { target.SetState(target.Info.DeathState); } target.Tics -= this.world.Random.Next() & 3; if (target.Tics < 1) { target.Tics = 1; } // Drop stuff. // This determines the kind of object spawned during the death frame of a thing. MobjType item; switch (target.Type) { case MobjType.Wolfss: case MobjType.Possessed: item = MobjType.Clip; break; case MobjType.Shotguy: item = MobjType.Shotgun; break; case MobjType.Chainguy: item = MobjType.Chaingun; break; default: return; } var mo = this.world.ThingAllocation.SpawnMobj(target.X, target.Y, Mobj.OnFloorZ, item); // Special versions of items. mo.Flags |= MobjFlags.Dropped; }
/// <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 && this.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.Info is WeaponChainsaw); 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) && (this.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) { this.KillMobj(source, target); return; } if ((this.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 = ThingInteraction.baseThreshold; if (target.State == DoomInfo.States[(int)target.Info.SpawnState] && target.Info.SeeState != MobjState.Null) { target.SetState(target.Info.SeeState); } } }
public void XYMovement(Mobj thing) { if (thing.MomX == Fixed.Zero && thing.MomY == Fixed.Zero) { if ((thing.Flags & MobjFlags.SkullFly) != 0) { // The skull slammed into something. thing.Flags &= ~MobjFlags.SkullFly; thing.MomX = thing.MomY = thing.MomZ = Fixed.Zero; thing.SetState(thing.Info.SpawnState); } return; } var player = thing.Player; if (thing.MomX > ThingMovement.maxMove) { thing.MomX = ThingMovement.maxMove; } else if (thing.MomX < -ThingMovement.maxMove) { thing.MomX = -ThingMovement.maxMove; } if (thing.MomY > ThingMovement.maxMove) { thing.MomY = ThingMovement.maxMove; } else if (thing.MomY < -ThingMovement.maxMove) { thing.MomY = -ThingMovement.maxMove; } var moveX = thing.MomX; var moveY = thing.MomY; do { Fixed pMoveX; Fixed pMoveY; if (moveX > ThingMovement.maxMove / 2 || moveY > ThingMovement.maxMove / 2) { pMoveX = thing.X + moveX / 2; pMoveY = thing.Y + moveY / 2; moveX >>= 1; moveY >>= 1; } else { pMoveX = thing.X + moveX; pMoveY = thing.Y + moveY; moveX = moveY = Fixed.Zero; } if (!this.TryMove(thing, pMoveX, pMoveY)) { // Blocked move. if (thing.Player != null) { // Try to slide along it. this.SlideMove(thing); } else if ((thing.Flags & MobjFlags.Missile) != 0) { // Explode a missile. if (this.currentCeilingLine != null && this.currentCeilingLine.BackSector != null && this.currentCeilingLine.BackSector.CeilingFlat == this.world.Map.SkyFlatNumber) { // Hack to prevent missiles exploding against the sky. // Does not handle sky floors. this.world.ThingAllocation.RemoveMobj(thing); return; } this.world.ThingInteraction.ExplodeMissile(thing); } else { thing.MomX = thing.MomY = Fixed.Zero; } } }while (moveX != Fixed.Zero || moveY != Fixed.Zero); // Slow down. if (player != null && (player.Cheats & CheatFlags.NoMomentum) != 0) { // Debug option for no sliding at all. thing.MomX = thing.MomY = Fixed.Zero; return; } if ((thing.Flags & (MobjFlags.Missile | MobjFlags.SkullFly)) != 0) { // No friction for missiles ever. return; } if (thing.Z > thing.FloorZ) { // No friction when airborne. return; } if ((thing.Flags & MobjFlags.Corpse) != 0) { // Do not stop sliding if halfway off a step with some momentum. if (thing.MomX > Fixed.One / 4 || thing.MomX < -Fixed.One / 4 || thing.MomY > Fixed.One / 4 || thing.MomY < -Fixed.One / 4) { if (thing.FloorZ != thing.Subsector.Sector.FloorHeight) { return; } } } if (thing.MomX > -ThingMovement.stopSpeed && thing.MomX < ThingMovement.stopSpeed && thing.MomY > -ThingMovement.stopSpeed && thing.MomY < ThingMovement.stopSpeed && (player == null || (player.Cmd.ForwardMove == 0 && player.Cmd.SideMove == 0))) { // If in a walking frame, stop moving. if (player != null && (player.Mobj.State.Number - (int)MobjState.PlayRun1) < 4) { player.Mobj.SetState(MobjState.Play); } thing.MomX = Fixed.Zero; thing.MomY = Fixed.Zero; } else { thing.MomX = thing.MomX * ThingMovement.friction; thing.MomY = thing.MomY * ThingMovement.friction; } }