#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 BasesScreen.cs * @date Created: 2007/01/21 * @author File creator: David Teviotdale * @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.Input; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; using CeGui; using ProjectXenocide.Utils; using ProjectXenocide.Model.Geoscape.Outposts; using ProjectXenocide.UI.Dialogs; using ProjectXenocide.UI.Scenes.Facility; using ProjectXenocide.Model.StaticData.Facilities; using Xenocide.Resources; #endregion namespace ProjectXenocide.UI.Screens { /// /// Screen that shows the layout of facilities in a X-Corp Outpost (Base) /// public class BasesScreen : Screen { /// /// Constructor (obviously) /// /// Index to X-Corp outpost screen is to show public BasesScreen(int selectedBase) : base("BasesScreen", @"Content\Textures\UI\BasesScreenBackground.png") { this.selectedBase = selectedBase; // Before showing, bring floorplan up to date scene = new FacilityScene(SelectedBaseFloorplan); Xenocide.AudioSystem.PlayRandomMusic("BaseView"); } /// /// Load the Scene's graphic content /// /// content manager that fetches the content /// the display public override void LoadContent(ContentManager content, GraphicsDevice device) { scene.LoadContent(content, device); } /// /// Perform processing which updates the screen. /// /// snapshot of timing values public override void Update(GameTime gameTime) { // If ESC key pressed while adding a facility (other than lift) cancel the add if (Keyboard.GetState().IsKeyDown(Keys.Escape) && (BasesScreenState.AddFacility == state)) { CancelFacility(); } } /// /// Render the 3D scene /// /// time interval since last render /// Device to render the globe to public override void Draw(GameTime gameTime, GraphicsDevice device) { // update funds shown on screen // Note, if display hasn't changed, don't write new value to text window // as this will make CeGui recompute all render quads. String funds = Util.StringFormat(Strings.SCREEN_BASES_FUNDS, Xenocide.GameState.GeoData.XCorp.Bank.CurrentBalance); if (fundsText.Text != funds) { fundsText.Text = funds; } scene.Draw(device, sceneWindow.Rect); } /// /// Set the screen into a build facility state, where the location of a new /// facility can be selected. /// /// Handle to the new facility to build. public void BuildFacility(FacilityHandle handle) { NewFacility = handle; State = BasesScreen.BasesScreenState.AddFacility; // Change build button to cancel button buildFacButton.Text = Strings.BUTTON_CANCEL_FACILITY; } #region Create the CeGui widgets /// /// add the buttons to the screen /// protected override void CreateCeguiWidgets() { // Window indicating where the 3D scene is sceneWindow = GuiBuilder.CreateImage(CeguiId + "_viewport"); //... dimensions chosen to make 3D scene 512 x 512 at 600 x 800 resolution. AddWidget(sceneWindow, 0.02f, 0.073f, 0.641f, 0.8534f); // combo box to allow user to pick base to work on basesListComboBox = GuiBuilder.CreateComboBox("basesListComboBox"); AddWidget(basesListComboBox, 0.7475f, 0.06f, 0.2275f, 0.40f); Misc.PopulateHumanBasesList(basesListComboBox, selectedBase); basesListComboBox.ListSelectionAccepted += new WindowEventHandler(OnBaseSelectionChanged); // add text giving available funds fundsText = GuiBuilder.CreateText(CeguiId + "_fundsText"); AddWidget(fundsText, 0.7475f, 0.40f, 0.2275f, 0.04125f); // other buttons newBaseButton = AddButton("BUTTON_BUILD_NEW_BASE", 0.7475f, 0.50f, 0.2275f, 0.04125f); baseInfoButton = AddButton("BUTTON_BASE_INFORMATION", 0.7475f, 0.55f, 0.2275f, 0.04125f); soldiersButton = AddButton("BUTTON_SOLDIERS", 0.7475f, 0.60f, 0.2275f, 0.04125f); equipCraftButton = AddButton("BUTTON_EQUIP_CRAFT", 0.7475f, 0.65f, 0.2275f, 0.04125f); buildFacButton = AddButton("BUTTON_BUILD_FACILITIES", 0.7475f, 0.70f, 0.2275f, 0.04125f); produceButton = AddButton("BUTTON_MANUFACTURE", 0.7475f, 0.75f, 0.2275f, 0.04125f); transferButton = AddButton("BUTTON_TRANSFER", 0.7475f, 0.80f, 0.2275f, 0.04125f); buyButton = AddButton("BUTTON_BUY", 0.7475f, 0.85f, 0.2275f, 0.04125f); sellButton = AddButton("BUTTON_SELL", 0.7475f, 0.90f, 0.2275f, 0.04125f); geoscapeButton = AddButton("BUTTON_GEOSCAPE", 0.7475f, 0.95f, 0.2275f, 0.04125f); // mouse activity on the "Scene" window sceneWindow.MouseMove += new CeGui.MouseEventHandler(OnMouseMoveInScene); sceneWindow.MouseButtonsDown += new CeGui.MouseEventHandler(OnMouseDownInScene); // other buttons being pressed newBaseButton.Clicked += new CeGui.GuiEventHandler(OnNewBase); baseInfoButton.Clicked += new CeGui.GuiEventHandler(ShowBaseInfoScreen); soldiersButton.Clicked += new CeGui.GuiEventHandler(OnSoldiersButton); equipCraftButton.Clicked += new CeGui.GuiEventHandler(OnEquipCraftButton); buildFacButton.Clicked += new CeGui.GuiEventHandler(OnBuildFacilitiesButton); produceButton.Clicked += new CeGui.GuiEventHandler(OnManufactureButton); transferButton.Clicked += new CeGui.GuiEventHandler(OnTransferButton); buyButton.Clicked += new CeGui.GuiEventHandler(OnBuyButton); sellButton.Clicked += new CeGui.GuiEventHandler(OnSellButton); geoscapeButton.Clicked += new CeGui.GuiEventHandler(OnGeoscapeButton); } /// /// CeGui widget that indicates where to draw the 3D scene /// private CeGui.Widgets.StaticImage sceneWindow; private CeGui.Widgets.ComboBox basesListComboBox; private CeGui.Widgets.StaticText fundsText; private CeGui.Widgets.PushButton newBaseButton; private CeGui.Widgets.PushButton baseInfoButton; private CeGui.Widgets.PushButton soldiersButton; private CeGui.Widgets.PushButton equipCraftButton; private CeGui.Widgets.PushButton buildFacButton; private CeGui.Widgets.PushButton produceButton; private CeGui.Widgets.PushButton transferButton; private CeGui.Widgets.PushButton buyButton; private CeGui.Widgets.PushButton sellButton; private CeGui.Widgets.PushButton geoscapeButton; #endregion Create the CeGui widgets #region event handlers /// React to user moving the mouse in the 3D scene /// Not used /// Mouse information private void OnMouseMoveInScene(object sender, CeGui.MouseEventArgs e) { switch (state) { case BasesScreenState.NotAdding: // nothing to do break; case BasesScreenState.AddAccessLift: case BasesScreenState.AddFacility: // Update position of facility we're adding UpdateNewFacilityPosition(MouseToCell(e)); break; default: Debug.Assert(false); break; } } /// React to user clicking mouse in the 3D scene /// CeGui widget sending the event /// Mouse information private void OnMouseDownInScene(object sender, CeGui.MouseEventArgs e) { Xenocide.AudioSystem.PlaySound("Menu\\buttonclick2_changesetting.ogg"); switch (state) { case BasesScreenState.NotAdding: RemoveFacility(MouseToCell(e)); break; case BasesScreenState.AddAccessLift: case BasesScreenState.AddFacility: // Start building facility at this position AddFacility(MouseToCell(e)); break; default: Debug.Assert(false); break; } } /// user wants to look at a different base /// Not used /// Not used private void OnBaseSelectionChanged(object sender, WindowEventArgs e) { if (BasesScreenState.NotAdding == state) { CeGui.Widgets.ListboxItem item = basesListComboBox.SelectedItem; if (item != null) { selectedBase = basesListComboBox.GetItemIndex(item); // Need to completely redraw scene. ScreenManager.ScheduleScreen(new BasesScreen(selectedBase)); } } } /// Replace this screen with matching BaseInfoScreen /// Not used /// Not used private void ShowBaseInfoScreen(object sender, CeGui.GuiEventArgs e) { if (BasesScreenState.NotAdding == state) { ScreenManager.ScheduleScreen(new BaseInfoScreen(selectedBase)); } } /// Replace this screen with soldier list screen. /// Not used /// Not used private void OnSoldiersButton(object sender, CeGui.GuiEventArgs e) { if (BasesScreenState.NotAdding == state) { ScreenManager.ScheduleScreen(new SoldiersListScreen(selectedBase)); } } /// user wants to add a new base /// Not used /// Not used private void OnNewBase(object sender, CeGui.GuiEventArgs e) { // can't create a new base if we're adding a facility to this one if (BasesScreenState.NotAdding == state) { // check that we haven't hit maximum number of bases allowed if (8 <= Xenocide.GameState.GeoData.Outposts.Count) { Util.ShowMessageBox(Strings.MSGBOX_MAX_EIGHT_BASES); } else { // stop time Xenocide.GameState.GeoData.GeoTime.StopTime(); GeoscapeScreen geoscapeScreen = new GeoscapeScreen(); geoscapeScreen.State = new GeoscapeScreen.AddingBaseScreenState(geoscapeScreen); ScreenManager.ScheduleScreen(geoscapeScreen); } } } /// user wants to equip the craft assigned to this outpost /// Not used /// Not used private void OnEquipCraftButton(object sender, CeGui.GuiEventArgs e) { if (BasesScreenState.NotAdding == state) { ScreenManager.ScheduleScreen(new EquipCraftScreen(selectedBase)); } } /// user wants to add a facility to the current base /// Not used /// Not used private void OnBuildFacilitiesButton(object sender, CeGui.GuiEventArgs e) { if (BasesScreenState.NotAdding == state) { // if base is empty, first step is to add an access lift if (SelectedBaseFloorplan.IsBaseEmpty()) { State = BasesScreenState.AddAccessLift; } else { ScreenManager.ShowDialog(new BuildFacilityDialog(this)); } } else if (BasesScreenState.AddFacility == state) { // have to explictly play this sound, because sound is turned off Xenocide.AudioSystem.PlaySound(DefaultButtonClickSound); // In this state, it is a cancel button CancelFacility(); } } /// User has clicked the "Manufacture" button /// Not used /// Not used private void OnManufactureButton(object sender, CeGui.GuiEventArgs e) { ShowManufactureScreen(); } /// User has clicked the "Transfer" button /// Not used /// Not used private void OnTransferButton(object sender, CeGui.GuiEventArgs e) { ShowMakeTransferScreen(); } /// User has clicked the "Buy" button /// Not used /// Not used private void OnBuyButton(object sender, CeGui.GuiEventArgs e) { if (BasesScreenState.NotAdding == state) { ScreenManager.ScheduleScreen(new PurchaseScreen(selectedBase)); } } /// User has clicked the "Sell" button /// Not used /// Not used private void OnSellButton(object sender, CeGui.GuiEventArgs e) { if (BasesScreenState.NotAdding == state) { ScreenManager.ScheduleScreen(new SellScreen(selectedBase)); } } /// User has clicked the "go to geoscape" button /// Not used /// Not used private void OnGeoscapeButton(object sender, CeGui.GuiEventArgs e) { if (BasesScreenState.NotAdding == state) { ScreenManager.ScheduleScreen(new GeoscapeScreen()); } } #endregion event handlers #region helper functions for event handlers /// /// Convert the mouse's position to the cell co-ordinates on the Base's floorplan /// /// Mouse information (specifically, location of mouse) /// Cell in base cursor is over private Microsoft.Xna.Framework.Vector2 MouseToCell(CeGui.MouseEventArgs e) { //convert co-ords from absolute to relative into the scene window PointF coords2 = sceneWindow.AbsoluteToRelative(new PointF( e.Position.X - sceneWindow.AbsoluteX, e.Position.Y - sceneWindow.AbsoluteY)); // convert to cell position return scene.WindowToCell(coords2); } /// /// Set the position for the new facility we're adding to the base /// /// top left corner of position, in base floorplan "cells" private void UpdateNewFacilityPosition(Microsoft.Xna.Framework.Vector2 cellCoords) { Debug.Assert(null != newFacility); newFacility.X = (SByte)cellCoords.X; newFacility.Y = (SByte)cellCoords.Y; } /// /// Add the new facility to the base /// /// top left corner of position, in base floorplan "cells" private void AddFacility(Microsoft.Xna.Framework.Vector2 cellCoords) { UpdateNewFacilityPosition(cellCoords); XenoError error = SelectedBaseFloorplan.IsPositionLegal(newFacility); if ((XenoError.None == error) || ((BasesScreenState.AddAccessLift == state) && (XenoError.CellHasNoNeighbours == error))) { Xenocide.GameState.GeoData.XCorp.Bank.Debit(newFacility.FacilityInfo.BuildCost); SelectedBaseFloorplan.AddFacility(newFacility); // Redraw scene with changes ScreenManager.ScheduleScreen(new BasesScreen(selectedBase)); } else { Util.ShowMessageBox(Util.GetErrorMessage(error)); } } /// /// Cancel building a new facility /// private void CancelFacility() { State = BasesScreenState.NotAdding; NewFacility = null; // Set text back to normal buildFacButton.Text = Strings.BUTTON_BUILD_FACILITIES; } /// /// Remove a facility from the base /// /// location in base of facility to remove private void RemoveFacility(Microsoft.Xna.Framework.Vector2 cellCoords) { FacilityHandle facility = SelectedBaseFloorplan.GetFacilityAt((int)cellCoords.X, (int)cellCoords.Y); if (null != facility) { XenoError error = SelectedBaseFloorplan.CanRemoveFacility(facility); if (XenoError.None == error) { YesNoDialog dlg = new YesNoDialog( Util.StringFormat(Strings.YESNOMSG_DISMANTLE_FACILITY, facility.FacilityInfo.Name) ); // if yes is pressed, delete the facility and redraw scene with changes dlg.YesAction += delegate() { Xenocide.GameState.GeoData.XCorp.Bank.Credit(facility.FacilityInfo.ScrapRevenue); SelectedBaseFloorplan.RemoveFacility(facility); ScreenManager.ScheduleScreen(new BasesScreen(selectedBase)); }; Xenocide.ScreenManager.ShowDialog(dlg); } else { Util.ShowMessageBox(Util.GetErrorMessage(error)); } } } /// Bring up the "Manufacture" Screen private void ShowManufactureScreen() { if (BasesScreenState.NotAdding == state) { ScreenManager.ScheduleScreen(new ManufactureScreen(selectedBase)); } } /// Try bringing up the "MakeTransfer" Screen private void ShowMakeTransferScreen() { if (BasesScreenState.NotAdding == state) { // if there's only one outpost, we can't transfer int numOutposts = Xenocide.GameState.GeoData.Outposts.Count; if (numOutposts < 2) { Util.ShowMessageBox(Strings.MSGBOX_NEED_2_BASES_TO_TRANSFER); } else { // pick another base for initial destination outpost for items int destination = (selectedBase + 1) % numOutposts; ScreenManager.ScheduleScreen(new MakeTransferScreen(selectedBase, destination)); } } } #endregion helper functions for event handlers /// /// The state this screen is in /// public enum BasesScreenState { /// /// We're in "not adding anything to base" mode /// /// NotAdding, /// /// We're in "Add the Access Lift facility to the base" mode /// AddAccessLift, /// /// We're in "Add a (not access lift) facility to the base" mode /// AddFacility, } #region Fields /// /// What mode are we in? /// public BasesScreenState State { get { return state; } set { // if adding a facility, buttons are disabled EnableButtonSounds = (value == BasesScreenState.NotAdding); state = value; if (BasesScreenState.AddAccessLift == state) { NewFacility = new FacilityHandle("FAC_BASE_ACCESS_FACILITY"); } } } /// /// The facility we are adding to the base /// public FacilityHandle NewFacility { get { return newFacility; } set { newFacility = value; scene.NewFacility = newFacility; } } /// /// The 3D view shown on the screen /// private FacilityScene scene; /// /// The floorplan of the currently selected base /// public Floorplan SelectedBaseFloorplan { get { return Xenocide.GameState.GeoData.Outposts[selectedBase].Floorplan; } } // index specifying the X-Corp outpost that screen is showing private int selectedBase; /// /// What mode are we in? /// private BasesScreenState state; /// /// The facility we are adding to the base /// private FacilityHandle newFacility; #endregion Fields } }