/// <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);
                }
            }
        }
Exemple #4
0
        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;
            }
        }