Пример #1
0
        public override IEnumerable <Status> Run()
        {
            if (Zone == null)
            {
                Creature.DrawIndicator(IndicatorManager.StandardIndicators.Question);
                yield return(Status.Fail);

                yield break;
            }

            if (Resource.Count <= 0)
            {
                yield return(Status.Success);

                yield break;
            }

            List <GameComponent> createdItems = Creature.Inventory.RemoveAndCreate(Resource, Inventory.RestockType.RestockResource);

            if (createdItems.Count == 0)
            {
                yield return(Status.Success);
            }

            foreach (GameComponent b in createdItems)
            {
                if (Zone.AddItem(b))
                {
                    Creature.Stats.NumItemsGathered++;
                }
                else
                {
                    Creature.Inventory.AddResource(new ResourceAmount(Resource.Type, 1), Inventory.RestockType.RestockResource);
                    b.Delete();
                }
            }

            Creature.NoiseMaker.MakeNoise("Stockpile", Creature.AI.Position);
            Creature.CurrentCharacterMode = Creature.Stats.CurrentClass.AttackMode;
            Creature.Sprite.ResetAnimations(Creature.Stats.CurrentClass.AttackMode);
            Creature.Sprite.PlayAnimations(Creature.Stats.CurrentClass.AttackMode);
            while (!Creature.Sprite.AnimPlayer.IsDone())
            {
                yield return(Status.Running);
            }

            var resource = Library.GetResourceType(Resource.Type);

            if (resource.Tags.Contains(DwarfCorp.Resource.ResourceTags.Corpse))
            {
                Creature.AddThought("I laid a friend to rest.", new TimeSpan(0, 8, 0, 0), 10.0f);
            }

            yield return(Status.Running);

            Creature.CurrentCharacterMode = CharacterMode.Idle;
            yield return(Status.Success);
        }
Пример #2
0
        public override IEnumerable <Status> Run()
        {
            var zone     = Agent.Blackboard.GetData <Zone>(StockpileName);
            var resource = Agent.Blackboard.GetData <Resource>(ResourceName);

            if (zone == null || !(zone is Stockpile) || resource == null)
            {
                Creature.DrawIndicator(IndicatorManager.StandardIndicators.Question);
                yield return(Status.Fail);

                yield break;
            }

            var createdItems = Creature.Inventory.RemoveAndCreate(resource, Inventory.RestockType.RestockResource);

            if ((zone as Stockpile).AddResource(resource))
            {
                var toss = new TossMotion(1.0f, 2.5f, createdItems.LocalTransform, zone.GetBoundingBox().Center() + new Vector3(0.5f, 0.5f, 0.5f));

                if (createdItems.GetRoot().GetComponent <Physics>().HasValue(out var physics))
                {
                    physics.CollideMode = Physics.CollisionMode.None;
                }

                createdItems.AnimationQueue.Add(toss);
                toss.OnComplete += createdItems.Die;

                Creature.Stats.NumItemsGathered++;
            }
            else
            {
                Creature.Inventory.AddResource(resource, Inventory.RestockType.RestockResource);
                createdItems.Delete();
            }

            Creature.NoiseMaker.MakeNoise("Stockpile", Creature.AI.Position);
            Creature.CurrentCharacterMode = Creature.Stats.CurrentClass.AttackMode;
            Creature.Sprite.ResetAnimations(Creature.Stats.CurrentClass.AttackMode);
            Creature.Sprite.PlayAnimations(Creature.Stats.CurrentClass.AttackMode);

            while (!Creature.Sprite.AnimPlayer.IsDone())
            {
                yield return(Status.Running);
            }

            if (Library.GetResourceType(resource.TypeName).HasValue(out var res) && res.Tags.Contains("Corpse"))
            {
                Creature.AddThought("I laid a friend to rest.", new TimeSpan(0, 8, 0, 0), 10.0f);
            }

            yield return(Status.Running);

            Creature.CurrentCharacterMode = CharacterMode.Idle;
            yield return(Status.Success);
        }
Пример #3
0
        public override void Initialize()
        {
            var closestItem = Agent.Faction.FindNearestItemWithTags("Bed", Agent.Position, true, Agent);
            var closestZone = Agent.World.FindNearestZone(Agent.Position);

            if (!Agent.Stats.Energy.IsSatisfied() && closestItem != null)
            {
                closestItem.ReservedFor = Agent;
                Creature.AI.Blackboard.SetData("Bed", closestItem);

                var unreserveAct = new Wrap(() => Creature.Unreserve("Bed"));

                Tree =
                    new Select(
                        new Sequence(
                            new GoToEntityAct(closestItem, Creature.AI),
                            new TeleportAct(Creature.AI)
                {
                    Location = closestItem.GetRotatedBoundingBox().Center() + new Vector3(-0.0f, 0.75f, -0.0f)
                },
                            new SleepAct(Creature.AI)
                {
                    RechargeRate = 1.0f, Teleport = true, TeleportLocation = closestItem.GetRotatedBoundingBox().Center() + new Vector3(-0.0f, 0.75f, -0.0f)
                },
                            unreserveAct),
                        unreserveAct);
            }
            else if (!Agent.Stats.Energy.IsSatisfied() && closestItem == null && closestZone != null)
            {
                Creature.AddThought("I slept on the ground.", new TimeSpan(0, 8, 0, 0), -6.0f);

                Tree = new Sequence(
                    new GoToZoneAct(Creature.AI, closestZone),
                    new SleepAct(Creature.AI)
                {
                    RechargeRate = 1.0f
                });
            }
            else if (!Agent.Stats.Energy.IsSatisfied() && closestItem == null && closestZone == null)
            {
                Creature.AddThought("I slept on the ground.", new TimeSpan(0, 8, 0, 0), -6.0f);

                Tree = new SleepAct(Creature.AI)
                {
                    RechargeRate = 1.0f
                };
            }
            else
            {
                Tree = null;
            }
            base.Initialize();
        }
Пример #4
0
        public override IEnumerable <Status> Run()
        {
            if (Zone == null)
            {
                Creature.DrawIndicator(IndicatorManager.StandardIndicators.Question);
                yield return(Status.Fail);

                yield break;
            }

            if (Resource.NumResources <= 0)
            {
                yield return(Status.Success);

                yield break;
            }

            List <Body> createdItems = Creature.Inventory.RemoveAndCreate(Resource, Inventory.RestockType.RestockResource);

            if (createdItems.Count == 0)
            {
                yield return(Status.Success);
            }

            foreach (Body b in createdItems)
            {
                if (Zone.AddItem(b))
                {
                    Creature.Stats.NumItemsGathered++;
                }
            }

            Creature.NoiseMaker.MakeNoise("Stockpile", Creature.AI.Position);
            Creature.CurrentCharacterMode = Creature.AttackMode;
            Creature.Sprite.ResetAnimations(Creature.AttackMode);
            Creature.Sprite.PlayAnimations(Creature.AttackMode);
            while (!Creature.Sprite.AnimPlayer.IsDone())
            {
                yield return(Status.Running);
            }

            var resource = ResourceLibrary.GetResourceByName(Resource.ResourceType);

            if (resource.Tags.Contains(DwarfCorp.Resource.ResourceTags.Corpse))
            {
                Creature.AddThought(Thought.ThoughtType.BuriedDead);
            }

            yield return(Status.Running);

            Creature.CurrentCharacterMode = CharacterMode.Idle;
            yield return(Status.Success);
        }
