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