#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; } } } }