Пример #5
0
        public override void Initialize()
        {
            Body closestItem = Agent.Faction.FindNearestItemWithTags("Bed", Agent.Position, true, Agent);
            Zone closestZone = Agent.Faction.GetNearestRoom(Agent.Position);

            if (!Agent.Status.Energy.IsSatisfied() && closestItem != null)
            {
                closestItem.ReservedFor = Agent;
                Creature.AI.Blackboard.SetData("Bed", closestItem);
                closestItem.ReservedFor = Agent;
                Act unreserveAct = new Wrap(() => Creature.Unreserve("Bed"));
                Tree =
                    new Sequence
                    (
                        new GoToEntityAct(closestItem, Creature.AI),
                        new TeleportAct(Creature.AI)
                {
                    Location = closestItem.GetRotatedBoundingBox().Center() + new Vector3(-0.0f, 0.75f, -0.0f)
                },
                        new SleepAct(Creature.AI)
                {
                    RechargeRate = 1.0f, Teleport = true, TeleportLocation = closestItem.GetRotatedBoundingBox().Center() + new Vector3(-0.0f, 0.75f, -0.0f)
                },
                        unreserveAct
                    ) | unreserveAct;
            }
            else if (!Agent.Status.Energy.IsSatisfied() && closestItem == null && closestZone != null)
            {
                Creature.AddThought(Thought.ThoughtType.SleptOnGround);

                Tree = new Sequence(new GoToZoneAct(Creature.AI, closestZone),
                                    new SleepAct(Creature.AI)
                {
                    RechargeRate = 1.0f
                });
            }
            else if (!Agent.Status.Energy.IsSatisfied() && closestItem == null && closestZone == null)
            {
                Creature.AddThought(Thought.ThoughtType.SleptOnGround);

                Tree = new SleepAct(Creature.AI)
                {
                    RechargeRate = 1.0f
                };
            }
            else
            {
                Tree = null;
            }
            base.Initialize();
        }
Пример #6
0
        // If true, this creature can fight the other creature. Otherwise, we want to flee it.
        public FightOrFlightResponse FightOrFlight(CreatureAI creature)
        {
            if (IsDead || creature == null || creature.IsDead)
            {
                return(FightOrFlightResponse.Fight);
            }

            if (!Stats.Species.FeelsFear)
            {
                return(FightOrFlightResponse.Fight);
            }

            var fear = 0.0f;

            // If our health is low, we're a little afraid.
            if (Creature.Hp < Creature.MaxHealth * 0.25f)
            {
                fear += 0.125f;
            }

            // If there are a lot of nearby threats vs allies, we are even more afraid.
            if (Faction.Threats.Where(threat => threat != null && threat.AI != null && !threat.IsDead).Sum(threat => (threat.AI.Position - Position).Length() < 5.0f ? 1 : 0) -
                Faction.Minions.Where(minion => minion != null && !minion.IsDead).Sum(minion => (minion.Position - Position).Length() < 6.0f ? 1 : 0) > Creature.Stats.Constitution)
            {
                fear += 0.125f;
            }

            // In this case, we have a very very weak weapon in comparison to our enemy.
            if (Creature.Attacks[0].Weapon.DamageAmount * 20 < creature.Creature.Hp)
            {
                fear += 0.125f;
            }

            // If the creature has formidible weapons, we're in trouble.
            if (creature.Creature.Attacks[0].Weapon.DamageAmount * 4 > Creature.Hp)
            {
                fear += 0.125f;
            }

            fear = Math.Min(fear, 0.99f);

            if (MathFunctions.RandEvent(1.0f - fear))
            {
                return(FightOrFlightResponse.Fight);
            }
            else
            {
                Creature.AddThought("I was frightened recently.", new TimeSpan(0, 4, 0, 0), -2.0f);
                return(FightOrFlightResponse.Flee);
            }
        }
Пример #7
0
        public IEnumerable <Status> FarmATile()
        {
            if (Farm == null)
            {
                yield return(Status.Fail);

                yield break;
            }
            else if (Farm.Finished)
            {
                yield return(Status.Success);
            }
            else
            {
                Creature.CurrentCharacterMode = Creature.Stats.CurrentClass.AttackMode;
                Creature.Sprite.ResetAnimations(Creature.Stats.CurrentClass.AttackMode);
                Creature.Sprite.PlayAnimations(Creature.Stats.CurrentClass.AttackMode);

                while (Farm.Progress < Farm.TargetProgress && !Farm.Finished)
                {
                    Creature.Physics.Velocity *= 0.1f;
                    Farm.Progress             += 3 * Creature.Stats.BaseFarmSpeed * DwarfTime.Dt;

                    Drawer2D.DrawLoadBar(Agent.Manager.World.Renderer.Camera, Agent.Position + Vector3.Up, Color.LightGreen, Color.Black, 64, 4,
                                         Farm.Progress / Farm.TargetProgress);

                    if (Farm.Progress >= (Farm.TargetProgress * 0.5f) && Farm.Voxel.Type.Name != "TilledSoil" &&
                        Library.GetVoxelType("TilledSoil").HasValue(out VoxelType tilledSoil))
                    {
                        Farm.Voxel.Type = tilledSoil;
                    }

                    if (Farm.Progress >= Farm.TargetProgress && !Farm.Finished)
                    {
                        if (Library.GetResourceType(Farm.SeedType).HasValue(out var seedType))
                        {
                            var plant = EntityFactory.CreateEntity <Plant>(seedType.PlantToGenerate, Farm.Voxel.WorldPosition + new Vector3(0.5f, 1.0f, 0.5f));

                            plant.Farm = Farm;

                            var original = plant.LocalTransform;
                            original.Translation += Vector3.Down;
                            plant.AnimationQueue.Add(new EaseMotion(0.5f, original, plant.LocalTransform.Translation));

                            Creature.Manager.World.ParticleManager.Trigger("puff", original.Translation, Color.White, 20);
                            SoundManager.PlaySound(ContentPaths.Audio.Oscar.sfx_env_plant_grow, Farm.Voxel.WorldPosition, true);
                        }

                        Farm.Finished = true;
                        DestroyResources();
                    }

                    if (MathFunctions.RandEvent(0.01f))
                    {
                        Creature.Manager.World.ParticleManager.Trigger("dirt_particle", Creature.AI.Position, Color.White, 1);
                    }
                    yield return(Status.Running);

                    Creature.Sprite.ReloopAnimations(Creature.Stats.CurrentClass.AttackMode);
                }

                Creature.CurrentCharacterMode = CharacterMode.Idle;
                Creature.AddThought("I farmed something!", new TimeSpan(0, 4, 0, 0), 1.0f);
                Creature.AI.AddXP(1);
                ActHelper.ApplyWearToTool(Creature.AI, GameSettings.Current.Wear_Dig);
                Creature.Sprite.PauseAnimations(Creature.Stats.CurrentClass.AttackMode);
                yield return(Status.Success);
            }
        }
