public void UpdateWarParties(Faction faction) { foreach (var party in faction.WarParties) { bool doneWaiting = party.UpdateTimer(World.Time.CurrentDate); party.Creatures.RemoveAll(creature => creature.IsDead); if (party.DeathTimer.Update(World.Time.CurrentDate)) { party.Creatures.ForEach((creature) => creature.Die()); } Diplomacy.Politics politics = faction.World.Diplomacy.GetPolitics(faction, party.OtherFaction); if (politics.GetCurrentRelationship() != Relationship.Hateful) { RecallWarParty(party); } if (party.Creatures.All(creature => creature.IsDead)) { party.ShouldRemove = true; // Killed entire war party. Wonderful! World.GoalManager.OnGameEvent(new Goals.Triggers.WarPartyDefeated { PlayerFaction = party.OtherFaction, OtherFaction = party.OwnerFaction }); } if (!doneWaiting) { continue; } else { foreach (var creature in party.OwnerFaction.Minions) { if (creature.Tasks.Count == 0) { CreatureAI enemyMinion = party.OtherFaction.GetNearestMinion(creature.Position); if (enemyMinion != null) { creature.AssignTask(new KillEntityTask(enemyMinion.Physics, KillEntityTask.KillType.Auto)); } } } if (party.ExpiditionState == Expedition.State.Arriving) { World.MakeAnnouncement(String.Format("The war party from {0} is attacking!", party.OwnerFaction.Name)); SoundManager.PlaySound(ContentPaths.Audio.Oscar.sfx_gui_negative_generic, 0.15f); party.ExpiditionState = Expedition.State.Fighting; } } foreach (var creature in party.Creatures) { if (MathFunctions.RandEvent(0.001f)) { creature.AssignTask(new ActWrapperTask(new GetMoneyAct(creature, (decimal)MathFunctions.Rand(0, 64.0f), party.OtherFaction)) { Priority = Task.PriorityType.Medium }); } } } faction.WarParties.RemoveAll(w => w.ShouldRemove); }
public void Update(DwarfGame game, DwarfTime time) { GamblingState.Update(time); TaskManager.Update(Faction.Minions); CurrentTool.Update(game, time); Faction.RoomBuilder.Update(); UpdateOrphanedTasks(); if (!World.Paused) { } else { CameraController.LastWheel = Mouse.GetState().ScrollWheelValue; } UpdateInput(game, time); if (Faction.Minions.Any(m => m.IsDead && m.TriggersMourning)) { foreach (CreatureAI minion in Faction.Minions) { minion.Creature.AddThought(Thought.ThoughtType.FriendDied); if (!minion.IsDead) { continue; } World.MakeAnnouncement( String.Format("{0} ({1}) died!", minion.Stats.FullName, minion.Stats.CurrentClass.Name)); SoundManager.PlaySound(ContentPaths.Audio.Oscar.sfx_gui_negative_generic); World.Tutorial("death"); } } Faction.Minions.RemoveAll(m => m.IsDead); UpdateRooms(); HandlePosessedDwarf(); if (sliceDownheld) { sliceDownTimer.Update(time); if (sliceDownTimer.HasTriggered) { SetMaxViewingLevel(MaxViewingLevel - 1); sliceDownTimer.Reset(sliceDownTimer.TargetTimeSeconds * 0.6f); } } else if (sliceUpheld) { sliceUpTimer.Update(time); if (sliceUpTimer.HasTriggered) { SetMaxViewingLevel(MaxViewingLevel + 1); sliceUpTimer.Reset(sliceUpTimer.TargetTimeSeconds * 0.6f); } } // Make sure that the faction's money is identical to the money in treasuries. Faction.Economy.CurrentMoney = Faction.Treasurys.Sum(treasury => treasury.Money); checkFoodTimer.Update(time); if (checkFoodTimer.HasTriggered) { var food = Faction.CountResourcesWithTag(Resource.ResourceTags.Edible); if (food == 0) { Faction.World.MakeAnnouncement("We're out of food!", null, () => { return(Faction.CountResourcesWithTag(Resource.ResourceTags.Edible) == 0); }); } } foreach (var minion in Faction.Minions) { if (minion == null) { throw new InvalidProgramException("Null minion?"); } if (minion.Status == null) { throw new InvalidProgramException("Minion has null status?"); } if (minion.Status.IsAsleep) { continue; } if (minion.CurrentTask == null) { continue; } if (minion.Stats.IsTaskAllowed(Task.TaskCategory.Dig)) { minion.Movement.SetCan(MoveType.Dig, GameSettings.Default.AllowAutoDigging); } minion.ResetPositionConstraint(); } foreach (var applicant in NewArrivals) { if (World.Time.CurrentDate >= applicant.ArrivalTime) { Faction.HireImmediately(applicant.Applicant); } } NewArrivals.RemoveAll(a => World.Time.CurrentDate >= a.ArrivalTime); }
public TradeEnvoy SendTradeEnvoy(Faction natives, WorldManager world) { if (!world.PlayerFaction.GetRooms().Any(room => room is BalloonPort && room.IsBuilt)) { world.MakeAnnouncement(String.Format("Trade envoy from {0} left: No balloon port!", natives.Name)); SoundManager.PlaySound(ContentPaths.Audio.Oscar.sfx_gui_negative_generic, 0.15f); return(null); } TradeEnvoy envoy = null; List <CreatureAI> creatures = world.MonsterSpawner.Spawn(world.MonsterSpawner.GenerateSpawnEvent(natives, world.PlayerFaction, MathFunctions.Random.Next(4) + 1, false)); if (natives.TradeMoney < 100m) { natives.TradeMoney += MathFunctions.Rand(250.0f, 5000.0f); } envoy = new TradeEnvoy(world.Time.CurrentDate) { Creatures = creatures, OtherFaction = world.PlayerFaction, ShouldRemove = false, OwnerFaction = natives, TradeGoods = natives.Race.GenerateResources(world), TradeMoney = natives.TradeMoney }; if (natives.Race.IsNative) { if (natives.Economy == null) { natives.Economy = new Economy(natives, 1000.0m, World, new CompanyInformation() { Name = natives.Name }); } foreach (CreatureAI creature in envoy.Creatures) { creature.Physics.AddChild(new ResourcePack(World.ComponentManager)); creature.Physics.AddChild(new Flag(World.ComponentManager, Vector3.Up * 0.5f + Vector3.Backward * 0.25f, natives.Economy.Company.Information)); } } else { Body balloon = world.PlayerFaction.DispatchBalloon(); if (balloon != null) { foreach (CreatureAI creature in creatures) { Matrix tf = creature.Physics.LocalTransform; tf.Translation = balloon.LocalTransform.Translation; creature.Physics.LocalTransform = tf; } } else { if (natives.Economy == null) { natives.Economy = new Economy(natives, 1000.0m, World, new CompanyInformation() { Name = natives.Name }); } foreach (CreatureAI creature in envoy.Creatures) { creature.Physics.AddChild(new ResourcePack(World.ComponentManager)); creature.Physics.AddChild(new Flag(World.ComponentManager, Vector3.Up * 0.5f + Vector3.Backward * 0.25f, natives.Economy.Company.Information)); } } } foreach (CreatureAI creature in envoy.Creatures) { creature.Physics.AddChild(new ResourcePack(World.ComponentManager)); } envoy.DistributeGoods(); natives.TradeEnvoys.Add(envoy); world.MakeAnnouncement(new DwarfCorp.Gui.Widgets.QueuedAnnouncement { Text = String.Format("Trade envoy from {0} has arrived!", natives.Name), ClickAction = (gui, sender) => { if (envoy.Creatures.Count > 0) { envoy.Creatures.First().ZoomToMe(); World.MakeWorldPopup(String.Format("Traders from {0} ({1}) have entered our territory.\nThey will try to get to our balloon port to trade with us.", natives.Name, natives.Race.Name), envoy.Creatures.First().Physics, -10); } }, ShouldKeep = () => { return(envoy.ExpiditionState == Expedition.State.Arriving); } }); SoundManager.PlaySound(ContentPaths.Audio.Oscar.sfx_gui_positive_generic, 0.15f); world.Tutorial("trade"); if (!String.IsNullOrEmpty(natives.Race.TradeMusic)) { SoundManager.PlayMusic(natives.Race.TradeMusic); } return(envoy); }
public void UpdateTradeEnvoys(Faction faction) { foreach (TradeEnvoy envoy in faction.TradeEnvoys) { if (envoy.ExpiditionState == Expedition.State.Trading) { if (envoy.UpdateWaitTimer(World.Time.CurrentDate)) { World.MakeAnnouncement(String.Format("The envoy from {0} is leaving.", envoy.OwnerFaction.Name)); RecallEnvoy(envoy); } } envoy.Creatures.RemoveAll(creature => creature.IsDead); if (envoy.DeathTimer.Update(faction.World.Time.CurrentDate)) { envoy.Creatures.ForEach((creature) => creature.GetRoot().Die()); } Diplomacy.Politics politics = faction.World.Diplomacy.GetPolitics(faction, envoy.OtherFaction); if (politics.GetCurrentRelationship() == Relationship.Hateful) { World.MakeAnnouncement(String.Format("The envoy from {0} left: we are at war with them.", envoy.OwnerFaction.Name)); RecallEnvoy(envoy); } else { if (envoy.Creatures.Any( // TODO (mklingen) why do I need this null check? creature => creature.Creature != null && envoy.OtherFaction.Designations.IsDesignation(creature.Physics, DesignationType.Attack))) { if (!politics.HasEvent("You attacked our trade delegates")) { politics.RecentEvents.Add(new Diplomacy.PoliticalEvent() { Change = -1.0f, Description = "You attacked our trade delegates", Duration = new TimeSpan(1, 0, 0, 0), Time = faction.World.Time.CurrentDate }); } else { politics.RecentEvents.Add(new Diplomacy.PoliticalEvent() { Change = -2.0f, Description = "You attacked our trade delegates more than once", Duration = new TimeSpan(1, 0, 0, 0), Time = faction.World.Time.CurrentDate }); } } } if (!envoy.ShouldRemove && envoy.ExpiditionState == Expedition.State.Arriving) { foreach (CreatureAI creature in envoy.Creatures) { Room tradePort = envoy.OtherFaction.GetNearestRoomOfType("Balloon Port", creature.Position); if (tradePort == null) { World.MakeAnnouncement("We need a balloon trade port to trade.", null, () => { return(envoy.OtherFaction.GetNearestRoomOfType("Balloon Port", creature.Position) == null); }); World.Tutorial("trade"); SoundManager.PlaySound(ContentPaths.Audio.Oscar.sfx_gui_negative_generic, 0.5f); RecallEnvoy(envoy); break; } if (creature.Tasks.Count == 0) { TradeEnvoy envoy1 = envoy; creature.Tasks.Add(new TradeTask(tradePort, envoy1)); } if (!tradePort.IsRestingOnZone(creature.Position)) { continue; } if (envoy.ExpiditionState != Expedition.State.Trading || !envoy.IsTradeWidgetValid()) { envoy.MakeTradeWidget(World); } envoy.StartTrading(World.Time.CurrentDate); envoy.ExpiditionState = Expedition.State.Trading; break; } } else if (envoy.ExpiditionState == Expedition.State.Leaving) { BoundingBox worldBBox = faction.World.ChunkManager.Bounds; foreach (CreatureAI creature in envoy.Creatures) { if (creature.Tasks.Count == 0) { creature.LeaveWorld(); } } foreach (CreatureAI creature in envoy.Creatures) { if (MathFunctions.Dist2D(worldBBox, creature.Position) < 2.0f) { creature.GetRoot().Delete(); } } } else { if (!envoy.IsTradeWidgetValid()) { envoy.MakeTradeWidget(World); } } if (envoy.Creatures.All(creature => creature.IsDead)) { envoy.ShouldRemove = true; World.GoalManager.OnGameEvent(new Goals.Triggers.TradeEnvoyKilled { PlayerFaction = envoy.OtherFaction, OtherFaction = envoy.OwnerFaction }); } } bool hadFactions = faction.TradeEnvoys.Count > 0; faction.TradeEnvoys.RemoveAll(t => t == null || t.ShouldRemove); if (hadFactions && faction.TradeEnvoys.Count == 0) { var music = World.Time.IsDay() ? "main_theme_day" : "main_theme_night"; SoundManager.PlayMusic(music); } }
public void Update(DwarfGame game, DwarfTime time) { TaskManager.Update(Faction.Minions); CurrentTool.Update(game, time); UpdateOrphanedTasks(); if (!World.Paused) { } else { CameraController.LastWheel = Mouse.GetState().ScrollWheelValue; } UpdateInput(game, time); if (Faction.Minions.Any(m => m.IsDead && m.TriggersMourning)) { foreach (CreatureAI minion in Faction.Minions) { minion.AddThought(Thought.ThoughtType.FriendDied); if (!minion.IsDead) { continue; } World.MakeAnnouncement( String.Format("{0} ({1}) died!", minion.Stats.FullName, minion.Stats.CurrentClass.Name)); SoundManager.PlaySound(ContentPaths.Audio.Oscar.sfx_gui_negative_generic); World.Tutorial("death"); } } Faction.Minions.RemoveAll(m => m.IsDead); UpdateRooms(); Faction.CraftBuilder.Update(time, this); HandlePosessedDwarf(); if (sliceDownheld) { sliceDownTimer.Update(time); if (sliceDownTimer.HasTriggered) { World.ChunkManager.ChunkData.SetMaxViewingLevel(World.ChunkManager.ChunkData.MaxViewingLevel - 1, ChunkManager.SliceMode.Y); sliceDownTimer.Reset(0.1f); } } if (sliceUpheld) { sliceUpTimer.Update(time); if (sliceUpTimer.HasTriggered) { World.ChunkManager.ChunkData.SetMaxViewingLevel(World.ChunkManager.ChunkData.MaxViewingLevel + 1, ChunkManager.SliceMode.Y); sliceUpTimer.Reset(0.1f); } } // Make sure that the faction's money is identical to the money in treasuries. Faction.Economy.CurrentMoney = Faction.Treasurys.Sum(treasury => treasury.Money); checkFoodTimer.Update(time); if (checkFoodTimer.HasTriggered) { var food = Faction.CountResourcesWithTag(Resource.ResourceTags.Edible); if (food == 0) { Faction.World.MakeAnnouncement("We're out of food!"); } } }
/// <summary> /// Called every frame /// </summary> /// <param name="gameTime">The current time</param> public void Update(DwarfTime gameTime) { EntityFactory.DoLazyActions(); if (FastForwardToDay) { if (Time.IsDay()) { FastForwardToDay = false; foreach (CreatureAI minion in Master.Faction.Minions) { minion.Status.Energy.CurrentValue = minion.Status.Energy.MaxValue; } //Master.ToolBar.SpeedButton.SetSpeed(1); Time.Speed = 100; } else { //Master.ToolBar.SpeedButton.SetSpecialSpeed(3); Time.Speed = 1000; } } //Drawer3D.DrawPlane(0, Camera.Position.X - 1500, Camera.Position.Z - 1500, Camera.Position.X + 1500, Camera.Position.Z + 1500, Color.Black); FillClosestLights(gameTime); IndicatorManager.Update(gameTime); AspectRatio = GraphicsDevice.Viewport.AspectRatio; Camera.AspectRatio = AspectRatio; Camera.Update(gameTime, ChunkManager); HandleAmbientSound(); Master.Update(Game, gameTime); GoalManager.Update(this); Time.Update(gameTime); if (Paused) { ComponentManager.UpdatePaused(); } // If not paused, we want to just update the rest of the game. else { TutorialManager.Update(Gui); GamePerformance.Instance.StartTrackPerformance("Diplomacy"); Diplomacy.Update(gameTime, Time.CurrentDate, this); GamePerformance.Instance.StopTrackPerformance("Diplomacy"); GamePerformance.Instance.StartTrackPerformance("Factions"); Factions.Update(gameTime); GamePerformance.Instance.StopTrackPerformance("Factions"); GamePerformance.Instance.StartTrackPerformance("Components"); ComponentManager.Update(gameTime, ChunkManager, Camera); GamePerformance.Instance.StopTrackPerformance("Components"); Sky.TimeOfDay = Time.GetSkyLightness(); Sky.CosTime = (float)(Time.GetTotalHours() * 2 * Math.PI / 24.0f); DefaultShader.TimeOfDay = Sky.TimeOfDay; GamePerformance.Instance.StartTrackPerformance("Monster Spawner"); MonsterSpawner.Update(gameTime); GamePerformance.Instance.StopTrackPerformance("Monster Spawner"); GamePerformance.Instance.StartTrackPerformance("All Asleep"); bool allAsleep = Master.AreAllEmployeesAsleep(); #if !UPTIME_TEST if (SleepPrompt == null && allAsleep && !FastForwardToDay && Time.IsNight()) { SleepPrompt = Gui.ConstructWidget(new Gui.Widgets.Confirm { Text = "All of your employees are asleep. Skip to daytime?", OkayText = "Skip to Daytime", CancelText = "Don't Skip", OnClose = (sender) => { if ((sender as Confirm).DialogResult == Confirm.Result.OKAY) { FastForwardToDay = true; } } }); Gui.ShowModalPopup(SleepPrompt); } else if (!allAsleep) { if (SleepPrompt != null) { SleepPrompt.Close(); } Time.Speed = 100; FastForwardToDay = false; SleepPrompt = null; } #endif GamePerformance.Instance.StopTrackPerformance("All Asleep"); } // These things are updated even when the game is paused GamePerformance.Instance.StartTrackPerformance("Chunk Manager"); ChunkManager.Update(gameTime, Camera, GraphicsDevice); ChunkRenderer.Update(gameTime, Camera, GraphicsDevice); GamePerformance.Instance.StopTrackPerformance("Chunk Manager"); GamePerformance.Instance.StartTrackPerformance("Sound Manager"); SoundManager.Update(gameTime, Camera, Time); GamePerformance.Instance.StopTrackPerformance("Sound Manager"); GamePerformance.Instance.StartTrackPerformance("Weather"); Weather.Update(this.Time.CurrentDate, this); GamePerformance.Instance.StopTrackPerformance("Weather"); if (gameFile != null) { // Cleanup game file. gameFile = null; } }
public void HandleAmbientSound() { AmbienceTimer.Update(DwarfTime.LastTime); if (!AmbienceTimer.HasTriggered && !firstAmbience) { return; } firstAmbience = false; // Before doing anything, determine if there is a rain or snow storm. if (Weather.IsRaining()) { PlaySpecialAmbient("sfx_amb_rain_storm"); return; } if (Weather.IsSnowing()) { PlaySpecialAmbient("sfx_amb_snow_storm"); return; } // First check voxels to see if we're underground or underwater. var vox = VoxelHelpers.FindFirstVisibleVoxelOnScreenRay(ChunkManager.ChunkData, GraphicsDevice.Viewport.Width / 2, GraphicsDevice.Viewport.Height / 2, Camera, GraphicsDevice.Viewport, 100.0f, false, null); if (vox.IsValid) { float height = WaterRenderer.GetTotalWaterHeightCells(vox); if (height > 0) { PlaySpecialAmbient("sfx_amb_ocean"); return; } else { // Unexplored voxels assumed to be cave. if (vox.IsValid && !vox.IsExplored) { PlaySpecialAmbient("sfx_amb_cave"); return; } var above = VoxelHelpers.GetVoxelAbove(vox); // Underground, do the cave test. if (above.IsValid && above.IsEmpty && above.SunColor == 0) { PlaySpecialAmbient("sfx_amb_cave"); return; } } } else { return; } // Now check for biome ambience. var pos = vox.WorldPosition; var biome = Overworld.GetBiomeAt(pos); if (!string.IsNullOrEmpty(biome.DayAmbience)) { if (prevAmbience[0] != biome.DayAmbience) { if (!string.IsNullOrEmpty(prevAmbience[0])) { SoundManager.StopAmbience(prevAmbience[0]); prevAmbience[0] = null; } if (!string.IsNullOrEmpty(prevAmbience[1])) { SoundManager.StopAmbience(prevAmbience[1]); prevAmbience[1] = null; } SoundManager.PlayAmbience(biome.DayAmbience); } prevAmbience[0] = biome.DayAmbience; } if (!string.IsNullOrEmpty(biome.NightAmbience) && prevAmbience[1] != biome.NightAmbience) { prevAmbience[1] = biome.NightAmbience; SoundManager.PlayAmbience(biome.NightAmbience); } }
protected override void LoadContent() { #if SHARP_RAVEN && !DEBUG try { #endif AssetManager.Initialize(Content, GraphicsDevice, GameSettings.Default); //var palette = TextureTool.ExtractPaletteFromDirectoryRecursive("Entities/Dwarf"); //var paletteTexture = TextureTool.Texture2DFromMemoryTexture(GraphicsDevice, TextureTool.MemoryTextureFromPalette(palette)); //paletteTexture.SaveAsPng(System.IO.File.OpenWrite("palette.png"), paletteTexture.Width, paletteTexture.Height); // Prepare GemGui GumInputMapper = new Gui.Input.GumInputMapper(Window.Handle); GumInput = new Gui.Input.Input(GumInputMapper); // Register all bindable actions with the input system. //GumInput.AddAction("TEST", Gui.Input.KeyBindingType.Pressed); GuiSkin = new RenderData(GraphicsDevice, Content); // Create console. ConsoleGui = new Gui.Root(GuiSkin); ConsoleGui.RootItem.AddChild(new Gui.Widgets.AutoGridPanel { Rows = 2, Columns = 4, AutoLayout = AutoLayout.DockFill }); ConsoleGui.RootItem.Layout(); if (_logwriter != null) { _logwriter.SetConsole(GetConsoleTile("LOG")); } Console.Out.WriteLine("Console created."); if (SoundManager.Content == null) { SoundManager.Content = Content; SoundManager.LoadDefaultSounds(); #if XNA_BUILD //SoundManager.SetActiveSongs(ContentPaths.Music.dwarfcorp, ContentPaths.Music.dwarfcorp_2, // ContentPaths.Music.dwarfcorp_3, ContentPaths.Music.dwarfcorp_4, ContentPaths.Music.dwarfcorp_5); #endif } if (GameSettings.Default.DisplayIntro) { StateManager.PushState(new IntroState(this, StateManager)); } else { StateManager.PushState(new MainMenuState(this, StateManager)); } BiomeLibrary.InitializeStatics(); EmbarkmentLibrary.InitializeDefaultLibrary(); VoxelChunk.InitializeStatics(); ControlSettings.Load(); Drawer2D.Initialize(Content, GraphicsDevice); ResourceLibrary.Initialize(); base.LoadContent(); #if SHARP_RAVEN && !DEBUG } catch (Exception exception) { if (ravenClient != null) { ravenClient.Capture(new SentryEvent(exception)); } throw; } #endif }
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. { if (GetRoot().GetComponent <DwarfThoughts>().HasValue(out var thoughts)) { 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! The last straw: {1}", Stats.FullName, thoughts.Thoughts.Last(t => t.HappinessModifier < 0.0f).Description), ClickAction: null, logEvent: true, eventDetails: String.Join("\n", thoughts.Thoughts.Where(t => t.HappinessModifier < 0.0f).Select(t => t.Description))); thoughts.Thoughts.Clear(); } else { 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!", Stats.FullName), ClickAction: null, logEvent: true, eventDetails: "So sick of this place!"); } LeaveWorld(); if (GetRoot().GetComponent <Inventory>().HasValue(out var inv)) { inv.Die(); } if (GetRoot().GetComponent <SelectionCircle>().HasValue(out var sel)) { sel.Die(); } 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.HasValue()) // 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.HasValue()) { ChangeTask(null); } } if (CurrentAct.HasValue(out Act currentAct)) { var status = currentAct.Tick(); bool retried = false; if (CurrentAct.HasValue(out Act newCurrentAct) && CurrentTask != null) { if (status == Act.Status.Fail) { LastFailedAct = newCurrentAct.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(); } }