Пример #1
0
        public IEnumerable <Status> CreateResources()
        {
            foreach (var status in CreateResource())
            {
                if (status == Status.Fail)
                {
                    yield return(Status.Fail);

                    yield break;
                }
            }

            if (ActualCreatedResource.HasValue(out var res))
            {
                for (var i = 0; i < ItemType.Craft_ResultsCount; ++i)
                {
                    Creature.Inventory.AddResource(res);
                }
                Creature.AI.AddXP((int)ItemType.Craft_BaseCraftTime);
                ActHelper.ApplyWearToTool(Creature.AI, GameSettings.Current.Wear_Craft);
                Des.Finished = true;
                yield return(Status.Success);
            }
            else
            {
                Agent.SetTaskFailureReason("Invalid meta resource");
                yield return(Act.Status.Fail);

                yield break;
            }
        }
Пример #2
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);
            }
        }
Пример #3
0
        public override IEnumerable <Status> Run()
        {
            foreach (var res in Agent.Blackboard.GetData <List <Resource> >(ResourceBlackboardName))
            {
                if (!Creature.Inventory.Contains(res))
                {
                    yield return(Status.Fail);
                }
            }

            foreach (var status in Creature.HitAndWait(1.0f, true, () => Location.Coordinate.ToVector3() + Vector3.One * 0.5f))
            {
                if (status == Status.Running)
                {
                    yield return(status);
                }
            }

            foreach (var res in Agent.Blackboard.GetData <List <Resource> >(ResourceBlackboardName))
            {
                var grabbed = Creature.Inventory.RemoveAndCreate(res, Inventory.RestockType.Any);

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

                    yield break;
                }
                else
                {
                    // If the creature intersects the box, find a voxel adjacent to it that is free, and jump there to avoid getting crushed.
                    if (Creature.Physics.BoundingBox.Intersects(Location.GetBoundingBox()))
                    {
                        var neighbors = VoxelHelpers.EnumerateAllNeighbors(Location.Coordinate)
                                        .Select(c => new VoxelHandle(Agent.World.ChunkManager, c));

                        var closest     = VoxelHandle.InvalidHandle;
                        var closestDist = float.MaxValue;
                        foreach (var voxel in neighbors)
                        {
                            if (!voxel.IsValid)
                            {
                                continue;
                            }

                            float dist = (voxel.WorldPosition - Creature.Physics.Position).LengthSquared();
                            if (dist < closestDist && voxel.IsEmpty)
                            {
                                closestDist = dist;
                                closest     = voxel;
                            }
                        }

                        if (closest.IsValid)
                        {
                            TossMotion teleport = new TossMotion(0.5f, 1.0f, Creature.Physics.GlobalTransform, closest.WorldPosition + Vector3.One * 0.5f);
                            Creature.Physics.AnimationQueue.Add(teleport);
                        }
                    }

                    // Todo: Shitbox - what happens if the player saves while this animation is in progress?? How is the OnComplete restored?
                    var motion = new TossMotion(1.0f, 2.0f, grabbed.LocalTransform, Location.Coordinate.ToVector3() + new Vector3(0.5f, 0.5f, 0.5f));
                    if (grabbed.GetRoot().GetComponent <Physics>().HasValue(out var grabbedPhysics))
                    {
                        grabbedPhysics.CollideMode = Physics.CollisionMode.None;
                    }
                    grabbed.AnimationQueue.Add(motion);

                    motion.OnComplete += () => grabbed.Die();
                }

                if (Library.GetVoxelType(VoxelType).HasValue(out var vType))
                {
                    PlaceVoxel(Location, vType, Creature.Manager.World);
                }

                Creature.Stats.NumBlocksPlaced++;
                Creature.AI.AddXP(1);
                ActHelper.ApplyWearToTool(Creature.AI, GameSettings.Current.Wear_Craft);

                yield return(Status.Success);

                yield break;
            }
        }