Пример #8
0
 public override void OnApply(Creature creature)
 {
     SavedThought = creature.AddThought(Description, new TimeSpan(1, 0, 0, 0), HappinessModifier);
     base.OnApply(creature);
 }
Пример #9
0
        public override IEnumerable <Status> Run()
        {
            float startingHealth = Creature.Status.Health.CurrentValue;

            PreTeleport = Creature.AI.Position;
            if (Type == SleepType.Sleep)
            {
                while (!Creature.Status.Energy.IsSatisfied() && Creature.Manager.World.Time.IsNight())
                {
                    if (Creature.Physics.IsInLiquid)
                    {
                        Creature.Status.IsAsleep       = false;
                        Creature.CurrentCharacterMode  = CharacterMode.Idle;
                        Creature.OverrideCharacterMode = false;
                        yield return(Status.Fail);
                    }
                    if (Teleport)
                    {
                        Creature.AI.Position               = TeleportLocation;
                        Creature.Physics.Velocity          = Vector3.Zero;
                        Creature.Physics.LocalPosition     = TeleportLocation;
                        Creature.Physics.AllowPhysicsSleep = true;
                        Creature.Physics.IsSleeping        = true;
                    }
                    Creature.CurrentCharacterMode        = CharacterMode.Sleeping;
                    Creature.Status.Energy.CurrentValue += DwarfTime.Dt * RechargeRate;
                    if (Creature.Status.Health.CurrentValue < startingHealth)
                    {
                        Creature.Status.IsAsleep       = false;
                        Creature.CurrentCharacterMode  = CharacterMode.Idle;
                        Creature.OverrideCharacterMode = false;
                        yield return(Status.Fail);
                    }

                    Creature.Status.IsAsleep       = true;
                    Creature.OverrideCharacterMode = false;
                    yield return(Status.Running);
                }

                if (Teleport)
                {
                    Creature.AI.Position               = PreTeleport;
                    Creature.Physics.Velocity          = Vector3.Zero;
                    Creature.Physics.LocalPosition     = TeleportLocation;
                    Creature.Physics.IsSleeping        = false;
                    Creature.Physics.AllowPhysicsSleep = false;
                }

                Creature.AddThought(Thought.ThoughtType.Slept);
                Creature.Status.IsAsleep           = false;
                Creature.Physics.IsSleeping        = false;
                Creature.Physics.AllowPhysicsSleep = false;
                yield return(Status.Success);
            }
            else
            {
                while (Creature.Status.Health.IsDissatisfied() || Creature.Buffs.Any(buff => buff is Disease))
                {
                    if (Creature.Physics.IsInLiquid)
                    {
                        Creature.Status.IsAsleep       = false;
                        Creature.CurrentCharacterMode  = CharacterMode.Idle;
                        Creature.OverrideCharacterMode = false;
                        yield return(Status.Fail);
                    }
                    if (Teleport)
                    {
                        Creature.AI.Position               = TeleportLocation;
                        Creature.Physics.Velocity          = Vector3.Zero;
                        Creature.Physics.LocalPosition     = TeleportLocation;
                        Creature.Physics.IsSleeping        = true;
                        Creature.Physics.AllowPhysicsSleep = true;
                    }
                    Creature.CurrentCharacterMode        = CharacterMode.Sleeping;
                    Creature.Status.Energy.CurrentValue += DwarfTime.Dt * RechargeRate;
                    Creature.Heal(DwarfTime.Dt * HealRate);
                    Creature.Status.IsAsleep       = true;
                    Creature.OverrideCharacterMode = false;
                    yield return(Status.Running);
                }

                if (Teleport)
                {
                    Creature.AI.Position               = PreTeleport;
                    Creature.Physics.Velocity          = Vector3.Zero;
                    Creature.Physics.LocalPosition     = TeleportLocation;
                    Creature.Physics.IsSleeping        = false;
                    Creature.Physics.AllowPhysicsSleep = false;
                }
                Creature.AddThought(Thought.ThoughtType.Slept);
                Creature.Status.IsAsleep           = false;
                Creature.Physics.IsSleeping        = false;
                Creature.Physics.AllowPhysicsSleep = false;
                yield return(Status.Success);
            }
        }
Пример #10
0
        public IEnumerable <Status> FarmATile()
        {
            if (FarmToWork == null)
            {
                yield return(Status.Fail);

                yield break;
            }
            else if (FarmToWork.Finished)
            {
                yield return(Status.Success);
            }
            else
            {
                Creature.CurrentCharacterMode = Creature.AttackMode;
                Creature.Sprite.ResetAnimations(Creature.AttackMode);
                Creature.Sprite.PlayAnimations(Creature.AttackMode);
                while (FarmToWork.Progress < FarmToWork.TargetProgress && !FarmToWork.Finished)
                {
                    Creature.Physics.Velocity *= 0.1f;
                    FarmToWork.Progress       += 3 * Creature.Stats.BaseFarmSpeed * DwarfTime.Dt;

                    Drawer2D.DrawLoadBar(Agent.Manager.World.Camera, Agent.Position + Vector3.Up, Color.LightGreen, Color.Black, 64, 4,
                                         FarmToWork.Progress / FarmToWork.TargetProgress);

                    if (FarmToWork.Progress >= (FarmToWork.TargetProgress * 0.5f) && FarmToWork.Voxel.Type.Name != "TilledSoil")
                    {
                        FarmToWork.Voxel.Type = VoxelLibrary.GetVoxelType("TilledSoil");
                    }

                    if (FarmToWork.Progress >= FarmToWork.TargetProgress && !FarmToWork.Finished)
                    {
                        var plant = EntityFactory.CreateEntity <Plant>(
                            ResourceLibrary.Resources[FarmToWork.SeedResourceType].PlantToGenerate,
                            FarmToWork.Voxel.WorldPosition + new Vector3(0.5f, 1.0f, 0.5f));

                        plant.Farm = FarmToWork;

                        Matrix original = plant.LocalTransform;
                        original.Translation += Vector3.Down;
                        plant.AnimationQueue.Add(new EaseMotion(0.5f, original, plant.LocalTransform.Translation));

                        Creature.Manager.World.ParticleManager.Trigger("puff", original.Translation, Color.White, 20);

                        SoundManager.PlaySound(ContentPaths.Audio.Oscar.sfx_env_plant_grow, FarmToWork.Voxel.WorldPosition, true);

                        FarmToWork.Finished = true;

                        DestroyResources();
                    }

                    if (MathFunctions.RandEvent(0.01f))
                    {
                        Creature.Manager.World.ParticleManager.Trigger("dirt_particle", Creature.AI.Position, Color.White, 1);
                    }
                    yield return(Status.Running);

                    Creature.Sprite.ReloopAnimations(Creature.AttackMode);
                }
                Creature.CurrentCharacterMode = CharacterMode.Idle;
                Creature.AddThought(Thought.ThoughtType.Farmed);
                Creature.AI.AddXP(1);
                Creature.Sprite.PauseAnimations(Creature.AttackMode);
                yield return(Status.Success);
            }
        }
