#region Copyright /* -------------------------------------------------------------------------------- This source file is part of Xenocide by Project Xenocide Team For the latest info on Xenocide, see http://www.projectxenocide.com/ This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 2.5 License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/2.5/ or send a letter to Creative Commons, 543 Howard Street, 5th Floor, San Francisco, California, 94105, USA. -------------------------------------------------------------------------------- */ /* * @file GeoscapeScreenStates.cs * @date Created: 2007/01/21 * @author File creator: dteviot * @author Credits: none */ #endregion #region Using Statements using System; using System.Collections.Generic; using System.Text; using System.Diagnostics; using System.Drawing; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using ProjectXenocide.Utils; using ProjectXenocide.UI.Scenes.Geoscape; using ProjectXenocide.UI.Dialogs; using ProjectXenocide.Model; using ProjectXenocide.Model.Geoscape; using ProjectXenocide.Model.Geoscape.Vehicles; using ProjectXenocide.Model.Geoscape.Outposts; using ProjectXenocide.Model.Geoscape.Geography; using ProjectXenocide.Model.Geoscape.AI; using System.Threading; using Xenocide.Resources; #endregion namespace ProjectXenocide.UI.Screens { /* This file holds the Geoscape's nested ScreenState classes */ public partial class GeoscapeScreen : PolarScreen { /// /// Control behaviour, based on state screen is in. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")] public abstract class ScreenState { /// /// Constructor /// /// The parent geoscape protected ScreenState(GeoscapeScreen geoscapeScreen) { this.geoscapeScreen = geoscapeScreen; } /// /// Create the widgets shown when in this state /// /// a button that needs to go over the SceneWindow public virtual CeGui.Window CreateCeguiWidgets() { return null; } /// /// Update any model data /// /// Provides a snapshot of timing values. public virtual void Update(GameTime gameTime) { // default behaviour is do nothing } /// /// React to "Bases" button being pressed /// public virtual void OnBasesButton() { // default behaviour is do nothing } /// /// React to "Research" button being pressed /// public virtual void OnResearchButton() { // default behaviour is do nothing } /// /// React to "Options" button being pressed /// public virtual void OnOptionsButton() { // default behaviour is do nothing } /// /// React to "Funding" button being pressed /// public virtual void OnFundingButton() { // default behaviour is do nothing } /// /// React to "Statistics" button being pressed /// public virtual void OnStatisticsButton() { // default behaviour is do nothing } /// /// React to "X-Net" button being pressed /// public virtual void OnXNetButton() { // default behaviour is do nothing } /// /// React to "Intercept" button being pressed /// public virtual void OnInterceptButton() { // default behaviour is do nothing } /// React to user clicking on one of the "time rate" buttons /// Button the user clicked public virtual void OnTimeRateButtonClicked(CeGui.Widgets.PushButton button) { // default behaviour is do nothing } /// React to user clicking left mouse button in the 3D geoscape scene /// Position on earth where mouse was clicked public virtual void OnLeftMouseDownInScene(GeoPosition pos) { } #region Fields /// /// The parent geoscape /// public GeoscapeScreen GeoscapeScreen { get { return geoscapeScreen; } } /// /// The parent geoscape /// private GeoscapeScreen geoscapeScreen; #endregion Fields } /// /// Screen behaviour, when we're just viewing the Geoscape /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")] public class ViewGeoscapeScreenState : ScreenState { /// /// Constructor /// /// The parent geoscape public ViewGeoscapeScreenState(GeoscapeScreen geoscapeScreen) : base(geoscapeScreen) { } /// /// Create extra debug button for view state /// /// a button that needs to go over the SceneWindow public override CeGui.Window CreateCeguiWidgets() { // debug buttons if (Xenocide.StaticTables.StartSettings.Cheats.ControlAlienMissions) { alienMissionButton = GeoscapeScreen.AddButton("BUTTON_ALIEN_MISSION", 0.76f, 0.95f, 0.23f, 0.04125f); alienMissionButton.Clicked += new CeGui.GuiEventHandler(OnAlienMissionsClicked); return alienMissionButton; } return null; } /// /// Update any model data /// /// Provides a snapshot of timing values. public override void Update(GameTime gameTime) { Xenocide.GameState.GeoData.Update(gameTime); } /// /// React to "Bases" button being pressed /// public override void OnBasesButton() { // bring up the bases screen GeoscapeScreen.ScreenManager.ScheduleScreen(new BasesScreen(0)); } /// /// React to "Research" button being pressed /// public override void OnResearchButton() { // bring up the research screen GeoscapeScreen.ScreenManager.ScheduleScreen(new ResearchScreen()); } /// /// React to "Options" button being pressed /// public override void OnOptionsButton() { // bring up the options screen GeoscapeScreen.ScreenManager.ShowDialog(new OptionsDialog()); } /// /// React to "Funding" button being pressed /// public override void OnFundingButton() { // bring up the funding screen GeoscapeScreen.ScreenManager.ScheduleScreen(new MonthlyReportScreen(false)); } /// /// React to "Statistics" button being pressed /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "FxCop False Positive")] public override void OnStatisticsButton() { // bring up the statistics screen GeoscapeScreen.ScreenManager.ScheduleScreen(new StatisticsScreen()); } /// /// React to "X-Net" button being pressed /// public override void OnXNetButton() { // bring up the X-Net screen GeoscapeScreen.ScreenManager.ScheduleScreen(new XNetScreen()); } /// /// React to "Intercept" button being pressed /// public override void OnInterceptButton() { // bring up the launch intercept Dialog GeoscapeScreen.ScreenManager.ShowDialog(new LaunchInterceptDialog()); } /// React to user clicking on one of the "time rate" buttons /// Button the user clicked public override void OnTimeRateButtonClicked(CeGui.Widgets.PushButton button) { float ratio = 0.0f; if (GeoscapeScreen.IsButton(button, "BUTTON_TIME_STOP")) { ratio = 0.0f; } else if (GeoscapeScreen.IsButton(button, "BUTTON_TIME_X60")) { ratio = 60.0f; } else if (GeoscapeScreen.IsButton(button, "BUTTON_TIME_X3600")) { ratio = 3600.0f; } else if (GeoscapeScreen.IsButton(button, "BUTTON_TIME_X86400")) { ratio = 86400.0f; } Xenocide.GameState.GeoData.GeoTime.TimeRatio = ratio; } /// React to user clicking the AlienMission button /// unused /// unused private void OnAlienMissionsClicked(object sender, CeGui.GuiEventArgs e) { // bring up the alien mission dialog GeoscapeScreen.ScreenManager.ShowDialog(new AlienMissionDialog()); } /// React to user clicking left mouse button in the 3D geoscape scene /// Position on earth where mouse was clicked public override void OnLeftMouseDownInScene(GeoPosition pos) { // if there's a craft near the location, bring up the appropriate info dialog Craft craft = GeoscapeScreen.FindClosestCraft(pos); if (null != craft) { // bit nasty, craft could be either UFO or aircraft Aircraft aircraft = craft as Aircraft; if (null != aircraft) { Xenocide.ScreenManager.ShowDialog(new AircraftOrdersDialog(aircraft)); } else { Ufo ufo = craft as Ufo; Debug.Assert(null != ufo); Xenocide.ScreenManager.ShowDialog(new UfoInfoDialog(ufo)); } } } private CeGui.Widgets.PushButton alienMissionButton; } /// /// Screen behaviour, when we're adding a base /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")] public class AddingBaseScreenState : ScreenState { /// /// Constructor /// /// The parent geoscape public AddingBaseScreenState(GeoscapeScreen geoscapeScreen) : base(geoscapeScreen) { } /// /// Create the widgets shown when in this state /// /// a button that needs to go over the SceneWindow public override CeGui.Window CreateCeguiWidgets() { // buttons are disabled, so turn off sound GeoscapeScreen.EnableButtonSounds = false; // The "cancel adding base" button cancelNewBaseButton = GeoscapeScreen.AddButton("BUTTON_CANCEL_NEW_BASE", 0.02f, 0.06f, 0.915f, 0.04125f); cancelNewBaseButton.Clicked += new CeGui.GuiEventHandler(GeoscapeScreen.OnCancelNewBase); return cancelNewBaseButton; } /// React to user clicking left mouse button in the 3D geoscape scene /// Position on earth where mouse was clicked public override void OnLeftMouseDownInScene(GeoPosition pos) { Xenocide.AudioSystem.PlaySound("PlanetView\\clickobjectonplanet.ogg"); // Build base at selected point GeoscapeScreen.ConfirmBasePositionDialog(pos, false); } private CeGui.Widgets.PushButton cancelNewBaseButton; } /// /// Screen behaviour, when we're adding the first base /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")] public class AddingFirstBaseScreenState : ScreenState { /// /// Constructor /// /// The parent geoscape public AddingFirstBaseScreenState(GeoscapeScreen geoscapeScreen) : base(geoscapeScreen) { } /// /// Create the widgets shown when in this state /// /// a button that needs to go over the SceneWindow public override CeGui.Window CreateCeguiWidgets() { // buttons are disabled, so turn off sound GeoscapeScreen.EnableButtonSounds = false; // static text to show the string "set position of first base" message setFirstBaseTextWindow = GeoscapeScreen.GuiBuilder.CreateText(GeoscapeScreen.CeguiId + "_setFirstBase"); GeoscapeScreen.AddWidget(setFirstBaseTextWindow, 0.02f, 0.06f, 0.641f, 0.04125f); setFirstBaseTextWindow.Text = Strings.SCREEN_GEOSCAPE_FIRST_BASE; return null; } /// React to user clicking left mouse button in the 3D geoscape scene /// Position on earth where mouse was clicked public override void OnLeftMouseDownInScene(GeoPosition pos) { Xenocide.AudioSystem.PlaySound("PlanetView\\clickobjectonplanet.ogg"); // Build base at selected point GeoscapeScreen.ConfirmBasePositionDialog(pos, true); } private CeGui.Widgets.StaticText setFirstBaseTextWindow; } /// /// Screen behaviour, when we're setting an intercept target /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")] public class TargetingScreenState : ScreenState { /// /// Constructor /// /// The parent geoscape /// The craft we're setting a destination for public TargetingScreenState(GeoscapeScreen geoscapeScreen, Craft craft) : base(geoscapeScreen) { this.craft = craft; } /// /// Create the widgets shown when in this state /// /// a button that needs to go over the SceneWindow public override CeGui.Window CreateCeguiWidgets() { // buttons are disabled, so turn off sound GeoscapeScreen.EnableButtonSounds = false; // The "Cancel target selection" button cancelTargetingButton = GeoscapeScreen.AddButton("BUTTON_CANCEL_TARGETING", 0.02f, 0.06f, 0.915f, 0.04125f); cancelTargetingButton.Clicked += new CeGui.GuiEventHandler(OnCancelTargeting); return cancelTargetingButton; } /// React to user clicking left mouse button in the 3D geoscape scene /// Position on earth where mouse was clicked public override void OnLeftMouseDownInScene(GeoPosition pos) { Xenocide.AudioSystem.PlaySound("PlanetView\\clickobjectonplanet.ogg"); Craft ufo = FindClosestUfo(pos); AlienSite site = FindClosestAlienSite(pos); if ((null == ufo) && (null == site)) { // no ufo or site, so target the position TargetGeoposition(pos); } else if ((ufo != null) && ((site == null) || (pos.Distance(ufo.Position) <= pos.Distance(site.Position))) ) { // no site or UFO closer than site, so target the UFO Target(ufo); } else { Target(site); } } /// /// Return the UFO that is closest to position on the Geoscape. /// /// 1. UFOs more than 500km away from position will be ignored. /// /// center of search /// closest UFO, or null if nothing found private static Craft FindClosestUfo(GeoPosition pos) { return pos.FindClosest(Xenocide.GameState.GeoData.Overmind.Ufos, FindClosestMaxDistance); } /// /// Return the AlienSite that is closest to position on the Geoscape. /// /// 1. Sites more than 500km away from position will be ignored. /// /// center of search /// closest Alien site, or null if nothing found private static AlienSite FindClosestAlienSite(GeoPosition pos) { return pos.FindClosest(Xenocide.GameState.GeoData.Overmind.Sites, FindClosestMaxDistance); } /// /// If player approves, set UFO as craft's target /// /// Ufo to set as target private void Target(Craft ufo) { YesNoDialog dlg = new YesNoDialog( Util.StringFormat(Strings.YESNOMSG_TARGET_UFO, ufo.Name) ); // if yes is pressed, send aircraft after the ufo and exit target mode dlg.YesAction += delegate() { craft.Mission.Abort(); craft.Mission = new InterceptMission(craft, ufo); GeoscapeScreen.ScreenManager.ScheduleScreen(new GeoscapeScreen()); }; Xenocide.ScreenManager.ShowDialog(dlg); } /// /// If player approves, set Alien Site as craft's target /// /// AlienSite to set as target private void Target(AlienSite site) { // can only target ground sites if craft is carrying soldiers if (craft.IsCarryingSoldiers) { YesNoDialog dlg = new YesNoDialog( Util.StringFormat(Strings.YESNOMSG_TARGET_ALIEN_SITE, site.Name) ); // if yes is pressed, send aircraft after the site and exit target mode dlg.YesAction += delegate() { craft.Mission.Abort(); craft.Mission = new AttackAlienSiteMission(craft, site); GeoscapeScreen.ScreenManager.ScheduleScreen(new GeoscapeScreen()); }; Xenocide.ScreenManager.ShowDialog(dlg); } else { Util.ShowMessageBox(Strings.MSGBOX_NOT_CARRYING_SOLDIERS, craft.Name, site.Name); } } /// /// If player approves, set Geoposition as craft's target /// /// GeoPosition to set as target private void TargetGeoposition(GeoPosition pos) { YesNoDialog dlg = new YesNoDialog(Strings.YESNOMSG_TARGET_GEOPOSITION); // if yes is pressed, send aircraft to the location and exit target mode dlg.YesAction += delegate() { craft.Mission.Abort(); craft.Mission = new PatrolMission(craft, pos); GeoscapeScreen.ScreenManager.ScheduleScreen(new GeoscapeScreen()); }; Xenocide.ScreenManager.ShowDialog(dlg); } /// User has clicked on the "Cancel Target Selection" button /// Not used /// Not used [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "FxCop False Positive")] private void OnCancelTargeting(object sender, CeGui.GuiEventArgs e) { // put Geoscape back in "view anything" mode GeoscapeScreen.ScreenManager.ScheduleScreen(new GeoscapeScreen()); } /// /// The "Cancel target selection" button /// private CeGui.Widgets.PushButton cancelTargetingButton; #region Fields /// /// Maximum distance to look at when using FindClosestXXXXX() functions. /// Currently 500 km. /// private static readonly double FindClosestMaxDistance = GeoPosition.KilometersToRadians(501); /// /// The craft we're setting a destination for /// Craft craft; #endregion Fields } /// /// Screen behaviour, when selecting an alien mission site /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")] public class TargetAlienMissionState : ScreenState { /// /// Constructor /// /// The parent geoscape /// The mission type to create public TargetAlienMissionState(GeoscapeScreen geoscapeScreen, AlienMission missionType) : base(geoscapeScreen) { this.missionType = missionType; } /// /// Create the widgets shown when in this state /// /// a button that needs to go over the SceneWindow public override CeGui.Window CreateCeguiWidgets() { // buttons are disabled, so turn off sound GeoscapeScreen.EnableButtonSounds = false; // The "cancel adding base" button cancelTargetButton = GeoscapeScreen.AddButton("BUTTON_CANCEL_ALIEN_MISSION", 0.02f, 0.06f, 0.915f, 0.04125f); cancelTargetButton.Clicked += new CeGui.GuiEventHandler(OnCancelAlienMission); return cancelTargetButton; } /// React to user clicking left mouse button in the 3D geoscape scene /// Position on earth where mouse was clicked [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "FxCop false positive")] public override void OnLeftMouseDownInScene(GeoPosition pos) { // Generate mission at selected point Xenocide.GameState.GeoData.Overmind.DebugCreateMission(missionType, pos); GeoscapeScreen.ScreenManager.ScheduleScreen(new GeoscapeScreen()); } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "FxCop false positive")] private void OnCancelAlienMission(object sender, CeGui.GuiEventArgs e) { GeoscapeScreen.ScreenManager.ScheduleScreen(new GeoscapeScreen()); } private CeGui.Widgets.PushButton cancelTargetButton; private AlienMission missionType; } } }