public void VileChase(Mobj actor) { if (actor.MoveDir != Direction.None) { // Check for corpses to raise. vileTryX = actor.X + actor.Info.Speed * xSpeed[(int)actor.MoveDir]; vileTryY = actor.Y + actor.Info.Speed * ySpeed[(int)actor.MoveDir]; var bm = world.Map.BlockMap; var maxRadius = GameConstants.MaxThingRadius * 2; var blockX1 = bm.GetBlockX(vileTryX - maxRadius); var blockX2 = bm.GetBlockX(vileTryX + maxRadius); var blockY1 = bm.GetBlockY(vileTryY - maxRadius); var blockY2 = bm.GetBlockY(vileTryY + maxRadius); for (var bx = blockX1; bx <= blockX2; bx++) { for (var by = blockY1; by <= blockY2; by++) { // Call VileCheck to check whether object is a corpse that canbe raised. if (!bm.IterateThings(bx, by, vileCheckFunc)) { // Got one! var temp = actor.Target; actor.Target = vileTargetCorpse; FaceTarget(actor); actor.Target = temp; actor.SetState(MobjState.VileHeal1); world.StartSound(vileTargetCorpse, Sfx.SLOP); var info = vileTargetCorpse.Info; vileTargetCorpse.SetState(info.Raisestate); vileTargetCorpse.Height <<= 2; vileTargetCorpse.Flags = info.Flags; vileTargetCorpse.Health = info.SpawnHealth; vileTargetCorpse.Target = null; return; } } } } // Return to normal attack. Chase(actor); }
public void CPosRefire(Mobj actor) { // Keep firing unless target got out of sight. FaceTarget(actor); if (world.Random.Next() < 40) { return; } if (actor.Target == null || actor.Target.Health <= 0 || !world.VisibilityCheck.CheckSight(actor, actor.Target)) { actor.SetState(actor.Info.SeeState); } }
/// <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 -= world.Random.Next() & 3; if (thing.Tics < 1) { thing.Tics = 1; } thing.Flags &= ~MobjFlags.Missile; if (thing.Info.DeathSound != 0) { 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 (!world.Options.NetGame && (target.Flags & MobjFlags.CountKill) != 0) { // Count all monster deaths, even those caused by other monsters. 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; world.PlayerBehavior.DropWeapon(target.Player); var am = world.AutoMap; if (target.Player.Number == 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 -= 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 = 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 && 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); } } }
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 > maxMove) { thing.MomX = maxMove; } else if (thing.MomX < -maxMove) { thing.MomX = -maxMove; } if (thing.MomY > maxMove) { thing.MomY = maxMove; } else if (thing.MomY < -maxMove) { thing.MomY = -maxMove; } var moveX = thing.MomX; var moveY = thing.MomY; do { Fixed pMoveX; Fixed pMoveY; if (moveX > maxMove / 2 || moveY > 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 (!TryMove(thing, pMoveX, pMoveY)) { // Blocked move. if (thing.Player != null) { // Try to slide along it. SlideMove(thing); } else if ((thing.Flags & MobjFlags.Missile) != 0) { // Explode a missile. if (currentCeilingLine != null && currentCeilingLine.BackSector != null && currentCeilingLine.BackSector.CeilingFlat == world.Map.SkyFlatNumber) { // Hack to prevent missiles exploding against the sky. // Does not handle sky floors. world.ThingAllocation.RemoveMobj(thing); return; } 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 > -stopSpeed && thing.MomX < stopSpeed && thing.MomY > -stopSpeed && thing.MomY < 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 * friction; thing.MomY = thing.MomY * friction; } }
private bool CheckThing(Mobj thing) { if ((thing.Flags & (MobjFlags.Solid | MobjFlags.Special | MobjFlags.Shootable)) == 0) { return(true); } var blockDist = thing.Radius + currentThing.Radius; if (Fixed.Abs(thing.X - currentX) >= blockDist || Fixed.Abs(thing.Y - currentY) >= blockDist) { // Didn't hit it. return(true); } // Don't clip against self. if (thing == currentThing) { return(true); } // Check for skulls slamming into things. if ((currentThing.Flags & MobjFlags.SkullFly) != 0) { var damage = ((world.Random.Next() % 8) + 1) * currentThing.Info.Damage; world.ThingInteraction.DamageMobj(thing, currentThing, currentThing, damage); currentThing.Flags &= ~MobjFlags.SkullFly; currentThing.MomX = currentThing.MomY = currentThing.MomZ = Fixed.Zero; currentThing.SetState(currentThing.Info.SpawnState); // Stop moving. return(false); } // Missiles can hit other things. if ((currentThing.Flags & MobjFlags.Missile) != 0) { // See if it went over / under. if (currentThing.Z > thing.Z + thing.Height) { // Overhead. return(true); } if (currentThing.Z + currentThing.Height < thing.Z) { // Underneath. return(true); } if (currentThing.Target != null && (currentThing.Target.Type == thing.Type //(|| //(currentThing.Target.Type == MobjType.Knight && thing.Type == MobjType.Bruiser) || //(currentThing.Target.Type == MobjType.Bruiser && thing.Type == MobjType.Knight)) )) { // Don't hit same species as originator. if (thing == currentThing.Target) { return(true); } if (thing.Type != MobjType.Player && !DoomInfo.DeHackEdConst.MonstersInfight) { // Explode, but do no damage. // Let players missile other players. return(false); } } if ((thing.Flags & MobjFlags.Shootable) == 0) { // Didn't do any damage. return((thing.Flags & MobjFlags.Solid) == 0); } // Damage / explode. var damage = ((world.Random.Next() % 8) + 1) * currentThing.Info.Damage; world.ThingInteraction.DamageMobj(thing, currentThing, currentThing.Target, damage); // Don't traverse any more. return(false); } // Check for special pickup. if ((thing.Flags & MobjFlags.Special) != 0) { var solid = (thing.Flags & MobjFlags.Solid) != 0; if ((currentFlags & MobjFlags.PickUp) != 0) { // Can remove thing. world.ItemPickup.TouchSpecialThing(thing, currentThing); } return(!solid); } return((thing.Flags & MobjFlags.Solid) == 0); }
public void Look(Mobj actor) { // Any shot will wake up. actor.Threshold = 0; var target = actor.Subsector.Sector.SoundTarget; if (target != null && (target.Flags & MobjFlags.Shootable) != 0) { actor.Target = target; if ((actor.Flags & MobjFlags.Ambush) != 0) { if (world.VisibilityCheck.CheckSight(actor, actor.Target)) { goto seeYou; } } else { goto seeYou; } } if (!LookForPlayers(actor, false)) { return; } // Go into chase state. seeYou: if (actor.Info.SeeSound != 0) { int sound; switch (actor.Info.SeeSound) { case Sfx.POSIT1: case Sfx.POSIT2: case Sfx.POSIT3: sound = (int)Sfx.POSIT1 + world.Random.Next() % 3; break; case Sfx.BGSIT1: case Sfx.BGSIT2: sound = (int)Sfx.BGSIT1 + world.Random.Next() % 2; break; default: sound = (int)actor.Info.SeeSound; break; } if (actor.Type == MobjType.Spider || actor.Type == MobjType.Cyborg) { // Full volume for boss monsters. world.StartSound(null, (Sfx)sound); } else { world.StartSound(actor, (Sfx)sound); } } actor.SetState(actor.Info.SeeState); }
public void Chase(Mobj actor) { if (actor.ReactionTime > 0) { actor.ReactionTime--; } // Modify target threshold. if (actor.Threshold > 0) { if (actor.Target == null || actor.Target.Health <= 0) { actor.Threshold = 0; } else { actor.Threshold--; } } // Turn towards movement direction if not there yet. if ((int)actor.MoveDir < 8) { actor.Angle = new Angle((int)actor.Angle.Data & (7 << 29)); var delta = (int)(actor.Angle - new Angle((int)actor.MoveDir << 29)).Data; if (delta > 0) { actor.Angle -= new Angle(Angle.Ang90.Data / 2); } else if (delta < 0) { actor.Angle += new Angle(Angle.Ang90.Data / 2); } } if (actor.Target == null || (actor.Target.Flags & MobjFlags.Shootable) == 0) { // Look for a new target. if (LookForPlayers(actor, true)) { // Got a new target. return; } actor.SetState(actor.Info.SpawnState); return; } // Do not attack twice in a row. if ((actor.Flags & MobjFlags.JustAttacked) != 0) { actor.Flags &= ~MobjFlags.JustAttacked; if (world.Options.Skill != GameSkill.Nightmare && !world.Options.FastMonsters) { NewChaseDir(actor); } return; } // Check for melee attack. if (actor.Info.MeleeState != 0 && CheckMeleeRange(actor)) { if (actor.Info.AttackSound != 0) { world.StartSound(actor, actor.Info.AttackSound); } actor.SetState(actor.Info.MeleeState); return; } // Check for missile attack. if (actor.Info.MissileState != 0) { if (world.Options.Skill < GameSkill.Nightmare && !world.Options.FastMonsters && actor.MoveCount != 0) { goto noMissile; } if (!CheckMissileRange(actor)) { goto noMissile; } actor.SetState(actor.Info.MissileState); actor.Flags |= MobjFlags.JustAttacked; return; } noMissile: // Possibly choose another target. if (world.Options.NetGame && actor.Threshold == 0 && !world.VisibilityCheck.CheckSight(actor, actor.Target)) { if (LookForPlayers(actor, true)) { // Got a new target. return; } } // Chase towards player. if (--actor.MoveCount < 0 || !Move(actor)) { NewChaseDir(actor); } // Make active sound. if (actor.Info.ActiveSound != 0 && world.Random.Next() < 3) { world.StartSound(actor, actor.Info.ActiveSound); } }