Пример #11
0
        public override IEnumerable <Status> Run()
        {
            Creature.IsCloaked = false;

            if (CurrentAttack == null)
            {
                yield return(Status.Fail);

                yield break;
            }

            Timeout.Reset();
            FailTimer.Reset();
            if (Target == null && TargetName != null)
            {
                Target = Agent.Blackboard.GetData <GameComponent>(TargetName);

                if (Target == null)
                {
                    yield return(Status.Fail);

                    yield break;
                }
            }

            if (Agent.Faction.Race.IsIntelligent)
            {
                var targetInventory = Target.GetRoot().GetComponent <Inventory>();
                if (targetInventory != null)
                {
                    targetInventory.SetLastAttacker(Agent);
                }
            }

            CharacterMode defaultCharachterMode = Creature.AI.Movement.CanFly
                ? CharacterMode.Flying
                : CharacterMode.Walking;

            bool avoided = false;

            while (true)
            {
                Timeout.Update(DwarfTime.LastTime);
                FailTimer.Update(DwarfTime.LastTime);
                if (FailTimer.HasTriggered)
                {
                    Creature.Physics.Orientation   = Physics.OrientMode.RotateY;
                    Creature.OverrideCharacterMode = false;
                    Creature.CurrentCharacterMode  = defaultCharachterMode;
                    yield return(Status.Fail);

                    yield break;
                }

                if (Timeout.HasTriggered)
                {
                    if (Training)
                    {
                        Agent.AddXP(1);
                        Creature.Physics.Orientation   = Physics.OrientMode.RotateY;
                        Creature.OverrideCharacterMode = false;
                        Creature.CurrentCharacterMode  = defaultCharachterMode;
                        yield return(Status.Success);

                        yield break;
                    }
                    else
                    {
                        Creature.Physics.Orientation   = Physics.OrientMode.RotateY;
                        Creature.OverrideCharacterMode = false;
                        Creature.CurrentCharacterMode  = defaultCharachterMode;
                        yield return(Status.Fail);

                        yield break;
                    }
                }

                if (Target == null || Target.IsDead)
                {
                    Creature.CurrentCharacterMode = defaultCharachterMode;
                    Creature.Physics.Orientation  = Physics.OrientMode.RotateY;
                    yield return(Status.Success);
                }

                // Find the location of the melee target
                Vector3 targetPos = new Vector3(Target.GlobalTransform.Translation.X,
                                                Target.GetBoundingBox().Min.Y,
                                                Target.GlobalTransform.Translation.Z);

                Vector2 diff = new Vector2(targetPos.X, targetPos.Z) - new Vector2(Creature.AI.Position.X, Creature.AI.Position.Z);

                Creature.Physics.Face(targetPos);

                bool  intersectsbounds = Creature.Physics.BoundingBox.Intersects(Target.BoundingBox);
                float dist             = diff.Length();
                // If we are really far from the target, something must have gone wrong.
                if (DefensiveStructure == null && !intersectsbounds && dist > CurrentAttack.Weapon.Range * 4)
                {
                    Creature.Physics.Orientation   = Physics.OrientMode.RotateY;
                    Creature.OverrideCharacterMode = false;
                    Creature.CurrentCharacterMode  = defaultCharachterMode;
                    yield return(Status.Fail);

                    yield break;
                }

                if (DefensiveStructure != null)
                {
                    if (Creature.Hp < LastHp)
                    {
                        float damage = LastHp - Creature.Hp;
                        Creature.Heal(Math.Min(5.0f, damage));
                        var health = DefensiveStructure.GetRoot().GetComponent <Health>();
                        if (health != null)
                        {
                            health.Damage(damage);
                            Drawer2D.DrawLoadBar(health.World.Renderer.Camera, DefensiveStructure.Position, Color.White, Color.Black, 32, 1, health.Hp / health.MaxHealth, 0.1f);
                        }
                        LastHp = Creature.Hp;
                    }

                    if (dist > CurrentAttack.Weapon.Range)
                    {
                        float sqrDist = dist * dist;
                        foreach (var threat in Creature.AI.Faction.Threats)
                        {
                            float threatDist = (threat.AI.Position - Creature.AI.Position).LengthSquared();
                            if (threatDist < sqrDist)
                            {
                                sqrDist = threatDist;
                                Target  = threat.Physics;
                                break;
                            }
                        }
                        dist = (float)Math.Sqrt(sqrDist);
                    }

                    if (dist > CurrentAttack.Weapon.Range * 4)
                    {
                        yield return(Status.Fail);

                        yield break;
                    }

                    if (DefensiveStructure.IsDead)
                    {
                        DefensiveStructure = null;
                    }
                }

                LastHp = Creature.Hp;


                // If we're out of attack range, run toward the target.
                if (DefensiveStructure == null && !Creature.AI.Movement.IsSessile && !intersectsbounds && diff.Length() > CurrentAttack.Weapon.Range)
                {
                    Creature.CurrentCharacterMode = defaultCharachterMode;
                    var greedyPath = new GreedyPathAct(Creature.AI, Target, CurrentAttack.Weapon.Range * 0.75f)
                    {
                        PathLength = 5
                    };
                    greedyPath.Initialize();

                    foreach (Act.Status stat in greedyPath.Run())
                    {
                        if (stat == Act.Status.Running)
                        {
                            yield return(Status.Running);
                        }
                        else
                        {
                            break;
                        }
                    }
                }
                // If we have a ranged weapon, try avoiding the target for a few seconds to get within range.
                else if (DefensiveStructure == null && !Creature.AI.Movement.IsSessile && !intersectsbounds && !avoided && (CurrentAttack.Weapon.Mode == Weapon.AttackMode.Ranged &&
                                                                                                                            dist < CurrentAttack.Weapon.Range * 0.15f))
                {
                    FailTimer.Reset();
                    foreach (Act.Status stat in AvoidTarget(CurrentAttack.Weapon.Range, 3.0f))
                    {
                        yield return(Status.Running);
                    }
                    avoided = true;
                }
                // Else, stop and attack
                else if ((DefensiveStructure == null && dist < CurrentAttack.Weapon.Range) ||
                         (DefensiveStructure != null && dist < CurrentAttack.Weapon.Range * 2.0))
                {
                    if (CurrentAttack.Weapon.Mode == Weapon.AttackMode.Ranged &&
                        VoxelHelpers.DoesRayHitSolidVoxel(Creature.World.ChunkManager, Creature.AI.Position, Target.Position))
                    {
                        yield return(Status.Fail);

                        yield break;
                    }

                    FailTimer.Reset();
                    avoided = false;
                    Creature.Physics.Orientation = Physics.OrientMode.Fixed;
                    Creature.Physics.Velocity    = new Vector3(Creature.Physics.Velocity.X * 0.9f, Creature.Physics.Velocity.Y, Creature.Physics.Velocity.Z * 0.9f);
                    CurrentAttack.RechargeTimer.Reset(CurrentAttack.Weapon.RechargeRate);

                    Creature.Sprite.ResetAnimations(Creature.Stats.CurrentClass.AttackMode);
                    Creature.Sprite.PlayAnimations(Creature.Stats.CurrentClass.AttackMode);
                    Creature.CurrentCharacterMode  = Creature.Stats.CurrentClass.AttackMode;
                    Creature.OverrideCharacterMode = true;
                    Timer timeout = new Timer(10.0f, true);
                    while (!CurrentAttack.Perform(Creature, Target, DwarfTime.LastTime, Creature.Stats.Strength + Creature.Stats.Size,
                                                  Creature.AI.Position, Creature.Faction.ParentFaction.Name))
                    {
                        timeout.Update(DwarfTime.LastTime);
                        if (timeout.HasTriggered)
                        {
                            break;
                        }

                        Creature.Physics.Velocity = new Vector3(Creature.Physics.Velocity.X * 0.9f, Creature.Physics.Velocity.Y, Creature.Physics.Velocity.Z * 0.9f);
                        if (Creature.AI.Movement.CanFly)
                        {
                            Creature.Physics.ApplyForce(-Creature.Physics.Gravity * 0.1f, DwarfTime.Dt);
                        }
                        yield return(Status.Running);
                    }

                    timeout.Reset();
                    while (!Agent.Creature.Sprite.AnimPlayer.IsDone())
                    {
                        timeout.Update(DwarfTime.LastTime);
                        if (timeout.HasTriggered)
                        {
                            break;
                        }
                        if (Creature.AI.Movement.CanFly)
                        {
                            Creature.Physics.ApplyForce(-Creature.Physics.Gravity * 0.1f, DwarfTime.Dt);
                        }
                        yield return(Status.Running);
                    }

                    var targetCreature = Target.GetRoot().GetComponent <CreatureAI>();
                    if (targetCreature != null && Creature.AI.FightOrFlight(targetCreature) == CreatureAI.FightOrFlightResponse.Flee)
                    {
                        yield return(Act.Status.Fail);

                        yield break;
                    }
                    Creature.CurrentCharacterMode = CharacterMode.Attacking;

                    Vector3 dogfightTarget = Vector3.Zero;
                    while (!CurrentAttack.RechargeTimer.HasTriggered && !Target.IsDead)
                    {
                        CurrentAttack.RechargeTimer.Update(DwarfTime.LastTime);
                        if (CurrentAttack.Weapon.Mode == Weapon.AttackMode.Dogfight)
                        {
                            dogfightTarget += MathFunctions.RandVector3Cube() * 0.1f;
                            Vector3 output = Creature.Controller.GetOutput(DwarfTime.Dt, dogfightTarget + Target.Position, Creature.Physics.GlobalTransform.Translation) * 0.9f;
                            Creature.Physics.ApplyForce(output - Creature.Physics.Gravity, DwarfTime.Dt);
                        }
                        else
                        {
                            Creature.Physics.Velocity = Vector3.Zero;
                            if (Creature.AI.Movement.CanFly)
                            {
                                Creature.Physics.ApplyForce(-Creature.Physics.Gravity, DwarfTime.Dt);
                            }
                        }
                        yield return(Status.Running);
                    }

                    Creature.CurrentCharacterMode = defaultCharachterMode;
                    Creature.Physics.Orientation  = Physics.OrientMode.RotateY;

                    if (Target.IsDead)
                    {
                        Target = null;
                        Agent.AddXP(10);
                        Creature.Physics.Face(Creature.Physics.Velocity + Creature.Physics.GlobalTransform.Translation);
                        Creature.Stats.NumThingsKilled++;
                        Creature.AddThought("I killed somehing!", new TimeSpan(0, 8, 0, 0), 1.0f);
                        Creature.Physics.Orientation   = Physics.OrientMode.RotateY;
                        Creature.OverrideCharacterMode = false;
                        Creature.CurrentCharacterMode  = defaultCharachterMode;
                        yield return(Status.Success);

                        break;
                    }
                }

                yield return(Status.Running);
            }
        }
