#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 ManufactureScreen.cs
* @date Created: 2007/10/07
* @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 CeGui;
using ProjectXenocide.Utils;
using ProjectXenocide.Model.Geoscape;
using ProjectXenocide.Model.Geoscape.Outposts;
using ProjectXenocide.Model.StaticData;
using ProjectXenocide.Model.StaticData.Items;
using ProjectXenocide.Model.StaticData.Research;
using ProjectXenocide.Model;
using Xenocide.Resources;
#endregion
namespace ProjectXenocide.UI.Screens
{
///
/// This is the screen where player sets the items an outpost is manufacturing
///
public class ManufactureScreen : Screen
{
///
/// Constructor (obviously)
///
/// Index to outpost screen is to show
public ManufactureScreen(int selectedOutpostIndex)
: base("Manufacture")
{
this.selectedOutpostIndex = selectedOutpostIndex;
}
#region Create the CeGui widgets
///
/// add the buttons to the screen
///
protected override void CreateCeguiWidgets()
{
// Get projects to bring their progress up to date
ProjectMgr.Update();
FindIdleEngineers();
// Text giving number of idle engineers
availableText = AddStaticText(0.01f, 0.01f, 0.7f, 0.08f);
availableText.Text = MakeIdleEngineersString();
availableText.HorizontalFormat = HorizontalTextFormat.WordWrapLeft;
// The grids
InitializeGrids();
PopulateProjectGrid();
// PopulateRequirementsGrid();
// buttons
buildMoreButton = AddButton("BUTTON_BUILD_MORE", 0.7475f, 0.60f, 0.2275f, 0.04125f, "PlanetView\\zoomin.ogg");
buildLessButton = AddButton("BUTTON_BUILD_LESS", 0.7475f, 0.65f, 0.2275f, 0.04125f, "PlanetView\\zoomout.ogg");
cancelBuildButton = AddButton("BUTTON_CANCEL_BUILD", 0.7475f, 0.70f, 0.2275f, 0.04125f);
addIdleEngineersButton = AddButton("BUTTON_ADD_IDLE_ENGINEERS", 0.7475f, 0.75f, 0.2275f, 0.04125f, "PlanetView\\zoomin.ogg");
moreEngineersButton = AddButton("BUTTON_MORE_ENGINEERS", 0.7475f, 0.80f, 0.2275f, 0.04125f, "PlanetView\\zoomin.ogg");
lessEngineersButton = AddButton("BUTTON_LESS_ENGINEERS", 0.7475f, 0.85f, 0.2275f, 0.04125f, "PlanetView\\zoomout.ogg");
removeAllEngineersButton = AddButton("BUTTON_REMOVE_ALL_ENGINEERS", 0.7475f, 0.90f, 0.2275f, 0.04125f, "PlanetView\\zoomout.ogg");
closeButton = AddButton("BUTTON_CLOSE", 0.7475f, 0.95f, 0.2275f, 0.04125f);
buildMoreButton.Clicked += new CeGui.GuiEventHandler(OnBuildMoreButton);
buildLessButton.Clicked += new CeGui.GuiEventHandler(OnBuildLessButton);
cancelBuildButton.Clicked += new CeGui.GuiEventHandler(OnCancelBuildButton);
moreEngineersButton.Clicked += new CeGui.GuiEventHandler(OnMoreButton);
lessEngineersButton.Clicked += new CeGui.GuiEventHandler(OnLessButton);
addIdleEngineersButton.Clicked += new CeGui.GuiEventHandler(OnAddIdleButton);
removeAllEngineersButton.Clicked += new CeGui.GuiEventHandler(OnRemoveAllButton);
closeButton.Clicked += new CeGui.GuiEventHandler(OnCloseButton);
}
private CeGui.Widgets.StaticText availableText;
private CeGui.Widgets.MultiColumnList projectGrid;
private CeGui.Widgets.MultiColumnList requirementsGrid;
private CeGui.Widgets.PushButton buildMoreButton;
private CeGui.Widgets.PushButton buildLessButton;
private CeGui.Widgets.PushButton cancelBuildButton;
private CeGui.Widgets.PushButton moreEngineersButton;
private CeGui.Widgets.PushButton lessEngineersButton;
private CeGui.Widgets.PushButton removeAllEngineersButton;
private CeGui.Widgets.PushButton addIdleEngineersButton;
private CeGui.Widgets.PushButton closeButton;
///
/// Create MultiColumnListBoxs which appear on the screen
///
private void InitializeGrids()
{
projectGrid = AddGrid(0.01f, 0.13f, 0.70f, 0.56f,
Strings.SCREEN_MANUFACTURE_COLUMN_PROJECT, 0.50f,
Strings.SCREEN_MANUFACTURE_COLUMN_ENGINEERS, 0.15f,
Strings.SCREEN_MANUFACTURE_COLUMN_BUILD_QUANTITY, 0.15f,
Strings.SCREEN_MANUFACTURE_COLUMN_ETA, 0.15f
);
projectGrid.SelectionChanged += new WindowEventHandler(OnProjectGridSelectionChanged);
requirementsGrid = AddGrid(0.01f, 0.72f, 0.70f, 0.27f,
Strings.SCREEN_MANUFACTURE_COLUMN_RESOURCE, 0.50f,
Strings.SCREEN_MANUFACTURE_COLUMN_QUANTITY_NEEDED, 0.23f,
Strings.SCREEN_MANUFACTURE_COLUMN_QUANTITY_AVAILABLE, 0.25f
);
}
///
/// Put the list of list of items being built/available to build into the grid
///
private void PopulateProjectGrid()
{
// put items being built at top of list
foreach (BuildProject project in ProjectMgr)
{
AddRowToProjectGrid(new ProjectLineItem(this, project));
}
// now add everything else that can be built
foreach (ItemInfo item in Xenocide.StaticTables.ItemList)
{
if ((null != item.BuildInfo) && TechMgr.IsAvailable(item.Id) && !ProjectMgr.IsInProgress(item.Id))
{
AddRowToProjectGrid(new IdleLineItem(this, item));
}
}
// give message if there's nothing that can be built
if (0 == projectGrid.RowCount)
{
Util.ShowMessageBox(Strings.MSGBOX_NO_BUILDABLE_TECHNOLOGIES);
}
}
///
/// Add a row to the grid
///
/// data to put on line
private void AddRowToProjectGrid(LineItem lineItem)
{
CeGui.ListboxTextItem listboxItem = Util.CreateListboxItem(lineItem.Name);
int rowNum = projectGrid.AddRow(listboxItem, 0);
listboxItem.ID = rowNum;
Util.AddStringElementToGrid(projectGrid, 1, rowNum, lineItem.DisplayNumWorkers);
Util.AddStringElementToGrid(projectGrid, 2, rowNum, lineItem.DisplayQuantity);
Util.AddStringElementToGrid(projectGrid, 3, rowNum, lineItem.Eta);
// and record details of this item
lineItems[rowNum] = lineItem;
}
///
/// Update the Requirements grid to show what is needed to build the specified item
///
/// Requirements to build specified item
private void ShowRequirements(BuildInfo buildInfo)
{
requirementsGrid.ResetList();
// workspace
if (0 < buildInfo.Space)
{
string needed = Util.ToString(buildInfo.Space);
string available = Util.ToString((int)buildInfo.GetCapacityInfo(SelectedOutpost).Available);
AddRowToRequirementsGrid(Strings.SCREEN_MANUFACTURE_REPORT_ROW_WORKSPACE, needed, available);
}
// hours
if (0 < buildInfo.Hours)
{
string needed = Util.ToString(buildInfo.Hours);
string available = String.Empty;
AddRowToRequirementsGrid(Strings.SCREEN_MANUFACTURE_REPORT_ROW_HOURS, needed, available);
}
// dollars
if (0 < buildInfo.Dollars)
{
string needed = Util.FormatCurrency(buildInfo.Dollars);
string available = Xenocide.GameState.GeoData.XCorp.Bank.DisplayCurrentBalance;
AddRowToRequirementsGrid(Strings.SCREEN_MANUFACTURE_REPORT_ROW_MONEY, needed, available);
}
// materials
foreach (ItemLine material in buildInfo.Materials)
{
string needed = Util.ToString(material.Quantity);
string available = Util.ToString(SelectedOutpost.Inventory.NumberInInventory(material.ItemInfo));
AddRowToRequirementsGrid(material.ItemInfo.Name, needed, available);
}
}
///
/// Add a row of data the requirements grid
///
/// name of the required resource
/// quantity needed
/// quantity available
private void AddRowToRequirementsGrid(string resourceName, string needed, string available)
{
int rowNum = requirementsGrid.AddRow(Util.CreateListboxItem(resourceName), 0);
Util.AddStringElementToGrid(requirementsGrid, 1, rowNum, needed);
Util.AddStringElementToGrid(requirementsGrid, 2, rowNum, available);
}
#endregion Create the CeGui widgets
#region event handlers
/// React to user pressing the "Build More" button
/// Not used
/// Not used
private void OnBuildMoreButton(object sender, CeGui.GuiEventArgs e)
{
ChangeBuildNumber(1);
}
/// React to user pressing the "Build Less" button
/// Not used
/// Not used
private void OnBuildLessButton(object sender, CeGui.GuiEventArgs e)
{
ChangeBuildNumber(-1);
}
/// React to user pressing the "Cancel Build" button
/// Not used
/// Not used
private void OnCancelBuildButton(object sender, CeGui.GuiEventArgs e)
{
CancelProject();
}
/// React to user pressing the More Engineers
/// Not used
/// Not used
private void OnMoreButton(object sender, CeGui.GuiEventArgs e)
{
AddIdleEngineers(1);
}
/// React to user pressing the Add Idle Engineers Button
/// Not used
/// Not used
private void OnAddIdleButton(object sender, CeGui.GuiEventArgs e)
{
AddIdleEngineers(idleEngineers.Count);
}
/// React to user pressing the Remove All Engineers Button
/// Not used
/// Not used
private void OnRemoveAllButton(object sender, CeGui.GuiEventArgs e)
{
RemoveAllEngineers();
}
/// React to user pressing the Less Engineers button
/// Not used
/// Not used
private void OnLessButton(object sender, CeGui.GuiEventArgs e)
{
RemoveScientist();
}
/// React to user pressing the Close button
/// Not used
/// Not used
private void OnCloseButton(object sender, CeGui.GuiEventArgs e)
{
ShowBasesScreen();
}
/// Handle user clicking on an item in the projects grid
/// Not used
/// Not used
private void OnProjectGridSelectionChanged(object sender, WindowEventArgs e)
{
CeGui.Widgets.ListboxItem item = projectGrid.GetFirstSelectedItem();
if (item != null)
{
ShowRequirements(lineItems[item.ID].BuildInfo);
}
}
#endregion event handlers
///
/// Add Engineers to the currently selected project
///
/// number of Engineers to add
private void AddIdleEngineers(int count)
{
CeGui.Widgets.ListboxItem selectedItem = GetSelectedItem();
if (null != selectedItem)
{
// can only add scientist if we have one that's idle
if (0 < idleEngineers.Count)
{
Debug.Assert((0 < count) && (count <= idleEngineers.Count));
ProjectLineItem project = lineItems[selectedItem.ID].GetProject();
if (null != project)
{
// update lineItems in case we've promoted from TopicLine to ProjectLine
lineItems[selectedItem.ID] = project;
// add specified number of idle Engineers to the project
// note, if we've just spawned the project, available workspace has been reduced
count = Math.Min(count, idleEngineers.Count);
for (int i = 0; i < count; ++i)
{
project.AddWorker(idleEngineers);
}
UpdateDetails(selectedItem, project);
}
}
else
{
Util.ShowMessageBox(Strings.MSGBOX_NO_IDLE_ENGINEERS);
}
}
}
///
/// Remove a scientist from the currently selected project
///
private void RemoveScientist()
{
CeGui.Widgets.ListboxItem selectedItem = GetSelectedItem();
if (null != selectedItem)
{
LineItem lineItem = lineItems[selectedItem.ID];
lineItem.RemoveWorker(idleEngineers);
UpdateDetails(selectedItem, lineItem);
}
}
///
/// Remove all Engineers from the currently selected project
///
private void RemoveAllEngineers()
{
CeGui.Widgets.ListboxItem selectedItem = GetSelectedItem();
if (null != selectedItem)
{
LineItem lineItem = lineItems[selectedItem.ID];
while (0 < lineItem.NumWorkers)
{
lineItem.RemoveWorker(idleEngineers);
}
UpdateDetails(selectedItem, lineItem);
}
}
///
/// Change the number of items to build
///
/// how much to change the number by
private void ChangeBuildNumber(int change)
{
CeGui.Widgets.ListboxItem selectedItem = GetSelectedItem();
if (null != selectedItem)
{
ProjectLineItem project = lineItems[selectedItem.ID] as ProjectLineItem;
if (null != project)
{
project.BuildCount += change;
UpdateDetails(selectedItem, project);
}
else
{
// user clicked on an empty project, so either start it or ignore
if (0 < change)
{
AddIdleEngineers(1);
}
}
}
}
///
/// Cancel the selected project
///
private void CancelProject()
{
CeGui.Widgets.ListboxItem selectedItem = GetSelectedItem();
if (null != selectedItem)
{
ProjectLineItem project = lineItems[selectedItem.ID] as ProjectLineItem;
if (null != project)
{
project.Cancel();
FindIdleEngineers();
// change row back to just an item row
IdleLineItem lineItem = new IdleLineItem(this, project.Item);
lineItems[selectedItem.ID] = lineItem;
UpdateDetails(selectedItem, lineItem);
}
}
}
///
/// Update the screen to reflect the latest changes
///
/// row in grid that is selected
/// LineItem associated with this row
private void UpdateDetails(CeGui.Widgets.ListboxItem selectedItem, LineItem lineItem)
{
availableText.Text = MakeIdleEngineersString();
// update row in grid
int row = projectGrid.GetRowIndexOfItem(selectedItem);
CeGui.Widgets.GridReference position = new CeGui.Widgets.GridReference(row, 1);
projectGrid.GetItemAtGridReference(position).Text = lineItem.DisplayNumWorkers;
position.Column = 2;
projectGrid.GetItemAtGridReference(position).Text = lineItem.DisplayQuantity;
position.Column = 3;
projectGrid.GetItemAtGridReference(position).Text = lineItem.Eta;
// Update the requirements as well (in case we've consumed resources)
ShowRequirements(lineItem.BuildInfo);
}
///
/// Close this screen and go back to the Bases screen
///
private void ShowBasesScreen()
{
ScreenManager.ScheduleScreen(new BasesScreen(selectedOutpostIndex));
}
// Get currently selected item from Grid. Give error message if nothing selected
private CeGui.Widgets.ListboxItem GetSelectedItem()
{
CeGui.Widgets.ListboxItem selectedItem = projectGrid.GetFirstSelectedItem();
if (null == selectedItem)
{
Util.ShowMessageBox(Strings.MSGBOX_NO_PROJECT_SELECTED);
}
return selectedItem;
}
///
/// Make up text to show, giving number of Idle engineers
///
/// text to show
private String MakeIdleEngineersString()
{
return Util.StringFormat(Strings.SCREEN_MANUFACTURE_IDLE_ENGINEERS, idleEngineers.Count);
}
///
/// Find all the engineers (in X-Corp this outpost) that are not doing anything
/// but have available space to work in
///
private void FindIdleEngineers()
{
idleEngineers.Clear();
uint spaceFree = SelectedOutpost.Statistics.Capacities["STORAGE_ENGINEER"].Available;
int count = -1;
foreach (Person p in SelectedOutpost.Inventory.ListStaff("ITEM_PERSON_ENGINEER", false))
{
if (++count < spaceFree)
{
idleEngineers.Add(p);
}
}
}
///
/// Holds the data for a line in the grid
///
private abstract class LineItem
{
///
/// Constructor
///
/// Manufacturing Screen this line has info for
protected LineItem(ManufactureScreen parent)
{
this.parent = parent;
}
///
/// Remove a worker from the line item
///
/// list of idle workers to add now idle woker to
public virtual void RemoveWorker(IList idle) { }
///
/// Get the project line represented by this line in the Grid, creating a project if necessary
///
/// line to put in grid for this topic
public abstract ProjectLineItem GetProject();
#region Fields
///
/// Value to show in "Name" column
///
public abstract string Name { get; }
///
/// Value to show in "Engineers" column
///
public virtual string DisplayNumWorkers { get { return String.Empty; } }
///
/// Number of workers assigned to project
///
public virtual int NumWorkers { get { return 0; } }
///
/// Value to show in "Quantity" column
///
public virtual string DisplayQuantity { get { return String.Empty; } }
///
/// Number of items to build
///
public virtual int Quantity { get { return 0; } }
///
/// Value to show in "Days Left" column
///
public virtual string Eta { get { return String.Empty; } }
///
/// The requirements to manufacture an instance of this item
///
public abstract BuildInfo BuildInfo { get; }
///
/// Manufacturing Screen this line has info for
///
public ManufactureScreen Parent { get { return parent; } }
///
/// Manufacturing Screen this line has info for
///
private ManufactureScreen parent;
#endregion Fields
}
///
/// A row on the grid for an item we're building
///
private class ProjectLineItem : LineItem
{
///
/// Constructor
///
/// Project this line is giving details for
/// Manufacturing Screen this line has info for
public ProjectLineItem(ManufactureScreen parent, BuildProject project)
:
base(parent)
{
this.project = project;
}
///
/// Add a worker to the line item
///
/// list of idle workers to get worker from
public void AddWorker(IList idle)
{
Person worker = idle[idle.Count - 1];
idle.RemoveAt(idle.Count - 1);
project.Add(worker);
}
///
/// Remove a worker from the line item
///
/// list of idle workers to add now idle woker to
public override void RemoveWorker(IList idle)
{
if (0 < project.NumWorkers)
{
idle.Add(project.RemoveWorker());
}
}
///
/// Get the project line represented by this line in the Grid, creating a project if necessary
///
/// line to put in grid for this topic
public override ProjectLineItem GetProject()
{
return this;
}
///
/// Cancel this project, loosing all investment
///
public void Cancel()
{
project.Cancel();
Parent.FindIdleEngineers();
}
#region Fields
///
/// Value to show in "Name" column
///
public override string Name { get { return project.Name; } }
///
/// Value to show in "Engineers" column
///
public override string DisplayNumWorkers { get { return Util.ToString(NumWorkers); } }
///
/// Number of workers assigned to project
///
public override int NumWorkers { get { return project.NumWorkers; } }
///
/// Number of items to build, formatted for display to user
///
public override string DisplayQuantity { get { return Util.ToString(project.BuildCount); } }
///
/// Number of items to build
///
public int BuildCount { get { return project.BuildCount; } set { project.BuildCount = value; } }
///
/// Value to show in "Days Left" column
///
public override string Eta { get { return project.CalcTotalItemsEtaToShow(); } }
///
/// The requirements to manufacture an instance of this item
///
public override BuildInfo BuildInfo { get { return Item.BuildInfo; } }
///
/// Type of item project is building
///
public ItemInfo Item { get { return project.Item; } }
///
/// Project this line gives details for
///
private BuildProject project;
#endregion Fields
}
///
/// A row on the grid for an item we're not building
///
private class IdleLineItem : LineItem
{
///
/// Constructor
///
/// Item this line is giving details for
/// Manufacturing Screen this line has info for
public IdleLineItem(ManufactureScreen parent, ItemInfo item)
:
base(parent)
{
this.item = item;
}
///
/// Get the project line represented by this line in the Grid, creating a project if necessary
///
/// line to put in grid for this topic
public override ProjectLineItem GetProject()
{
ProjectLineItem project = null;
string error = item.CanStartManufacture(TechMgr, Parent.SelectedOutpost, Bank);
if (null != error)
{
Util.ShowMessageBox(Strings.MSGBOX_CANT_BUILD_ITEM, item.Name, error);
}
else
{
project = new ProjectLineItem(
Parent,
Parent.ProjectMgr.CreateProject(item.Id, TechMgr, Parent.SelectedOutpost, Bank)
);
// project takes up space, reduces that available for engineers.
Parent.FindIdleEngineers();
}
return project;
}
#region Fields
///
/// Value to show in "Project" column
///
public override string Name { get { return item.Name; } }
///
/// The requirements to manufacture an instance of this item
///
public override BuildInfo BuildInfo { get { return item.BuildInfo; } }
///
/// Item this line is giving details for
///
private ItemInfo item;
#endregion Fields
}
#region Fields
///
/// Shortcut
///
private BuildProjectManager ProjectMgr
{
get { return SelectedOutpost.BuildProjectManager; }
}
///
/// Shortcut
///
private static TechnologyManager TechMgr
{
get { return Xenocide.GameState.GeoData.XCorp.TechManager; }
}
///
/// Shortcut
///
private static Bank Bank
{
get { return Xenocide.GameState.GeoData.XCorp.Bank; }
}
///
/// Bind lines in grid to object providing data to show.
/// format is Dictionary<line id, LineData>
///
private Dictionary lineItems = new Dictionary();
///
/// Engineers that currently are not working, but could work
///
private List idleEngineers = new List();
///
/// The outpost we're showing the details for
///
private Outpost SelectedOutpost { get { return Xenocide.GameState.GeoData.Outposts[selectedOutpostIndex]; } }
// index specifying the outpost that screen is showwing
private int selectedOutpostIndex;
#endregion Fields
}
}