#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 XNetScreen.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.Collections.Specialized;
using System.Collections.ObjectModel;
using System.Text;
using CeGui;
using ProjectXenocide.UI.Scenes.XNet;
using ProjectXenocide.Model.StaticData;
using ProjectXenocide.Utils;
#endregion
namespace ProjectXenocide.UI.Screens
{
///
/// Displays the Encyclopedia of research and info
///
class XNetScreen : PolarScreen
{
///
/// constructor (obviously)
///
public XNetScreen()
: base("XNetScreen", @"Content\Textures\UI\XnetScreenBackground.png")
{
Scene = new XNetScene();
Xenocide.AudioSystem.PlayRandomMusic("XNet");
}
#region Create the CeGui widgets
///
/// add the buttons to the screen
///
protected override void CreateCeguiWidgets()
{
SetView(0.005f, 0.0733f, 0.5038f, 0.4417f);
// Fake the list of items with a list box
InitEntriesTree();
// CeGui# doesn't allow scrolling static text, so fake with list box
textWindow = GuiBuilder.CreateListBox(CeguiId + "_text");
AddWidget(textWindow, 0.002f, 0.52f, 0.541f, 0.475f);
// and provide a close button
closeButton = AddButton("BUTTON_CLOSE", 0.7475f, 0.9400f, 0.2275f, 0.04125f);
closeButton.Clicked += new CeGui.GuiEventHandler(OnCloseButton);
}
///
/// Create the "tree" of available xnet entries
/// Currently we're faking it with a list box
///
private void InitEntriesTree()
{
// create the list box
entriesTree = GuiBuilder.CreateListBox("entriesTree");
AddWidget(entriesTree, 0.606f, 0.073f, 0.366f, 0.8600f);
Dictionary catNames = new Dictionary();
foreach (XNetEntry e in Xenocide.StaticTables.XNetEntryList)
{
if (!catNames.ContainsKey(e.Category) && EntryAvailableToPlayer(e))
{
catNames.Add(e.Category, 0);
categories.Add(new Category(e.Category, categories, entriesTree));
}
}
entriesTree.SelectionChanged += new WindowEventHandler(OnEntrySelected);
}
private CeGui.Widgets.Listbox entriesTree;
private CeGui.Widgets.PushButton closeButton;
private CeGui.Widgets.Listbox textWindow;
#endregion Create the CeGui widgets
#region event handlers
/// User has clicked the close button
/// Not used
/// Not used
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope",
Justification = "FxCop False Positive")]
private void OnCloseButton(object sender, CeGui.GuiEventArgs e)
{
ScreenManager.ScheduleScreen(new GeoscapeScreen());
}
/// User has selected an X-Net entry to display
/// Not used
/// Not used
private void OnEntrySelected(object sender, WindowEventArgs e)
{
CeGui.Widgets.ListboxItem item = entriesTree.GetFirstSelectedItem();
if (item != null)
{
Xenocide.AudioSystem.PlaySound("Menu\\buttonclick2_changesetting.ogg");
if (Category.categoryIndexOffset <= item.ID)
{
// user clicked on a category (so expand or collapse it)
categories[item.ID - Category.categoryIndexOffset].Toggle();
//unselect header, to avoid having to double click to close topic
item.Selected = false;
}
else
{
// user clicked an entry, so show its text and model
XNetEntry entry = Xenocide.StaticTables.XNetEntryList[item.ID];
PopulateEntryText(entry);
// and set the 3D model to show
xnetScene.SetModel(entry.Graphic.Model, entry.Graphic.InitialRotation);
}
}
}
#endregion event handlers
///
/// Returns true if player is allowed to view this entry
///
/// entry to examine
/// true if player is allowed to view entry
private static bool EntryAvailableToPlayer(XNetEntry e)
{
return Xenocide.StaticTables.StartSettings.Cheats.ShowAllXNetEntries ||
Xenocide.GameState.GeoData.XCorp.TechManager.IsAvailable(e.Id);
}
#region Functions to put the entry's text into the "text box"
///
/// Fill the X-Net entry "text box" with the text from the specified entry
///
/// entry to get text from
private void PopulateEntryText(XNetEntry entry)
{
// clear the current text
textWindow.ResetList();
AddToEntryText(entry.ShortEntry);
if (0 < entry.Stats.Count)
{
AddToEntryText(String.Empty);
AddToEntryText(entry.Stats);
}
AddToEntryText(String.Empty);
AddToEntryText(entry.Body);
AddToEntryText(String.Empty);
AddToEntryText(entry.Fluff);
}
///
/// Append these strings to the end of the entry's text
///
/// strings to append
private void AddToEntryText(StringCollection strings)
{
foreach (string s in strings)
{
AddToEntryText(s);
}
}
///
/// Add this text as a new line to the entry's "text box"
///
/// text to add
private void AddToEntryText(string text)
{
string line = "";
string textLeft = FixupSpecialChars(text);
do
{
line = ExtractLine(ref textLeft);
CeGui.ListboxTextItem item = new CeGui.ListboxTextItem(line);
item.SetSelectionBrushImage("TaharezLook", "MultiListSelectionBrush");
textWindow.AddItem(item);
}
while (0 < textLeft.Length);
}
///
/// Extract enough text from string to fill one line of Text box
///
/// String to get text from. (Text is removed from value)
/// Enough text to fill a line
private static string ExtractLine(ref string value)
{
const int MaxChars = 52;
int length = value.Length;
string temp = "";
if (MaxChars < length)
{
// find a space (to avoid splitting a word between lines)
length = MaxChars;
while ((0 < length) && (' ' != value[length - 1]))
{
--length;
}
if (length == 0)
{
length = MaxChars + 1;
}
temp = value.Substring(0, length - 1);
value = value.Substring(length, value.Length - length);
}
else
{
temp = value;
value = "";
}
return temp;
}
///
/// XNA fonts currently only work with ASCII chars, but the xnet.xml is generated from
/// RTF, so has special open and close quotes and apostrophies.
/// This function replaces them with their ASCII equivelents
///
/// String that may contain special characters
/// String with special characters replaced
private static string FixupSpecialChars(string text)
{
text = text.Replace((char)8220, '\"');
text = text.Replace((char)8221, '\"');
text = text.Replace((char)8217, '\'');
return text;
}
#endregion Functions to put the entry's text into the "text box"
///
/// Move camera due to mouse move
///
/// details of the mouse move
protected override void RotateSceneUsingMouse(CeGui.MouseEventArgs e)
{
// Calculate pointer position (in pixels)
// relative to upper left corner of SceneWindow
float x = e.Position.X - SceneWindow.AbsoluteX;
float y = e.Position.Y - SceneWindow.AbsoluteY;
float halfWidth = SceneWindow.AbsoluteWidth / 2.0f;
float halfHeight = SceneWindow.AbsoluteHeight / 2.0f;
// Calculate how percentually close the pointer is to the edges of SceneWindow,
// 0.0f if pointer is in center, 1.0f if pointer is on the edge
float factorFromCenterX = Math.Abs((x - halfWidth) / halfWidth);
float factorFromCenterY = Math.Abs((y - halfHeight) / halfHeight);
// Calculate roll amount caused by horizontal movement.
// The closer the pointer is to an edge the more roll.
float rollCausedByHorizontalMove = -e.MoveDelta.X * factorFromCenterY;
// Calculate roll amount caused by vertical movement.
float rollCausedByVerticalMove = e.MoveDelta.Y * factorFromCenterX;
// Correct roll direction
if (y > halfHeight)
rollCausedByHorizontalMove = -rollCausedByHorizontalMove;
if (x > halfWidth)
rollCausedByVerticalMove = -rollCausedByVerticalMove;
// Apply rotations
float rotationSpeed = -0.0045f * (Scene.CameraHeight + 1.0f);
Scene.RotateCamera(rotationSpeed * e.MoveDelta.X * (1 - factorFromCenterY),
rotationSpeed * e.MoveDelta.Y * (1 - factorFromCenterX));
float totalRoll = rollCausedByHorizontalMove + rollCausedByVerticalMove;
xnetScene.RollCamera(rotationSpeed * totalRoll);
}
///
/// Manage the categories in the tree
///
private class Category
{
///
/// Constructor (duh)
///
/// Name of the category
/// List of all the categories so far
/// List box (tree) to put the category into
public Category(string name, List categories, CeGui.Widgets.Listbox entriesTree)
{
this.name = name;
this.categories = categories;
this.entriesTree = entriesTree;
index = categories.Count;
categoryItem = CreateCategoryMenuItem(name, index + categoryIndexOffset);
entriesTree.AddItem(categoryItem);
}
///
/// Toggle this category between the expanded and colapsed states in the tree (list)
///
public void Toggle()
{
if (isExpanded)
{
Collapse();
}
else
{
Expand();
}
}
///
/// Populate tree (list) with the X-Net entries in this category
///
private void Expand()
{
if (!isExpanded)
{
isExpanded = true;
int i = 0;
foreach(XNetEntry e in Xenocide.StaticTables.XNetEntryList)
{
if (e.Category == name && XNetScreen.EntryAvailableToPlayer(e))
{
ShowEntry(e, i);
}
++i;
}
}
}
///
/// Put the specified entry on the tree (list) under this context
///
/// entry to show
/// id to assign entry (it's the index to entries list)
private void ShowEntry(XNetEntry entry, int id)
{
CeGui.ListboxTextItem item = CreateEntryMenuItem(entry.Name, id);
// if this is last category, must add the entries to bottom of list
if (index == (categories.Count - 1))
{
entriesTree.AddItem(item);
}
else
{
entriesTree.InsertItem(item, categories[index + 1].categoryItem);
}
childItems.Add(item);
}
///
/// Remove from the tree (list) the X-Net entries in this category
///
private void Collapse()
{
isExpanded = false;
foreach (CeGui.ListboxTextItem item in childItems)
{
entriesTree.RemoveItem(item);
}
childItems.Clear();
}
///
/// Create an item that will be a type (topic) heading in the menu
///
/// Text to show in menu
/// index to categories
///
private static CeGui.ListboxTextItem CreateCategoryMenuItem(string categoryName, int id)
{
return CreateListboxItem("", categoryName, id);
}
///
/// Create an item that will be a XNet entry in the menu
///
/// Text to show in the menu
/// index to X-Net Entries
///
private static CeGui.ListboxTextItem CreateEntryMenuItem(string entryName, int id)
{
return CreateListboxItem(" ", entryName, id);
}
///
/// Create an item to go into the menu of available X-Net entries
///
/// indetaion of item, to mimic a tree
/// Text to show in menu
/// index to XNetEntries
private static CeGui.ListboxTextItem CreateListboxItem(string padding, string itemText, int id)
{
CeGui.ListboxTextItem item = Util.CreateListboxItem(padding + itemText);
item.ID = id;
return item;
}
///
/// Put IDs for categories in separate range to entries
///
public const int categoryIndexOffset = 100000;
///
/// Index for this category in categories
///
private int index;
///
/// Name this category appears as in the tree
///
private string name;
///
/// The item in the list box representing this category
///
private CeGui.ListboxTextItem categoryItem;
///
/// List of all the categories
///
private List categories;
///
/// The list box (pretending to be a tree) that shows the available X-Net entries
///
private CeGui.Widgets.Listbox entriesTree;
///
/// Has this category been expanded to show all it's child entries?
///
private bool isExpanded;
private List childItems = new List();
}
private List categories = new List();
///
/// Return the scene field as its real type (a XNetScene)
///
private XNetScene xnetScene { get { return (XNetScene)Scene; } }
}
}