Пример #12
0
        public override IEnumerable <Status> Run()
        {
            Creature.IsCloaked = false;

            if (CurrentAttack == null)
            {
                yield return(Status.Fail);

                yield break;
            }

            Timeout.Reset();
            FailTimer.Reset();
            if (Target == null && TargetName != null)
            {
                Target = Agent.Blackboard.GetData <Body>(TargetName);

                if (Target == null)
                {
                    yield return(Status.Fail);

                    yield break;
                }
            }

            if (Agent.Faction.Race.IsIntelligent)
            {
                var targetInventory = Target.GetRoot().GetComponent <Inventory>();
                if (targetInventory != null)
                {
                    targetInventory.SetLastAttacker(Agent);
                }
            }

            CharacterMode defaultCharachterMode = Creature.AI.Movement.CanFly
                ? CharacterMode.Flying
                : CharacterMode.Walking;

            bool avoided = false;

            while (true)
            {
                Timeout.Update(DwarfTime.LastTime);
                FailTimer.Update(DwarfTime.LastTime);
                if (FailTimer.HasTriggered)
                {
                    Creature.Physics.Orientation   = Physics.OrientMode.RotateY;
                    Creature.OverrideCharacterMode = false;
                    Creature.CurrentCharacterMode  = defaultCharachterMode;
                    yield return(Status.Fail);

                    yield break;
                }

                if (Timeout.HasTriggered)
                {
                    if (Training)
                    {
                        Agent.AddXP(1);
                        Creature.Physics.Orientation   = Physics.OrientMode.RotateY;
                        Creature.OverrideCharacterMode = false;
                        Creature.CurrentCharacterMode  = defaultCharachterMode;
                        yield return(Status.Success);

                        yield break;
                    }
                    else
                    {
                        Creature.Physics.Orientation   = Physics.OrientMode.RotateY;
                        Creature.OverrideCharacterMode = false;
                        Creature.CurrentCharacterMode  = defaultCharachterMode;
                        yield return(Status.Fail);

                        yield break;
                    }
                }

                if (Target == null || Target.IsDead)
                {
                    Creature.CurrentCharacterMode = defaultCharachterMode;
                    Creature.Physics.Orientation  = Physics.OrientMode.RotateY;
                    yield return(Status.Success);
                }

                // Find the location of the melee target
                Vector3 targetPos = new Vector3(Target.GlobalTransform.Translation.X,
                                                Target.GetBoundingBox().Min.Y,
                                                Target.GlobalTransform.Translation.Z);

                Vector2 diff = new Vector2(targetPos.X, targetPos.Z) - new Vector2(Creature.AI.Position.X, Creature.AI.Position.Z);

                Creature.Physics.Face(targetPos);

                bool intersectsbounds = Creature.Physics.BoundingBox.Intersects(Target.BoundingBox);

                // If we are really far from the target, something must have gone wrong.
                if (!intersectsbounds && diff.Length() > CurrentAttack.Range * 4)
                {
                    Creature.Physics.Orientation   = Physics.OrientMode.RotateY;
                    Creature.OverrideCharacterMode = false;
                    Creature.CurrentCharacterMode  = defaultCharachterMode;
                    yield return(Status.Fail);

                    yield break;
                }

                // If we're out of attack range, run toward the target.
                if (!Creature.AI.Movement.IsSessile && !intersectsbounds && diff.Length() > CurrentAttack.Range)
                {
                    Creature.CurrentCharacterMode = defaultCharachterMode;

                    /*
                     * Vector3 output = Creature.Controller.GetOutput(DwarfTime.Dt, targetPos, Creature.Physics.GlobalTransform.Translation) * 0.9f;
                     * output.Y = 0.0f;
                     * if (Creature.AI.Movement.CanFly)
                     * {
                     *  Creature.Physics.ApplyForce(-Creature.Physics.Gravity, DwarfTime.Dt);
                     * }
                     * if (Creature.AI.Movement.IsSessile)
                     * {
                     *  output *= 0.0f;
                     * }
                     * Creature.Physics.ApplyForce(output, DwarfTime.Dt);
                     * Creature.Physics.Orientation = Physics.OrientMode.RotateY;
                     */
                    GreedyPathAct greedyPath = new GreedyPathAct(Creature.AI, Target, CurrentAttack.Range * 0.75f)
                    {
                        PathLength = 5
                    };
                    greedyPath.Initialize();

                    foreach (Act.Status stat in greedyPath.Run())
                    {
                        if (stat == Act.Status.Running)
                        {
                            yield return(Status.Running);
                        }
                        else
                        {
                            break;
                        }
                    }
                }
                // If we have a ranged weapon, try avoiding the target for a few seconds to get within range.
                else if (!Creature.AI.Movement.IsSessile && !intersectsbounds && !avoided && (CurrentAttack.Mode == Attack.AttackMode.Ranged &&
                                                                                              diff.Length() < CurrentAttack.Range * 0.15f))
                {
                    FailTimer.Reset();
                    foreach (Act.Status stat in AvoidTarget(CurrentAttack.Range, 3.0f))
                    {
                        yield return(Status.Running);
                    }
                    avoided = true;
                }
                // Else, stop and attack
                else
                {
                    if (CurrentAttack.Mode == Attack.AttackMode.Ranged &&
                        VoxelHelpers.DoesRayHitSolidVoxel(Creature.World.ChunkManager.ChunkData,
                                                          Creature.AI.Position, Target.Position))
                    {
                        yield return(Status.Fail);

                        yield break;
                    }

                    FailTimer.Reset();
                    avoided = false;
                    Creature.Physics.Orientation = Physics.OrientMode.Fixed;
                    Creature.Physics.Velocity    = new Vector3(Creature.Physics.Velocity.X * 0.9f, Creature.Physics.Velocity.Y, Creature.Physics.Velocity.Z * 0.9f);
                    CurrentAttack.RechargeTimer.Reset(CurrentAttack.RechargeRate);

                    Creature.Sprite.ResetAnimations(Creature.AttackMode);
                    Creature.Sprite.PlayAnimations(Creature.AttackMode);
                    Creature.CurrentCharacterMode  = Creature.AttackMode;
                    Creature.OverrideCharacterMode = true;
                    Timer timeout = new Timer(10.0f, true);
                    while (!CurrentAttack.Perform(Creature, Target, DwarfTime.LastTime, Creature.Stats.BuffedStr + Creature.Stats.BuffedSiz,
                                                  Creature.AI.Position, Creature.Faction.Name))
                    {
                        timeout.Update(DwarfTime.LastTime);
                        if (timeout.HasTriggered)
                        {
                            break;
                        }

                        Creature.Physics.Velocity = new Vector3(Creature.Physics.Velocity.X * 0.9f, Creature.Physics.Velocity.Y, Creature.Physics.Velocity.Z * 0.9f);
                        if (Creature.AI.Movement.CanFly)
                        {
                            Creature.Physics.ApplyForce(-Creature.Physics.Gravity * 0.1f, DwarfTime.Dt);
                        }
                        yield return(Status.Running);
                    }

                    timeout.Reset();
                    while (!Agent.Creature.Sprite.AnimPlayer.IsDone())
                    {
                        timeout.Update(DwarfTime.LastTime);
                        if (timeout.HasTriggered)
                        {
                            break;
                        }
                        if (Creature.AI.Movement.CanFly)
                        {
                            Creature.Physics.ApplyForce(-Creature.Physics.Gravity * 0.1f, DwarfTime.Dt);
                        }
                        yield return(Status.Running);
                    }

                    var targetCreature = Target.GetRoot().GetComponent <CreatureAI>();
                    if (targetCreature != null && !Creature.AI.FightOrFlight(targetCreature))
                    {
                        yield return(Act.Status.Fail);

                        yield break;
                    }
                    Creature.CurrentCharacterMode = CharacterMode.Attacking;

                    Vector3 dogfightTarget = Vector3.Zero;
                    while (!CurrentAttack.RechargeTimer.HasTriggered && !Target.IsDead)
                    {
                        CurrentAttack.RechargeTimer.Update(DwarfTime.LastTime);
                        if (CurrentAttack.Mode == Attack.AttackMode.Dogfight)
                        {
                            dogfightTarget += MathFunctions.RandVector3Cube() * 0.1f;
                            Vector3 output = Creature.Controller.GetOutput(DwarfTime.Dt, dogfightTarget + Target.Position, Creature.Physics.GlobalTransform.Translation) * 0.9f;
                            Creature.Physics.ApplyForce(output - Creature.Physics.Gravity, DwarfTime.Dt);
                        }
                        else
                        {
                            Creature.Physics.Velocity = Vector3.Zero;
                            if (Creature.AI.Movement.CanFly)
                            {
                                Creature.Physics.ApplyForce(-Creature.Physics.Gravity, DwarfTime.Dt);
                            }
                        }
                        yield return(Status.Running);
                    }

                    Creature.CurrentCharacterMode = defaultCharachterMode;
                    Creature.Physics.Orientation  = Physics.OrientMode.RotateY;

                    if (Target.IsDead)
                    {
                        Target = null;
                        Agent.AddXP(10);
                        Creature.Physics.Face(Creature.Physics.Velocity + Creature.Physics.GlobalTransform.Translation);
                        Creature.Stats.NumThingsKilled++;
                        Creature.AddThought(Thought.ThoughtType.KilledThing);
                        Creature.Physics.Orientation   = Physics.OrientMode.RotateY;
                        Creature.OverrideCharacterMode = false;
                        Creature.CurrentCharacterMode  = defaultCharachterMode;
                        yield return(Status.Success);

                        break;
                    }
                }

                yield return(Status.Running);
            }
        }