Пример #4
0
        public override IEnumerable <Status> Run()
        {
            Creature.Sprite.ResetAnimations(Creature.Stats.CurrentClass.AttackMode);

            // Block since we're in a coroutine.
            while (true)
            {
                var vox = OwnerTask.Voxel;

                // Somehow, there wasn't a voxel to mine.
                if (!vox.IsValid)
                {
                    Creature.DrawIndicator(IndicatorManager.StandardIndicators.Question);
                    Agent.SetTaskFailureReason("Failed to dig. Invalid voxel.");
                    yield return(Act.Status.Fail);

                    break;
                }

                // If the voxel has already been destroyed, just ignore it and return.
                if (OwnerTask.VoxelHealth <= 0 || (CheckOwnership && !Creature.World.PersistentData.Designations.IsVoxelDesignation(vox, DesignationType.Dig)))
                {
                    Creature.CurrentCharacterMode = CharacterMode.Idle;
                    yield return(Act.Status.Success);

                    break;
                }

                // Look at the block and slow your velocity down.
                Creature.Physics.Face(vox.WorldPosition + Vector3.One * 0.5f);
                Creature.Physics.Velocity *= 0.01f;

                // Play the attack animations.
                Creature.CurrentCharacterMode  = Creature.Stats.CurrentClass.AttackMode;
                Creature.OverrideCharacterMode = true;
                Creature.Sprite.ResetAnimations(Creature.CurrentCharacterMode);
                Creature.Sprite.PlayAnimations(Creature.CurrentCharacterMode);

                // Wait until an attack was successful...
                foreach (var status in
                         PerformOnVoxel(Creature,
                                        Creature.Physics.Position,
                                        OwnerTask, DwarfTime.LastTime,
                                        Creature.Stats.BaseDigSpeed,
                                        Creature.Faction.ParentFaction.Name))
                {
                    if (status == Act.Status.Running)
                    {
                        Creature.Physics.Face(vox.WorldPosition + Vector3.One * 0.5f);
                        Creature.Physics.Velocity *= 0.01f;
                        yield return(Act.Status.Running);
                    }
                }

                Creature.OverrideCharacterMode = false;

                // If the voxel has been destroyed by you, gather it.
                if (OwnerTask.VoxelHealth <= 0.0f)
                {
                    if (Library.GetVoxelType(vox.Type.Name).HasValue(out VoxelType voxelType))
                    {
                        Creature.AI.AddXP(Math.Max((int)(voxelType.StartingHealth / 4), 1));
                        Creature.Stats.NumBlocksDestroyed++;
                        ActHelper.ApplyWearToTool(Creature.AI, GameSettings.Current.Wear_Dig);

                        var items = VoxelHelpers.KillVoxel(Creature.World, vox);

                        if (items != null)
                        {
                            foreach (GameComponent item in items)
                            {
                                Creature.Gather(item, TaskPriority.Eventually);
                            }
                        }

                        yield return(Act.Status.Success);
                    }
                }

                // Wait until the animation is done playing before continuing.
                while (!Creature.Sprite.AnimPlayer.IsDone() && Creature.Sprite.AnimPlayer.IsPlaying)
                {
                    Creature.Physics.Face(vox.WorldPosition + Vector3.One * 0.5f);
                    Creature.Physics.Velocity *= 0.01f;
                    yield return(Act.Status.Running);
                }

                // Pause the animation and wait for a recharge timer.
                Creature.Sprite.PauseAnimations(Creature.CurrentCharacterMode);

                Creature.CurrentCharacterMode = CharacterMode.Idle;
                yield return(Act.Status.Running);
            }
        }
Пример #5
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;
                }
            }

            // In case we kill it - this tells the inventory who to give the loot to.
            if (Agent.Faction.Race.HasValue(out var race) && race.IsIntelligent && Target.GetRoot().GetComponent <Inventory>().HasValue(out var targetInventory))
            {
                targetInventory.SetLastAttacker(Agent);
            }

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

            var 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)
                {
                    Creature.Physics.Orientation   = Physics.OrientMode.RotateY;
                    Creature.OverrideCharacterMode = false;
                    Creature.CurrentCharacterMode  = defaultCharachterMode;

                    if (Training)
                    {
                        yield return(Status.Success);
                    }
                    else
                    {
                        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) // Defensive structures have damage reduction.
                    {
                        float damage = LastHp - Creature.Hp;
                        Creature.Heal(Math.Min(5.0f, damage)); // Todo: Make this configurable in the structure.

                        if (DefensiveStructure.GetRoot().GetComponent <Health>().HasValue(out var health))
                        {
                            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))
                {
                    // Need line of sight for ranged weapons.
                    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;
                    var 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);
                    }

                    Agent.AddXP(2); // Gain XP for attacking.
                    ActHelper.ApplyWearToTool(Creature.AI, GameSettings.Current.Wear_Attack);

                    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);
                    }

                    if (Target.GetRoot().GetComponent <CreatureAI>().HasValue(out var targetCreature) && 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);
            }
        }