Пример #13
0
        public override IEnumerable <Act.Status> Run()
        {
            if (Spell.IsResearched)
            {
                Creature.CurrentCharacterMode  = CharacterMode.Idle;
                Creature.OverrideCharacterMode = false;
                yield return(Status.Success);

                yield break;
            }
            Timer   starParitcle  = new Timer(0.5f, false);
            float   totalResearch = 0.0f;
            Sound3D sound         = null;

            while (!Spell.IsResearched)
            {
                Creature.CurrentCharacterMode  = Creature.AttackMode;
                Creature.OverrideCharacterMode = true;
                Creature.Sprite.ReloopAnimations(Creature.AttackMode);
                float research = Creature.Stats.BuffedInt * 0.25f * DwarfTime.Dt;
                Spell.ResearchProgress    += research;
                totalResearch             += research;
                Creature.Physics.Velocity *= 0;
                Drawer2D.DrawLoadBar(Creature.World.Camera, Creature.Physics.Position, Color.Cyan, Color.Black, 64, 4, Spell.ResearchProgress / Spell.ResearchTime);
                if ((int)totalResearch > 0)
                {
                    if (sound == null || sound.EffectInstance.IsDisposed || sound.EffectInstance.State == SoundState.Stopped)
                    {
                        sound = SoundManager.PlaySound(ContentPaths.Audio.Oscar.sfx_ic_dwarf_magic_research,
                                                       Creature.AI.Position,
                                                       true);
                    }
                    //SoundManager.PlaySound(ContentPaths.Audio.tinkle, Creature.AI.Position, true);
                    Creature.AI.AddXP((int)(totalResearch));
                    totalResearch = 0.0f;
                }

                if (Spell.ResearchProgress >= Spell.ResearchTime)
                {
                    Creature.Manager.World.MakeAnnouncement(
                        new Gui.Widgets.QueuedAnnouncement
                    {
                        Text = String.Format("{0} ({1}) discovered the {2} spell!",
                                             Creature.Stats.FullName,
                                             Creature.Stats.CurrentLevel.Name, Spell.Spell.Name),
                        ClickAction = (gui, sender) => Agent.ZoomToMe()
                    });

                    SoundManager.PlaySound(ContentPaths.Audio.Oscar.sfx_gui_positive_generic, 0.15f);
                }

                starParitcle.Update(DwarfTime.LastTime);
                if (starParitcle.HasTriggered)
                {
                    Creature.Manager.World.ParticleManager.Trigger("star_particle", Creature.AI.Position, Color.White, 3);
                }
                yield return(Status.Running);
            }

            if (sound != null)
            {
                sound.Stop();
            }
            Creature.AddThought(Thought.ThoughtType.Researched);
            Creature.OverrideCharacterMode = false;
            Creature.CurrentCharacterMode  = CharacterMode.Idle;
            yield return(Status.Success);

            yield break;
        }
Пример #14
0
        override public void Update(DwarfTime gameTime, ChunkManager chunks, Camera camera)
        {
            //base.Update(gameTime, chunks, camera);

            if (!Active)
            {
                return;
            }
            Creature.NoiseMaker.BasePitch = Stats.VoicePitch;

            AutoGatherTimer.Update(gameTime);
            IdleTimer.Update(gameTime);
            SpeakTimer.Update(gameTime);

            if (AutoGatherTimer.HasTriggered)
            {
                foreach (var body in World.EnumerateIntersectingObjects(Physics.BoundingBox.Expand(3.0f)).OfType <ResourceEntity>().Where(r => r.Active && r.AnimationQueue.Count == 0))
                {
                    Creature.GatherImmediately(body, Inventory.RestockType.RestockResource);
                }

                OrderEnemyAttack();
            }

            DeleteBadTasks();
            PreEmptTasks();

            if (CurrentTask != null)
            {
                Stats.Boredom.CurrentValue -= (float)(CurrentTask.BoredomIncrease * gameTime.ElapsedGameTime.TotalSeconds);
                if (Stats.Boredom.IsCritical())
                {
                    Creature.AddThought("I have been overworked recently.", new TimeSpan(0, 4, 0, 0), -2.0f);
                }

                Stats.Energy.CurrentValue += (float)(CurrentTask.EnergyDecrease * gameTime.ElapsedGameTime.TotalSeconds);
            }

            // Heal thyself
            if (Stats.Health.IsDissatisfied())
            {
                Task toReturn = new GetHealedTask();
                if (!Tasks.Contains(toReturn) && CurrentTask != toReturn)
                {
                    AssignTask(toReturn);
                }
            }

            // Try to go to sleep if we are low on energy and it is night time.
            if (Stats.Energy.IsCritical())
            {
                Task toReturn = new SatisfyTirednessTask();
                if (!Tasks.Contains(toReturn) && CurrentTask != toReturn)
                {
                    AssignTask(toReturn);
                }
            }

            // Try to find food if we are hungry.
            if (Stats.Hunger.IsDissatisfied() && World.CountResourcesWithTag(Resource.ResourceTags.Edible) > 0)
            {
                Task toReturn = new SatisfyHungerTask()
                {
                    MustPay = true
                };
                if (Stats.Hunger.IsCritical())
                {
                    toReturn.Priority = TaskPriority.Urgent;
                }
                if (!Tasks.Contains(toReturn) && CurrentTask != toReturn)
                {
                    AssignTask(toReturn);
                }
            }

            if (Stats.Boredom.IsDissatisfied())
            {
                if (!Tasks.Any(task => task.BoredomIncrease < 0))
                {
                    Task toReturn = SatisfyBoredom();
                    if (toReturn != null && !Tasks.Contains(toReturn) && CurrentTask != toReturn)
                    {
                        AssignTask(toReturn);
                    }
                }
            }

            restockTimer.Update(DwarfTime.LastTime);
            if (restockTimer.HasTriggered && Creature.Inventory.Resources.Count > 10)
            {
                Creature.RestockAllImmediately();
            }

            if (CurrentTask == null)               // We need something to do.
            {
                if (Stats.Happiness.IsSatisfied()) // We're happy, so make sure we aren't on strike.
                {
                    Stats.IsOnStrike = false;
                    UnhappinessTime  = 0.0f;
                }

                if (Stats.IsOnStrike) // We're on strike, so track how long this job has sucked.
                {
                    UnhappinessTime += gameTime.ElapsedGameTime.TotalMinutes;
                    if (UnhappinessTime > GameSettings.Default.HoursUnhappyBeforeQuitting) // If we've been unhappy long enough, quit.
                    {
                        var thoughts = GetRoot().GetComponent <DwarfThoughts>();
                        Manager.World.MakeAnnouncement( // Can't use a popup because the dwarf will soon not exist. Also - this is a serious event!
                            Message: String.Format("{0} has quit!{1}",
                                                   Stats.FullName,
                                                   (thoughts == null ? "" : (" The last straw: " + thoughts.Thoughts.Last(t => t.HappinessModifier < 0.0f).Description))),
                            ClickAction: null,
                            logEvent: true,
                            eventDetails: (thoughts == null ? "So sick of this place!" : String.Join("\n", thoughts.Thoughts.Where(t => t.HappinessModifier < 0.0f).Select(t => t.Description)))
                            );

                        LeaveWorld();

                        GetRoot().GetComponent <Inventory>().Die();
                        GetRoot().GetComponent <SelectionCircle>().Die();

                        if (thoughts != null)
                        {
                            thoughts.Thoughts.Clear();
                        }

                        Faction.Minions.Remove(this);
                        World.PersistentData.SelectedMinions.Remove(this);

                        return;
                    }
                }
                else if (Stats.Happiness.IsDissatisfied()) // We aren't on strike, but we hate this place.
                {
                    if (MathFunctions.Rand(0, 1) < 0.25f)  // We hate it so much that we might just go on strike! This can probably be tweaked. As it stands,
                                                           // dorfs go on strike almost immediately every time.
                    {
                        Manager.World.UserInterface.MakeWorldPopup(String.Format("{0} ({1}) refuses to work!",
                                                                                 Stats.FullName, Stats.CurrentClass.Name), Creature.Physics, -10, 10);
                        Manager.World.Tutorial("happiness");
                        SoundManager.PlaySound(ContentPaths.Audio.Oscar.sfx_gui_negative_generic, 0.25f);
                        Stats.IsOnStrike = true;
                    }
                }

                if (!Stats.IsOnStrike) // We aren't on strike, so find a new task.
                {
                    var goal = GetEasiestTask(Tasks);
                    if (goal == null)
                    {
                        goal = World.TaskManager.GetBestTask(this);
                    }

                    if (goal != null)
                    {
                        IdleTimer.Reset(IdleTimer.TargetTimeSeconds);
                        ChangeTask(goal);
                    }
                    else
                    {
                        var newTask = ActOnIdle();
                        if (newTask != null)
                        {
                            ChangeTask(newTask);
                        }
                    }
                }
                else
                {
                    ChangeTask(ActOnIdle());
                }
            }
            else
            {
                if (CurrentAct == null) // Should be impossible to have a current task and no current act.
                {
                    // Try and recover the correct act.
                    // <blecki> I always run with a breakpoint set here... just in case.
                    ChangeAct(CurrentTask.CreateScript(Creature));

                    // This is a bad situation!
                    if (CurrentAct == null)
                    {
                        ChangeTask(null);
                    }
                }

                if (CurrentAct != null)
                {
                    var  status  = CurrentAct.Tick();
                    bool retried = false;
                    if (CurrentAct != null && CurrentTask != null)
                    {
                        if (status == Act.Status.Fail)
                        {
                            LastFailedAct = CurrentAct.Name;

                            if (!FailedTasks.Any(task => task.TaskFailure.Equals(CurrentTask)))
                            {
                                FailedTasks.Add(new FailedTask()
                                {
                                    TaskFailure = CurrentTask, FailedTime = World.Time.CurrentDate
                                });
                            }

                            if (CurrentTask.ShouldRetry(Creature))
                            {
                                if (!Tasks.Contains(CurrentTask))
                                {
                                    ReassignCurrentTask();
                                    retried = true;
                                }
                            }
                        }
                    }

                    if (CurrentTask != null && CurrentTask.IsComplete(World))
                    {
                        ChangeTask(null);
                    }
                    else if (status != Act.Status.Running && !retried)
                    {
                        ChangeTask(null);
                    }
                }
            }

            // With a small probability, the creature will drown if its under water.
            if (MathFunctions.RandEvent(0.01f))
            {
                var  above       = VoxelHelpers.GetVoxelAbove(Physics.CurrentVoxel);
                var  below       = VoxelHelpers.GetVoxelBelow(Physics.CurrentVoxel);
                bool shouldDrown = (above.IsValid && (!above.IsEmpty || above.LiquidLevel > 0));
                if ((Physics.IsInLiquid || (!Movement.CanSwim && (below.IsValid && (below.LiquidLevel > 5)))) &&
                    (!Movement.CanSwim || shouldDrown))
                {
                    Creature.Damage(Movement.CanSwim ? 1.0f : 30.0f, Health.DamageType.Normal);
                }
            }

            if (PositionConstraint.Contains(Physics.LocalPosition) == ContainmentType.Disjoint)
            {
                Physics.LocalPosition = MathFunctions.Clamp(Physics.Position, PositionConstraint);
                Physics.PropogateTransforms();
            }
        }