diff options
Diffstat (limited to 'ShiftOS.Frontend')
37 files changed, 7332 insertions, 0 deletions
diff --git a/ShiftOS.Frontend/Apps/CodeShop.cs b/ShiftOS.Frontend/Apps/CodeShop.cs new file mode 100644 index 0000000..ad44fda --- /dev/null +++ b/ShiftOS.Frontend/Apps/CodeShop.cs @@ -0,0 +1,156 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ShiftOS.Engine; +using ShiftOS.Frontend.GraphicsSubsystem; + +namespace ShiftOS.Frontend.Apps +{ + [WinOpen("shiftorium")] + public class CodeShop : GUI.Control, IShiftOSWindow + { + private GUI.ListBox upgradelist = null; + private ShiftoriumUpgrade selectedUpgrade = null; + private GUI.ProgressBar upgradeprogress = null; + private GUI.Button buy = null; + + public CodeShop() + { + Width = 720; + Height = 480; + } + + protected override void OnLayout() + { + try + { + upgradelist.X = 30; + upgradelist.Y = 75; + upgradelist.Width = this.Width / 2; + upgradelist.Width -= 30; + upgradelist.Height = this.Height - upgradelist.Y - 75; + upgradeprogress.X = upgradelist.X; + upgradeprogress.Y = upgradelist.Y + upgradelist.Height + 10; + upgradeprogress.Width = upgradelist.Width; + upgradeprogress.Height = 24; + upgradeprogress.Maximum = Shiftorium.GetDefaults().Count; + upgradeprogress.Value = SaveSystem.CurrentSave.CountUpgrades(); + buy.X = Width - buy.Width - 15; + buy.Y = Height - buy.Height - 15; + buy.Visible = (selectedUpgrade != null); + + } + catch + { + + } + } + + public void OnLoad() + { + buy = new GUI.Button(); + buy.Text = "Buy upgrade"; + buy.AutoSize = true; + buy.Font = SkinEngine.LoadedSkin.MainFont; + buy.Click += () => + { + if(Shiftorium.Buy(selectedUpgrade.ID, selectedUpgrade.Cost) == true) + { + Engine.Infobox.Show("Upgrade installed!", "You have successfully bought and installed the " + selectedUpgrade.Name + " upgrade for " + selectedUpgrade.Cost + " Codepoints."); + SelectUpgrade(null); + PopulateList(); + } + else + { + Engine.Infobox.Show("Insufficient funds.", "You do not have enough Codepoints to buy this upgrade. You need " + (selectedUpgrade.Cost - SaveSystem.CurrentSave.Codepoints) + " more."); + } + }; + AddControl(buy); + upgradelist = new GUI.ListBox(); + upgradeprogress = new GUI.ProgressBar(); + AddControl(upgradeprogress); + AddControl(upgradelist); + upgradelist.SelectedIndexChanged += () => + { + string itemtext = upgradelist.SelectedItem.ToString(); + var upg = Shiftorium.GetAvailable().FirstOrDefault(x => $"{x.Category}: {x.Name} - {x.Cost}CP" == itemtext); + if(upg != null) + { + SelectUpgrade(upg); + } + }; + PopulateList(); + } + + public void SelectUpgrade(ShiftoriumUpgrade upgrade) + { + if(selectedUpgrade != upgrade) + { + selectedUpgrade = upgrade; + Invalidate(); + } + } + + public void PopulateList() + { + upgradelist.ClearItems(); + foreach(var upgrade in Shiftorium.GetAvailable()) + { + upgradelist.AddItem($"{upgrade.Category}: {upgrade.Name} - {upgrade.Cost}CP"); + Invalidate(); + } + } + + public void OnSkinLoad() + { + } + + public bool OnUnload() + { + return true; + } + + public void OnUpgrade() + { + PopulateList(); + } + + protected override void OnPaint(GraphicsContext gfx) + { + base.OnPaint(gfx); + + string title = "Welcome to the Shiftorium!"; + string desc = @"The Shiftorium is a place where you can buy upgrades for your computer. These upgrades include hardware enhancements, kernel and software optimizations and features, new programs, upgrades to existing programs, and more. + +As you continue through your job, going further up the ranks, you will unlock additional upgrades which can be found here. You may also find upgrades which are not available within the Shiftorium when hacking more difficult and experienced targets. These upgrades are very rare and hard to find, though. You'll find them in the ""Installed Upgrades"" list."; + + if(selectedUpgrade != null) + { + title = selectedUpgrade.Category + ": " + selectedUpgrade.Name; + + desc = selectedUpgrade.Description; + } + + int wrapwidth = (Width - (upgradelist.X + upgradelist.Width)) - 45; + var titlemeasure = gfx.MeasureString(title, SkinEngine.LoadedSkin.Header2Font, wrapwidth); + + var descmeasure = gfx.MeasureString(desc, SkinEngine.LoadedSkin.MainFont, wrapwidth); + + int availablewidth = Width - (upgradelist.X + upgradelist.Width); + int titlelocx = (availablewidth - (int)titlemeasure.X) / 2; + titlelocx += (Width - availablewidth); + int titlelocy = 30; + gfx.DrawString(title, titlelocx, titlelocy, SkinEngine.LoadedSkin.ControlTextColor.ToMonoColor(), SkinEngine.LoadedSkin.Header2Font); + + int desclocy = (Height - (int)descmeasure.Y) / 2; + int desclocx = (Width - availablewidth) + ((availablewidth - (int)descmeasure.X) / 2); + gfx.DrawString(desc, desclocx, desclocy, SkinEngine.LoadedSkin.ControlTextColor.ToMonoColor(), SkinEngine.LoadedSkin.MainFont, wrapwidth); + + string shiftorium = "Shiftorium"; + var smeasure = gfx.MeasureString(shiftorium, SkinEngine.LoadedSkin.HeaderFont); + gfx.DrawString(shiftorium, upgradelist.X + ((upgradelist.Width - (int)smeasure.X) / 2), 20, SkinEngine.LoadedSkin.ControlTextColor.ToMonoColor(), SkinEngine.LoadedSkin.HeaderFont); + } + } +} diff --git a/ShiftOS.Frontend/Apps/Pong.cs b/ShiftOS.Frontend/Apps/Pong.cs new file mode 100644 index 0000000..aed7cf1 --- /dev/null +++ b/ShiftOS.Frontend/Apps/Pong.cs @@ -0,0 +1,171 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Xna.Framework; +using ShiftOS.Engine; +using ShiftOS.Frontend.GraphicsSubsystem; + +namespace ShiftOS.Frontend.Apps +{ + [Launcher("{TITLE_PONG}", true, "al_pong", "{AL_GAMES}")] + [WinOpen("{WO_PONG}")] + [DefaultTitle("{TITLE_PONG}")] + [DefaultIcon("iconPong")] + public class Pong : GUI.Control, IShiftOSWindow + { + public Pong() + { + Width = 720; + Height = 480; + MouseMove += (loc) => + { + double _y = linear(loc.Y, 0, Height, -1, 1); + if(_y != playerY) + { + playerY = _y; + Invalidate(); + } + }; + } + + #region Private variables + private double ballX = 0.0f; + private double ballY = 0.0f; + private double aiBallX = 0.0f; + private double aiBallY = 0.0f; + private double speedFactor = 0.0125; + private double xVel = 1; + private double yVel = 1; + private double aiXVel = 1; + private double aiYVel = 1; + private int paddleWidth; + private long codepointsToEarn = 0; + private int level = 1; + private double playerY = 0.0; + private double opponentY = 0.0; + private int secondsleft = 60; + bool doAi = true; + bool doBallCalc = true; + private string header = ""; + private string counter = ""; + #endregion + + #region Control behaviour overrides + + protected override void OnPaint(GraphicsContext gfx) + { + //This is where we'll dump the winforms painting code + //By now, Layout() would have calculated the game's state + + paddleWidth = Width / 30; + double ballXLocal = linear(ballX, -1.0, 1.0, 0, Width); + double ballYLocal = linear(ballY, -1.0, 1.0, 0, Height); + + ballXLocal -= ((double)paddleWidth / 2); + ballYLocal -= ((double)paddleWidth / 2); + + double aiballXLocal = linear(aiBallX, -1.0, 1.0, 0, Width); + double aiballYLocal = linear(aiBallY, -1.0, 1.0, 0, Height); + + aiballXLocal -= ((double)paddleWidth / 2); + aiballYLocal -= ((double)paddleWidth / 2); + + + base.OnPaint(gfx); + + //draw the ball + if (doBallCalc) + { + gfx.DrawRectangle((int)ballXLocal, (int)ballYLocal, paddleWidth, paddleWidth, UIManager.SkinTextures["ControlTextColor"]); + } + double playerYLocal = linear(playerY, -1.0, 1.0, 0, Height); + double opponentYLocal = linear(opponentY, -1.0, 1.0, 0, Height); + + int paddleHeight = Height / 5; + + int paddleStart = paddleWidth; + + //draw player paddle + gfx.DrawRectangle(paddleWidth, (int)playerYLocal - (paddleHeight / 2), paddleWidth, paddleHeight, UIManager.SkinTextures["ControlTextColor"]); + + + //draw opponent + gfx.DrawRectangle(Width - (paddleWidth*2), (int)opponentYLocal - (paddleHeight / 2), paddleWidth, paddleHeight, UIManager.SkinTextures["ControlTextColor"]); + + string cp_text = Localization.Parse("{PONG_STATUSCP}", new Dictionary<string, string> + { + ["%cp"] = codepointsToEarn.ToString() + }); + + var tSize = gfx.MeasureString(cp_text, SkinEngine.LoadedSkin.Header3Font); + + var tLoc = new Vector2((Width - (int)tSize.X) / 2, + (Height - (int)tSize.Y) + + ); + + gfx.DrawString(cp_text, (int)tLoc.X, (int)tLoc.Y, SkinEngine.LoadedSkin.ControlTextColor.ToMonoColor(), SkinEngine.LoadedSkin.Header3Font); + + tSize = gfx.MeasureString(counter, SkinEngine.LoadedSkin.Header2Font); + + tLoc = new Vector2((Width - (int)tSize.X) / 2, + (Height - (int)tSize.Y) / 2 + + ); + gfx.DrawString(counter, (int)tLoc.X, (int)tLoc.Y, SkinEngine.LoadedSkin.ControlTextColor.ToMonoColor(), SkinEngine.LoadedSkin.Header2Font); + tSize = gfx.MeasureString(header, SkinEngine.LoadedSkin.Header2Font); + + tLoc = new Vector2((Width - (int)tSize.X) / 2, + (Height - (int)tSize.Y) / 4 + + ); + gfx.DrawString(header, (int)tLoc.X, (int)tLoc.Y, SkinEngine.LoadedSkin.ControlTextColor.ToMonoColor(), SkinEngine.LoadedSkin.Header2Font); + + string l = Localization.Parse("{PONG_STATUSLEVEL}", new Dictionary<string, string> + { + ["%level"] = level.ToString(), + ["%time"] = secondsleft.ToString() + }); + tSize = gfx.MeasureString(l, SkinEngine.LoadedSkin.Header3Font); + + tLoc = new Vector2((Width - (int)tSize.X) / 2, + (tSize.Y) + ); + gfx.DrawString(l, (int)tLoc.X, (int)tLoc.Y, SkinEngine.LoadedSkin.ControlTextColor.ToMonoColor(), SkinEngine.LoadedSkin.Header3Font); + + + } + + #endregion + + + static public double linear(double x, double x0, double x1, double y0, double y1) + { + if ((x1 - x0) == 0) + { + return (y0 + y1) / 2; + } + return y0 + (x - x0) * (y1 - y0) / (x1 - x0); + } + + public void OnLoad() + { + doBallCalc = true; + } + + public void OnSkinLoad() + { + } + + public bool OnUnload() + { + return true; + } + + public void OnUpgrade() + { + } + } +} diff --git a/ShiftOS.Frontend/Apps/Terminal.cs b/ShiftOS.Frontend/Apps/Terminal.cs new file mode 100644 index 0000000..67c7f7f --- /dev/null +++ b/ShiftOS.Frontend/Apps/Terminal.cs @@ -0,0 +1,403 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Microsoft.Xna.Framework.Input; +using ShiftOS.Engine; +using ShiftOS.Frontend.GraphicsSubsystem; +using static ShiftOS.Engine.SkinEngine; + +namespace ShiftOS.Frontend.Apps +{ + [FileHandler("Shell script", ".trm", "fileicontrm")] + [Launcher("{TITLE_TERMINAL}", false, null, "{AL_UTILITIES}")] + [WinOpen("{WO_TERMINAL}")] + [DefaultTitle("{TITLE_TERMINAL}")] + [DefaultIcon("iconTerminal")] + public class Terminal : GUI.Control, IShiftOSWindow + { + private TerminalControl _terminal = null; + + public Terminal() + { + Width = 493; + Height = 295; + } + + public void OnLoad() + { + _terminal = new Apps.TerminalControl(); + _terminal.Dock = GUI.DockStyle.Fill; + AddControl(_terminal); + _terminal.Layout(); + AppearanceManager.ConsoleOut = _terminal; + AppearanceManager.StartConsoleOut(); + TerminalBackend.PrintPrompt(); + SaveSystem.GameReady += () => + { + if (Shiftorium.UpgradeInstalled("desktop")) + { + AppearanceManager.Close(this); + } + else + TerminalBackend.PrintPrompt(); + }; + } + + protected override void OnLayout() + { + if (ContainsFocusedControl || IsFocusedControl) + AppearanceManager.ConsoleOut = _terminal; + } + + public void OnSkinLoad() + { + + } + + public bool OnUnload() + { + return true; + } + + public void OnUpgrade() + { + } + } + + public class TerminalControl : GUI.TextInput, ITerminalWidget + { + public TerminalControl() + { + Dock = GUI.DockStyle.Fill; + + } + + public string[] Lines + { + get + { + return Text.Split(new[] { "\r\n" }, StringSplitOptions.None); + + } + } + + public void Clear() + { + Text = ""; + Index = 0; + _vertOffset = 0; + Invalidate(); + } + + public void SelectBottom() + { + Index = Text.Length - 1; + RecalculateLayout(); + InvalidateTopLevel(); + } + + + + public void Write(string text) + { + Engine.Desktop.InvokeOnWorkerThread(() => + { + Text += Localization.Parse(text); + SelectBottom(); + Index += text.Length; + RecalculateLayout(); + InvalidateTopLevel(); + }); + } + + public void WriteLine(string text) + { + Write(text + Environment.NewLine); + } + + + public int GetCurrentLine() + { + int line = 0; + for(int i = 0; i < Text.Length; i++) + { + if(Text[i]=='\n') + { + line++; + continue; + } + if (i == Index) + return line; + } + return 0; + } + + float _vertOffset = 0.0f; + + protected void RecalculateLayout() + { + if(!string.IsNullOrEmpty(Text)) + using (var gfx = Graphics.FromImage(new Bitmap(1, 1))) + { + var textsize = gfx.SmartMeasureString(Text, LoadedSkin.TerminalFont, Width); + float initial = textsize.Height - _vertOffset; + if(initial > Height) + { + float difference = Height - initial; + _vertOffset = initial - difference; + } + else if(initial < 0) + { + float difference = Height - initial; + _vertOffset = initial + difference; + } + } + } + + protected override void OnLayout() + { + + } + + /// <summary> + /// Gets the X and Y coordinates (in pixels) of the caret. + /// </summary> + /// <param name="gfx">A <see cref="System.Drawing.Graphics"/> object used for font measurements</param> + /// <returns>An absolute fucking mess. Seriously, can someone fix this method so it uhh WORKS PROPERLY?</returns> + public Point GetPointAtIndex(Graphics gfx) + { + int vertMeasure = 2; + int horizMeasure = 2; + var textSize = gfx.SmartMeasureString(Text, LoadedSkin.TerminalFont, Width - 4); + int lineindex = 0; + int line = GetCurrentLine(); + for (int l = 0; l < line; l++) + { + lineindex += Lines[l].Length; + var stringMeasure = gfx.SmartMeasureString(Lines[l] == "\r" ? " " : Lines[l], LoadedSkin.TerminalFont, Width - 4); + vertMeasure += (int)stringMeasure.Height; + + } + var lnMeasure = gfx.SmartMeasureString(Text.Substring(lineindex, Index - lineindex), LoadedSkin.TerminalFont); + int w = (int)Math.Floor(lnMeasure.Width); + if (w > Width - 4) + w = w - (Width - 4); + horizMeasure = w; + return new Point(horizMeasure, vertMeasure); + } + + private PointF CaretPosition = new PointF(2, 2); + private Size CaretSize = new Size(2, 15); + + protected override void OnKeyEvent(KeyEvent a) + { + if (a.Key == Keys.Enter) + { + try + { + if (!TerminalBackend.PrefixEnabled) + { + string textraw = Lines[Lines.Length - 1]; + TerminalBackend.SendText(textraw); + return; + } + var text = Lines; + var text2 = text[text.Length - 1]; + var text3 = ""; + var text4 = Regex.Replace(text2, @"\t|\n|\r", ""); + WriteLine(""); + { + if (TerminalBackend.PrefixEnabled) + { + text3 = text4.Remove(0, $"{SaveSystem.CurrentUser.Username}@{SaveSystem.CurrentSave.SystemName}:~$ ".Length); + } + TerminalBackend.LastCommand = text3; + TerminalBackend.SendText(text4); + if (TerminalBackend.InStory == false) + { + { + var result = SkinEngine.LoadedSkin.CurrentParser.ParseCommand(text3); + + if (result.Equals(default(KeyValuePair<string, Dictionary<string, string>>))) + { + Console.WriteLine("{ERR_SYNTAXERROR}"); + } + else + { + TerminalBackend.InvokeCommand(result.Key, result.Value); + } + + } + } + if (TerminalBackend.PrefixEnabled) + { + TerminalBackend.PrintPrompt(); + } + } + } + catch + { + } + } + else if (a.Key == Keys.Back) + { + try + { + var tostring3 = Lines[Lines.Length - 1]; + var tostringlen = tostring3.Length + 1; + var workaround = $"{SaveSystem.CurrentUser.Username}@{SaveSystem.CurrentSave.SystemName}:~$ "; + var derp = workaround.Length + 1; + if (tostringlen != derp) + { + AppearanceManager.CurrentPosition--; + base.OnKeyEvent(a); + RecalculateLayout(); + InvalidateTopLevel(); + } + } + catch + { + Debug.WriteLine("Drunky alert in terminal."); + } + } + else if (a.Key == Keys.Left) + { + if (SaveSystem.CurrentSave != null) + { + var getstring = Lines[Lines.Length - 1]; + var stringlen = getstring.Length + 1; + var header = $"{SaveSystem.CurrentUser.Username}@{SaveSystem.CurrentSave.SystemName}:~$ "; + var headerlen = header.Length + 1; + var selstart = Index; + var remstrlen = Text.Length - stringlen; + var finalnum = selstart - remstrlen; + + if (finalnum != headerlen) + { + AppearanceManager.CurrentPosition--; + base.OnKeyEvent(a); + } + } + } + else if (a.Key == Keys.Up) + { + var tostring3 = Lines[Lines.Length - 1]; + if (tostring3 == $"{SaveSystem.CurrentUser.Username}@{SaveSystem.CurrentSave.SystemName}:~$ ") + Console.Write(TerminalBackend.LastCommand); + ConsoleEx.OnFlush?.Invoke(); + return; + + } + else + { + if (TerminalBackend.InStory) + { + return; + } + if (a.KeyChar != '\0') + { + Text = Text.Insert(Index, a.KeyChar.ToString()); + Index++; + AppearanceManager.CurrentPosition++; +// RecalculateLayout(); + InvalidateTopLevel(); + } + } + } + + protected override void OnPaint(GraphicsContext gfx) + { + gfx.Clear(LoadedSkin.TerminalBackColorCC.ToColor().ToMonoColor()); + if (!string.IsNullOrEmpty(Text)) + { + //Draw the caret. + if (IsFocusedControl) + { + PointF cursorPos; + using (var cgfx = System.Drawing.Graphics.FromImage(new System.Drawing.Bitmap(1, 1))) + { + cursorPos = GetPointAtIndex(cgfx); + + } + var cursorSize = gfx.MeasureString(Text[Index-1].ToString(), LoadedSkin.TerminalFont); + gfx.DrawRectangle((int)cursorPos.X, (int)cursorPos.Y - (int)_vertOffset, (int)cursorSize.X, (int)cursorSize.Y, LoadedSkin.TerminalForeColorCC.ToColor().ToMonoColor()); + }//Draw the text + + + gfx.DrawString(Text, 2, 2 - (int)Math.Floor(_vertOffset), LoadedSkin.TerminalForeColorCC.ToColor().ToMonoColor(), LoadedSkin.TerminalFont, Width - 4); + } + } + + } + + public static class ConsoleColorExtensions + { + public static Color ToColor(this ConsoleColor cc) + { + switch (cc) + { + case ConsoleColor.Black: + return Color.Black; + case ConsoleColor.Blue: + return Color.Blue; + case ConsoleColor.Cyan: + return Color.Cyan; + case ConsoleColor.DarkBlue: + return Color.DarkBlue; + case ConsoleColor.DarkCyan: + return Color.DarkCyan; + case ConsoleColor.DarkGray: + return Color.DarkGray; + case ConsoleColor.DarkGreen: + return Color.DarkGreen; + case ConsoleColor.DarkMagenta: + return Color.DarkMagenta; + case ConsoleColor.DarkRed: + return Color.DarkRed; + case ConsoleColor.DarkYellow: + return Color.Orange; + case ConsoleColor.Gray: + return Color.Gray; + case ConsoleColor.Green: + return Color.Green; + case ConsoleColor.Magenta: + return Color.Magenta; + case ConsoleColor.Red: + return Color.Red; + case ConsoleColor.White: + return Color.White; + case ConsoleColor.Yellow: + return Color.Yellow; + } + return Color.Empty; + } + } + + public static class GraphicsExtensions + { + public static SizeF SmartMeasureString(this Graphics gfx, string s, Font font, int width) + { + if (string.IsNullOrEmpty(s)) + s = " "; + var textformat = new StringFormat(StringFormat.GenericTypographic); + textformat.FormatFlags = StringFormatFlags.MeasureTrailingSpaces; + textformat.Trimming = StringTrimming.None; + textformat.FormatFlags |= StringFormatFlags.NoClip; + + gfx.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias; + var measure = gfx.MeasureString(s, font, width, textformat); + return new SizeF((float)Math.Ceiling(measure.Width), (float)Math.Ceiling(measure.Height)); + } + + public static SizeF SmartMeasureString(this Graphics gfx, string s, Font font) + { + return SmartMeasureString(gfx, s, font, int.MaxValue); + } + + } +} diff --git a/ShiftOS.Frontend/Commands.cs b/ShiftOS.Frontend/Commands.cs new file mode 100644 index 0000000..f3f81aa --- /dev/null +++ b/ShiftOS.Frontend/Commands.cs @@ -0,0 +1,474 @@ +/* + * MIT License + * + * Copyright (c) 2017 Michael VanOverbeek and ShiftOS devs + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define DEVEL + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Forms; +using ShiftOS.Engine.Properties; +using System.IO; +using Newtonsoft.Json; +using System.IO.Compression; + +using ShiftOS.Objects; +using ShiftOS.Engine.Scripting; +using ShiftOS.Objects.ShiftFS; +using ShiftOS.Engine; + +namespace ShiftOS.Frontend +{ + [TutorialLock] + public static class TerminalCommands + { + [Command("clear", description = "{DESC_CLEAR}")] + public static bool Clear() + { + Engine.Desktop.InvokeOnWorkerThread(() => + { + AppearanceManager.ConsoleOut.Clear(); + }); + return true; + } + } + + public static class ShiftOSCommands + { + + [Command("setsfxenabled", description = "{DESC_SETSFXENABLED}")] + [RequiresArgument("value")] + public static bool SetSfxEnabled(Dictionary<string, object> args) + { + try + { + bool value = Convert.ToBoolean(args["value"].ToString()); + SaveSystem.CurrentSave.SoundEnabled = value; + SaveSystem.SaveGame(); + } + catch + { + Console.WriteLine("{ERR_BADBOOL}"); + } + return true; + } + + + + [Command("setmusicenabled", description = "{DESC_SETMUSICENABLED}")] + [RequiresArgument("value")] + public static bool SetMusicEnabled(Dictionary<string, object> args) + { + try + { + bool value = Convert.ToBoolean(args["value"].ToString()); + SaveSystem.CurrentSave.MusicEnabled = value; + SaveSystem.SaveGame(); + } + catch + { + Console.WriteLine("{ERR_BADBOOL}"); + } + return true; + } + + + + [Command("setvolume", description ="{DESC_SETVOLUME}")] + [RequiresArgument("value")] + public static bool SetSfxVolume(Dictionary<string, object> args) + { + int value = int.Parse(args["value"].ToString()); + if(value >= 0 && value <= 100) + { + SaveSystem.CurrentSave.MusicVolume = value; + SaveSystem.SaveGame(); + } + else + { + Console.WriteLine("{ERR_BADPERCENT}"); + } + return true; + } + + [RemoteLock] + [Command("shutdown", description = "{DESC_SHUTDOWN}")] + public static bool Shutdown() + { + SaveSystem.SaveGame(); + AppearanceManager.Exit(); + return true; + } + + [Command("lang", description = "{DESC_LANG}")] + [RequiresArgument("language")] + public static bool SetLanguage(Dictionary<string, object> userArgs) + { + try + { + string lang = ""; + + lang = (string)userArgs["language"]; + + if (Localization.GetAllLanguages().Contains(lang)) + { + SaveSystem.CurrentSave.Language = lang; + SaveSystem.SaveGame(); + Console.WriteLine("{RES_LANGUAGE_CHANGED}"); + return true; + } + + throw new Exception("{ERR_NOLANG}"); + } + catch + { + return false; + } + } + + [Command("commands", "", "{DESC_COMMANDS}")] + public static bool Commands() + { + var sb = new StringBuilder(); + sb.AppendLine("{GEN_COMMANDS}"); + sb.AppendLine("================="); + sb.AppendLine(); + //print all unique namespaces. + foreach (var n in TerminalBackend.Commands.Where(x => !(x is TerminalBackend.WinOpenCommand) && Shiftorium.UpgradeInstalled(x.Dependencies) && x.CommandInfo.hide == false).OrderBy(x => x.CommandInfo.name)) + { + sb.Append(" - " + n.CommandInfo.name); + if (!string.IsNullOrWhiteSpace(n.CommandInfo.description)) + if (Shiftorium.UpgradeInstalled("help_description")) + sb.Append(" - " + n.CommandInfo.description); + sb.AppendLine(); + } + + Console.WriteLine(sb.ToString()); + + return true; + } + + [Command("help", description = "{DESC_HELP}")] + public static bool Help() + { + Commands(); + WindowCommands.Programs(); + return true; + } + + + [MultiplayerOnly] + [Command("save", description = "{DESC_SAVE}")] + public static bool Save() + { + SaveSystem.SaveGame(); + return true; + } + + [MultiplayerOnly] + [Command("status", description = "{DESC_STATUS}")] + public static bool Status() + { + string version = Assembly.GetExecutingAssembly().GetName().Version.ToString(); + + string cp = SaveSystem.CurrentSave.Codepoints.ToString(); + string installed = SaveSystem.CurrentSave.CountUpgrades().ToString(); + string available = Shiftorium.GetAvailable().Length.ToString(); + + Console.WriteLine(Localization.Parse("{COM_STATUS}", new Dictionary<string, string> + { + ["%cp"] = cp, + ["%version"] = version, + ["%installed"] = installed, + ["%available"] = available + })); + Console.WriteLine("{GEN_OBJECTIVES}"); + try + { + if (Story.CurrentObjectives.Count > 0) + { + foreach (var obj in Story.CurrentObjectives) + { + Console.WriteLine(obj.Name); + Console.WriteLine("-------------------------------"); + Console.WriteLine(); + Console.WriteLine(obj.Description); + } + } + else + { + Console.WriteLine("{RES_NOOBJECTIVES}"); + } + } + catch + { + Console.WriteLine("{RES_NOOBJECTIVES}"); + } + return true; + } + } + + public static class ShiftoriumCommands + { + [Command("buy", description = "{DESC_BUY}")] + [RequiresArgument("upgrade")] + public static bool BuyUpgrade(Dictionary<string, object> userArgs) + { + try + { + string upgrade = ""; + + upgrade = (string)userArgs["upgrade"]; + + var upg = Shiftorium.GetAvailable().FirstOrDefault(x => x.ID == upgrade); + if(upg != null) + { + if (!Shiftorium.Buy(upg.ID, upg.Cost) == true) + Console.WriteLine("{ERR_NOTENOUGHCODEPOINTS}"); + } + else + { + Console.WriteLine("{ERR_NOUPGRADE}"); + } + + } + catch + { + Console.WriteLine("{ERR_GENERAL}"); + } + return true; + } + + [RequiresUpgrade("shiftorium_bulk_buy")] + [Command("bulkbuy", description = "{DESC_BULKBUY}")] + [RequiresArgument("upgrades")] + public static bool BuyBulk(Dictionary<string, object> args) + { + if (args.ContainsKey("upgrades")) + { + string[] upgrade_list = (args["upgrades"] as string).Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries); + foreach (var upg in upgrade_list) + { + var dict = new Dictionary<string, object>(); + dict.Add("upgrade", upg); + BuyUpgrade(dict); + } + } + return true; + } + + + [Command("upgradeinfo", description ="{DESC_UPGRADEINFO}")] + [RequiresArgument("upgrade")] + public static bool ViewInfo(Dictionary<string, object> userArgs) + { + try + { + string upgrade = ""; + + upgrade = (string)userArgs["upgrade"]; + + foreach (var upg in Shiftorium.GetDefaults()) + { + if (upg.ID == upgrade) + { + Console.WriteLine(Localization.Parse("{COM_UPGRADEINFO}", new Dictionary<string, string> + { + ["%id"] = upg.ID, + ["%category"] = upg.Category, + ["%name"] = upg.Name, + ["%cost"] = upg.Cost.ToString(), + ["%description"] = upg.Description + })); + + return true; + } + } + + throw new Exception("{ERR_NOUPGRADE}"); + } + catch + { + return false; + } + } + + [Command("upgradecategories", description = "{DESC_UPGRADECATEGORIES}")] + public static bool ListCategories() + { + foreach(var cat in Shiftorium.GetCategories()) + { + Console.WriteLine(Localization.Parse("{SHFM_CATEGORY}", new Dictionary<string, string> + { + ["%name"] = cat, + ["%available"] = Shiftorium.GetAvailable().Where(x=>x.Category==cat).Count().ToString() + })); + } + return true; + } + + [Command("upgrades", description ="{DESC_UPGRADES}")] + public static bool ListAll(Dictionary<string, object> args) + { + try + { + bool showOnlyInCategory = false; + + string cat = "Other"; + + if (args.ContainsKey("cat")) + { + showOnlyInCategory = true; + cat = args["cat"].ToString(); + } + + Dictionary<string, ulong> upgrades = new Dictionary<string, ulong>(); + int maxLength = 5; + + IEnumerable<ShiftoriumUpgrade> upglist = Shiftorium.GetAvailable(); + if (showOnlyInCategory) + { + if (Shiftorium.IsCategoryEmptied(cat)) + { + ConsoleEx.Bold = true; + ConsoleEx.ForegroundColor = ConsoleColor.Red; + Console.WriteLine("{SHFM_QUERYERROR}"); + Console.WriteLine(); + ConsoleEx.Bold = false; + ConsoleEx.ForegroundColor = ConsoleColor.Gray; + Console.WriteLine("{ERR_EMPTYCATEGORY}"); + return true; + } + upglist = Shiftorium.GetAvailable().Where(x => x.Category == cat); + } + + + if(upglist.Count() == 0) + { + ConsoleEx.Bold = true; + ConsoleEx.ForegroundColor = ConsoleColor.Red; + Console.WriteLine("{SHFM_NOUPGRADES}"); + Console.WriteLine(); + ConsoleEx.Bold = false; + ConsoleEx.ForegroundColor = ConsoleColor.Gray; + Console.WriteLine("{ERR_NOMOREUPGRADES}"); + return true; + + } + foreach (var upg in upglist) + { + if (upg.ID.Length > maxLength) + { + maxLength = upg.ID.Length; + } + + upgrades.Add(upg.ID, upg.Cost); + } + + foreach (var upg in upgrades) + { + Console.WriteLine(Localization.Parse("{SHFM_UPGRADE}", new Dictionary<string, string> + { + ["%id"] = upg.Key, + ["%cost"] = upg.Value.ToString() + })); + } + return true; + } + catch (Exception e) + { + CrashHandler.Start(e); + return false; + } + } + } + + public static class WindowCommands + { + [RemoteLock] + [Command("processes", description = "{DESC_PROCESSES}")] + public static bool List() + { + Console.WriteLine("{GEN_CURRENTPROCESSES}"); + foreach (var app in AppearanceManager.OpenForms) + { + //Windows are displayed the order in which they were opened. + Console.WriteLine($"{AppearanceManager.OpenForms.IndexOf(app)}\t{app.Text}"); + } + return true; + } + + [Command("programs", description = "{DESC_PROGRAMS}")] + public static bool Programs() + { + Console.WriteLine("{GEN_PROGRAMS}"); + Console.WriteLine("==============="); + Console.WriteLine(); + foreach(var cmd in TerminalBackend.Commands.Where(x=>x is TerminalBackend.WinOpenCommand && Shiftorium.UpgradeInstalled(x.Dependencies)).OrderBy(x => x.CommandInfo.name)) + { + Console.Write(" - " + cmd.CommandInfo.name); + if (!string.IsNullOrWhiteSpace(cmd.CommandInfo.description)) + if (Shiftorium.UpgradeInstalled("help_description")) + Console.Write(": " + cmd.CommandInfo.description); + Console.WriteLine(); + } + return true; + } + + [RemoteLock] + [Command("close", usage = "{win:integer32}", description ="{DESC_CLOSE}")] + [RequiresArgument("win")] + [RequiresUpgrade("close_command")] + public static bool CloseWindow(Dictionary<string, object> args) + { + int winNum = -1; + if (args.ContainsKey("win")) + winNum = Convert.ToInt32(args["win"].ToString()); + string err = null; + + if (winNum < 0 || winNum >= AppearanceManager.OpenForms.Count) + err = Localization.Parse("{ERR_BADWINID}", new Dictionary<string, string> + { + ["%max"] = (AppearanceManager.OpenForms.Count - 1).ToString() + }); + + if (string.IsNullOrEmpty(err)) + { + Console.WriteLine("{RES_WINDOWCLOSED}"); + AppearanceManager.Close(AppearanceManager.OpenForms[winNum].ParentWindow); + } + else + { + Console.WriteLine(err); + } + + return true; + } + + } +} diff --git a/ShiftOS.Frontend/Content/Content.mgcb b/ShiftOS.Frontend/Content/Content.mgcb new file mode 100644 index 0000000..ddc4c36 --- /dev/null +++ b/ShiftOS.Frontend/Content/Content.mgcb @@ -0,0 +1,15 @@ + +#----------------------------- Global Properties ----------------------------# + +/outputDir:bin/$(Platform) +/intermediateDir:obj/$(Platform) +/platform:DesktopGL +/config: +/profile:Reach +/compress:False + +#-------------------------------- References --------------------------------# + + +#---------------------------------- Content ---------------------------------# + diff --git a/ShiftOS.Frontend/Desktop/Desktop.cs b/ShiftOS.Frontend/Desktop/Desktop.cs new file mode 100644 index 0000000..85949e2 --- /dev/null +++ b/ShiftOS.Frontend/Desktop/Desktop.cs @@ -0,0 +1,384 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ShiftOS.Engine; +using ShiftOS.Frontend.Apps; +using ShiftOS.Frontend.GraphicsSubsystem; +using static ShiftOS.Engine.SkinEngine; + + +namespace ShiftOS.Frontend.Desktop +{ + public class Desktop : GUI.Control, IDesktop + { + bool alOpen = false; + int alX = 0; + int alY = 0; + + public Desktop() + { + SaveSystem.GameReady += () => + { + Show(); + SetupDesktop(); + }; + + MouseMove += (loc) => + { + if(alOpen == true) + { + if(loc.X >= alX && loc.Y >= alY) + { + int height = LauncherItems[0].Height * LauncherItems.Count; + int width = LauncherItems[0].Width; + if(loc.X <= alX + width && loc.Y <= alY + height) + { + foreach(var item in LauncherItems) + { + if(loc.X >= alX && loc.Y >= alY + item.Y && loc.X <= alX + width && loc.Y <= alY + item.Y + item.Height) + { + alSelectedItem = LauncherItems.IndexOf(item); + Invalidate(); + return; + } + } + } + else + { + alSelectedItem = -1; + Invalidate(); + return; + } + } + } + }; + } + + public string DesktopName + { + get + { + return "ShiftOS MonoGame Desktop"; + } + } + + private int alSelectedItem = -1; + + public void Close() + { + UIManager.StopHandling(this); + } + + public Size GetSize() + { + return UIManager.Viewport; + } + + public void HideAppLauncher() + { + alOpen = false; + Invalidate(); + } + + public void InvokeOnWorkerThread(Action act) + { + UIManager.CrossThreadOperations.Enqueue(act); + } + + public void KillWindow(IWindowBorder border) + { + } + + public void MaximizeWindow(IWindowBorder brdr) + { + } + + public void MinimizeWindow(IWindowBorder brdr) + { + } + + public void OpenAppLauncher(Point loc) + { + alX = loc.X; + alY = loc.Y; + alOpen = true; + alSelectedItem = -1; + Invalidate(); + } + + public void PopulateAppLauncher(LauncherItem[] items) + { + int y = 0; + int height = 0; + int[] widths = new int[items.Length]; + using(var gfx = System.Drawing.Graphics.FromImage(new System.Drawing.Bitmap(1, 1))) + { + LauncherItems.Clear(); + for(int i = 0; i < items.Length; i++) + { + string name = Localization.Parse(items[i].DisplayData.Name); + var measure = gfx.SmartMeasureString(name, LoadedSkin.MainFont); + if (height < (int)measure.Height) + height = (int)measure.Height; + widths[i] = 120 + (int)measure.Width; + + } + + int width = widths.Max(); + + foreach(var aitem in items) + { + var item = new AppLauncherItem + { + Data = aitem, + X = 0, + Y = y, + Height = height, + Width = width + }; + LauncherItems.Add(item); + y += item.Height; + } + + } + Invalidate(); + } + + public void PopulatePanelButtons() + { + PanelButtons.Clear(); + foreach(var win in AppearanceManager.OpenForms) + { + var border = win as WindowBorder; + var pbtn = new PanelButtonData(); + pbtn.Title = border.Text; + pbtn.Window = border; + PanelButtons.Add(pbtn); + } + + Invalidate(); + } + + public void PushNotification(string app, string title, string message) + { + } + + public void RestoreWindow(IWindowBorder brdr) + { + } + + public void SetupDesktop() + { + Invalidate(); + } + + public void Show() + { + UIManager.AddTopLevel(this); + Visible = true; + Invalidate(); + } + + public void ShowWindow(IWindowBorder border) + { + } + + private string dateTimeString = ""; + + protected override void OnLayout() + { + SendToBack(); + X = 0; + Y = 0; + Width = GetSize().Width; + Height = GetSize().Height; + var now = DateTime.Now.TimeOfDay; + var newDateTimeString = $"{now.Hours}:{now.Minutes}:{now.Seconds}"; + if(newDateTimeString != dateTimeString) + { + dateTimeString = newDateTimeString; + Invalidate(); + } + + } + + private List<PanelButtonData> PanelButtons = new List<PanelButtonData>(); + private List<AppLauncherItem> LauncherItems = new List<AppLauncherItem>(); + + public override void MouseStateChanged() + { + //This statement closes the app launcher. If we do this after opening it, we can't open it at all as it instantly closes. + if (alOpen == true && MouseLeftDown == true) + { + if(alSelectedItem != -1) + { + var item = LauncherItems[alSelectedItem]; + AppearanceManager.SetupWindow((IShiftOSWindow)Activator.CreateInstance(item.Data.LaunchType, null)); + } + alOpen = false; + Invalidate(); + return; + } + + + var al_left = LoadedSkin.AppLauncherFromLeft; + var al_size = LoadedSkin.AppLauncherHolderSize; + if(MouseX >= al_left.X && MouseY >= al_left.Y && MouseX <= al_left.X + al_size.Width && MouseY <= al_left.Y + al_size.Height) + { + if(alOpen == false && MouseLeftDown == true) + { + alX = 0; + if(LoadedSkin.DesktopPanelPosition == 0) + { + alY = LoadedSkin.DesktopPanelHeight; + } + else + { + alY = (Height - LoadedSkin.DesktopPanelHeight) - (LauncherItems[0].Height * LauncherItems.Count); + } + alOpen = true; + Invalidate(); + } + + } + + } + + protected override void OnPaint(GraphicsContext gfx) + { + //Let's get data for the desktop panel. + + //We need the width and the height and the position. + + int dp_height = LoadedSkin.DesktopPanelHeight; + int dp_position = (LoadedSkin.DesktopPanelPosition == 0) ? 0 : Height - dp_height; + int dp_width = Width; + + //Alright, now we need to know if we should draw using a texture or a color + if (UIManager.SkinTextures.ContainsKey("desktoppanel")) + { + //Draw with the texture + gfx.DrawRectangle(0, dp_position, dp_width, dp_height, UIManager.SkinTextures["desktoppanel"]); + } + else + { + //draw with a color + var color = UIManager.SkinTextures["DesktopPanelColor"]; + gfx.DrawRectangle(0, dp_position, dp_width, dp_height, color); + } + + //Alright, now App Launcher. + var al_left = LoadedSkin.AppLauncherFromLeft; + var holderSize = LoadedSkin.AppLauncherHolderSize; + if (UIManager.SkinTextures.ContainsKey("applauncher")) + { + gfx.DrawRectangle(al_left.X, dp_position + al_left.Y, holderSize.Width, holderSize.Height, UIManager.SkinTextures["applauncher"]); + } + var altextmeasure = gfx.MeasureString(LoadedSkin.AppLauncherText, LoadedSkin.AppLauncherFont); + int altextx = (holderSize.Width - (int)altextmeasure.X) / 2; + int altexty = (holderSize.Height - (int)altextmeasure.Y) / 2; + gfx.DrawString(LoadedSkin.AppLauncherText, altextx, altexty, LoadedSkin.AppLauncherTextColor.ToMonoColor(), LoadedSkin.AppLauncherFont); + //Panel clock. + + var panelClockRight = LoadedSkin.DesktopPanelClockFromRight; + var panelClockTextColor = LoadedSkin.DesktopPanelClockColor.ToMonoColor(); + + + var measure = gfx.MeasureString(dateTimeString, LoadedSkin.DesktopPanelClockFont); + + int panelclockleft = Width - (int)measure.X; + int panelclockwidth = Width - panelclockleft; + + if (UIManager.SkinTextures.ContainsKey("panelclockbg")) + { + //draw the background using panelclock texture + gfx.DrawRectangle(panelclockleft, dp_position, panelclockwidth, dp_height, UIManager.SkinTextures["panelclockbg"]); + } + else + { + //draw using the bg color + var pcBGColor = UIManager.SkinTextures["DesktopPanelClockBackgroundColor"]; + gfx.DrawRectangle(panelclockleft, dp_position, panelclockwidth, dp_height, pcBGColor); + } + + int text_left = (panelclockwidth - (int)measure.X) / 2; + int text_top = (dp_height - (int)measure.Y) / 2; + + //draw string + gfx.DrawString(dateTimeString, panelclockleft + text_left, dp_position + text_top, panelClockTextColor, LoadedSkin.DesktopPanelClockFont); + + int initialGap = LoadedSkin.PanelButtonHolderFromLeft; + int offset = initialGap; + + foreach(var pbtn in PanelButtons) + { + offset += LoadedSkin.PanelButtonFromLeft.X; + + int pbtnfromtop = LoadedSkin.PanelButtonFromTop; + int pbtnwidth = LoadedSkin.PanelButtonSize.Width; + int pbtnheight = LoadedSkin.PanelButtonSize.Height; + + //Draw panel button background... + if (UIManager.SkinTextures.ContainsKey("panelbutton")) + { + gfx.DrawRectangle(offset, dp_position + pbtnfromtop, pbtnwidth, pbtnheight, UIManager.SkinTextures["panelbutton"]); + } + else + { + gfx.DrawRectangle(offset, dp_position + pbtnfromtop, pbtnwidth, pbtnheight, UIManager.SkinTextures["PanelButtonColor"]); + } + + //now we draw the text + + gfx.DrawString(pbtn.Title, offset + 2, dp_position + pbtnfromtop + 2, LoadedSkin.PanelButtonTextColor.ToMonoColor(), LoadedSkin.PanelButtonFont); + + offset += LoadedSkin.PanelButtonSize.Width; + } + + if (alOpen) + { + int height = (LauncherItems[0].Height * LauncherItems.Count) + 2; + int width = LauncherItems[0].Width + 2; + gfx.DrawRectangle(alX, alY, width, height, UIManager.SkinTextures["Menu_MenuBorder"]); + gfx.DrawRectangle(alX+1, alY+1, width-2, height-2, UIManager.SkinTextures["Menu_ToolStripDropDownBackground"]); + gfx.DrawRectangle(alX+1, alY+1, 18, height-2, UIManager.SkinTextures["Menu_ImageMarginGradientBegin"]); + + foreach(var item in LauncherItems) + { + if(LauncherItems.IndexOf(item) == alSelectedItem) + { + gfx.DrawRectangle(alX+1, alY + item.Y+1, item.Width-2, item.Height, UIManager.SkinTextures["Menu_MenuItemSelected"]); + } + gfx.DrawString(Localization.Parse(item.Data.DisplayData.Name), alX + 21, alY + item.Y+1, LoadedSkin.Menu_TextColor.ToMonoColor(), LoadedSkin.MainFont); + } + } + } + } + + public class PanelButtonData + { + public string Title { get; set; } + public WindowBorder Window { get; set; } + + public bool IsActive + { + get + { + return Window.IsFocusedControl || Window.ContainsFocusedControl; + } + } + } + + + public class AppLauncherItem + { + public Engine.LauncherItem Data { get; set; } + public int X { get; set; } + public int Y { get; set; } + public int Width { get; set; } + public int Height { get; set; } + } +} diff --git a/ShiftOS.Frontend/Desktop/WindowManager.cs b/ShiftOS.Frontend/Desktop/WindowManager.cs new file mode 100644 index 0000000..22c61bf --- /dev/null +++ b/ShiftOS.Frontend/Desktop/WindowManager.cs @@ -0,0 +1,480 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Xna.Framework.Graphics; +using ShiftOS.Engine; +using ShiftOS.Frontend.GraphicsSubsystem; +using static ShiftOS.Engine.SkinEngine; + +namespace ShiftOS.Frontend.Desktop +{ + public class WindowManager : Engine.WindowManager + { + public override void Close(IShiftOSWindow win) + { + var brdr = RunningBorders.FirstOrDefault(x => x.ParentWindow == win); + if (brdr != null) + { + brdr.Close(); + RunningBorders.Remove(brdr); + if (AppearanceManager.OpenForms.Contains(brdr)) + { + AppearanceManager.OpenForms.Remove(brdr); + TileWindows(); + Engine.Desktop.ResetPanelButtons(); + } + win = null; + } + } + + private List<WindowBorder> RunningBorders = new List<WindowBorder>(); + + public override void InvokeAction(Action act) + { + UIManager.CrossThreadOperations.Enqueue(act); + } + + public override void Maximize(IWindowBorder border) + { + throw new NotImplementedException(); + } + + public override void Minimize(IWindowBorder border) + { + throw new NotImplementedException(); + } + + public override void SetTitle(IShiftOSWindow win, string title) + { + var brdr = RunningBorders.FirstOrDefault(x => x.ParentWindow == win); + if (brdr != null) + brdr.Text = title; + } + + public override void SetupDialog(IShiftOSWindow win) + { + var wb = new WindowBorder(); + wb.Width = (win as GUI.Control).Width + LoadedSkin.LeftBorderWidth + LoadedSkin.RightBorderWidth; + wb.Height = (win as GUI.Control).Height + LoadedSkin.TitlebarHeight + LoadedSkin.BottomBorderWidth; + wb.ParentWindow = win; + wb.IsDialog = true; + UIManager.AddTopLevel(wb); + RunningBorders.Add(wb); + win.OnLoad(); + win.OnUpgrade(); + win.OnSkinLoad(); + } + + private int MaxCount + { + get + { + if (Shiftorium.UpgradeInstalled("wm_unlimited_windows")) + return int.MaxValue; + if (Shiftorium.UpgradeInstalled("wm_4_windows")) + return 4; + if (Shiftorium.UpgradeInstalled("wm_2_windows")) + return 2; + return 1; + } + } + + public override void SetupWindow(IShiftOSWindow win) + { + if (!Shiftorium.UpgradeAttributesUnlocked(win.GetType())) + { + Console.WriteLine("Application not found on system."); + return; + } + while(AppearanceManager.OpenForms.Count >= MaxCount) + { + AppearanceManager.OpenForms[0].Close(); + AppearanceManager.OpenForms.RemoveAt(0); + } + var wb = new WindowBorder(); + wb.Width = (win as GUI.Control).Width + LoadedSkin.LeftBorderWidth + LoadedSkin.RightBorderWidth; + wb.Height = (win as GUI.Control).Height + LoadedSkin.TitlebarHeight + LoadedSkin.BottomBorderWidth; + wb.ParentWindow = win; + wb.IsDialog = true; + UIManager.AddTopLevel(wb); + AppearanceManager.OpenForms.Add(wb); + RunningBorders.Add(wb); + win.OnLoad(); + win.OnUpgrade(); + win.OnSkinLoad(); + if (!Shiftorium.UpgradeInstalled("wm_free_placement")) + { + TileWindows(); + } + } + + public void TileWindows() + { + if (AppearanceManager.OpenForms.Count == 0) + return; + else if(AppearanceManager.OpenForms.Count == 1) + { + var wb = (WindowBorder)AppearanceManager.OpenForms[0]; + wb.X = 0; + wb.Y = 0; + wb.ResizeWindow(UIManager.Viewport.Width, UIManager.Viewport.Height); + } + } + } + + public class WindowBorder : GUI.Control, IWindowBorder + { + private string _text = "ShiftOS window"; + private GUI.Control _hostedwindow = null; + + public void ResizeWindow(int width, int height) + { + int titleheight = Shiftorium.UpgradeInstalled("wm_titlebar") ? LoadedSkin.TitlebarHeight : 0; + int leftwidth = Shiftorium.UpgradeInstalled("window_borders") ? LoadedSkin.LeftBorderWidth : 0; + int bottomheight = Shiftorium.UpgradeInstalled("window_borders") ? LoadedSkin.BottomBorderWidth : 0; + int rightwidth = Shiftorium.UpgradeInstalled("window_borders") ? LoadedSkin.RightBorderWidth : 0; + _hostedwindow.Width = width - leftwidth - rightwidth; + _hostedwindow.Height = height - bottomheight - titleheight; + Width = width; + Height = height; + + } + + public WindowBorder() + { + X = 720; + Y = 480; + MouseDown += () => + { + var scnloc = PointToScreen(MouseX, MouseY); + mouseprevx = scnloc.X; + mouseprevy = scnloc.Y; + moving = true; + }; + MouseMove += (loc) => + { + if (moving == true) + { + var scnloc = PointToScreen(MouseX, MouseY); + int differencex = scnloc.X - mouseprevx; + int differencey = scnloc.Y - mouseprevy; + X -= differencex; + Y -= differencey; + mouseprevx = scnloc.X; + mouseprevy = scnloc.Y; + } + }; + MouseUp += () => + { + moving = false; + }; + } + + private bool moving = false; + private int mouseprevx = 0; + private int mouseprevy = 0; + + + public IShiftOSWindow ParentWindow + { + get + { + return (IShiftOSWindow)_hostedwindow; + } + + set + { + _hostedwindow = (GUI.Control)value; + ClearControls(); + AddControl(_hostedwindow); + Width = LoadedSkin.LeftBorderWidth + _hostedwindow.Width + LoadedSkin.RightBorderWidth; + Height = LoadedSkin.BottomBorderWidth + _hostedwindow.Height + LoadedSkin.TitlebarHeight; + + } + } + + public bool IsDialog { get; set; } + + public string Text + { + get + { + return _text; + } + + set + { + _text = value; + } + } + + public void Close() + { + Visible = false; + UIManager.StopHandling(this); + } + + private int lastmousex, lastmousey = 0; + + protected override void OnLayout() + { + if (moving) + { + var screenpoint = PointToScreen(MouseX, MouseY); + this.X = lastmousex + screenpoint.X; + this.Y = lastmousey + screenpoint.Y; + } + int titlebarheight = Shiftorium.UpgradeInstalled("wm_titlebar") ? LoadedSkin.TitlebarHeight : 0; + int borderleft = Shiftorium.UpgradeInstalled("window_borders") ? LoadedSkin.LeftBorderWidth : 0; + int borderright = Shiftorium.UpgradeInstalled("window_borders") ? LoadedSkin.RightBorderWidth : 0; + int borderbottom = Shiftorium.UpgradeInstalled("window_borders") ? LoadedSkin.BottomBorderWidth : 0; + _hostedwindow.X = borderleft; + _hostedwindow.Y = titlebarheight; + Width = _hostedwindow.X + _hostedwindow.Width + LoadedSkin.RightBorderWidth; + Height = _hostedwindow.Y + _hostedwindow.Height + LoadedSkin.BottomBorderWidth; + } + + + + protected override void OnPaint(GraphicsContext gfx) + { + int titleheight = LoadedSkin.TitlebarHeight; + int leftborderwidth = LoadedSkin.LeftBorderWidth; + int rightborderwidth = LoadedSkin.RightBorderWidth; + int bottomborderwidth = LoadedSkin.BottomBorderWidth; + + if (Shiftorium.UpgradeInstalled("wm_titlebar")) + { + var titlebarcolor = UIManager.SkinTextures["TitleBackgroundColor"]; + var titlefont = LoadedSkin.TitleFont; + var titletextcolor = LoadedSkin.TitleTextColor; + var titletextleft = LoadedSkin.TitleTextLeft; + bool titletextcentered = LoadedSkin.TitleTextCentered; + + var drawcorners = LoadedSkin.ShowTitleCorners; + int titlebarleft = 0; + int titlebarwidth = Width; + if (drawcorners) + { + //set titleleft to the first corner width + titlebarleft = LoadedSkin.TitleLeftCornerWidth; + titlebarwidth -= titlebarleft; + titlebarwidth -= LoadedSkin.TitleRightCornerWidth; + + + //Let's get the left and right images. + //and the colors + var leftcolor = UIManager.SkinTextures["TitleLeftCornerBackground"]; + var rightcolor = UIManager.SkinTextures["TitleRightCornerBackground"]; + //and the widths + var leftwidth = LoadedSkin.TitleLeftCornerWidth; + var rightwidth = LoadedSkin.TitleRightCornerWidth; + + //draw left corner + if(UIManager.SkinTextures.ContainsKey("titleleft")) + { + gfx.DrawRectangle(0, 0, leftwidth, titleheight, UIManager.SkinTextures["titleleft"]); + } + else + { + gfx.DrawRectangle(0, 0, leftwidth, titleheight, leftcolor); + } + + //draw right corner + if (UIManager.SkinTextures.ContainsKey("titleright")) + { + gfx.DrawRectangle(titlebarleft + titlebarwidth, 0, rightwidth, titleheight, UIManager.SkinTextures["titleright"]); + } + else + { + gfx.DrawRectangle(titlebarleft + titlebarwidth, 0, rightwidth, titleheight, rightcolor); + } + } + + if (!UIManager.SkinTextures.ContainsKey("titlebar")) + { + //draw the title bg + gfx.DrawRectangle(titlebarleft, 0, titlebarwidth, titleheight, titlebarcolor); + + } + else + { + gfx.DrawRectangle(titlebarleft, 0, titlebarwidth, titleheight, UIManager.SkinTextures["titlebar"]); + } + //Now we draw the title text. + var textMeasure = gfx.MeasureString(Text, titlefont); + PointF textloc; + if (titletextcentered) + textloc = new PointF((titlebarwidth - textMeasure.X) / 2, + titletextleft.Y); + else + textloc = new PointF(titlebarleft + titletextleft.X, titletextleft.Y); + + gfx.DrawString(Text, (int)textloc.X, (int)textloc.Y, titletextcolor.ToMonoColor(), titlefont); + + var tbuttonpos = LoadedSkin.TitleButtonPosition; + + //Draw close button + if(Shiftorium.UpgradeInstalled("close_button")) + { + var closebuttonsize = LoadedSkin.CloseButtonSize; + var closebuttonright = LoadedSkin.CloseButtonFromSide; + if (LoadedSkin.TitleButtonPosition == 0) + closebuttonright = new Point(Width - closebuttonsize.Width - closebuttonright.X, closebuttonright.Y); + if (!UIManager.SkinTextures.ContainsKey("closebutton")) + { + gfx.DrawRectangle(closebuttonright.X, closebuttonright.Y, closebuttonsize.Width, closebuttonsize.Height, UIManager.SkinTextures["CloseButtonColor"]); + } + else + { + gfx.DrawRectangle(closebuttonright.X, closebuttonright.Y, closebuttonsize.Width, closebuttonsize.Height, UIManager.SkinTextures["closebutton"]); + } + } + //Draw maximize button + if (Shiftorium.UpgradeInstalled("maximize_button")) + { + var closebuttonsize = LoadedSkin.MaximizeButtonSize; + var closebuttonright = LoadedSkin.MaximizeButtonFromSide; + if (LoadedSkin.TitleButtonPosition == 0) + closebuttonright = new Point(Width - closebuttonsize.Width - closebuttonright.X, closebuttonright.Y); + + if (!UIManager.SkinTextures.ContainsKey("maximizebutton")) + { + gfx.DrawRectangle(closebuttonright.X, closebuttonright.Y, closebuttonsize.Width, closebuttonsize.Height, UIManager.SkinTextures["MaximizeButtonColor"]); + } + else + { + gfx.DrawRectangle(closebuttonright.X, closebuttonright.Y, closebuttonsize.Width, closebuttonsize.Height, UIManager.SkinTextures["maximizebutton"]); + } + } + //Draw minimize button + if (Shiftorium.UpgradeInstalled("minimize_button")) + { + var closebuttonsize = LoadedSkin.MinimizeButtonSize; + var closebuttonright = LoadedSkin.MinimizeButtonFromSide; + if (LoadedSkin.TitleButtonPosition == 0) + closebuttonright = new Point(Width - closebuttonsize.Width - closebuttonright.X, closebuttonright.Y); + if (!UIManager.SkinTextures.ContainsKey("minimizebutton")) + { + gfx.DrawRectangle(closebuttonright.X, closebuttonright.Y, closebuttonsize.Width, closebuttonsize.Height, UIManager.SkinTextures["MinimizeButtonColor"]); + } + else + { + gfx.DrawRectangle(closebuttonright.X, closebuttonright.Y, closebuttonsize.Width, closebuttonsize.Height, UIManager.SkinTextures["minimizebutton"]); + } + + } + } + else + { + //Set the titleheight to 0. + titleheight = 0; + + } + + if (Shiftorium.UpgradeInstalled("window_borders")) + { + //Some variables we'll need... + int bottomlocy = Height - LoadedSkin.BottomBorderWidth; + int bottomlocx = leftborderwidth; + int bottomwidth = Width - bottomlocx - rightborderwidth; + int brightlocx = Width - rightborderwidth; + + var borderleftcolor = (ContainsFocusedControl || IsFocusedControl) ? UIManager.SkinTextures["BorderLeftBackground"] : UIManager.SkinTextures["BorderInactiveLeftBackground"]; + var borderrightcolor = (ContainsFocusedControl || IsFocusedControl) ? UIManager.SkinTextures["BorderRightBackground"] : UIManager.SkinTextures["BorderInactiveRightBackground"]; + var borderbottomcolor = (ContainsFocusedControl || IsFocusedControl) ? UIManager.SkinTextures["BorderBottomBackground"] : UIManager.SkinTextures["BorderInactiveBottomBackground"]; + var borderbleftcolor = (ContainsFocusedControl || IsFocusedControl) ? UIManager.SkinTextures["BorderBottomLeftBackground"] : UIManager.SkinTextures["BorderInactiveBottomLeftBackground"]; + var borderbrightcolor = (ContainsFocusedControl || IsFocusedControl) ? UIManager.SkinTextures["BorderBottomRightBackground"] : UIManager.SkinTextures["BorderInactiveBottomRightBackground"]; + + + //draw border corners + //BOTTOM LEFT + if (!UIManager.SkinTextures.ContainsKey("bottomlborder")) + { + gfx.DrawRectangle(0, bottomlocy, leftborderwidth, bottomborderwidth, borderbleftcolor); + } + else + { + gfx.DrawRectangle(0, bottomlocy, leftborderwidth, bottomborderwidth, UIManager.SkinTextures["bottomlborder"]); + } + + //BOTTOM RIGHT + if (!UIManager.SkinTextures.ContainsKey("bottomrborder")) + { + gfx.DrawRectangle(brightlocx, bottomlocy, rightborderwidth, bottomborderwidth, borderbrightcolor); + } + else + { + gfx.DrawRectangle(brightlocx, bottomlocy, rightborderwidth, bottomborderwidth, UIManager.SkinTextures["bottomrborder"]); + } + + //BOTTOM + if (!UIManager.SkinTextures.ContainsKey("bottomborder")) + { + gfx.DrawRectangle(leftborderwidth, bottomlocy, bottomwidth, bottomborderwidth, borderbottomcolor); + } + else + { + gfx.DrawRectangle(leftborderwidth, bottomlocy, bottomwidth, bottomborderwidth, UIManager.SkinTextures["bottomborder"]); + } + + //LEFT + if (!UIManager.SkinTextures.ContainsKey("leftborder")) + { + gfx.DrawRectangle(0, titleheight, leftborderwidth, Height - titleheight - bottomborderwidth, borderleftcolor); + } + else + { + gfx.DrawRectangle(0, titleheight, leftborderwidth, Height - titleheight - bottomborderwidth, UIManager.SkinTextures["leftborder"]); + } + + //RIGHT + if (!UIManager.SkinTextures.ContainsKey("rightborder")) + { + gfx.DrawRectangle(brightlocx, titleheight, rightborderwidth, Height - titleheight - bottomborderwidth, borderrightcolor); + } + else + { + gfx.DrawRectangle(brightlocx, titleheight, rightborderwidth, Height - titleheight - bottomborderwidth, UIManager.SkinTextures["rightborder"]); + } + + } + + gfx.DrawRectangle(leftborderwidth, titleheight, Width - leftborderwidth - rightborderwidth, Height - titleheight - bottomborderwidth, UIManager.SkinTextures["ControlColor"]); + //So here's what we're gonna do now. + //Now that we have a titlebar and window borders... + //We're going to composite the hosted window + //and draw it to the remaining area. + + //Painting of the canvas is done by the Paint() method. + } + + } + + public static class ImageExtensioons + { + public static Texture2D ToTexture2D(this Image image, GraphicsDevice device) + { + var bmp = (Bitmap)image; + var lck = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); + var data = new byte[Math.Abs(lck.Stride) * lck.Height]; + Marshal.Copy(lck.Scan0, data, 0, data.Length); + bmp.UnlockBits(lck); + for(int i = 0; i < data.Length; i += 4) + { + byte r = data[i]; + byte b = data[i + 2]; + data[i] = b; + data[i + 2] = r; + } + var tex2 = new Texture2D(device, bmp.Width, bmp.Height); + tex2.SetData<byte>(data); + return tex2; + } + } +} diff --git a/ShiftOS.Frontend/GUI/Button.cs b/ShiftOS.Frontend/GUI/Button.cs new file mode 100644 index 0000000..c2e55b9 --- /dev/null +++ b/ShiftOS.Frontend/GUI/Button.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Xna.Framework; +using ShiftOS.Engine; +using ShiftOS.Frontend.GraphicsSubsystem; + +namespace ShiftOS.Frontend.GUI +{ + public class Button : TextControl + { + public Button() + { + TextAlign = TextAlign.MiddleCenter; + Text = "Click me!"; + } + + protected override void OnLayout() + { + if(AutoSize == true) + { + int borderwidth = SkinEngine.LoadedSkin.ButtonBorderWidth * 2; + + using (var gfx = Graphics.FromImage(new Bitmap(1, 1))) + { + var measure = gfx.MeasureString(this.Text, this.Font); + Width = borderwidth + (int)measure.Width + 16; + Height = borderwidth + (int)measure.Height + 12; + } + } + } + + protected override void OnPaint(GraphicsContext gfx) + { + var bgCol = UIManager.SkinTextures["ButtonBackgroundColor"]; + var fgCol = SkinEngine.LoadedSkin.ControlTextColor.ToMonoColor(); + if (ContainsMouse) + bgCol = UIManager.SkinTextures["ButtonHoverColor"]; + if (MouseLeftDown) + bgCol = UIManager.SkinTextures["ButtonPressedColor"]; + + gfx.DrawRectangle(0, 0, Width, Height, UIManager.SkinTextures["ControlTextColor"]); + gfx.DrawRectangle(1, 1, Width - 2, Height - 2, bgCol); + + var measure = gfx.MeasureString(Text, Font); + + var loc = new Vector2((Width - measure.X) / 2, (Height - measure.Y) / 2); + + gfx.DrawString(Text, (int)loc.X, (int)loc.Y, fgCol, Font); + + } + } +} diff --git a/ShiftOS.Frontend/GUI/Control.cs b/ShiftOS.Frontend/GUI/Control.cs new file mode 100644 index 0000000..c16792b --- /dev/null +++ b/ShiftOS.Frontend/GUI/Control.cs @@ -0,0 +1,707 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; +using System.Drawing; +using ShiftOS.Frontend.GraphicsSubsystem; +using System.Drawing.Imaging; +using System.Drawing.Drawing2D; +using Microsoft.Xna.Framework; +using System.Runtime.InteropServices; + +namespace ShiftOS.Frontend.GUI +{ + public abstract class Control + { + private int _x = 0; + private int _y = 0; + private int _w = 0; + private int _h = 0; + private Control _parent = null; + private List<Control> _children = new List<Control>(); + private bool _wasMouseInControl = false; + private bool _leftState = false; + private bool _rightState = false; + private bool _middleState = false; + private bool _visible = true; + private DockStyle _dock = DockStyle.None; + private bool _focused = false; + private bool _autoSize = false; + private double _opacity = 1.0; + private bool _invalidated = true; + private Bitmap _texCache = null; + private Anchor _anchor = null; + private int _mouseX = 0; + private int _mouseY = 0; + private bool _captureMouse = false; + + public bool RequiresPaint + { + get + { + bool requires_child_repaint = false; + foreach (var child in _children) + { + requires_child_repaint = child.RequiresPaint; + if (requires_child_repaint) + break; + } + return _invalidated || requires_child_repaint; + } + } + + public Image TextureCache + { + get + { + return _texCache; + } + } + + public byte[] PaintCache + { + get + { + var data = _texCache.LockBits(new System.Drawing.Rectangle(0, 0, Width, Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); + var rgb = new byte[Math.Abs(data.Stride) * data.Height]; + Marshal.Copy(data.Scan0, rgb, 0, rgb.Length); + for(int i = 0; i < rgb.Length; i += 4) + { + byte r = rgb[i]; + byte b = rgb[i + 2]; + rgb[i] = b; + rgb[i + 2] = r; + } + _texCache.UnlockBits(data); + return rgb; + } + } + + public bool CaptureMouse + { + get + { + return _captureMouse; + } + set + { + _captureMouse = value; + } + } + + public int MouseX + { + get + { + return _mouseX; + } + } + + public int MouseY + { + get + { + return _mouseY; + } + } + + + public Anchor Anchor + { + get + { + return _anchor; + } + set + { + if (_anchor == value) + return; + + _anchor = value; + Invalidate(); + } + } + + public void Invalidate() + { + _invalidated = true; + foreach(var child in _children) + { + child.Invalidate(); + } + } + + public double Opacity + { + get + { + return _opacity; + } + set + { + if (_opacity == value) + return; + _opacity = value; + Invalidate(); + } + } + + public bool AutoSize + { + get + { + return _autoSize; + } + set + { + _autoSize = value; + } + } + + //Thank you, StackOverflow. + public static Bitmap ResizeImage(Image image, int width, int height) + { + var destRect = new System.Drawing.Rectangle(0, 0, width, height); + var destImage = new Bitmap(width, height); + + destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution); + + using (var graphics = Graphics.FromImage(destImage)) + { + graphics.CompositingMode = CompositingMode.SourceCopy; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + + using (var wrapMode = new ImageAttributes()) + { + wrapMode.SetWrapMode(WrapMode.TileFlipXY); + graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode); + } + } + + return destImage; + } + + public DockStyle Dock + { + get + { + return _dock; + } + set + { + _dock = value; + } + } + + public bool ContainsMouse + { + get { return _wasMouseInControl; } + } + + public bool Visible + { + get + { + return _visible; + } + set + { + if (_visible == value) + return; + + _visible = value; + Invalidate(); + } + } + + public void AddControl(Control ctrl) + { + if (!_children.Contains(ctrl)) + { + ctrl._parent = this; + _children.Add(ctrl); + Invalidate(); + } + } + + public bool MouseLeftDown + { + get + { + return _leftState; + } + } + + public bool MouseMiddleDown + { + get + { + return _middleState; + } + } + + public bool MouseRightDown + { + get + { + return _rightState; + } + } + + + + public int X + { + get + { + return _x; + } + set + { + if (_x == value) + return; + _x = value; + Invalidate(); + } + } + + public int Y + { + get + { + return _y; + } + set + { + if (_y == value) + return; + _y = value; + Invalidate(); + } + } + + public int Width + { + get + { + return _w; + } + set + { + if (_w == value) + return; + _w = value; + Invalidate(); + } + } + + public int Height + { + get + { + return _h; + } + set + { + if (_h == value) + return; + _h = value; + Invalidate(); + } + } + + public Control Parent + { + get + { + return _parent; + } + } + + public Control[] Children + { + get + { + return _children.ToArray(); + } + } + + public Point PointToParent(int x, int y) + { + return new Point(x + _x, y + _y); + } + + public Point PointToScreen(int x, int y) + { + var parentCoords = PointToParent(x, y); + Control parent = this._parent; + while(parent != null) + { + parentCoords = parent.PointToParent(parentCoords.X, parentCoords.Y); + parent = parent.Parent; + } + return parentCoords; + } + + public void ClearControls() + { + _children.Clear(); + Invalidate(); + } + + public Point PointToLocal(int x, int y) + { + return new GUI.Point(x - _x, y - _y); + } + + public virtual void MouseStateChanged() { } + + protected virtual void OnPaint(GraphicsContext gfx) + { + gfx.DrawRectangle(0, 0, Width, Height, UIManager.SkinTextures["ControlColor"]); + } + + public void SendToBack() + { + if(_parent != null) + { + _parent._children.Remove(this); + _parent._children.Insert(0, this); + } + else + { + UIManager.SendToBack(this); + } + } + + public void InvalidateTopLevel() + { + var parent = this; + while (parent.Parent != null) + parent = parent.Parent; + parent.Invalidate(); + } + + public void Paint(GraphicsContext gfx) + { + if (_visible == true) + { + OnPaint(gfx); + int draw_x = gfx.X; + int draw_y = gfx.Y; + int draw_width = gfx.Width; + int draw_height = gfx.Height; + foreach (var ctrl in _children) + { + if (ctrl.Visible == true) + { + gfx.X = draw_x + ctrl.X; + gfx.Y = draw_y + ctrl.Y; + gfx.Width = ctrl.Width; + gfx.Height = ctrl.Height; + ctrl.Paint(gfx); + gfx.X = draw_x; + gfx.Y = draw_y; + } + gfx.Width = draw_width; + gfx.Height = draw_height; + } + _invalidated = false; + } + } + + public void Layout() + { + //Dock style + if(_parent != null) + { + if(_anchor != null) + { + + } + + switch (_dock) + { + case DockStyle.Top: + X = 0; + Y = 0; + Width = _parent.Width; + break; + case DockStyle.Left: + X = 0; + Y = 0; + Height = _parent.Height; + break; + case DockStyle.Right: + Y = 0; + X = _parent.Width - Width; + Height = _parent.Height; + break; + case DockStyle.Bottom: + X = 0; + Y = _parent.Height - Height; + Width = _parent.Width; + break; + case DockStyle.Fill: + X = 0; + Y = 0; + Width = _parent.Width; + Height = _parent.Height; + break; + } + } + OnLayout(); + foreach (var child in _children) + child.Layout(); + } + + protected virtual void OnLayout() + { + //do nothing + } + + public bool IsFocusedControl + { + get + { + return UIManager.FocusedControl == this; + } + } + + public bool ContainsFocusedControl + { + get + { + if (UIManager.FocusedControl == null) + return false; + else + { + bool contains = false; + + var ctrl = UIManager.FocusedControl; + while(ctrl.Parent != null) + { + ctrl = ctrl.Parent; + if (ctrl == this) + contains = true; + } + return contains; + } + } + } + + public virtual bool ProcessMouseState(MouseState state) + { + //If we aren't rendering the control, we aren't accepting input. + if (_visible == false) + return false; + + + //Firstly, we get the mouse coordinates in the local space + var coords = PointToLocal(state.Position.X, state.Position.Y); + _mouseX = coords.X; + _mouseY = coords.Y; + //Now we check if the mouse is within the bounds of the control + if(coords.X >= 0 && coords.Y >= 0 && coords.X <= _w && coords.Y <= _h) + { + //We're in the local space. Let's fire the MouseMove event. + MouseMove?.Invoke(coords); + //Also, if the mouse hasn't been in the local space last time it moved, fire MouseEnter. + if(_wasMouseInControl == false) + { + _wasMouseInControl = true; + MouseEnter?.Invoke(); + Invalidate(); + } + + //Things are going to get a bit complicated. + //Firstly, we need to find out if we have any children. + bool _requiresMoreWork = true; + if(_children.Count > 0) + { + //We do. We're going to iterate through them all and process the mouse state. + foreach(var control in _children) + { + + //If the process method returns true, then we do not need to do anything else on our end. + + //We need to first create a new mousestate object with the new coordinates + + var nstate = new MouseState(coords.X, coords.Y, state.ScrollWheelValue, state.LeftButton, state.MiddleButton, state.RightButton, state.XButton1, state.XButton2); + //pass that state to the process method, and set the _requiresMoreWork value to the opposite of the return value + _requiresMoreWork = !control.ProcessMouseState(nstate); + //If it's false, break the loop. + if (_requiresMoreWork == false) + break; + } + } + + //If we need to do more work... + if(_requiresMoreWork == true) + { + bool fire = false; //so we know to fire a MouseStateChanged method + //Let's get the state values of each button + bool ld = state.LeftButton == ButtonState.Pressed; + bool md = state.MiddleButton == ButtonState.Pressed; + bool rd = state.RightButton == ButtonState.Pressed; + if(ld != _leftState || md != _middleState || rd != _rightState) + { + fire = true; + } + if (_leftState == true && ld == false) + { + Click?.Invoke(); + Invalidate(); + MouseUp?.Invoke(); + } + if (_leftState == false && ld == true) + { + var focused = UIManager.FocusedControl; + UIManager.FocusedControl = this; + focused?.InvalidateTopLevel(); + InvalidateTopLevel(); + MouseDown?.Invoke(); + + } + _leftState = ld; + _middleState = md; + _rightState = rd; + if (fire) + MouseStateChanged(); + } + return true; + } + else + { + _leftState = false; + _rightState = false; + _middleState = false; + MouseStateChanged(); + //If the mouse was in local space before, fire MouseLeave + if (_wasMouseInControl == true) + { + if (CaptureMouse == true) + { + _wasMouseInControl = true; + int newX = MathHelper.Clamp(state.X, X, X + Width); + int newY = MathHelper.Clamp(state.Y, Y, Y + Height); + Mouse.SetPosition(newX, newY); + + } + else + { + _wasMouseInControl = false; + MouseLeave?.Invoke(); + Invalidate(); + } + } + } + if (CaptureMouse == true) + { + _mouseX = coords.X; + _mouseY = coords.Y; + Layout(); + _wasMouseInControl = true; + int newX = MathHelper.Clamp(state.X, X, X + Width); + int newY = MathHelper.Clamp(state.Y, Y, Y + Height); + Mouse.SetPosition(newX, newY); + return true; + } + + //Mouse is not in the local space, don't do anything. + return false; + } + + protected virtual void OnKeyEvent(KeyEvent e) + { + + } + + public void ProcessKeyEvent(KeyEvent e) + { + OnKeyEvent(e); + KeyEvent?.Invoke(e); + } + + public event Action<Point> MouseMove; + public event Action MouseEnter; + public event Action MouseLeave; + public event Action Click; + public event Action<KeyEvent> KeyEvent; + public event Action MouseDown; + public event Action MouseUp; + } + + public struct Point + { + public Point(int x, int y) + { + X = x; + Y = y; + } + + public int X { get; set; } + public int Y { get; set; } + } + + public enum DockStyle + { + None, + Top, + Bottom, + Left, + Right, + Fill + } + + //Thanks, StackOverflow. + public static class BitmapExtensions + { + public static Image SetOpacity(this Image image, float opacity) + { + var colorMatrix = new ColorMatrix(); + colorMatrix.Matrix33 = opacity; + var imageAttributes = new ImageAttributes(); + imageAttributes.SetColorMatrix( + colorMatrix, + ColorMatrixFlag.Default, + ColorAdjustType.Bitmap); + var output = new Bitmap(image.Width, image.Height); + using (var gfx = Graphics.FromImage(output)) + { + gfx.SmoothingMode = SmoothingMode.AntiAlias; + gfx.DrawImage( + image, + new System.Drawing.Rectangle(0, 0, image.Width, image.Height), + 0, + 0, + image.Width, + image.Height, + GraphicsUnit.Pixel, + imageAttributes); + } + return output; + } + } + + [Flags] + public enum AnchorStyle + { + Top, + Left, + Bottom, + Right + } + + public class Anchor + { + public AnchorStyle Style { get; set; } + public int Distance { get; set; } + } +} diff --git a/ShiftOS.Frontend/GUI/ItemGroup.cs b/ShiftOS.Frontend/GUI/ItemGroup.cs new file mode 100644 index 0000000..e52a17f --- /dev/null +++ b/ShiftOS.Frontend/GUI/ItemGroup.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ShiftOS.Frontend.GUI +{ + public class ItemGroup : Control + { + private int _gap = 3; + private FlowDirection _flowDir = FlowDirection.LeftToRight; + private int _initialgap = 2; + + protected override void OnLayout() + { + if (AutoSize) + { + int _highesty = _initialgap; + int _xx = _initialgap; + foreach(var ctrl in Children) + { + _xx += ctrl.Width + _gap; + if (_highesty < ctrl.Height + _initialgap + _gap) + _highesty = ctrl.Height + _initialgap + _gap; + } + Width = _xx; + Height = _highesty; + } + + int _x = _initialgap; + int _y = _initialgap; + int _maxYForRow = 0; + foreach (var ctrl in Children) + { + if (_x + ctrl.Width + _gap > Width) + { + _x = _initialgap; + _y = _maxYForRow; + _maxYForRow = 0; + if (_maxYForRow < ctrl.Height + _gap) + _maxYForRow = ctrl.Height + _gap; + } + ctrl.X = _x; + ctrl.Y = _y; + ctrl.Dock = DockStyle.None; + ctrl.Layout(); + _x += ctrl.Width + _gap; + + if (_maxYForRow < ctrl.Height + _gap) + _maxYForRow = ctrl.Height + _gap; + + } + } + } + + public enum FlowDirection + { + LeftToRight, + TopDown, + RightToLeft, + BottomUp + } +} diff --git a/ShiftOS.Frontend/GUI/ListBox.cs b/ShiftOS.Frontend/GUI/ListBox.cs new file mode 100644 index 0000000..f9354e0 --- /dev/null +++ b/ShiftOS.Frontend/GUI/ListBox.cs @@ -0,0 +1,162 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Xna.Framework; +using ShiftOS.Frontend.GraphicsSubsystem; +using static ShiftOS.Engine.SkinEngine; + +namespace ShiftOS.Frontend.GUI +{ + public class ListBox : GUI.Control + { + private int fontheight = 0; + private List<object> items = new List<object>(); + private int selectedIndex = -1; + private int itemOffset = 0; + private int itemsPerPage = 1; + + public int SelectedIndex + { + get + { + return MathHelper.Clamp(selectedIndex, 0, items.Count - 1); + } + set + { + selectedIndex = MathHelper.Clamp(value, 0, items.Count - 1); + RecalculateItemsPerPage(); + SelectedIndexChanged?.Invoke(); + } + } + + public object SelectedItem + { + get + { + try + { + return items[SelectedIndex]; + } + catch + { + return ""; + } + } + } + + public void ClearItems() + { + selectedIndex = -1; + items.Clear(); + SelectedIndexChanged?.Invoke(); + Invalidate(); + } + + public void AddItem(object item) + { + items.Add(item); + RecalculateItemsPerPage(); + Invalidate(); + } + + public void RemoveItem(object item) + { + items.Remove(item); + selectedIndex = -1; + RecalculateItemsPerPage(); + SelectedIndexChanged?.Invoke(); + Invalidate(); + } + + public void RecalculateItemsPerPage() + { + itemsPerPage = 0; + while(itemsPerPage * fontheight < Height && itemsPerPage < items.Count - 1) + { + itemsPerPage++; + } + //We have the amount of items we can fit on screen. + //Now let's calculate the offset based on this, as well + //as the currently selected item. + //of course, if there IS one. + if(selectedIndex > -1) + { + if(selectedIndex >= items.Count) + { + selectedIndex = items.Count - 1; + } + while(this.itemOffset > selectedIndex) + { + itemOffset--; + } + while(this.itemOffset + itemsPerPage < selectedIndex) + { + itemOffset++; + } + } + } + + protected override void OnKeyEvent(KeyEvent e) + { + if(e.Key== Microsoft.Xna.Framework.Input.Keys.Down) + { + if(selectedIndex < items.Count - 2) + { + selectedIndex++; + RecalculateItemsPerPage(); + SelectedIndexChanged?.Invoke(); + Invalidate(); + } + } + else if(e.Key == Microsoft.Xna.Framework.Input.Keys.Up) + { + if(selectedIndex > 0) + { + selectedIndex--; + RecalculateItemsPerPage(); + SelectedIndexChanged?.Invoke(); + Invalidate(); + } + } + } + + protected override void OnPaint(GraphicsContext gfx) + { + gfx.Clear(LoadedSkin.ControlTextColor.ToMonoColor()); + gfx.DrawRectangle(1, 1, Width - 2, Height - 2, UIManager.SkinTextures["ControlColor"]); + for(int i = itemOffset; i < items.Count - 1 && i < itemsPerPage; i++) + { + int x = 1; + int y = fontheight * (i - itemOffset); + int width = Width - 2; + int height = fontheight; + if(i == selectedIndex) + { + //draw the string as selected + gfx.DrawRectangle(x, y, width, height, UIManager.SkinTextures["ControlTextColor"]); + gfx.DrawString(items[i].ToString(), x, y, LoadedSkin.ControlColor.ToMonoColor(), LoadedSkin.MainFont); + } + else + { + gfx.DrawRectangle(x, y, width, height, UIManager.SkinTextures["ControlColor"]); + gfx.DrawString(items[i].ToString(), x, y, LoadedSkin.ControlTextColor.ToMonoColor(), LoadedSkin.MainFont); + + } + } + } + + protected override void OnLayout() + { + if(fontheight != LoadedSkin.MainFont.Height) + { + fontheight = LoadedSkin.MainFont.Height; + Invalidate(); + } + base.OnLayout(); + } + + public event Action SelectedIndexChanged; + } +} diff --git a/ShiftOS.Frontend/GUI/PictureBox.cs b/ShiftOS.Frontend/GUI/PictureBox.cs new file mode 100644 index 0000000..6f60b29 --- /dev/null +++ b/ShiftOS.Frontend/GUI/PictureBox.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ShiftOS.Engine; +using System.Drawing.Imaging; +using ShiftOS.Frontend.GraphicsSubsystem; +using Microsoft.Xna.Framework.Graphics; + +namespace ShiftOS.Frontend.GUI +{ + public class PictureBox : Control + { + private Texture2D img = null; + private ImageLayout _layout = ImageLayout.Fit; + + public ImageLayout ImageLayout + { + get + { + return _layout; + } + set + { + _layout = value; + } + } + + public Texture2D Image + { + get + { + return img; + } + set + { + if (img != null) + img.Dispose(); + img = value; + } + } + + protected override void OnLayout() + { + if (AutoSize) + { + Width = (img == null) ? 0 : img.Width; + Height = (img == null) ? 0 : img.Height; + } + } + + protected override void OnPaint(GraphicsContext gfx) + { + switch (_layout) + { + case ImageLayout.Stretch: + gfx.DrawRectangle(0, 0, Width, Height, Image); + break; + case ImageLayout.None: + gfx.DrawRectangle(0, 0, Image.Width, Image.Height, Image); + break; + } + + } + + //Again, thanks StackOverflow + static Image FixedSize(Image imgPhoto, int Width, int Height) + { + int sourceWidth = imgPhoto.Width; + int sourceHeight = imgPhoto.Height; + int sourceX = 0; + int sourceY = 0; + int destX = 0; + int destY = 0; + + float nPercent = 0; + float nPercentW = 0; + float nPercentH = 0; + + nPercentW = ((float)Width / (float)sourceWidth); + nPercentH = ((float)Height / (float)sourceHeight); + if (nPercentH < nPercentW) + { + nPercent = nPercentH; + destX = System.Convert.ToInt16((Width - + (sourceWidth * nPercent)) / 2); + } + else + { + nPercent = nPercentW; + destY = System.Convert.ToInt16((Height - + (sourceHeight * nPercent)) / 2); + } + + int destWidth = (int)(sourceWidth * nPercent); + int destHeight = (int)(sourceHeight * nPercent); + + Bitmap bmPhoto = new Bitmap(Width, Height, + PixelFormat.Format24bppRgb); + bmPhoto.SetResolution(imgPhoto.HorizontalResolution, + imgPhoto.VerticalResolution); + + Graphics grPhoto = Graphics.FromImage(bmPhoto); + grPhoto.Clear(SkinEngine.LoadedSkin.ControlColor); + grPhoto.InterpolationMode = + InterpolationMode.HighQualityBicubic; + + grPhoto.DrawImage(imgPhoto, + new Rectangle(destX, destY, destWidth, destHeight), + new Rectangle(sourceX, sourceY, sourceWidth, sourceHeight), + GraphicsUnit.Pixel); + + grPhoto.Dispose(); + return bmPhoto; + } + } + + public enum ImageLayout + { + None, + Stretch, + Tile, + Fit, + } +} diff --git a/ShiftOS.Frontend/GUI/ProgressBar.cs b/ShiftOS.Frontend/GUI/ProgressBar.cs new file mode 100644 index 0000000..a13bbf8 --- /dev/null +++ b/ShiftOS.Frontend/GUI/ProgressBar.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ShiftOS.Frontend.GraphicsSubsystem; +using static ShiftOS.Engine.SkinEngine; + +namespace ShiftOS.Frontend.GUI +{ + public class ProgressBar : Control + { + private int _maximum = 100; + private int _value = 0; + + public int Maximum + { + get + { + return _maximum; + } + set + { + _maximum = value; + } + } + + public int Value + { + get + { + return _value; + } + set + { + _value = value; + } + } + + protected override void OnPaint(GraphicsContext gfx) + { + gfx.Clear(LoadedSkin.ProgressBarBackgroundColor.ToMonoColor()); + int w = (int)linear(_value, 0, _maximum, 0, Width); + gfx.DrawRectangle(0, 0, w, Height, LoadedSkin.ProgressColor.ToMonoColor()); + } + + static public double linear(double x, double x0, double x1, double y0, double y1) + { + if ((x1 - x0) == 0) + { + return (y0 + y1) / 2; + } + return y0 + (x - x0) * (y1 - y0) / (x1 - x0); + } + + } +} diff --git a/ShiftOS.Frontend/GUI/TextControl.cs b/ShiftOS.Frontend/GUI/TextControl.cs new file mode 100644 index 0000000..f1bbef1 --- /dev/null +++ b/ShiftOS.Frontend/GUI/TextControl.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ShiftOS.Frontend.GraphicsSubsystem; + +namespace ShiftOS.Frontend.GUI +{ + public class TextControl : Control + { + private string _text = "Text Control"; + private TextAlign _textAlign = TextAlign.TopLeft; + private Font _font = new Font("Tahoma", 9f); + + protected override void OnLayout() + { + if (AutoSize) + { + using (var bmp = new Bitmap(1, 1)) + { + using(var gfx = Graphics.FromImage(bmp)) + { + var measure = gfx.MeasureString(_text, _font); + Width = (int)measure.Width; + Height = (int)measure.Height; + } + } + } + } + + public string Text + { + get { return _text; } + set { _text = value; } + } + + public Font Font + { + get + { + return _font; + } + set + { + _font = value; + } + } + + public TextAlign TextAlign + { + get { return _textAlign; } + set { _textAlign = value; } + } + + protected override void OnPaint(GraphicsContext gfx) + { + var sMeasure = gfx.MeasureString(_text, _font, Width); + PointF loc = new PointF(2, 2); + float centerH = (Width - sMeasure.X) / 2; + float centerV = (Height - sMeasure.Y) / 2; + switch (_textAlign) + { + case TextAlign.TopCenter: + loc.X = centerH; + break; + case TextAlign.TopRight: + loc.X = Width - sMeasure.X; + break; + case TextAlign.MiddleLeft: + loc.Y = centerV; + break; + case TextAlign.MiddleCenter: + loc.Y = centerV; + loc.X = centerH; + break; + case TextAlign.MiddleRight: + loc.Y = centerV; + loc.X = (Width - sMeasure.Y); + break; + case TextAlign.BottomLeft: + loc.Y = (Height - sMeasure.Y); + break; + case TextAlign.BottomCenter: + loc.Y = (Height - sMeasure.Y); + loc.X = centerH; + break; + case TextAlign.BottomRight: + loc.Y = (Height - sMeasure.Y); + loc.X = (Width - sMeasure.X); + break; + + } + + gfx.DrawString(_text, 0, 0, Engine.SkinEngine.LoadedSkin.ControlTextColor.ToMonoColor(), _font, this.Width); + } + } + + public enum TextAlign + { + TopLeft, + TopCenter, + TopRight, + MiddleLeft, + MiddleCenter, + MiddleRight, + BottomLeft, + BottomCenter, + BottomRight + } +} diff --git a/ShiftOS.Frontend/GUI/TextInput.cs b/ShiftOS.Frontend/GUI/TextInput.cs new file mode 100644 index 0000000..73954ef --- /dev/null +++ b/ShiftOS.Frontend/GUI/TextInput.cs @@ -0,0 +1,138 @@ +using System; +using System.Collections.Generic; + +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Xna.Framework; +using ShiftOS.Frontend.GraphicsSubsystem; +using static ShiftOS.Engine.SkinEngine; + +namespace ShiftOS.Frontend.GUI +{ + public class TextInput : Control + { + private string _label = "Type here!"; + private string _text = ""; + private int _index = 0; + private System.Drawing.Font _font = new System.Drawing.Font("Tahoma", 9f); + + public int Index + { + get + { + return _index; + } + set + { + if (_index == value) + return; + if(_text.Length == 0) + { + _index = 0; + return; + } + _index = MathHelper.Clamp(value, 0, _text.Length); + Invalidate(); + } + } + + public string Text + { + get + { + return _text; + } + set + { + if (_text == value) + return; + + _text = value; + if(_index >= _text.Length) + { + _index = _text.Length - 1; + } + Invalidate(); + } + } + + protected override void OnKeyEvent(KeyEvent e) + { + if(e.Key == Microsoft.Xna.Framework.Input.Keys.Left) + { + if (_index > 0) + _index--; + + } + if(e.Key == Microsoft.Xna.Framework.Input.Keys.Back) + { + if(_index > 0) + { + _text = _text.Remove(_index - 1, 1); + _index--; + } + } + if(e.Key == Microsoft.Xna.Framework.Input.Keys.Delete) + { + if(_index < _text.Length - 1) + { + _text = _text.Remove(_index, 1); + } + } + if (e.Key == Microsoft.Xna.Framework.Input.Keys.Right) + if (_index < _text.Length) + _index++; + if (e.KeyChar != '\0') { + _text = _text.Insert(_index, e.KeyChar.ToString()); + _index++; + } + CalculateVisibleText(); + Invalidate(); + base.OnKeyEvent(e); + } + + float caretPos = 2f; + + protected void CalculateVisibleText() + { + using(var gfx = System.Drawing.Graphics.FromImage(new System.Drawing.Bitmap(1, 1))) + { + string toCaret = _text.Substring(0, _index); + var measure = gfx.MeasureString(toCaret, _font); + caretPos = 2 + measure.Width; + while(caretPos - _textDrawOffset < 0) + { + _textDrawOffset -= 0.01f; + } + while(caretPos - _textDrawOffset > Width) + { + _textDrawOffset += 0.01f; + } + + } + } + + private float _textDrawOffset = 0; + + protected override void OnPaint(GraphicsContext gfx) + { + gfx.Clear(LoadedSkin.ControlColor.ToMonoColor()); + gfx.DrawString(_text, 2 - (int)Math.Floor(_textDrawOffset), 2, LoadedSkin.ControlTextColor.ToMonoColor(), _font); + if (IsFocusedControl) + { + //Draw caret. + + + gfx.DrawRectangle((int)(Math.Floor(caretPos) - Math.Floor(_textDrawOffset)), 2, 2, Height - 4, LoadedSkin.ControlTextColor.ToMonoColor()); + } + else + { + if (string.IsNullOrEmpty(_text)) + { + gfx.DrawString(_label, 2, 2, Color.Gray, _font); + } + } + } + } +} diff --git a/ShiftOS.Frontend/GraphicsSubsystem/GraphicsContext.cs b/ShiftOS.Frontend/GraphicsSubsystem/GraphicsContext.cs new file mode 100644 index 0000000..e2b93fe --- /dev/null +++ b/ShiftOS.Frontend/GraphicsSubsystem/GraphicsContext.cs @@ -0,0 +1,206 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using ShiftOS.Frontend.Apps; + +namespace ShiftOS.Frontend.GraphicsSubsystem +{ + public class GraphicsContext + { + public GraphicsDevice Device + { + get + { + return _graphicsDevice; + } + } + + private int _startx = 0; + private int _starty = 0; + + private int _maxwidth = 1; + private int _maxheight = 1; + + public int X + { + get + { + return _startx; + } + set + { + _startx = value; + } + } + + public int Y + { + get + { + return _starty; + } + set + { + _starty = value; + } + } + + public int Width + { + get + { + return _maxwidth; + } + set + { + _maxwidth = value; + } + } + + public int Height + { + get + { + return _maxheight; + } + set + { + _maxheight = value; + } + } + + + private GraphicsDevice _graphicsDevice; + private SpriteBatch _spritebatch; + + public GraphicsContext(GraphicsDevice device, SpriteBatch batch, int x, int y, int width, int height) + { + _graphicsDevice = device; + _spritebatch = batch; + _maxwidth = width; + _maxheight = height; + _startx = x; + _starty = y; + } + + public void Clear(Color c) + { + DrawRectangle(0, 0, _maxwidth, _maxheight, c); + } + + public void DrawLine(int x, int y, int x1, int y1, int thickness, Texture2D tex2) + { + x += _startx; + y += _starty; + int distance = (int)Vector2.Distance(new Vector2(x, y), new Vector2(x1, y1)); + float rotation = getRotation(x, y, x1, y1); + _spritebatch.Draw(tex2, new Rectangle(x, y, distance, thickness), null, Color.White, rotation, Vector2.Zero, SpriteEffects.None, 0); + } + + public void DrawLine(int x, int y, int x1, int y1, int thickness, Color color) + { + x += _startx; + y += _starty; + var tex2 = new Texture2D(_graphicsDevice, 1, 1, false, SurfaceFormat.Color); + byte[] colordata = new byte[] { color.B, color.G, color.R, color.A }; + tex2.SetData<byte>(colordata); + int distance = (int)Vector2.Distance(new Vector2(x, y), new Vector2(x1, y1)); + float rotation = getRotation(x, y, x1, y1); + _spritebatch.Draw(tex2, new Rectangle(x, y, distance, thickness), null, color, rotation, Vector2.Zero, SpriteEffects.None, 0); + } + + public void DrawRectangle(int x, int y, int width, int height, Color color) + { + x += _startx; + y += _starty; + var tex2 = new Texture2D(_graphicsDevice, 1, 1, false, SurfaceFormat.Color); + byte[] colordata = new byte[] { color.B, color.G, color.R, color.A }; + tex2.SetData<byte>(colordata); + _spritebatch.Draw(tex2, new Rectangle(x, y, width, height), color); + } + + public void DrawRectangle(int x, int y, int width, int height, Texture2D tex2) + { + x += _startx; + y += _starty; + _spritebatch.Draw(tex2, new Rectangle(x, y, width, height), Color.White); + } + + public Vector2 MeasureString(string text, System.Drawing.Font font, int wrapWidth = int.MaxValue) + { + using(var gfx = System.Drawing.Graphics.FromImage(new System.Drawing.Bitmap(1, 1))) + { + var s = gfx.SmartMeasureString(text, font, wrapWidth); + return new Vector2((float)Math.Ceiling(s.Width), (float)Math.Ceiling(s.Height)); + } + } + + public static List<TextCache> StringCaches = new List<TextCache>(); + + public void DrawString(string text, int x, int y, Color color, System.Drawing.Font font, int wrapWidth = 0) + { + x += _startx; + y += _starty; + var fontcache = StringCaches.FirstOrDefault(z => z.Text == text && z.FontFamily == font&&z.WrapWidth == wrapWidth); + if (fontcache == null) + { + Vector2 measure; + if (wrapWidth == 0) + measure = MeasureString(text, font); + else + measure = MeasureString(text, font, wrapWidth); + using(var bmp = new System.Drawing.Bitmap((int)measure.X, (int)measure.Y)) + { + using(var gfx = System.Drawing.Graphics.FromImage(bmp)) + { + var sFormat = System.Drawing.StringFormat.GenericTypographic; + sFormat.FormatFlags |= System.Drawing.StringFormatFlags.NoClip; + + gfx.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit; + + gfx.DrawString(text, font, System.Drawing.Brushes.White, new System.Drawing.RectangleF(0, 0, bmp.Width, bmp.Height), sFormat); + } + + var lck = bmp.LockBits(new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); + var data = new byte[Math.Abs(lck.Stride) * lck.Height]; + System.Runtime.InteropServices.Marshal.Copy(lck.Scan0, data, 0, data.Length); + var tex2 = new Texture2D(_graphicsDevice, bmp.Width, bmp.Height); + tex2.SetData<byte>(data); + fontcache = new TextCache(); + fontcache.Text = text; + fontcache.FontFamily = font; + fontcache.WrapWidth = wrapWidth; + fontcache.Cache = tex2; + StringCaches.Add(fontcache); + } + } + _spritebatch.Draw(fontcache.Cache, new Rectangle(x, y, fontcache.Cache.Width, fontcache.Cache.Height), color); + + + } + + private float getRotation(float x, float y, float x2, float y2) + { + float adj = x - x2; + float opp = y - y2; + float tan = opp / adj; + float res = MathHelper.ToDegrees((float)Math.Atan2(opp, adj)); + res = (res - 180) % 360; + if (res < 0) { res += 360; } + res = MathHelper.ToRadians(res); + return res; + } + } + + public class TextCache + { + public string Text { get; set; } + public System.Drawing.Font FontFamily { get; set; } + public Texture2D Cache { get; set; } + public int WrapWidth { get; set; } + } +} diff --git a/ShiftOS.Frontend/GraphicsSubsystem/UIManager.cs b/ShiftOS.Frontend/GraphicsSubsystem/UIManager.cs new file mode 100644 index 0000000..942b8b6 --- /dev/null +++ b/ShiftOS.Frontend/GraphicsSubsystem/UIManager.cs @@ -0,0 +1,483 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; +using ShiftOS.Engine; +using ShiftOS.Frontend.Desktop; +using ShiftOS.Frontend.GUI; + +namespace ShiftOS.Frontend.GraphicsSubsystem +{ + public static class UIManager + { + private static List<GUI.Control> topLevels = new List<GUI.Control>(); + public static System.Drawing.Size Viewport { get; set; } + public static GUI.Control FocusedControl = null; + + public static void LayoutUpdate() + { + foreach (var toplevel in topLevels.ToArray()) + toplevel.Layout(); + } + + public static void Animate(object owner, System.Reflection.PropertyInfo prop, double from, double to, int timeMs) + { + var t = new System.Threading.Thread(() => + { + for(int i = 0; i < timeMs; i++) + { + double value = ProgressBar.linear(i, 0, timeMs, from, to); + prop.SetValue(owner, value); + System.Threading.Thread.Sleep(1); + } + }); + t.IsBackground = true; + t.Start(); + } + + public static Dictionary<int, RenderTarget2D> TextureCaches = new Dictionary<int, RenderTarget2D>(); + + public static void DrawTArgets(SpriteBatch batch) + { + foreach(var ctrl in topLevels.ToArray()) + { + if (ctrl.Visible == true) + { + int hc = ctrl.GetHashCode(); + if (!TextureCaches.ContainsKey(hc)) + { + ctrl.Invalidate(); + continue; + } + var _target = TextureCaches[hc]; + if (ExperimentalEffects) + { + for (int i = 5; i > 0; i--) + { + batch.Draw(_target, new Rectangle(ctrl.X - i, ctrl.Y - i, ctrl.Width+(i*2), ctrl.Height+(i*2)), new Color(Color.Black, 255 / (i * 2))); + } + } + + batch.Draw(_target, new Rectangle(ctrl.X, ctrl.Y, ctrl.Width, ctrl.Height), Color.White); + } + } + } + + public static void SendToBack(Control ctrl) + { + topLevels.Remove(ctrl); + topLevels.Insert(0, ctrl); + } + + public static void DrawControlsToTargets(GraphicsDevice graphics, SpriteBatch batch, int width, int height) + { + foreach (var ctrl in topLevels.ToArray().Where(x=>x.Visible==true)) + { + RenderTarget2D _target; + int hc = ctrl.GetHashCode(); + if (!TextureCaches.ContainsKey(hc)) + { + _target = new RenderTarget2D( + graphics, + ctrl.Width, + ctrl.Height, + false, + graphics.PresentationParameters.BackBufferFormat, + DepthFormat.Depth24); + TextureCaches.Add(hc, _target); + } + else + { + _target = TextureCaches[hc]; + if(_target.Width != ctrl.Width || _target.Height != ctrl.Height) + { + _target = new RenderTarget2D( + graphics, + ctrl.Width, + ctrl.Height, + false, + graphics.PresentationParameters.BackBufferFormat, + DepthFormat.Depth24); + TextureCaches[hc] = _target; + + } + } + if (ctrl.RequiresPaint) + { + graphics.SetRenderTarget(_target); + graphics.DepthStencilState = new DepthStencilState() { DepthBufferEnable = true }; + batch.Begin(SpriteSortMode.Immediate, BlendState.NonPremultiplied, + SamplerState.LinearClamp, DepthStencilState.Default, + RasterizerState.CullNone); + graphics.Clear(Color.Transparent); + var gfxContext = new GraphicsContext(graphics, batch, 0, 0, _target.Width, _target.Height); + ctrl.Paint(gfxContext); + + graphics.SetRenderTarget(null); + TextureCaches[hc] = _target; + batch.End(); + } + } + } + + public static void AddTopLevel(GUI.Control ctrl) + { + if (!topLevels.Contains(ctrl)) + topLevels.Add(ctrl); + ctrl.Layout(); + } + + public static void InvalidateAll() + { + foreach(var ctrl in topLevels) + { + ctrl.Invalidate(); + } + } + + public static void ProcessMouseState(MouseState state) + { + foreach(var ctrl in topLevels.ToArray()) + { + ctrl.ProcessMouseState(state); + } + } + + public static void ProcessKeyEvent(KeyEvent e) + { + if (e.ControlDown && e.Key == Keys.T) + { + AppearanceManager.SetupWindow(new Apps.Terminal()); + return; + } + FocusedControl?.ProcessKeyEvent(e); + } + + private static Texture2D DesktopBackground = null; + + public static Dictionary<string, Texture2D> SkinTextures = new Dictionary<string, Texture2D>(); + + public static void ResetSkinTextures(GraphicsDevice graphics) + { + SkinTextures.Clear(); + foreach(var byteArray in SkinEngine.LoadedSkin.GetType().GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance).Where(x=>x.FieldType == typeof(byte[]))) + { + var imgAttrib = byteArray.GetCustomAttributes(false).FirstOrDefault(x => x is ImageAttribute) as ImageAttribute; + if(imgAttrib != null) + { + var img = SkinEngine.GetImage(imgAttrib.Name); + if(img != null) + { + var bmp = (System.Drawing.Bitmap)img; + var lck = bmp.LockBits(new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); + var data = new byte[Math.Abs(lck.Stride) * lck.Height]; + Marshal.Copy(lck.Scan0, data, 0, data.Length); + bmp.UnlockBits(lck); + var tex2 = new Texture2D(graphics, bmp.Width, bmp.Height); + for(int i = 0; i < data.Length; i += 4) + { + byte r = data[i]; + byte b = data[i + 2]; + if (r == 1 && b == 1 && data[i + 1] == 1) + { + data[i + 3] = 0; + } + data[i] = b; + data[i + 2] = r; + } + tex2.SetData<byte>(data); + SkinTextures.Add(imgAttrib.Name, tex2); + } + } + } + + foreach(var colorfield in SkinEngine.LoadedSkin.GetType().GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance).Where(x=>x.FieldType == typeof(System.Drawing.Color))) + { + var color = (System.Drawing.Color)colorfield.GetValue(SkinEngine.LoadedSkin); + var tex2 = new Texture2D(graphics, 1, 1); + tex2.SetData<byte>(new[] { color.R, color.G, color.B, color.A }); + SkinTextures.Add(colorfield.Name, tex2); + } + + + + } + + + public static bool ExperimentalEffects = true; + + public static Queue<Action> CrossThreadOperations = new Queue<Action>(); + internal static GraphicsDevice GraphicsDevice; + + public static void DrawBackgroundLayer(GraphicsDevice graphics, SpriteBatch batch, int width, int height) + { + if (SkinEngine.LoadedSkin == null) + SkinEngine.Init(); + graphics.Clear(SkinEngine.LoadedSkin.DesktopColor.ToMonoColor()); + if (SkinTextures.ContainsKey("desktopbackground")) + { + batch.Draw(SkinTextures["desktopbackground"], new Rectangle(0, 0, Viewport.Width, Viewport.Height), Color.White); + } + } + + public static Color ToMonoColor(this System.Drawing.Color color) + { + return new Color(color.R, color.G, color.B, color.A); + } + + internal static void StopHandling(GUI.Control ctrl) + { + if (topLevels.Contains(ctrl)) + topLevels.Remove(ctrl); + + int hc = ctrl.GetHashCode(); + if (TextureCaches.ContainsKey(hc)) + { + TextureCaches[hc].Dispose(); + TextureCaches.Remove(hc); + } + + ctrl = null; + } + } + + public class KeyEvent + { + public KeyEvent(bool control, bool alt, bool shift, Keys key) + { + ControlDown = control; + AltDown = alt; + ShiftDown = shift; + Key = key; + KeyChar = key.ToCharacter(shift); + } + + public bool ControlDown { get; private set; } + public bool AltDown { get; private set; } + public bool ShiftDown { get; set; } + public Keys Key { get; private set; } + + public char KeyChar { get; private set; } + } + + public static class KeysExtensions + { + public static char ToCharacter(this Keys key, bool shift) + { + char c = '\0'; + switch (key) + { + case Keys.Space: + c = ' '; + break; + case Keys.A: + c = 'a'; + break; + case Keys.B: + c = 'b'; + break; + case Keys.C: + c = 'c'; + break; + case Keys.D: + c = 'd'; + break; + case Keys.E: + c = 'e'; + break; + case Keys.F: + c = 'f'; + break; + case Keys.G: + c = 'g'; + break; + case Keys.H: + c = 'h'; + break; + case Keys.I: + c = 'i'; + break; + case Keys.J: + c = 'j'; + break; + case Keys.K: + c = 'k'; + break; + case Keys.L: + c = 'l'; + break; + case Keys.M: + c = 'm'; + break; + case Keys.N: + c = 'n'; + break; + case Keys.O: + c = 'o'; + break; + case Keys.P: + c = 'p'; + break; + case Keys.Q: + c = 'q'; + break; + case Keys.R: + c = 'r'; + break; + case Keys.S: + c = 's'; + break; + case Keys.T: + c = 't'; + break; + case Keys.U: + c = 'u'; + break; + case Keys.V: + c = 'v'; + break; + case Keys.W: + c = 'w'; + break; + case Keys.X: + c = 'x'; + break; + case Keys.Y: + c = 'y'; + break; + case Keys.Z: + c = 'z'; + break; + case Keys.D0: + if (shift) + c = ')'; + else + c = '0'; + break; + case Keys.D1: + if (shift) + c = '!'; + else + c = '1'; + break; + case Keys.D2: + if (shift) + c = '@'; + else + c = '2'; + break; + case Keys.D3: + if (shift) + c = '#'; + else + c = '3'; + break; + case Keys.D4: + if (shift) + c = '$'; + else + c = '4'; + break; + case Keys.D5: + if (shift) + c = '%'; + else + c = '5'; + break; + case Keys.D6: + if (shift) + c = '^'; + else + c = '6'; + break; + case Keys.D7: + if (shift) + c = '&'; + else + c = '7'; + break; + case Keys.D8: + if (shift) + c = '*'; + else + c = '8'; + break; + case Keys.D9: + if (shift) + c = '('; + else + c = '9'; + break; + case Keys.OemBackslash: + if (shift) + c = '|'; + else + c = '\\'; + break; + case Keys.OemCloseBrackets: + if (shift) + c = '}'; + else + c = ']'; + break; + case Keys.OemComma: + if (shift) + c = '<'; + else + c = ','; + break; + case Keys.OemPeriod: + if (shift) + c = '>'; + else + c = '.'; + break; + case Keys.OemQuestion: + if (shift) + c = '?'; + else + c = '/'; + break; + case Keys.OemSemicolon: + if (shift) + c = ':'; + else + c = ';'; + break; + case Keys.OemQuotes: + if (shift) + c = '"'; + else + c = '\''; + break; + case Keys.OemTilde: + if (shift) + c = '~'; + else + c = '`'; + break; + case Keys.OemMinus: + if (shift) + c = '_'; + else + c = '-'; + break; + case Keys.OemPlus: + if (shift) + c = '+'; + else + c = '='; + break; + } + if (char.IsLetter(c)) + if (shift) + c = char.ToUpper(c); + return c; + } + } +} diff --git a/ShiftOS.Frontend/Icon.bmp b/ShiftOS.Frontend/Icon.bmp Binary files differnew file mode 100644 index 0000000..2b48165 --- /dev/null +++ b/ShiftOS.Frontend/Icon.bmp diff --git a/ShiftOS.Frontend/Icon.ico b/ShiftOS.Frontend/Icon.ico Binary files differnew file mode 100644 index 0000000..7d9dec1 --- /dev/null +++ b/ShiftOS.Frontend/Icon.ico diff --git a/ShiftOS.Frontend/Infobox.cs b/ShiftOS.Frontend/Infobox.cs new file mode 100644 index 0000000..a71785b --- /dev/null +++ b/ShiftOS.Frontend/Infobox.cs @@ -0,0 +1,196 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ShiftOS.Engine; +using ShiftOS.Frontend.Desktop; +using ShiftOS.Frontend.GraphicsSubsystem; +using ShiftOS.Frontend.GUI; + +namespace ShiftOS.Frontend +{ + public class Infobox : IInfobox + { + public void Open(string title, string msg, Action callback = null) + { + var imsg = new InfoboxMessage(title, msg); + imsg.ShowPrompt(callback); + } + + public void PromptText(string title, string message, Action<string> callback, bool isPassword) + { + var imsg = new InfoboxMessage(title, message); + imsg.ShowText(callback); + } + + public void PromptYesNo(string title, string message, Action<bool> callback) + { + var imsg = new InfoboxMessage(title, message); + imsg.ShowYesNo(callback); + + } + } + + public class InfoboxMessage : GUI.Control, IShiftOSWindow + { + public InfoboxMessage(string title, string message) + { + InitializeComponent(); + lbmessage.Text = Localization.Parse(message); + Title = title; + } + + public string Title { get; private set; } + + public void OnLoad() + { + AppearanceManager.SetWindowTitle(this, Title); + } + + public void ShowPrompt(Action callback) + { + AppearanceManager.SetupDialog(this); + flyesno.Visible = false; + txtinput.Visible = false; + btnok.Visible = true; + btnok.Click += () => + { + callback?.Invoke(); + AppearanceManager.Close(this); + }; + } + + public void ShowYesNo(Action<bool> callback) + { + AppearanceManager.SetupDialog(this); + flyesno.Visible = true; + txtinput.Visible = false; + btnok.Visible = false; + btnyes.Click += () => + { + callback?.Invoke(true); + AppearanceManager.Close(this); + }; + btnno.Click += () => + { + callback?.Invoke(false); + AppearanceManager.Close(this); + }; + } + + public void ShowText(Action<string> callback) + { + AppearanceManager.SetupDialog(this); + flyesno.Visible = false; + btnok.Visible = true; + txtinput.Visible = true; + btnok.Click += () => + { + callback?.Invoke(txtinput.Text); + AppearanceManager.Close(this); + }; + } + + public void OnSkinLoad() + { + } + + public bool OnUnload() + { + return true; + } + + public void OnUpgrade() + { + } + + //NOTE: The following code is ported over from Windows Forms. + /// <summary> + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// </summary> + private void InitializeComponent() + { + this.txtinput = new TextInput(); + this.lbmessage = new TextControl(); + this.flyesno = new ItemGroup(); + this.btnyes = new Button(); + this.btnno = new Button(); + this.btnok = new Button(); + this.pbicon = new PictureBox(); + // + // txtinput + // + this.txtinput.X = 88; + this.txtinput.Y = 116; + this.txtinput.Width = 250; + this.txtinput.Height = 20; + // + // lbmessage + // + this.lbmessage.X = 85; + this.lbmessage.Y = 19; + this.lbmessage.Width = 213; + this.lbmessage.Height = 94; + this.lbmessage.Text = "label1"; + this.lbmessage.TextAlign = TextAlign.MiddleLeft; + // + // flyesno + // + this.flyesno.AutoSize = true; + this.flyesno.AddControl(this.btnyes); + this.flyesno.AddControl(this.btnno); + this.flyesno.X = 129; + this.flyesno.Y = 134; + // + // btnyes + // + this.btnyes.AutoSize = true; + this.btnyes.Text = Localization.Parse("{GEN_YES}"); + // + // btnno + // + this.btnno.AutoSize = true; + this.btnno.Text = Localization.Parse("{GEN_NO}"); + // + // btnok + // + this.btnok.AutoSize = true; + this.btnok.X = 140; + this.btnok.Y = 140; + this.btnok.Text = Localization.Parse("{GEN_OK}"); + // + // pbicon + // + this.pbicon.X = 14; + this.pbicon.Y = 19; + this.pbicon.Width = 64; + this.pbicon.Height = 64; + this.pbicon.Image = Properties.Resources.justthes.ToTexture2D(UIManager.GraphicsDevice); + this.pbicon.ImageLayout = ImageLayout.Stretch; + // + // Dialog + // + this.Width = 341; + this.Height = 157; + this.AddControl(pbicon); + this.AddControl(btnok); + this.AddControl(flyesno); + this.AddControl(lbmessage); + + this.Layout(); + } + + + private Control panel1; + private TextControl lbmessage; + private ItemGroup flyesno; + private Button btnyes; + private Button btnno; + private Button btnok; + private PictureBox pbicon; + private TextInput txtinput; + + } +} diff --git a/ShiftOS.Frontend/MonoGameLanguageProvider.cs b/ShiftOS.Frontend/MonoGameLanguageProvider.cs new file mode 100644 index 0000000..9010cfa --- /dev/null +++ b/ShiftOS.Frontend/MonoGameLanguageProvider.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; +using ShiftOS.Engine; + +namespace ShiftOS.Frontend +{ + public class MonoGameLanguageProvider : ILanguageProvider + { + private string resourcesPath + { + get + { + return System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "ShiftOS", "languages"); + } + } + + public string[] GetAllLanguages() + { + if (!System.IO.Directory.Exists(resourcesPath)) + { + System.IO.Directory.CreateDirectory(resourcesPath); + } + return System.IO.Directory.GetFiles(resourcesPath).Where(x => x.ToLower().EndsWith(".lang")).ToArray(); + + } + + public string GetCurrentTranscript() + { + string lang = Objects.UserConfig.Get().Language; + if (string.IsNullOrWhiteSpace(lang)) + { + lang = "english"; + var conf = Objects.UserConfig.Get(); + conf.Language = lang; + System.IO.File.WriteAllText("servers.json", JsonConvert.SerializeObject(conf, Formatting.Indented)); + } + string foundPath = GetAllLanguages().FirstOrDefault(x => x.ToLower().EndsWith(lang + ".lang")); + //Update the english file. + System.IO.File.WriteAllText(System.IO.Path.Combine(resourcesPath, "english.lang"), Properties.Resources.strings_en); + //Update the french language pack. + System.IO.File.WriteAllText(System.IO.Path.Combine(resourcesPath, "french.lang"), Properties.Resources.strings_fr); + + if (!System.IO.File.Exists(foundPath)) + { + lang = "english"; + var conf = Objects.UserConfig.Get(); + conf.Language = lang; + System.IO.File.WriteAllText("servers.json", JsonConvert.SerializeObject(conf, Formatting.Indented)); + return Properties.Resources.strings_en; + } + else + { + return System.IO.File.ReadAllText(foundPath); + } + + } + + public string GetLanguagePath() + { + var lang = Objects.UserConfig.Get().Language; + if (string.IsNullOrWhiteSpace(lang) || !System.IO.File.Exists(System.IO.Path.Combine(resourcesPath, lang + ".lang"))) + { + lang = "english"; + var conf = Objects.UserConfig.Get(); + conf.Language = lang; + System.IO.File.WriteAllText("servers.json", JsonConvert.SerializeObject(conf, Formatting.Indented)); + System.IO.File.WriteAllText(System.IO.Path.Combine(resourcesPath, lang + ".lang"), Properties.Resources.strings_en); + } + return GetAllLanguages().FirstOrDefault(x => x.ToLower().EndsWith(lang + ".lang")); + } + + public List<string> GetJSONTranscripts() + { + var strings = new List<string>(); + foreach (var path in GetAllLanguages()) + strings.Add(System.IO.File.ReadAllText(path)); + return strings; + } + + public void WriteDefaultTranscript() + { + if (!System.IO.Directory.Exists(resourcesPath)) + System.IO.Directory.CreateDirectory(resourcesPath); + + //Update the english file. + System.IO.File.WriteAllText(System.IO.Path.Combine(resourcesPath, "english.lang"), Properties.Resources.strings_en); + //Update the french language pack. + System.IO.File.WriteAllText(System.IO.Path.Combine(resourcesPath, "french.lang"), Properties.Resources.strings_fr); + } + + public void WriteTranscript() + { + System.IO.File.WriteAllText(GetLanguagePath(), GetCurrentTranscript()); + } + + private string getDefault() + { + return Properties.Resources.strings_en; + } + + } +} diff --git a/ShiftOS.Frontend/Program.cs b/ShiftOS.Frontend/Program.cs new file mode 100644 index 0000000..031b2ab --- /dev/null +++ b/ShiftOS.Frontend/Program.cs @@ -0,0 +1,20 @@ +using System; + +namespace ShiftOS.Frontend +{ + /// <summary> + /// The main class. + /// </summary> + public static class Program + { + /// <summary> + /// The main entry point for the application. + /// </summary> + [STAThread] + static void Main() + { + using (var game = new ShiftOS()) + game.Run(); + } + } +} diff --git a/ShiftOS.Frontend/Properties/AssemblyInfo.cs b/ShiftOS.Frontend/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..f25b1d7 --- /dev/null +++ b/ShiftOS.Frontend/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ShiftOS.Frontend")] +[assembly: AssemblyProduct("ShiftOS.Frontend")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("13e2a4c8-a6cc-405b-a4ec-5d39531993c2")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/ShiftOS.Frontend/Properties/Resources.Designer.cs b/ShiftOS.Frontend/Properties/Resources.Designer.cs new file mode 100644 index 0000000..aaca596 --- /dev/null +++ b/ShiftOS.Frontend/Properties/Resources.Designer.cs @@ -0,0 +1,176 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +//------------------------------------------------------------------------------ + +namespace ShiftOS.Frontend.Properties { + using System; + + + /// <summary> + /// A strongly-typed resource class, for looking up localized strings, etc. + /// </summary> + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// <summary> + /// Returns the cached ResourceManager instance used by this class. + /// </summary> + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ShiftOS.Frontend.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// <summary> + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// </summary> + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap cursor_9x_pointer { + get { + object obj = ResourceManager.GetObject("cursor_9x_pointer", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap justthes { + get { + object obj = ResourceManager.GetObject("justthes", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// <summary> + /// Looks up a localized string similar to [ + /////Virus Scanner Grades + /// { + /// Name: "Virus Scanner Grade 2", + /// Description: "Update the Virus Scanner database to include threatlevel 2 viruses.", + /// Dependencies: "virus_scanner", + /// Category: "Virus Scanner", + /// Cost: 75 + /// }, + /// { + /// Name: "Virus Scanner Grade 3", + /// Description: "Update the Virus Scanner database to include threatlevel 3 viruses.", + /// Dependencies: "virus_scanner_grade_2", + /// Category: "Virus Scanner", + /// Cost: 150 + /// }, + /// { + /// Name: "Virus Scanner Grade 4", + /// Description: "Update the [rest of string was truncated]";. + /// </summary> + internal static string Shiftorium { + get { + return ResourceManager.GetString("Shiftorium", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to { + /// "{SUBMIT}":"Bestätigen", + /// + ///"{TERMINAL_TUTORIAL_1}":"Wilkommen zum ShiftOS Terminal. Hier wirst du die meiste Zeit in ShiftOS verbringen. + /// + ///Eine kurze Erklärung wie du das Terminal benutzt lautet wiefolgt. Du kannst das command 'sos.help' benutzen um eine Liste aller commands aufzurufen. Schreib es + ///einfach in das Terminal und drücke <enter> um alle commands anzuzeigen. + /// + ///Commands können mit argumenten versehen werden, indem du ein key-value Paar in einem {} Block hinter dem command angibst. Zum Be [rest of string was truncated]";. + /// </summary> + internal static string strings_de { + get { + return ResourceManager.GetString("strings_de", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to /* + /// * ShiftOS English Language Pack + /// * + /// * This is the default language pack distributed within the game. + /// */ + /// + ///{ + /// //General strings + /// //These strings can be used anywhere in the UI where language context isn't necessary. + /// "{GEN_PROGRAMS}": "Programs", + /// "{GEN_COMMANDS}": "Commands", + /// "{GEN_OBJECTIVES}": "Objectives", + /// "{GEN_CURRENTPROCESSES}": "Current processes", + /// "{GEN_WELCOME}": "Welcome to ShiftOS.", + /// "{GEN_SYSTEMNAME}": "System name", + /// "{GEN_PASSWORD}": "Password", + /// "{GEN_LPROMPT [rest of string was truncated]";. + /// </summary> + internal static string strings_en { + get { + return ResourceManager.GetString("strings_en", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to /* + /// * ShiftOS French Language Pack + /// * + /// * This is a default language pack distributed within the game. + /// */ + /// + ///{ + /// //General strings + /// //These strings can be used anywhere in the UI where language context isn't necessary. + /// "{GEN_PROGRAMS}": "Les programmes", + /// "{GEN_COMMANDS}": "Les ordres", + /// "{GEN_OBJECTIVES}": "Les objectifs", + /// "{GEN_CURRENTPROCESSES}": "Les procèdures actuelles", + /// "{GEN_WELCOME}": "Bienvenue au ShiftOS.", + /// "{GEN_SYSTEMNAME}": "Nom de système", + /// "{GEN_PASSWORD}": "Mot de [rest of string was truncated]";. + /// </summary> + internal static string strings_fr { + get { + return ResourceManager.GetString("strings_fr", resourceCulture); + } + } + } +} diff --git a/ShiftOS.Frontend/Properties/Resources.resx b/ShiftOS.Frontend/Properties/Resources.resx new file mode 100644 index 0000000..1a04f46 --- /dev/null +++ b/ShiftOS.Frontend/Properties/Resources.resx @@ -0,0 +1,139 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> + <data name="cursor_9x_pointer" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\Resources\cursor_9x_pointer.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="justthes" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\Resources\justthes.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="Shiftorium" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\Resources\Shiftorium.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252</value> + </data> + <data name="strings_de" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\Resources\strings_de.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252</value> + </data> + <data name="strings_en" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\Resources\strings_en.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252</value> + </data> + <data name="strings_fr" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\Resources\strings_fr.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252</value> + </data> +</root>
\ No newline at end of file diff --git a/ShiftOS.Frontend/Resources/Shiftorium.txt b/ShiftOS.Frontend/Resources/Shiftorium.txt new file mode 100644 index 0000000..45a7b1f --- /dev/null +++ b/ShiftOS.Frontend/Resources/Shiftorium.txt @@ -0,0 +1,1089 @@ +[ +//Virus Scanner Grades + { + Name: "Virus Scanner Grade 2", + Description: "Update the Virus Scanner database to include threatlevel 2 viruses.", + Dependencies: "virus_scanner", + Category: "Virus Scanner", + Cost: 75 + }, + { + Name: "Virus Scanner Grade 3", + Description: "Update the Virus Scanner database to include threatlevel 3 viruses.", + Dependencies: "virus_scanner_grade_2", + Category: "Virus Scanner", + Cost: 150 + }, + { + Name: "Virus Scanner Grade 4", + Description: "Update the Virus Scanner database to include threatlevel 4 viruses.", + Dependencies: "virus_scanner_grade_3", + Category: "Virus Scanner", + Cost: 225 + }, +// SCREENSAVER + { + Name: "Screensavers", + Cost: 750, + Description: "Like to leave your PC idle for long periods of time? Save some energy and keep your screen from being tired by hiding the desktop behind a black screen with an image on it.", + Dependencies: "desktop", + Category: "Enhancements", + }, + { + Name: "Icon Manager", + Cost: 450, + Description: "This tool allows you to add and edit application icons within ShiftOS for the small prive of 450 Codepoints!", + Dependencies: "skinning", + Category: "Application" + }, + { + Name: "Shift States", + Cost: 325, + Description: "It's time to make ShiftOS more responsive. This upgrade adds various state-specific settings for window borders, title buttons, and more!", + Category: "Customizatuib & Scripting", + Dependencies: "shift_titlebar;shift_title_buttons;shift_panel_buttons;shift_window_borders", + }, + { + Name: "AL Icon Manager", + Costs: 150, + Description: "Add an App Launcher entry for the Icon Manager.", + Dependencies: "icon_manager;app_launcher", + Category: "Customization" + }, + { + Name: "Shift Progress Bar", + Cost: 150, + Description: "Want to customize the look of all ShiftOS Progress Bars? Buy this upgrade today and you'll get the ability to set the foreground and background color of the progress bar, and many more things!.", + Dependencies: "shifter;shift_buttons", + Category: "Customization" + }, + { + Name: "Shift Buttons", + Cost: 150, + Description: "Want to customize the look of all ShiftOS buttons? This Shifter upgrade gives you a new \"Buttons\" category in the System category to do just that.", + Dependencies: "shifter", + Category: "Customization" + }, + { + Name: "GUI Based Login Screen", + Cost: 500, + Description: "Tired of using the text-based login screen in ShiftOS? Well, we have a functioning window manager, and a functioning desktop, why not use these tools to create a functioning and awesome-looking login screen?", + Dependencies: "skinning;desktop;wm_free_placement", + Category: "Kernel & System" + }, + { + Name: "Shift Screensavers", + Cost: 100, + Description: "This Shifter upgrade will allow you to customize the screensaver.", + Dependencies: "screensavers;shifter", + Category: "Customization" + }, + { + Name: "Resizable Windows", + Cost: 250, + Dependencies: "draggable_windows", + Description: "We can drag windows around, which is nice, but what if you want to give a window more working space? This upgrade allows you to resize windows.", + Category: "Enhancements" + }, + +// CALCULATOR UPGRADES + { + Name: "Calculator", + Cost: 1000, + Dependencies: "wm_free_placement;desktop", + Description: "Crazy math problems getting you down? Well, this calculator will take care of that!", + Category: "Applications" + }, + { + Name: "AL Calculator", + Cost: 150, + Dependencies: "calculator;app_launcher", + Description: "Add an App Launcher Entry for the Calculator!", + Category: "GUI", + }, + { + Name: "Calc Equals Button", + Cost: 600, + Dependencies: "calculator", + Category: "Enhancements", + Description: "Right now, you can only type numbers, but this equals button opens the door to solving equations!" + }, + { + Name: "Calc Decimal Button", + Cost: 600, + Dependencies: "calculator", + Category: "Enhancements", + Description: "Whole numbers can get boring. With this button, you can type decimal numbers!" + }, + { + Name: "Calc Plus Button", + Cost: 700, + Dependencies: "calc_equals_button", + Category: "Enhancements", + Description: "With this extra button, your calculator can now do addition problems!" + }, + { + Name: "Calc Minus Button", + Cost: 700, + Dependencies: "calc_equals_button", + Category: "Enhancements", + Description: "With this extra button, your calculator can now do subtraction problems!" + }, + { + Name: "Calc Multiply Button", + Cost: 800, + Dependencies: "calc_plus_button", + Category: "Enhancements", + Description: "You can add numbers together, but it must be tiring to add the same number over and over. This multiplication button will make it easier for you!" + }, + { + Name: "FS Copy", + Cost: 75, + Dependencies: "file_skimmer", + Description: "It is cool to be able to browse your files, but what if you need a new copy of a file? This upgrade adds a button to the File Skimmer that allows you to copy single files to new locations.", + Category: "Enhancements" + }, + { + Name: "FS Move", + Cost: 75, + Dependencies: "file_skimmer", + Description: "Want to move files from one place to another? This upgrade adds a button to the File Skimmer that does just that!", + Category: "Enhancements" + }, + { + Name: "FS Copy Folder", + Cost: 100, + Dependencies: "fs_copy", + Description: "You can copy single files, but what if you have a lot of files in a folder that you need to get copied over? This upgrade adds the ability to copy a folder to a new location in the File Skimmer.", + Category: "Enhancements" + }, + { + Name: "FS Move Folder", + Cost: 100, + Dependencies: "fs_move", + Description: "Being able to move single files is great, but it takes time to move all the files in a folder to a new location. This upgrade will cut down on that time!", + Category: "Enhancements" + }, + { + Name: "Calc Divide Button", + Cost: 800, + Dependencies: "calc_multiply_button", + Category: "Enhancements", + Description: "You can multiply, but what about reversing multiplication? This divide button will sort that out!" + }, + { + Name: "Calc Clear Button", + Cost: 750, + Dependencies: "calc_equals_button", + Category: "Enhancements", + Description: "Typed the wrong number? No worries! With this Clear button, you can clear the number field, without messing up the equation you're trying to solve!" + }, + { + Name: "Calc CE Button", + Cost: 750, + Dependencies: "calc_clear_button", + Category: "Enhancements", + Description: "Wanna start all over with a new equation? With this CE (Clear Everything) button, you get rid of not only the numbers in the number field, but also the equation!" + }, + { + Name: "AL Notifications", + Cost: 150, + Dependencies: "app_launcher", + Category: "GUI", + Description: "Want to open the Notifications application from within the App Launcher? This upgrade is for you." + }, + + // SHIFTLETTERS AND WORDLISTS + { + Name: "AL ShiftLetters", + Cost: 150, + Dependencies: "app_launcher;shiftletters", + Category: "GUI", + Description: "This upgrade allows you to find ShiftLetters in your App Launcher." + }, + { + Name: "SL Contributors Wordlist", + Cost: 250, + Dependencies: "shiftletters", + Category: "Enhancements", + Description: "This nice wordlist lets you find out the people who contributed to the development of ShiftOS!" + }, + { + Name: "SL Operating Systems Wordlist", + Cost: 500, + Dependencies: "shiftletters", + Category: "Enhancements", + Description: "Know a lot about computer operating systems? This upgrade adds a wordlist to ShiftLetters, full of various Linux distros, Windows codenames and other OS names. All for the low price of 500 Codepoints! It's an incredible value but it's true! Upgrade today... except out of ShiftOS!" + }, + { + Name: "Panel Notifications", + Cost: 150, + Description: "It's good to know what time it is, but how about knowing how many notifications you have? After all, notifications are a great way to tell what's going on! This upgrade adds a button that displays how many notifications you have. If you click it, you will open the Notifications app!", + Category: "GUI", + Dependencies: "desktop_clock_widget" + }, + { + Name: "Audio Volume", + Cost: 80, + Category: "Kernel & System", + Description: "Want to adjust the volume of ShiftOS's audio? This upgrade will let you." + }, + + //SHIFTLOTTO UPGRADES + { + Name: "ShiftLotto", + Cost: 200, + Dependencies: null, + Category: "Applications", + Description: "Are you feeling lucky? Spend some money on this upgrade! If you have any left, go ahead and bet your money in ShiftLotto!" + }, + { + Name: "AL ShiftLotto", + Cost: 150, + Dependencies: "app_launcher;shiftlotto", + Category: "GUI", + Description: "This upgrade allows you to find ShiftLotto in your App Launcher." + }, + //STORY-DRIVEN AL UPGRADES + { + Name: "AL Shiftnet", + Cost: 150, + Dependencies: "app_launcher;victortran_shiftnet", + Category: "GUI", + Description: "This upgrade puts a Shiftnet entry into your app launcher - you know, for those times when you just feel like surfing the net." + }, + { + Name: "AL Installer", + Cost: 150, + Dependencies: "installer;app_launcher", + Category: "GUI", + Description: "Got a new .stp file and want to set it up, from your App Launcher? This upgrade is for you! It adds an AL entry for the Installer." + }, + { + Name: "AL Downloader", + Cost: 150, + Dependencies: "app_launcher;downloader", + Category: "GUI", + Description: "The Downloader is an easy, central way to check all your downloads' progress. This upgrade adds an app launcher entry for it." + }, + // COLOR DEPTH AND DITHERING + + { + Name: "Color Depth Dithering", + Cost: 1000, + Category: "Device Drivers", + Description: "Right now, if you try to display images on the screen, with a low color depth like we have, the image will be totally unrecognizable! With this upgrade, we can adapt a simple 1-dimensional dithering algorithm into the video driver to hopefully smooth out the transition between colors.", + }, + { + Name: "Color Depth Floyd-Steinberg Dithering", + Cost: 2000, + Description: "So your images look... alright... with the new dithering algorithm, but let's take things even further and get rid of the jagged lines in the image using a 2-dimensional algorithm called the Floyd-Steinberg algorithm. It'll sure make things look better.", + Category: "Device Drivers", + Dependencies: "color_depth_dithering" + }, + { + Name: "Shift Advanced App Launcher", + Cost: 150, + Description: "So you got yourself one of those fancy Advanced App Launchers. Well, it ain't so advanced if it can't be customized! This upgrade will add some settings to the Shifter so you can customize the Advanced App Launcher and its behaviour.", + Dependencies: "advanced_app_launcher;shifter", + Category: "Customization" + }, + { + Name: "Color Depth 2 bits", + Cost: 2000, + Category: "Device Drivers", + Description: "We can only display black and white - 0 or 1 in binary. Let's take it even further by adding an extra bit to our binary notation - making black, white, dark gray and light gray possible!", + }, + { + Name: "Color Depth 4 bits", + Cost: 4000, + Description: "4 colors is nice - but let's take it even further. With our dithering algorithm in place, let's make our images even smoother by giving a 16-color palette to the video driver!", + Category: "Device Drivers", + Dependencies: "color_depth_2_bits;color_depth_dithering" + }, + { + Name: "Color Depth 6 bits", + Cost: 6000, + Description: "Let's extend our color range into the depths of 6 bits! This'll make up to 64 different shades of gray possible! We're getting even closer to modern-day color...", + Category: "Device Drivers", + Dependencies: "color_depth_4_bits" + }, + { + Name: "Color Depth 8 bits", + Cost: 8000, + Description: "What do you get when you bite your food? You get perhaps a yummy taste. What do you get when you take a byte of memory? 256 shades of gray, of course!", + Category: "Device Drivers", + Dependencies: "color_depth_6_bits" + }, + { + Name: "Color Depth 16 bits", + Cost: 16000, + Description: "If we were just given another byte... we could divide the two up into three channels and allow mixing and matching of the channels to create colors other than gray! Let's do it.", + Category: "Device Drivers", + Dependencies: "color_depth_8_bits" + }, + { + Name: "AL Widget Manager", + Cost: 125, + Description: "Desktop Widgets are a huge advancement in ShiftOS technology, allowing access to system information, and quick control of your system, without even opening a window. However, we still need to be able to modify each widget. This upgrade adds an App Launcher entry for the Desktop Widget Manager.", + Dependencies: "app_launcher;desktop_widgets", + Category: "GUI" + }, + { + Name: "FS Delete", + Cost: 75, + Description: "Got some files that you want to get rid of? This upgrade adds a button to the File Skimmer for doing just that.", + Dependencies: "file_skimmer", + Category: "Enhancements", + }, + { + Name: "FS Recursive Delete", + Cost: 100, + Description: "Deleting files is great, but what if you have an entire folder you need to get rid of? This upgrade allows the deletion of folders and all files and folders inside them. Just, don't delete your system folder!", + Dependencies: "fs_delete", + Category: "Enhancements" + }, + { + Name: "Color Depth 24 Bits", + Cost: 24000, + Description: "Having actual color is nice for our images as we can truly see detail in our images - but if we had a third byte, each channel could have up to 256 values - adding up to almost 17 million different colors! Our eyes can't even distinguish that many.", + Category: "Device Drivers", + Dependencies: "color_depth_16_bits" + }, + { + Name: "AL MUD Control Centre", + Cost: 150, + Dependencies: "mud_control_centre;app_launcher", + Category: "Device Drivers", + Description: "Want to access your MUD profile, legions, jobs and shops, but don't want to open your Terminal? This upgrade is for you!" + }, + { + Name: "Kernel Coherence", + Cost: 10000, + Dependencies: "wm_free_placement", + Category: "Device Drivers", + Description: "With the free placement upgrade, you can place windows of any size anywhere on the desktop, which means theoretically you could add kernel coherence between ShiftOS and another GUI-based operating system and run their applications inside ShiftOS. This upgrade unlocks that.", + }, + { + Name: "WM 4 Windows", + Cost: 150, + Description: "Display up to 4 simultaneous windows on-screen in a 2x2 grid.", + Category: "Enhancements", + Dependencies: "window_manager" + }, + { + Name: "Virus Scanner", + Cost: 2000, + Description: "Being inside the multi-user domain comes with many risks, one of which being viruses. The Virus Scanner can mitigate this threat by allowing you to scan the files on your system for any viruses and delete them for you.", + Category: "Applications", + Dependencies: "mud_fundamentals;file_skimmer" + }, + { + Name: "AL Virus Scanner", + Cost: 150, + Description: "Add an App Launcher entry for the Virus Scanner.", + Category: "GUI", + Dependencies: "virus_scanner;app_launcher" + }, + { + Name: "WM Panel Buttons", + Cost: 200, + Description: "Sometimes it's useful to have a list of windows that are open on your system so you can easily switch between them.", + Category: "GUI", + Dependencies: "desktop;wm_unlimited_windows" + }, + { + Name: "AL Skin Loader", + Cost: 150, + Description: "Buy this upgrade to add an entry for the Skin Loader to the App Launcher.", + Category: "GUI", + Dependencies: "app_launcher;skinning" + }, + { + Name: "Shift Panel Buttons", + Cost: 150, + Description: "Want to customize your panel buttons? This Shifter category is for you!", + Category: "Customization", + Dependencies: "wm_panel_buttons" + }, + { + Name: "TextPad Lua Support", + Cost: 450, + Description: "Use TextPad to write Lua scripts!", + Category: "Enhancements", + Dependencies: "textpad;file_skimmer", + }, + { + Name: "App Launcher", + Cost: 7500, + Description: "It may be expensive, but having an easy-access menu to all your apps is very valuable.", + Category: "GUI", + Dependencies:"desktop;wm_unlimited_windows" + }, + { + Name: "Format Editor Optional Text", + Cost: 150, + Description: "Allows you to add an optional text to commands", + Category: "Enhancements", + Dependencies: "format_editor" + }, + { + Name: "Format Editor Regex", + Cost: 150, + Description: "Allows you to customize your commands with regexes.", + Category: "Enhancements", + Dependencies: "format_editor_optional_text" + }, + { + Name: "Format Editor Syntax Highligting", + Cost: 150, + Description: "Allows you to give color to commands as the user types them.", + Category: "Enhancements", + Dependencies: "format_editor_regex" + }, + { + Name: "AL Format Editor", + Cost: 150, + Description: "Add a launcher item for the Format Editor.", + Category: "GUI", + Dependencies: "format_editor;app_launcher" + }, + { + Name: "Textpad", + Cost: 2500, + Description: "\"Write, save and open a text document.\"", + Category: "Applications", + Dependencies: "file_skimmer" + }, + { + Name: "Shifter", + Cost: 10000, + Description: "Tired of the green and black look that is ShiftOS's default skin? Use the Shifter to shift it your way.", + Category: "Applications", + Dependencies: "desktop;wm_unlimited_windows", + }, + { + Name: "AL Shifter", + Cost: 150, + Description: "Launch the Shifter from the app launcher.", + Category: "GUI", + Dependencies: "app_launcher;shifter" + }, + { + Name: "AL Pong", + Cost: 150, + Description: "Launch Pong from the app launcher.", + Category: "GUI", + Dependencies: "app_launcher" + }, + { + Name: "AL Textpad", + Cost: 150, + Description: "Write, save and open text documents from the App Launcher.", + Category: "GUI", + Dependencies:"app_launcher;textpad" + }, + { + Name: "AL File Skimmer", + Cost: 150, + Description: "Open the File Skimmer from your App Launcher.", + Category: "GUI", + Dependencies:"app_launcher;file_skimmer" + }, + { + Name: "WM Free Placement", + Cost: 2000, + Description: "Disable the grid system and allow windows to be freely positioned, moved, and overlapped.", + Category: "Enhancements", + Dependencies: "wm_4_windows" + }, + { + Name: "Desktop", + Cost: 9000, + Description: "Use a fully customizable desktop in place of the terminal to control ShiftOS.", + Category: "GUI", + Dependencies: "window_manager" + }, + { + Name: "App Icons", + Cost: 400, + Description: "So you have a titlebar, well, let's add an icon to it to hopefully make it easier to tell which app is which.", + Category: "GUI", + Dependencies: "wm_titlebar;skinning" + }, + { + Name: "Close command", + Cost: 150, + Description: "Add a win.close script to allow you to close windows.", + Category: "Kernel & System", + Dependencies: "mud_fundamentals", + }, + { + Name: "WM Unlimited Windows", + Cost: 5000, + Description: "Break the limit of windows that can be run. Perfect for high-maintenance tasks.", + Category: "Enhancements", + Dependencies: "wm_free_placement;close_command" + }, + { + Name: "Minimize Command", + Cost: 1250, + Description: "Use the win.mini{id} command to minimize/restore windows.", + Category: "Kernel & System", + Dependencies: "useful_panel_buttons" + }, + { + Name: "Useful Panel Buttons", + Cost: 250, + Description: "Minimize and restore windows by clicking their Panel Button!", + Category: "Enhancements", + Dependencies: "desktop;wm_panel_buttons" + }, + { + Name: "Maximize Command", + Cost: 1250, + Description: "Use the win.max{id} command to maximize windows.", + Category: "Kernel & System", + Dependencies: "wm_titlebar;desktop;wm_free_placement" + }, + { + Name: "Close Button", + Cost: 1000, + Description: "Add a close button to the titlebar to easily close applications.", + Category: "GUI", + Dependencies: "wm_titlebar;close_command" + }, + { + Name: "Minimize Button", + Cost: 1000, + Description: "Minimize windows using a button on the titlebar", + Category: "GUI", + Dependencies: "wm_titlebar;minimize_command" + }, + { + Name: "Shiftorium Bulk Buy", + Cost: 2000, + Category: "Enhancements", + Description:"Tired of typing shiftorium.buy{} all the time? This upgrade will add a bulk buy command which allows you to specify a comma-separated list of upgrades to buy." + }, + { + Name: "Shiftorium GUI Bulk Buy", + Cost: 3000, + Category: "GUI", + Description: "Tired of the repetition of selecting an upgrade and hitting \"Buy\" a hundred times when binging? Using the bulkbuy command, we can make the Shiftorium GUI allow you to buy in bulk!", + Dependencies: "shiftorium_gui;shiftorium_bulk_buy" + }, + { + Name: "File Skimmer", + Cost: 500, + Description: "View the files on your computer using File Skimmer.", + Category: "Applications", + Dependencies: null + }, + { + Name: "Maximize Button", + Cost: 500, + Description: "Maximize windows using a button on the titlebar", + Category: "GUI", + Dependencies: "wm_titlebar;maximize_command" + }, + { + Name: "Clock", + Cost: 100, + Description: "Adds a script that shows the amount of seconds that have passed since Midnight. Use 'sys.clock' to activate it.", + Category: "Applications", + Dependencies: "mud_fundamentals" + }, + { + Name: "WM Titlebar", + Cost: 250, + Description: "Display a title on each window.", + Category: "GUI", + Dependencies: "window_manager" + }, + { + Name: "Clock Minutes", + Cost: 250, + Description: "Upgrade the sys.clock command to show minutes since midnight with a {type:\"m\"} argument.", + Category: "Enhancements", + Dependencies: "clock" + }, + { + Name: "Clock Hours", + Cost: 225, + Description: "Upgrade the sys.clock command to show hours since midnight with a {type:\"h\"} argument.", + Category: "Enhancements", + Dependencies: "clock_minutes" + }, + { + Name: "Clock AM and PM", + Cost: 75, + Description: "Change the clock to be 12-hour based, showing whether the current time is ante-meridiem or post-meridiem.", + Category: "Enhancements", + Dependencies: "clock_hours", + }, + { + Name: "Full Precision Time", + Cost: 500, + Description: "Show full-precision time by default when using sys.clock.", + Category: "Enhancements", + Dependencies: "clock_am_and_pm" + }, + { + Name: "Desktop Clock Widget", + Cost: 1000, + Description: "Add a widget to the desktop which shows the results of sys.clock as text on the desktop.", + Category: "GUI", + Dependencies: "clock;desktop" + }, + { + Name: "App Launcher Categories", + Cost: 1000, + Dependencies: "app_launcher", + Category: "Enhancements", + Description: "Is your App Launcher getting full of items? Perhaps a little too full? Are you having trouble finding things? This upgrade should help - it'll sort all App Launcher items into categories for you." + }, + { + Name: "Draggable windows", + Cost: 400, + Description: "Allows you to drag windows around with the mouse using the title bar.", + Category: "Enhancements", + Dependencies: "wm_titlebar;wm_free_placement" + }, + { + Name: "Window Manager", + Cost: 100, + Description: "Allows you to run two windows simultaneously within ShiftOS.", + Category: "Kernel & System", + Dependencies: "mud_fundamentals" + }, + { + Name: "Pong Upgrade", + Cost: 4000, + Description: "This upgrade makes pong double the codepoints you get from it so you can spend less time grinding!", + Category: "Enhancements", + Dependencies: "mud_fundamentals;window_manager" + }, + { + Name: "Pong Upgrade 2", + Cost: 8000, + Description: "So you lost in pong, it must be sad to lose all the codepoints you've gained. With this upgrade you can save 1 percent of the loss, so at least you get something for losing!", + Category: "Enhancements", + Dependencies: "mud_fundamentals;window_manager;pong_upgrade" + }, + { + Name: "Audio Player AL", + Cost: 150, + Description: "Just another app launcher, making it easier to listen to your favorite songs!", + Category: "GUI", + Dependencies: "desktop;wm_free_placement;audio_player" + }, + + //SHIFTER SUBCATEGORIES + + { + Name: "Shift Titlebar", + Cost: 200, + Description: "Customize the Titlebar within the Shifter.", + Category: "Customization", + Dependencies: "shifter;wm_titlebar" + }, + { + Name: "Shift Title Text", + Cost: 200, + Description: "Title text looking boring? This upgrade lets you customize the font, color, and position of the Title Text.", + Category: "Customization", + Dependencies: "shift_titlebar" + }, + { + Name: "Shift Window Borders", + Cost: 200, + Description: "Want to customize the look of the ShiftOS window borders? Buy this upgrade and you can customize the color and thickness of the borders.", + Category: "Customization", + Dependencies: "shifter" + }, + { + Name: "Shift Desktop Panel", + Cost: 200, + Description: "Not liking your desktop panel the way it is? Buy this upgrade to allow you to change the color, height, and position of the desktop panel.", + Category: "Customization", + Dependencies: "shifter;desktop" + }, + { + Name: "Shift App Launcher", + Cost: 200, + Description: "You've made your desktop panel look very nice, but your app launcher looks kinda out of place. This upgrade will fix that, allowing you to change the position, size, and appearance of the app launcher button.", + Category: "Customization", + Dependencies: "shift_desktop_panel;app_launcher" + }, + { + Name: "Shift Panel Clock", + Cost: 200, + Dependencies: "shift_desktop_panel;desktop_clock_widget", + Category: "Customization", + Description: "That clock is very simple - let's shift it! This upgrade allows you to customize the font and color of the panel clock." + }, + { + Name: "Shift Title Buttons", + Cost: 200, + Dependencies: "close_button;minimize_button;maximize_button;shift_titlebar", + Category: "Customization", + Description: "Those title buttons look very similar and primitive - with this upgrade you can change the size, position, and color of each button." + }, + + //SKINNING STUFF + + { + Name: "Skinning", + Cost: 10000, + Description: "It may be expensive, but with this upgrade, you can break the limitations of using just solid colors and gradients for your skin and start using images!", + Category: "Customization", + Dependencies: "shifter" + }, + + + //ARTPAD + { + Name: "Artpad", + Cost: 7500, + Category: "Applications", + Description: "ArtPad is a very extensible tool that allows you to draw images within ShiftOS. Buy this upgrade to gain access to it through win.open{}!", + Dependencies: "color_depth_8_bits" + }, + { + Name: "AL Artpad", + Cost: 150, + Description: "Add an App Launcher Entry for Artpad!", + Category: "GUI", + Dependencies: "artpad;app_launcher" + }, + + + + + //ARTPAD PIXEL LIMITS + + { + Name: "Artpad Pixel Limit 4", + Cost: 100, + Dependencies: "artpad", + Category: "Enhancements", + Description: "Having ArtPad is great, but there's not much you can draw with only 2 pixels. Buy this upgrade to increase the breathing room your imagination can have." + }, + { + Name: "Artpad Pixel Limit 8", + Cost: 150, + Dependencies: "artpad_pixel_limit_4", + Category: "Enhancements", + Description: "With a 4 pixel limit, you can do some simple patterns and such, but it's still not great. Buy this upgrade to double the pixel limit and add even more possibilities!" + }, + { + Name: "Artpad Pixel Limit 16", + Cost: 200, + Dependencies:"artpad_pixel_limit_8", + Category: "Enhancements", + Description: "Now we can have 8-pixel images, but we still can't do much more than simple patterns and icons. Use this upgrade to double the max image size yet again and allow even more images!" + }, + { + Name: "Artpad Pixel Limit 64", + Cost: 600, + Dependencies: "artpad_pixel_limit_16", + Category: "Enhancements", + Description: "Alright. Now it's time to kick it into high-gear. Patterns and icons are fun, but let's increase the image size even more to allow higher-detail icons/patterns and small sprites!" + }, + { + Name: "Artpad Pixel Limit 256", + Cost: 1000, + Dependencies: "artpad_pixel_limit_64", + Category: "Enhancements", + Description: "We can create high resolution icons and patterns, but we still can't really do too much more than that. Buy this upgrade and you'll be able to have up to 256 pixels in an image!" + }, + { + Name: "Artpad Pixel Limit 1024", + Cost: 1250, + Dependencies: "artpad_pixel_limit_256", + Category: "Enhancements", + Description: "Let's make things even higher quality! With this upgrade, we'll be able to increase the image size by 4 times! ArtPad is really starting to advance." + }, + { + Name: "Artpad Pixel Limit 4096", + Cost: 2600, + Dependencies: "artpad_pixel_limit_1024", + Category: "Enhancements", + Description: "Now we can do 1024-pixel images, but how about increasing the limit by 4 times yet again? That'll leave even more room for imagination and drawings!" + }, + { + Name: "Artpad Pixel Limit 16384", + Cost: 4800, + Dependencies: "artpad_pixel_limit_4096", + Category: "Enhancements", + Description: "We're ever-so-slightly approaching limitless possibilities. With this upgrade, images in ArtPad will be able to have up to 16384 pixels. We can make desktop backgrounds for small monitors!" + }, + { + Name: "Artpad Pixel Limit 65536", + Cost: 7000, + Dependencies: "artpad_pixel_limit_16384", + Category: "Enhancements", + Description: "Wow! This might be the last time we'll have to deal with pixel limits. It's amazing how far we've came since 2-pixel gradients. Now let's go even further." + }, + { + Name: "Artpad Limitless Pixels", + Cost: 12000, + Dependencies: "artpad_pixel_limit_65536", + Category: "Enhancements", + Description: "We have a pretty high pixel limit, but with this upgrade, pixel limits are no more! With limitless pixels comes limitless creativity. Have fun!" + }, + + { + Name: "AL Shutdown", + Cost: 300, + Dependencies: "app_launcher", + Category: "GUI", + Description: "Want to shut down ShiftOS from your app launcher? This is the perfect upgrade for you." + }, + + { + Name: "Help Description", + Id: "help_description", + Cost: 150, + Dependencies: "", + Category: "Enhancements", + Description: "Dont understand what some commands do in the terminal? With this upgrade, it adds a handy little description to almost every command when you run the command sos.help!" + }, + { + Name: "Help Usage", + Cost: 150, + Dependencies: "help_description", + Category: "Enhancements", + Description: "You got descriptions on what some commands do in the terminal, but wouldn't it be handy to also see what the proper usage is for? Now you can with this upgrade!" + }, + + //ARTPAD TOOLS + { + Name: "Artpad Pixel Placer", + Dependencies: "artpad", + Cost: 750, + Category: "Enhancements", + Description: "This tool extends the Pixel Setter to allow you to use your mouse to place pixels by clicking on the canvas." + }, + { + Name: "Artpad PP Movement Mode", + Dependencies: "artpad_pixel_placer", + Cost: 500, + Category: "Enhancements", + Description: "This tool extends the Pixel Placer and allows you to drag your mouse while the button is held down to draw pixels on the canvas." + }, + { + Name: "Artpad Pencil", + Dependencies: "artpad_pp_movement_mode", + Cost: 1000, + Category: "Enhancements", + Description: "Using the power of the Pixel Placer's movement mode, the Pencil can draw strokes of different thicknesses. Most tools will extend this tool." + }, + { + Name: "Artpad Paintbrush", + Cost: 1000, + Dependencies: "artpad_pencil", + Category: "Enhancements", + Description: "The Paintbrush allows you to draw more thick strokes on the canvas than the Pencil does." + }, + { + Name: "Artpad Eraser", + Cost: 500, + Dependencies: "artpad_paintbrush;artpad_undo", + Category: "Enhancements", + Description: "Undo not effective? Want to only erase a select bit of the canvas? Use this tool to get an eraser!" + }, + { + Name: "Artpad Load", + Cost: 350, + Dependencies: "artpad;file_skimmer", + Category: "Enhancements", + Description: "Want to start off from an existing masterpiece? This tool is for you. Select any .pic file and it'll be loaded onto the canvas!" + }, + { + Name: "Artpad Line Tool", + Cost: 800, + Dependencies: "artpad_pp_movement_mode", + Category: "Enhancements", + Description: "Using the power of linear interpolation and the Pixel Placer Movement Mode, the Line tool can help you draw straight lines from one point to another." + }, + { + Name: "Artpad Rectangle Tool", + Cost: 400, + Dependencies: "artpad_line_tool", + Category: "Enhancements", + Description: "With the line tool we are able to figure out the distance from point A to point B. Let's use that basic framework to draw rectangles!" + }, + { + Name: "Artpad Oval Tool", + Cost: 401, + Dependencies: "artpad_line_tool", + Category: "Enhancements", + Description: "Want to draw some ovals? With this tool, you can! It uses the data from the line tool to construct a circle as you drag the mouse." + }, + { + Name: "Artpad Fill Tool", + Cost: 1000, + Dependencies: "artpad_pixel_placer", + Category: "Enhancements", + Description: "The Pixel Placer is useful because we can grab pixel coordinates from the mouse, and determine how we can fill the area with a certain color - let's do that!" + }, + { + Name: "Artpad Text Tool", + Cost: 1500, + Category: "Enhancements", + Dependencies: "artpad_pixel_placer", + Description: "Want to place text on your canvas? Use the Text Tool to do so!" + }, + { + Name: "Artpad New", + Dependencies: "artpad", + Cost: 500, + Category: "Enhancements", + Description: "Made a mistake? Want a blank canvas? This tool gives you just that." + }, + { + Name: "Artpad Open", + Dependencies: "artpad;file_skimmer", + Cost: 600, + Category: "Enhancements", + Description: "Want to edit an artpad picture? If you have the File Skimmer, then this tool is for you!" + }, + { + Name: "Artpad Save", + Dependencies: "artpad;file_skimmer", + Cost: 1000, + Category: "Enhancements", + Description: "Have you been working extra-hard on a masterpiece in ArtPad and want to save? This upgrade is a must-have!" + }, + { + Name: "Artpad Undo", + Dependencies: "artpad_new", + Cost: 59, + Category: "Enhancements", + Description: "Mistakes happen - but if you have to clear the canvas every time you mess up one single pixel it can get annoying. This tool will help mitigate that - you'll be able to make your last change magically disappear!" + }, + { + Name: "Artpad Redo", + Dependencies: "artpad_undo", + Cost: 50, + Category: "Enhancements", + Description: "Did you change your mind about that mistake you've undone? Want it back? This tool is for you. Note that the second you add something new after an undo, the undone change is wiped forever!" + }, + + + + //ARTPAD COLOR PALETTES + + { + Name: "Artpad 4 Color Palettes", + Dependencies: "artpad", + Cost: 150, + Category: "Enhancements", + Description: "Want to add an extra 2 colors to your palette? Buy this upgrade to do so!" + }, + { + Name: "Artpad 8 Color Palettes", + Dependencies: "artpad_4_color_palettes", + Cost: 400, + Category: "Enhancements", + Description: "Want to add an extra 4 color palette entries to your Artpad to have even more colors used at once? Buy this upgrade, and that will happen!" + }, + { + Name: "Artpad 16 Color Palettes", + Dependencies: "artpad_8_color_palettes", + Cost: 600, + Category: "Enhancements", + Description: "With this upgrade, you can have up to 16 different colors in your ArtPad palette. Good for drawing intense scenes without constantly selecting different colors." + }, + { + Name: "Artpad 32 Color Palettes", + Dependencies: "artpad_16_color_palettes", + Cost: 850, + Category: "Enhancements", + Description: "Having 16 different color palettes is nice, but you know what's nicer? Having 32!" + }, + { + Name: "Artpad 64 Color Palettes", + Dependencies: "artpad_32_color_palettes", + Cost: 1700, + Category: "Enhancements", + Description: "Well then. We have 32 color palettes - let's double that." + }, + { + Name: "Artpad 128 Color Palettes", + Dependencies: "artpad_128_color_palettes", + Cost: 3400, + Category: "Enhancements", + Description: "With this upgrade we'll be able to have 128 simultaneous colors in our palette. It may get a bit glitchy though... maybe a window manager upgrade could help?" + }, + + + + //SHIFTORIUM UPGRADES FOR THE SHIFTORIUM ITSELF + + { + Name: "Shiftorium GUI", + Cost: 100, + Category: "Applications", + Description: "You may spend lots of time in your terminal - executing scripts, chatting, etc, but why make it so difficult and repetitive to upgrade your system? With this upgrade, a GUI will be added to the Shiftorium, and will be accessible using win.open{app:\"shiftorium\"}." + }, + { + Name: "AL Shiftorium", + Cost: 150, + Dependencies: "shiftorium_gui;app_launcher", + Category: "GUI", + Description: "Add an App Launcher Entry for the Shiftorium!" + }, + { + Name: "Shiftorium GUI Codepoints Display", + Cost: 2500, + Dependencies: "shiftorium_gui", + Category: "Enhancements", + Description: "In the shiftorium GUI but dont know what you can spend because you can't see how many code points are on hand? Well shop easy, because with this upgrade that is now possible! You have to restart the shiftorium for it to work." + }, + + //ADVANCED APP LAUNCHER + { + Name: "Advanced App Launcher", + Cost: 10000, + Description: "The app launcher can categorize items and is quite clean, but let's make it even more advanced by adding more than just a traditional menu as the app launcher.", + Dependencies: "app_launcher_categories", + Category: "GUI" + }, + + //UPDATE MANAGER + { + Name: "AL Update Manager", + Cost: 150, + Description: "Want to get the latest features of ShiftOS, right from your App Launcher? This upgrade is for you!", + Dependencies: "app_launcher", + Category: "GUI" + }, + + + // SHIFTSWEEPER + + { + Name: "AL ShiftSweeper", + Cost: 100, + Dependencies: "app_launcher;shiftsweeper", + Category: "GUI", + Description: "Play ShiftSweeper quickly with this dandy applauncher!" + }, + { + Name: "ShiftSweeper Medium", + Cost: 900, + Dependencies: "shiftsweeper", + Category: "Enhancements", + Description: "ShiftSweeper getting too easy? Obviously, since you can only play Easy difficulty! However, with this Medium button, you can get a better challenge, and more codepoints!" + }, + { + Name: "ShiftSweeper Hard", + Cost: 900, + Dependencies: "shiftsweeper_medium", + Category: "Enhancements", + Description: "Is ShiftSweeper still too easy for you? Buy the Hard difficulty and you can try to find 99 mines! It may be extremely difficult, but the reward is massive!" + } +]
\ No newline at end of file diff --git a/ShiftOS.Frontend/Resources/cursor_9x_pointer.png b/ShiftOS.Frontend/Resources/cursor_9x_pointer.png Binary files differnew file mode 100644 index 0000000..ff48150 --- /dev/null +++ b/ShiftOS.Frontend/Resources/cursor_9x_pointer.png diff --git a/ShiftOS.Frontend/Resources/justthes.png b/ShiftOS.Frontend/Resources/justthes.png Binary files differnew file mode 100644 index 0000000..a85ba27 --- /dev/null +++ b/ShiftOS.Frontend/Resources/justthes.png diff --git a/ShiftOS.Frontend/Resources/strings_de.txt b/ShiftOS.Frontend/Resources/strings_de.txt new file mode 100644 index 0000000..139ed69 --- /dev/null +++ b/ShiftOS.Frontend/Resources/strings_de.txt @@ -0,0 +1,245 @@ +{ + "{SUBMIT}":"Best�tigen", + +"{TERMINAL_TUTORIAL_1}":"Wilkommen zum ShiftOS Terminal. Hier wirst du die meiste Zeit in ShiftOS verbringen. + +Eine kurze Erkl�rung wie du das Terminal benutzt lautet wiefolgt. Du kannst das command 'sos.help' benutzen um eine Liste aller commands aufzurufen. Schreib es +einfach in das Terminal und dr�cke <enter> um alle commands anzuzeigen. + +Commands k�nnen mit argumenten versehen werden, indem du ein key-value Paar in einem {} Block hinter dem command angibst. Zum Beispiel: + +some.command{print:\"Guten Tag!\"} +math.add{op1:1, op2:2} +set.value{key:\"somekey\", value:true} + +Dir wurden 50 Codepoints als Startgeld gegeben - benutz dein Wissen um mit ihnen das MUD Fundamentals Shiftorium Upgrade zu kaufen. Das ganze mit dem Terminal. +Um das MUD Fundamentals Upgrade zu kaufen, tippe shiftorium.buy{upgrade:\"mud_fundamentals\"} in das Terminal. Das ganze funktioniert auch mit anderen Upgrades die +du kaufen willst, ersetze mud_fundamentals einfach mit einem anderen Upgradenamen. +", + + + "{TERMINAL_TUTORIAL_2}":"Du hast den Test erfolgreich bestanden. ShiftOS wird seine Grundfunktionen installieren.", + "{ABOUT}":"�ber", + "{START_SYSTEM_SCAN}":"Starte systemweiten Scan", + "{SCAN_HOME}":"Scanne 0:/home", + "{SCAN_SYSTEM}":"Scanne 0:/system", + "{RESULTS}":"Ergebnisse", + "{VIRUSSCANNER_ABOUT}":"Wilkommen zum ShiftOS Virenscanner. + +Der ShiftOS Virenscanner ist ein Werkzeug welches dir erlaubt jede/s Datei oder System zu scannen und herauszufinden +ob es ein Virus ist. Wenn ein Virus erkannt wird, hast do die Option ihn danach zu l�schen indem du 'Entfernen' +klickst. + +Wenn eine Systemdatei von dem Virenscanner erkannt wird, wird sie ersetzt.", + "{PLAY}":"Spielen", + "{APPLICATIONS}":"Anwendungen", + "{TERMINAL}":"Terminal", + "{PONG}":"Pong", + "{CODEPOINTS}":"Codepoints", + "{SHIFTORIUM}":"Shiftorium", + "{HACK}":"Hack", + "{SHIFTER}":"Shifter", + "{MUD_SHORT}":"MUD", + "{MUD}":"Multi-user domain", + "{DESKTOP}":"Desktop", + "{WINDOW}":"Fenster", + "{WINDOW_MANAGER}":"Fenstermanager", + "{UPGRADE}":"Upgrade", + "{UPGRADES}":"Upgrades", + "{APPLICATION}":"Anwendung", + "{SCRIPT}":"Skript", + "{ERROR}":"Fehler", + "{SCRIPTS}":"Skripts", + "{NULL}":"null", + "{ID}":"ID Num", + "{SYSTEM_INITIATED}":"System initialisiert", + "{PASSWORD}":"Passwort", + "{CRACK}":"Crack", + "{ARTPAD_UNDO_ERROR}":"Artpad - Fehler r�ckg�ngig machen", + "{ARTPAD_NEXT_STEP_WILL_KILL_CANVAS_JUST_FLIPPING_CLICK_NEW}":"You cannot undo the previous action as it would delete the canvas. If you'd like to clear the canvas, click New.", + "{ARTPAD_REDO_ERROR}":"Artpad - Wiederherstellungserror", + "{ARTPAD_NOTHING_TO_REDO}":"Artpad kann nichts wiederherstellen! Behalte im Kopf, dass wenn du etwas r�ckg�ngig machst und dann zeichnest, das der Verlauf gel�scht wird.", + "{ARTPAD_MAGNIFIER_ERROR}": "Artpad - Magnifier Error", + "{ARTPAD_MAGNIFICATION_ERROR_EXP_2}": "Artpad kann nicht weiter ranzoomen!", + "{ARTPAD_MAGNIFICATION_ERROR_EXP}": "Artpad kann nicht weiter ranzoomen. Naja, es kann, aber es will nicht.", + "{SHUTDOWN}":"Herunterfahren", + "{CONNECTING_TO_MUD}":"Verbinde mit dem Multi-User Domain...", + "{READING_FS}":"Lese Dateisystem...", + "{INIT_KERNEL}":"Initialisiere Kernel...", + "{START_DESKTOP}":"Starte Desktop-Session...", + "{DONE}": "Fertig", + "{READING_CONFIG}":"Lese Konfiguration...", + "{ID_TAKEN}":"ID Existiert bereits! Benutze chat.join um diesen Chat zu betreten.", + "{CHAT_NOT_FOUND_OR_TOO_MANY_MEMBERS}":"Dieser Chat existiert entweder nicht oder er ist voll.", + "{CHAT_NOT_FOUND_OR_NOT_IN_CHAT}":"Du bist zurzeit nicht in diesem Chat.", + "{CHAT_PLEASE_PROVIDE_VALID_CHANNEL_DATA}":"Du hast keine g�ltige Chat-Metadata angegeben! Bitte benutze chat.create{id:\"deine_id\", name:\"Dein Chatname\", topic:\"Thema deines Channels\"}.", + "{UPGRADE_PROGRESS}":"Upgrade progress""Upgrade Fortschritt", + "{WIN_PROVIDEID}":"Bitte gib eine g�ltige Fenster ID von win.list an.", + "{WIN_CANTCLOSETERMINAL}":"Du kannst dieses Terminal nicht schlie�en.", + "{WELCOME_TO_SHIFTORIUM}":"Willkommen zum Shiftorium!", + "{SUCCESSFULLY_CREATED_CHAT}":"Chat erfolgreich erstellt. Benutze chat.join{id:\"chat_id_hier\"} um ihm beizutreten.", + "{CHAT_HAS_JOINED}":"hat den Chat betreten.", + "{HAS_LEFT_CHAT}":"hat den Chat verlassen.", + "{SHIFTORIUM_EXP}":"The Shiftorium is your one-stop-shop for ShiftOS system enhancements, upgrades and applications. + + You can buy upgrades in the Shiftorium using a currency called Codepoints, which you can earn by doing various tasks within ShiftOS, such as playing Pong, stealing them from other users, and finding ways to make your own. It's up to you how you get your Codepoints. + + You can then use them to buy new applications, features, enhancements and upgrades for ShiftOS that make the user experience a lot better. Be careful though, buying too many system enhancements without buying new ways of earning Codepoints first can leave you in the dust and unable to upgrade the system. + + Anyways, feel free to browse from our wonderful selection! You can see a list of available upgrades on the left, as well as a progress bar showing how much you've upgraded the system compared to how much you still can.", + "{PONG_WELCOME}":"Welcome to Pong.", + "{PONG_DESC}":"Pong is an arcade game where your goal is to get the ball past the opponent paddle while keeping it from getting past yours. + + In ShiftOS, Pong is modified - you only have one chance, the game is divided into 60 second levels, and you can earn Codepoints by surviving a level, and beating the opponent.", + "{NO_APP_TO_OPEN}":"No app found for this file!", + "{NO_APP_TO_OPEN_EXP}":"File Skimmer could not find an application that can open this file.", + "{CLIENT_DIAGNOSTICS}":"Client diagnostics", + "{GUID}":"GUID", + "{CLIENT_DATA}":"Client data", + "{CLOSE}":"Schlie�en", + "{LOAD_DEFAULT}":"Load default", + "{IMPORT}":"Einf�hren", + "{EXPORT}":"Export", + "{APPLY}":"Apply", + "{TEMPLATE}":"Vorlage", + "{H_VEL}":"Horizontal velocity", + "{V_VEL}":"Vertical velocity", + "{LEVEL}":"Ebene", + "{UPGRADE_DEVELOPMENT}":"Development Upgrade", + "{UPGRADE_DEVELOPMENT_DESCRIPTION}":"Development Upgrade Don't Buy", + "{SECONDS_LEFT}":"sekunden �brig", + "{CASH_OUT_WITH_CODEPOINTS}":"Cash out with your codepoints", + "{PONG_PLAY_ON_FOR_MORE}":"Spiel f�r mehr!", + "{YOU_REACHED_LEVEL}":"You've reached level", + "{PONG_BEAT_AI_REWARD}":"Reward for beating AI (CP)", + "{PONG_BEAT_AI_REWARD_SECONDARY}":"Codepoints for beating AI:", + "{CODEPOINTS_FOR_BEATING_LEVEL}":"Codepoints for beating level", + "{YOU_WON}":"Du gewinnst", + "{YOU_LOSE}":"Du verlierst", + "{TRY_AGAIN}":"Try again", + "{CODEPOINTS_SHORT}":"CP", + "{TERMINAL_FORMATTING_DRIVE}":"Formatting drive... %percent %", + "{INSTALLING_SHIFTOS}":"Installing ShiftOS on %domain.", + "{YOU_MISSED_OUT_ON}":"You missed out on", + "{BUT_YOU_GAINED}":"Aber du hast gewonnen", + "{PONG_PLAYON_DESC}":"Or do you want to try your luck on the next level to increase your reward?", + "{PONG_CASHOUT_DESC}":"Would you like the end the game now and cash out with your reward?", + "{INITIAL_H_VEL}":"Initial H Vel", + "{INITIAL_V_VEL}":"Initial V Vel", + "{INC_H_VEL}":"Increment H Vel", + "{INC_V_VEL}":"Increment V Vel", + "{MULTIPLAYER_ONLY}":"Program not compatible with single-user domain.", + "{MULTIPLAYER_ONLY_EXP}":"This program cannot run within a single-user domain. You must be within a multi-user domain to use this program.", + "{SHIFTER_SKIN_APPLIED}":"Shifter - Settings applied!", + "{YOU_HAVE_EARNED}":"Du hast bekommen", + "{CREATING_PATH}":"Creating directory: %path", + "{CREATING_FILE}":"Creating file: %path", + "{SHIFTORIUM_HELP_DESCRIPTION}": "Help Descriptions", + "{CREATING_USER}":"Creating user %username", + "{SEPERATOR}":" - ", + "{NAMESPACE}":"Namespace ", + "{COMMAND}": "| Befehl ", + "{SHIFTOS_HAS_BEEN_INSTALLED}":"ShiftOS has been installed on %domain.", + "{WARN}": "WARN: ", + "{ERROR}": "!ERROR! ", + "{OBSOLETE_CHEATS_FREECP}": "The %ns.%cmd command is obsolete and has been replaced with %newcommand", + "{REBOOTING_SYSTEM}":"Rebooting system in %i seconds...", + "{ERROR_ARGUMENT_REQUIRED}": "You must supply an %argument value", + "{ERROR_ARGUMENT_REQUIRED_NO_USAGE}": "You are missing some arguments.", + "{GENERATING_PATHS}":"Generating paths...", + "{ERROR_COMMAND_WRONG}": "Check your syntax and try again", + "{LOGIN_EXP}": "Login as the admin of the multi user domain.", + + "{USAGE}": "Verwendung: ", + + "{NAMESPACE_SOS_DESCRIPTION}":"The ShiftOS Namespace", + "{COMMAND_HELP_USAGE}":"%ns.%cmd{[topic:]}", + "{COMMAND_HELP_DESCRIPTION}":"Listet alle Befehle auf", + "{COMMAND_SOS_SHUTDOWN_USAGE}":"%ns.%cmd", + "{COMMAND_SOS_SHUTDOWN_DESCRIPTION}":"Saves and shuts down ShiftOS", + "{COMMAND_SOS_STATUS_USAGE}":"%ns.%cmd", + "{COMMAND_SOS_STATUS_DESCRIPTION}":"Displays how many codepoints you have", + "{COMMAND_SOS_LANG_USAGE}":"%ns.%cmd{[language:\"english\"]}", + "{COMMAND_SOS_LANG_DESCRIPTION}":"Sprache �ndern.", + "{COMMAND_DEV_CRASH_USAGE}":"%ns.%cmd", + "{COMMAND_DEV_CRASH_DESCRIPTION}":"Shuts down ShiftOS forcefully", + "{COMMAND_DEV_UNLOCKEVERYTHING_USAGE}":"%ns.%cmd", + "{COMMAND_DEV_UNLOCKEVERYTHING_DESCRIPTION}":"Unlocks all shiftorium upgrades", + "{COMMAND_DEV_FREECP_USAGE}":"%ns.%cmd{[amount:1000]}", + "{COMMAND_DEV_FREECP_DESCRIPTION}":"Gives [ammount] codepoints", + "{COMMAND_TRM_CLEAR_USAGE}":"%ns.%cmd", + "{COMMAND_TRM_CLEAR_DESCRIPTION}":"Clears the terminal", + "{COMMAND_SHIFTORIUM_BUY_USAGE}":"%ns.%cmd{upgrade:}", + "{COMMAND_SHIFTORIUM_BUY_DESCRIPTION}":"Buys [upgrade]", + "{COMMAND_SHIFTORIUM_LIST_USAGE}":"%ns.%cmd", + "{COMMAND_SHIFTORIUM_LIST_DESCRIPTION}":"Lists the upgrades that you can get", + "{COMMAND_SHIFTORIUM_INFO_USAGE}":"%ns.%cmd{upgrade:}", + "{COMMAND_SHIFTORIUM_INFO_DESCRIPTION}":"Gives a description about an upgrade", + "{COMMAND_DEV_MULTARG_USAGE}":"%ns.%cmd{id:,name:,type:}", + "{COMMAND_DEV_MULTARG_DESCRIPTION}":"A command which requiers multiple arguments", + + "{ERR_COMMAND_NOT_FOUND}":"Befehl nicht gefunden.", + "{MUD_ERROR}":"MUD error", + + "{PROLOGUE_NO_USER_DETECTED}":"No user detected. Please enter a username.", + "{PROLOGUE_BADUSER}":"Invalid username detected.", + "{PROLOGUE_NOSPACES}":"Usernames must not contain spaces.", + "{PROLOGUE_PLEASE_ENTER_USERNAME}":"Please enter a valid username. Blank usernames are not permitted.", + + "{SHIFTORIUM_NOTENOUGHCP}":"Not enough codepoints: ", + "{SHIFTORIUM_TRANSFERRED_FROM}":"Received Codepoints from", + "{SHIFTORIUM_TRANSFERRED_TO}":"Transferred Codepoints to", + + "{SE_SAVING}":"Saving game to disk", + "{SE_TIPOFADVICE}":"Tip of advice: ShiftOS will always save your game after big events or when you shut down the operating system. You can also invoke a save yourself using 'sos.save'.", + + "{STORY_WELCOME}":"Welcome to ShiftOS", + "{STORY_SENTIENCEUNKNOWN}":"Your sentience is currently unknown. Please strike the Enter key to prove you are alive.", + + "{SENTIENCE_BASIC}":"Sentience: Basic - User can respond to basic instructions.", + "{SENTIENCE_BASICPLUS}":"Sentience: Basic+ - User can invoke commands within the ecosystem.", + "{SENTIENCE_POSSIBLEHUMAN}":"Sentience: Possible human - user can perform actions based on a choice.", + "{SENTIENCE_POSSIBLEHUMANPLUS}":"Sentience: Possible human+ - user can infer, and can pass arguments.", + "{SENTIENCE_HUMAN}":"Sentience: Human. Thanks for your patience.", + "{SENTIENCE_INVALIDPASSWORD}":"Das eingegebene Passwort ist ung�ltig.", + + "{ARGS_PASSWORD}":"password", + + "{SHIFTOS_PLUS_MOTTO}":"ShiftOS, Shift it YOUR way.", + "{SHIFTOS_VERSION_INFO}":"ShiftOS Version: ", + "{USER_NAME}":"Benutzername", + "{DISCOURSE_INTEGRATION}":"Discourse Integration", + "{SYSTEM_NAME}":"System Name", + "{USER_INFO}":"User Information", + "{SELECT_LANG}":"Select language", + "{WELCOME_TO_SHIFTOS}":"Welcome to ShiftOS Alpha!", + "{CREATE}":"Erstellen", + "{INSTALL}":"Installieren", + "{ALIAS}":"Alias:", + "{OBSOLETE_SYS_SHUTDOWN}":"sys.shutdown is obsolete", + "{PY_EXCEPTION}":"There was an error running python code.", + "{LUA_ERROR}":"There was an error running lua code.", + "{LANGUAGE_CHANGED}":"Die Sprache wurde ge�ndert. Bitte �ndern Sie ShiftOS neu, damit �nderungen voll wirksam werden.", + + "{TERMINAL_NAME}":"Terminal", + "{ARTPAD_NAME}":"Artpad", + "{PONG_NAME}":"Pong", + "{WAV_PLAYER_NAME}":"WAV Player", + "{SHIFTORIUM_NAME}":"Shiftorium", + "{TEXTPAD_NAME}":"TextPad", + "{VIRUS_SCANNER_NAME}":"Virus Scanner", + "{SKIN_LOADER_NAME}":"Skin Loader", + "{SHIFTER_NAME}":"Shifter", + "{NAME_CHANGER_NAME}":"Name Changer", + "{MUD_PASSWORD_CRACKER_NAME}":"Multi-User Domain Password Cracker v1.0", + "{MUD_CONTROL_CENTRE_NAME}":"MUD Control Centre", + "{MUD_AUTHENTICATOR_NAME}":"Multi-User Domain Admin Panel", + "{GRAPHIC_PICKER_NAME}":"Graphic Picker", + "{FILE_SKIMMER_NAME}":"File Skimmer", + "{FILE_DIALOG_NAME}":"File Dialog", + "{DIALOG_NAME}":"Dialog", + "{COLOR_PICKER_NAME}":"Color Picker", + "{CHAT_NAME}":"Chat", + "{NOTIFICATIONS}":"Benachrichtigungen", + "{GERMAN_SECRET}":"guten tag polen ist anschluss", +}
\ No newline at end of file diff --git a/ShiftOS.Frontend/Resources/strings_en.txt b/ShiftOS.Frontend/Resources/strings_en.txt new file mode 100644 index 0000000..d6816c1 --- /dev/null +++ b/ShiftOS.Frontend/Resources/strings_en.txt @@ -0,0 +1,286 @@ +/* + * ShiftOS English Language Pack + * + * This is the default language pack distributed within the game. + */ + +{ + //General strings + //These strings can be used anywhere in the UI where language context isn't necessary. + "{GEN_PROGRAMS}": "Programs", + "{GEN_COMMANDS}": "Commands", + "{GEN_OBJECTIVES}": "Objectives", + "{GEN_CURRENTPROCESSES}": "Current processes", + "{GEN_WELCOME}": "Welcome to ShiftOS.", + "{GEN_SYSTEMNAME}": "System name", + "{GEN_PASSWORD}": "Password", + "{GEN_LPROMPT}": "%sysname login: ", + "{GEN_SYSTEMSTATUS}": "System status", + "{GEN_USERS}": "Users", + "{GEN_CODEPOINTS}": "Codepoints", + "{GEN_LOADING}": "Loading...", + "{GEN_SAVE}": "Save", + "{GEN_LOAD}": "Load", + "{GEN_APPLY}": "Apply", + "{GEN_RESET}": "Reset", + "{GEN_LOADDEFAULT}": "Load default", + "{GEN_ROOTPASSWORD}":"Root password", + "{GEN_CANCEL}": "Cancel", + "{GEN_CONTINUE}": "Continue", + "{GEN_BACK}": "Back", + "{GEN_YES}": "Yes", + "{GEN_NO}": "No", + "{GEN_OK}": "OK", + "{GEN_SETTINGS}": "Settings", + "{GEN_ABOUT}": "About", + "{GEN_EXIT}": "Exit", + "{GEN_CLOSE}": "Close", + + //General errors + //Syntax errors, command errors, you name it.. + "{ERR_BADBOOL}": "The value you entered must be either true or false.", + "{ERR_BADPERCENT}": "The value you entered must be a value from 0 to 100.", + "{ERR_NOLANG}": "The language you entered does not exist!", + "{ERR_NOTENOUGHCODEPOINTS}": "You don't have enough Codepoints to do that.", + "{ERR_NOUPGRADE}": "We couldn't find that upgrade.", + "{ERR_GENERAL}": "An error has occurred performing this operation.", + "{ERR_EMPTYCATEGORY}": "The category you specified either has no items in it, or was not found.", + "{ERR_NOMOREUPGRADES}": "There are no more Shiftorium Upgrades to show!", + "{ERR_BADWINID}": "You must specify a value between 0 and %max.", + "{ERR_USERFOUND}": "That user already exists.", + "{ERR_NOUSER}": "That user was not found.", + "{ERR_REMOVEYOU}": "You can't remove your own user account.", + "{ERR_BADACL}": "You must specify a value between 0 (guest) and 3 (root) for a user permission.", + "{ERR_ACLHIGHERVALUE}": "You can't set a user's permissions to a value higher than your own.", + "{ERR_HIGHERPERMS}": "That user has more rights than you!", + "{ERR_PASSWD_MISMATCH}": "Passwords don't match!", + "{ERR_SYNTAXERROR}": "Syntax error.", + "{ERR_COMMANDNOTFOUND}": "Command not found.", + + //Command results + "{RES_ACLUPDATED}": "User permissions updated.", + "{RES_LANGUAGE_CHANGED}": "System language changed successfully.", + "{RES_NOOBJECTIVES}": "No objectives to display! Check back for more.", + "{RES_UPGRADEINSTALLED}": "Upgrade installed!", + "{RES_WINDOWCLOSED}": "The window was closed.", + "{RES_CREATINGUSER}": "Creating new user with username %name.", + "{RES_REMOVINGUSER}": "Removing user with username %name from your system.", + "{RES_DENIED}": "Access denied.", + "{RES_GRANTED}": "Access granted.", + "{RES_PASSWD_SET}": "Password set successfully.", + + //Shiftorium messages. + "{SHFM_UPGRADE}": "%id - %cost Codepoints", + "{SHFM_CATEGORY}": " - %name: %available upgrades left.", + "{SHFM_QUERYERROR}": "Shiftorium Query Error", + "{SHFM_NOUPGRADES}": "No upgrades!", + + //Command data strings + "{COM_STATUS}": "ShiftOS build %version\r\n\r\nCodepoints: %cp \r\n Upgrades: %installed installed, %available available\r\n\r\n", + "{COM_UPGRADEINFO}": "%category: %name - %cost Codepoints.\r\n\r\n%description\r\n\r\nUpgrade ID: %id", + + //Terminal Command Descriptions + //These strings show up when running the "commands" command in your Terminal. + "{DESC_CLEAR}": "Clears the screen of the current Terminal.", + "{DESC_SETSFXENABLED}": "Sets whether or not system sounds are enabled.", + "{DESC_SETMUSICENABLED}": "Sets whether or not music is enabled in ShiftOS.", + "{DESC_SETVOLUME}": "Sets the volume of sounds and music if they're enabled.", + "{DESC_SHUTDOWN}": "Safely shuts down your computer.", + "{DESC_LANG}": "Change the system language of ShiftOS.", + "{DESC_COMMANDS}": "Shows a list of Terminal commands inside ShiftOS.", + "{DESC_HELP}": "Type this command to get general help with using ShiftOS.", + "{DESC_SAVE}": "Saves the in-memory configuration of ShiftOS.", + "{DESC_STATUS}": "Shows basic status information such as how many Codepoints you have and your current objective.", + "{DESC_BUY}": "Buys the specified Shiftorium upgrade.", + "{DESC_BULKBUY}": "Buys the specified Shiftorium upgrades in bulk.", + "{DESC_UPGRADEINFO}": "Shows information about the specified Shiftorium upgrade.", + "{DESC_UPGRADECATEGORIES}": "Shows all the available Shiftorium categories and how many upgrades are available in them.", + "{DESC_UPGRADES}": "Shows a list of available Shiftorium upgrades.", + "{DESC_PROCESSES}": "Shows a list of currently running app processes.", + "{DESC_PROGRAMS}": "Shows a list of programs you can run.", + "{DESC_CLOSE}": "Closes the specified application window.", + "{DESC_ADDUSER}": "Add a user to your system.", + "{DESC_REMOVEUSER}": "Remove a user from your computer.", + "{DESC_SETUSERPERMISSIONS}": "Set the permissions of a user.", + "{DESC_USERS}": "Lists all users on your computer.", + "{DESC_SU}": "Change your identity to another user's.", + "{DESC_PASSWD}": "Change your user account password.", + "{DESC_NAMECHANGER}": "Change the name of virtually any program within ShiftOS!", + "{DESC_FILENOTFOUND}": "The file you requested could not be found.", + + //Window titles. + "{TITLE_FILENOTFOUND}": "File not found.", + "{TITLE_SKINAPPLIED}":"Shifter - Skin applied.", + "{TITLE_PONG_YOULOSE}": "You lose", + "{TITLE_CODEPOINTSTRANSFERRED}": "Codepoints transferred.", + "{TITLE_INVALIDPORT}": "Invalid port number.", + "{TITLE_ENTERSYSNAME}": "Enter system name", + "{TITLE_INVALIDNAME}": "Invalid name", + "{TITLE_VALUETOOSMALL}": "Value too small.", + "{TITLE_TEMPLATE}": "Template", + "{TITLE_SKINLOADER}": "Skin loader", + "{TITLE_NAMECHANGER}": "Name Changer", + "{TITLE_ADDRESSBOOK}": "Address Book", + "{TITLE_ARTPAD}": "ArtPad", + "{TITLE_SIMPLESRC}": "SimpleSRC Client", + "{TITLE_INFOBOX}": "Information", + "{TITLE_COLORPICKER}": "Choose color", + "{TITLE_AUDIOPLAYER}": "Audio Player", + "{TITLE_CALCULATOR}": "Calculator", + "{TITLE_DOWNLOADER}": "Downloader", + "{TITLE_ABOUT}": "About ShiftOS", + "{TITLE_FILESKIMMER}": "File Skimmer", + "{TITLE_CHOOSEGRAPHIC}": "Choose graphic", + "{TITLE_ICONMANAGER}": "Icon Manager", + "{TITLE_INSTALLER}": "Installer", + "{TITLE_MINDBLOW}": "MindBlow", + "{TITLE_SYSTEMPREPARATION}": "System preparation", + "{TITLE_PONG}": "Pong", + "{TITLE_SHIFTER}": "Shifter", + "{TITLE_SHIFTLETTERS}": "ShiftLetters", + "{TITLE_SHIFTLOTTO}": "ShiftLotto", + "{TITLE_SHIFTNET}": "Shiftnet", + "{TITLE_SHIFTORIUM}": "Shiftorium", + "{TITLE_SHIFTSWEEPER}": "ShiftSweeper", + "{TITLE_TERMINAL}": "Terminal", + "{TITLE_TEXTPAD}": "TextPad", + "{TITLE_RESTARTREQUIRED}": "Restart required.", + + + //App Launcher categories + "{AL_PROGRAMMING}": "Programming", + "{AL_CUSTOMIZATION}": "Customization", + "{AL_NETWORKING}": "Networking", + "{AL_UTILITIES}": "Utilities", + "{AL_GRAPHICS}": "Graphics", + "{AL_ACCESSORIES}": "Accessories", + "{AL_ENTERTAINMENT}": "Entertainment", + "{AL_OFFICE}": "Office", + "{AL_GAMES}": "Games", + + //Infobox prompt messages + "{PROMPT_SKINAPPLIED}": "New skin values applied! You have earned %cp Codepoints.", + "{PROMPT_PONGLOST}": "You lost this game of Pong. Guess you should've cashed out...", + "{PROMPT_CODEPOINTSTRANSFERRED}": "%transferrer has transferred %amount Codepoints to your system.", + "{PROMPT_INVALIDPORT}": "The Digital Society Port must be a valid whole number between 0 and 65535.", + "{PROMPT_ENTERSYSNAME}": "Please enter a system name for your computer.", + "{PROMPT_INVALIDNAME}": "The name you entered cannot be blank. Please enter another name.", + "{PROMPT_SMALLSYSNAME}": "Your system name must have at least 5 characters in it.", + "{PROMPT_RESTARTREQUIRED}": "For the changes you made to take effect, a restart of ShiftOS is required.", + + //Pong + "{PONG_LEVELREACHED}": "You've reached level %level!", + "{PONG_BEATAI}": "You've beaten the opponent! %amount Codepoints!", + "{PONG_STATUSCP}": "Codepoints: %cp", + "{PONG_STATUSLEVEL}": "Level %level. %time seconds to go!", + "{PONG_PLAY}": "Play some Pong!", + "{PONG_DESC}": "Pong is the modern-day recreation of the classic 70s game called, you guessed it, Pong.\r\n\r\nIn Pong, you must try your best to keep the fast-moving ball from going past your side of the screen using your mouse to move your paddle in the way of the ball, while getting the ball to go past the opponent's paddle.\r\n\r\nOnce you start Pong, you have 60 seconds to beat the opponent and survive as much as possible. The game will get faster as you play, and the more 60-second levels you play, the more Codepoints you'll earn. If you lose the level, you will start back at Level 1 with 0 Codepoints. At the end of the level, you may choose to cash out or play on.", + "{PONG_WELCOME}": "Welcome to Pong.", + "{PONG_BEATLEVELDESC}": "You have survived this level of Pong. You now have a chance to cash out your Codepoints or play on for more.", + "{PONG_PLAYON}": "Play on", + "{PONG_CASHOUT}": "Cash out", + + //Main menu tip messages + "{MAINMENU_TIPTEXT_0}": "Press CTRL+T to open a Terminal from any application within ShiftOS.", + "{MAINMENU_TIPTEXT_1}": "Codepoints can be used in the Shiftorium to buy upgrades for your system using the \"buy\" command.", + "{MAINMENU_TIPTEXT_2}": "The Shifter is a VERY powerful application. Not only can you customize ShiftOS to look however you want, but you'll earn heaps of Codepoints while you do so.", + "{MAINMENU_TIPTEXT_3}": "Not the language you want? Head over to \"Settings\" to choose the proper language for you. You may need to head to the forums if your language isn't supported.", + "{MAINMENU_TIPTEXT_4}": "Sandbox Mode is a nice way to use ShiftOS without having to progress through the main campaign. There's no Codepoints, no upgrades, and everything's unlocked.", + "{MAINMENU_TIPTEXT_5}": "The Skin Loader is an essential application. It can load in skins from anywhere - from your personal collection and from the forums, and you can even generate your own sharable skins for others to use!", + "{MAINMENU_TIPTEXT_6}": "ArtPad not cutting it for you? You can use any image editor you like on your host system and just import the assets into the ShiftOS Shared Folder to use in your skins.", + "{MAINMENU_TIPTEXT_7}": "What is that \"System Color Key-OUt\" you speak of, Shifter? Well, when composing skin assets, use that color to mark areas to be rendered as transparent!", + "{MAINMENU_TIPTEXT_8}": "Use the Format Editor to change the way Terminal commands are interpreted.", + "{MAINMENU_TIPTEXT_9}": "The Shiftnet is a wonderland of applications, games and services - only available in ShiftOS. Feel free to explore!", + + //Main menu - Settings + "{MAINMENU_DSADDRESS}": "Digital Society address: ", + "{MAINMENU_DSPORT": "Digital Society port: ", + "{MAINMENU_LANGUAGE}": "Language:", + + //Main Menu - General text + "{MAINMENU_TITLE}": "Main menu", + "{MAINMENU_CAMPAIGN}": "Campaign", + "{MAINMENU_SANDBOX}": "Sandbox", + "{MAINMENU_NEWGAME}": "New game", + + //Miscelaneous strings + "{MISC_KERNELVERSION}": "ShiftKernel - v0.9.4", + "{MISC_KERNELBOOTED}": "[sys] Kernel startup completed.", + "{MISC_SHIFTFSDRV}": "[sfs] ShiftFS core driver, version 2.7", + "{MISC_SHIFTFSBLOCKSREAD}": "[sfs] Driver initiated. 4096 blocks read.", + "{MISC_LOADINGCONFIG}": "[confd] Loading system configuration... success", + "{MISC_BUILDINGCMDS}": "[termdb] Terminal database is being parsed...", + "{MISC_CONNECTINGTONETWORK}": "[inetd] Connecting to network...", + "{MISC_CONNECTIONSUCCESSFUL}": "[inetd] Connection successful.", + "{MISC_DHCPHANDSHAKEFINISHED}": "[inetd] DHCP handshake finished.", + "{MISC_NONETWORK}": "[inetd] No network access points found.", + "{MISC_SANDBOXMODE}": "[sos] Sandbox Mode initiating...", + "{MISC_ACCEPTINGLOGINS}": "[systemd] Accepting logins on local tty1.", + + //ShiftOS engine strings + "{ENGINE_CANNOTLOADSAVE}": "[sos] Error. Cannot load user save file.", + + //Pre-connection loading messages + "{LOADINGMSG1_0}": "[systemd] The light is so bright...", + "{LOADINGMSG1_1}": "[systemd] Hold your colors...", + "{LOADINGMSG1_2}": "[systemd] Time to shift it my way...", + "{LOADINGMSG1_3}": "[systemd] Does anybody even read this?", + "{LOADINGMSG1_4}": "[systemd] I....just wanna play it right... We're....gonna get there tonight...", + "{LOADINGMSG1_5}": "[systemd] I'm a computer.", + "{LOADINGMSG1_6}": "[systemd] What ya gonna do, what ya gonna do, when DevX comes for you?", + "{LOADINGMSG1_7}": "[systemd] Artificial intelligence do everything now.", + "{LOADINGMSG1_8}": "[systemd] Nobody is really here.", + "{LOADINGMSG1_9}": "[systemd] I so want a giant cake for breakfast.", + + //Post-connection loading messages + "{LOADINGMSG2_0}": "[systemd] It's all yours, Shifter.", + "{LOADINGMSG2_1}": "[systemd] That's a nice network you got there...", + "{LOADINGMSG2_2}": "[systemd] Leave me alone and go earn some Codepoints.", + "{LOADINGMSG2_3}": "[systemd] I'm hungry... Cake would be appreciated.", + "{LOADINGMSG2_4}": "[systemd] SEVERE: CAKE NOT FOUND. PLEASE INSERT CAKE INTO DRIVE 1:/ AND PRESS ANY KEY TO CONTINUE.", + "{LOADINGMSG2_5}": "[systemd] Now SCREAM!", + "{LOADINGMSG2_6}": "[systemd] Yes, I'd like to order a giant 6-mile-long tripple-chocolate ice cream cake?", + "{LOADINGMSG2_7}": "[systemd] There's no antidote...", + "{LOADINGMSG2_8}": "[systemd] Can I at least have a muffin?", + "{LOADINGMSG2_9}": "[systemd] System initiated, but I still want a cake.", + "{LOADINGMSG2_10}": "[sysvinit] Your init system has been upgraded.", + + //Format editor + "{FORMATEDITOR_COMMAND_LOWER}": "command", + "{FORMATEDITOR_ARGUMENT_LOWER}": "argument", + "{FORMATEDITOR_VALUE_LOWER}": "value", + "{FORMATEDITOR_ENTERSYNTAX}": "Enter syntax here", + + //Program commands + "{WO_ABOUT}": "about", + "{WO_ADDRESSBOOK}": "address_book", + "{WO_ARTPAD}": "artpad", + "{WO_AUDIOPLAYER}": "audio_player", + "{WO_CALCULATOR}": "calculator", + "{WO_DOWNLOADER}": "downloader", + "{WO_FILESKIMMER}": "file_skimmer", + "{WO_INSTALLER}": "installer", + "{WO_MINDBLOW}": "mindblow", + "{WO_NAMECHANGER}": "name_changer", + "{WO_PONG}": "pong", + "{WO_SHIFTER}": "shifter", + "{WO_SHIFTLETTERS}": "shiftletters", + "{WO_SHIFTLOTTO}": "shiftlotto", + "{WO_SHIFTNET}": "shiftnet", + "{WO_SHIFTORIUM}": "shiftorium", + "{WO_SHIFTSWEEPER}": "shiftsweeper", + "{WO_SIMPLESRC}": "simplesrc", + "{WO_TERMINAL}": "terminal", + "{WO_TEXTPAD}": "textpad", + + //BIOS screen text + "{MISC_BIOSCOPYRIGHT}": "AMIBIOS (C) %year Australian Microtrends, Plc", + "{MISC_BIOSVERSION}": "Shiftsoft 480-L ACPI BIOS Revision 1002", + "{MISC_BIOSCPU}": "CPU : VTC 210-N %cores CPU 1.33GHz", + "{MISC_CLOCKSPEED}": " Speed : 1.337Ghz", + "{MISC_RUNSETUP}": "Press DEL to run Setup", + "{MISC_BBSPOPUP}": "Press <F8> for BBS POPUP", + "{MISC_RAMFREQ}": "DDR3 Frequency 1337MHz, Dual Channel, Linear Mode", + "{MISC_CHECKINGNVRAM}": "Checking NVRAM", + "{MISC_RAM}": "1337 MB OK", +}
\ No newline at end of file diff --git a/ShiftOS.Frontend/Resources/strings_fr.txt b/ShiftOS.Frontend/Resources/strings_fr.txt new file mode 100644 index 0000000..1986bcf --- /dev/null +++ b/ShiftOS.Frontend/Resources/strings_fr.txt @@ -0,0 +1,259 @@ +/* + * ShiftOS French Language Pack + * + * This is a default language pack distributed within the game. + */ + +{ + //General strings + //These strings can be used anywhere in the UI where language context isn't necessary. + "{GEN_PROGRAMS}": "Les programmes", + "{GEN_COMMANDS}": "Les ordres", + "{GEN_OBJECTIVES}": "Les objectifs", + "{GEN_CURRENTPROCESSES}": "Les proc�dures actuelles", + "{GEN_WELCOME}": "Bienvenue au ShiftOS.", + "{GEN_SYSTEMNAME}": "Nom de syst�me", + "{GEN_PASSWORD}": "Mot de passe", + "{GEN_LPROMPT}": "ouverture de session de %sysname: ", + "{GEN_SYSTEMSTATUS}": "�tat de syst�me", + "{GEN_USERS}": "Personnes", + "{GEN_CODEPOINTS}": "Codepoints", + "{GEN_LOADING}": "Remplissons...", + "{GEN_SAVE}": "Garder", + "{GEN_LOAD}": "Remplir", + "{GEN_APPLY}": "Appliquer", + "{GEN_RESET}": "Rer�gler", + "{GEN_LOADDEFAULT}": "Remplir la valeur par d�fault", + "{GEN_CANCEL}": "Annuter", + "{GEN_CONTINUE}": "Continuer", + "{GEN_BACK}": "Retourner", + "{GEN_YES}": "Oui", + "{GEN_NO}": "Non", + "{GEN_OK}": "D'accord", + "{GEN_SETTINGS}": "Les param�tres", + "{GEN_ABOUT}": "Les d�tails", + "{GEN_EXIT}": "Sortir", + "{GEN_CLOSE}": "Se fermer", + + //General errors + //Syntax errors, command errors, you name it.. + "{ERR_BADBOOL}": "La valeur tu es entr� faut qu'�tre solt \"true\", solt \"false\".", + "{ERR_BADPERCENT}": "La valeur tu es entr� faut qu'�tre une valeur entre 0 et 100.", + "{ERR_NOLANG}": "La langue tu es entr� n'exister pas!", + "{ERR_NOTENOUGHCODEPOINTS}": "Tu n'as pas assez de Codepoints � faire cela.", + "{ERR_NOUPGRADE}": "Nous ne pouvons pas trouver cela mise � jour.", + "{ERR_GENERAL}": "Une erreur a appair� en faisons l'op�ration.", + "{ERR_EMPTYCATEGORY}": "La cat�gorie tu as pr�cis� solt n'a pas d'objets en la cat�gorie, solt n'a trouv� pas.", + "{ERR_NOMOREUPGRADES}": "Nous ne sommes pas de mises de jours!", + "{ERR_BADWINID}": "Pr�ciser une valeur en 0 et %max, s'il vous plait.", + "{ERR_USERFOUND}": "�a personne d�j� existe.", + "{ERR_NOUSER}": "�a personne n'a trouv�.", + "{ERR_REMOVEYOU}": "Tu ne peux pas enlever ton compte de syst�me.", + "{ERR_BADACL}": "Pr�ciser une valeur en 0 (invit�) et 3 (dieu) pour une permission de personne.", + "{ERR_ACLHIGHERVALUE}": "Tu ne peux pas mettre une permission de personne au une valeur meilleure que ta permission.", + "{ERR_HIGHERPERMS}": "�a personne a meilleure permissions que toi!", + "{ERR_PASSWD_MISMATCH}": "Les mots de passe n'asscocier pas!", + "{ERR_SYNTAXERROR}": "Erreur de syntaxe.", + + //Command results + "{RES_ACLUPDATED}": "Permissions de personne ont appliqu�.", + "{RES_LANGUAGE_CHANGED}": "La langue de syst�me a chang�.", + "{RES_NOOBJECTIVES}": "Nous ne sommes pas des objectifs afficher!", + "{RES_UPGRADEINSTALLED}": "Mise de jour a appliqu�!", + "{RES_WINDOWCLOSED}": "La programme a se ferm�.", + "{RES_CREATINGUSER}": "Nous cr�ons une nouvelle compte avec le nom %name.", + "{RES_REMOVINGUSER}": "Nous sortissons la compte avec le nom %name depuis ton syst�me.", + "{RES_DENIED}": "L'acc�s a d�menti.", + "{RES_GRANTED}": "L'acc�s a accord�.", + "{RES_PASSWD_SET}": "Le mot de passe a appliqu�.", + + //Shiftorium messages. + "{SHFM_UPGRADE}": "%id - %cost Codepoints", + "{SHFM_CATEGORY}": " - %name: %available mises de jours restants.", + "{SHFM_QUERYERROR}": "Erreur en mettre en doubte de Shiftorium.", + "{SHFM_NOUPGRADES}": "Z�ro mises de jours restants!", + + //Command data strings + "{COM_STATUS}": "ShiftOS build %version\r\n\r\nCodepoints: %cp \r\n Upgrades: %installed installed, %available available\r\n\r\n", + "{COM_UPGRADEINFO}": "%category: %name - %cost Codepoints.\r\n\r\n%description\r\n\r\nUpgrade ID: %id", + + //Terminal Command Descriptions + //These strings show up when running the "commands" command in your Terminal. + "{DESC_CLEAR}": "Clears the screen of the current Terminal.", + "{DESC_SETSFXENABLED}": "Sets whether or not system sounds are enabled.", + "{DESC_SETMUSICENABLED}": "Sets whether or not music is enabled in ShiftOS.", + "{DESC_SETVOLUME}": "Sets the volume of sounds and music if they're enabled.", + "{DESC_SHUTDOWN}": "Safely shuts down your computer.", + "{DESC_LANG}": "Change the system language of ShiftOS.", + "{DESC_COMMANDS}": "Shows a list of Terminal commands inside ShiftOS.", + "{DESC_HELP}": "Type this command to get general help with using ShiftOS.", + "{DESC_SAVE}": "Saves the in-memory configuration of ShiftOS.", + "{DESC_STATUS}": "Shows basic status information such as how many Codepoints you have and your current objective.", + "{DESC_BUY}": "Buys the specified Shiftorium upgrade.", + "{DESC_BULKBUY}": "Buys the specified Shiftorium upgrades in bulk.", + "{DESC_UPGRADEINFO}": "Shows information about the specified Shiftorium upgrade.", + "{DESC_UPGRADECATEGORIES}": "Shows all the available Shiftorium categories and how many upgrades are available in them.", + "{DESC_UPGRADES}": "Shows a list of available Shiftorium upgrades.", + "{DESC_PROCESSES}": "Shows a list of currently running app processes.", + "{DESC_PROGRAMS}": "Shows a list of programs you can run.", + "{DESC_CLOSE}": "Closes the specified application window.", + "{DESC_ADDUSER}": "Add a user to your system.", + "{DESC_REMOVEUSER}": "Remove a user from your computer.", + "{DESC_SETUSERPERMISSIONS}": "Set the permissions of a user.", + "{DESC_USERS}": "Lists all users on your computer.", + "{DESC_SU}": "Change your identity to another user's.", + "{DESC_PASSWD}": "Change your user account password.", + "{DESC_NAMECHANGER}": "Change the name of virtually any program within ShiftOS!", + "{DESC_FILENOTFOUND}": "The file you requested could not be found.", + + //Window titles. + "{TITLE_FILENOTFOUND}": "File not found.", + "{TITLE_PONG_YOULOSE}": "You lose", + "{TITLE_CODEPOINTSTRANSFERRED}": "Codepoints transferred.", + "{TITLE_INVALIDPORT}": "Invalid port number.", + "{TITLE_ENTERSYSNAME}": "Enter system name", + "{TITLE_INVALIDNAME}": "Invalid name", + "{TITLE_VALUETOOSMALL}": "Value too small.", + "{TITLE_TEMPLATE}": "Template", + "{TITLE_SKINLOADER}": "Skin loader", + "{TITLE_NAMECHANGER}": "Name Changer", + "{TITLE_ADDRESSBOOK}": "Address Book", + "{TITLE_ARTPAD}": "ArtPad", + "{TITLE_SIMPLESRC}": "SimpleSRC Client", + "{TITLE_INFOBOX}": "Information", + "{TITLE_COLORPICKER}": "Choose color", + "{TITLE_AUDIOPLAYER}": "Audio Player", + "{TITLE_CALCULATOR}": "Calculator", + "{TITLE_DOWNLOADER}": "Downloader", + "{TITLE_ABOUT}": "About ShiftOS", + "{TITLE_FILESKIMMER}": "File Skimmer", + "{TITLE_CHOOSEGRAPHIC}": "Choose graphic", + "{TITLE_ICONMANAGER}": "Icon Manager", + "{TITLE_INSTALLER}": "Installer", + "{TITLE_MINDBLOW}": "MindBlow", + "{TITLE_PONG}": "Pong", + "{TITLE_SHIFTER}": "Shifter", + "{TITLE_SHIFTLETTERS}": "ShiftLetters", + "{TITLE_SHIFTLOTTO}": "ShiftLotto", + "{TITLE_SHIFTNET}": "Shiftnet", + "{TITLE_SHIFTORIUM}": "Shiftorium", + "{TITLE_SHIFTSWEEPER}": "ShiftSweeper", + "{TITLE_TERMINAL}": "Terminal", + "{TITLE_TEXTPAD}": "TextPad", + "{TITLE_RESTARTREQUIRED}": "Restart required.", + + + //App Launcher categories + "{AL_PROGRAMMING}": "Programming", + "{AL_CUSTOMIZATION}": "Customization", + "{AL_NETWORKING}": "Networking", + "{AL_UTILITIES}": "Utilities", + "{AL_GRAPHICS}": "Graphics", + "{AL_ACCESSORIES}": "Accessories", + "{AL_ENTERTAINMENT}": "Entertainment", + "{AL_OFFICE}": "Office", + "{AL_GAMES}": "Games", + + //Infobox prompt messages + "{PROMPT_PONGLOST}": "You lost this game of Pong. Guess you should've cashed out...", + "{PROMPT_CODEPOINTSTRANSFERRED}": "%transferrer has transferred %amount Codepoints to your system.", + "{PROMPT_INVALIDPORT}": "The Digital Society Port must be a valid whole number between 0 and 65535.", + "{PROMPT_ENTERSYSNAME}": "Please enter a system name for your computer.", + "{PROMPT_INVALIDNAME}": "The name you entered cannot be blank. Please enter another name.", + "{PROMPT_SMALLSYSNAME}": "Your system name must have at least 5 characters in it.", + "{PROMPT_RESTARTREQUIRED}": "For the changes you made to take effect, a restart of ShiftOS is required.", + + //Pong + "{PONG_LEVELREACHED}": "You've reached level %level!", + "{PONG_BEATAI}": "You've beaten the opponent! %amount Codepoints!", + "{PONG_STATUSCP}": "Codepoints: %cp", + "{PONG_STATUSLEVEL}": "Level %level. %time seconds to go!", + "{PONG_PLAY}": "Play some Pong!", + "{PONG_DESC}": "Pong is the modern-day recreation of the classic 70s game called, you guessed it, Pong.\r\n\r\nIn Pong, you must try your best to keep the fast-moving ball from going past your side of the screen using your mouse to move your paddle in the way of the ball, while getting the ball to go past the opponent's paddle.\r\n\r\nOnce you start Pong, you have 60 seconds to beat the opponent and survive as much as possible. The game will get faster as you play, and the more 60-second levels you play, the more Codepoints you'll earn. If you lose the level, you will start back at Level 1 with 0 Codepoints. At the end of the level, you may choose to cash out or play on.", + "{PONG_WELCOME}": "Welcome to Pong.", + "{PONG_BEATLEVELDESC}": "You have survived this level of Pong. You now have a chance to cash out your Codepoints or play on for more.", + "{PONG_PLAYON}": "Play on", + "{PONG_CASHOUT}": "Cash out", + + //Main menu tip messages + "{MAINMENU_TIPTEXT_0}": "Press CTRL+T to open a Terminal from any application within ShiftOS.", + "{MAINMENU_TIPTEXT_1}": "Codepoints can be used in the Shiftorium to buy upgrades for your system using the \"buy\" command.", + "{MAINMENU_TIPTEXT_2}": "The Shifter is a VERY powerful application. Not only can you customize ShiftOS to look however you want, but you'll earn heaps of Codepoints while you do so.", + "{MAINMENU_TIPTEXT_3}": "Not the language you want? Head over to \"Settings\" to choose the proper language for you. You may need to head to the forums if your language isn't supported.", + "{MAINMENU_TIPTEXT_4}": "Sandbox Mode is a nice way to use ShiftOS without having to progress through the main campaign. There's no Codepoints, no upgrades, and everything's unlocked.", + "{MAINMENU_TIPTEXT_5}": "The Skin Loader is an essential application. It can load in skins from anywhere - from your personal collection and from the forums, and you can even generate your own sharable skins for others to use!", + "{MAINMENU_TIPTEXT_6}": "ArtPad not cutting it for you? You can use any image editor you like on your host system and just import the assets into the ShiftOS Shared Folder to use in your skins.", + "{MAINMENU_TIPTEXT_7}": "What is that \"System Color Key-OUt\" you speak of, Shifter? Well, when composing skin assets, use that color to mark areas to be rendered as transparent!", + "{MAINMENU_TIPTEXT_8}": "Use the Format Editor to change the way Terminal commands are interpreted.", + "{MAINMENU_TIPTEXT_9}": "The Shiftnet is a wonderland of applications, games and services - only available in ShiftOS. Feel free to explore!", + + //Main menu - Settings + "{MAINMENU_DSADDRESS}": "Digital Society address: ", + "{MAINMENU_DSPORT": "Digital Society port: ", + "{MAINMENU_LANGUAGE}": "Language:", + + //Main Menu - General text + "{MAINMENU_TITLE}": "Main menu", + "{MAINMENU_CAMPAIGN}": "Campaign", + "{MAINMENU_SANDBOX}": "Sandbox", + "{MAINMENU_NEWGAME}": "New game", + + //Miscelaneous strings + "{MISC_KERNELVERSION}": "ShiftKernel - v0.9.4", + "{MISC_KERNELBOOTED}": "[sys] Kernel startup completed.", + "{MISC_SHIFTFSDRV}": "[sfs] ShiftFS core driver, version 2.7", + "{MISC_SHIFTFSBLOCKSREAD}": "[sfs] Driver initiated. 4096 blocks read.", + "{MISC_LOADINGCONFIG}": "[confd] Loading system configuration... success", + "{MISC_BUILDINGCMDS}": "[termdb] Terminal database is being parsed...", + "{MISC_CONNECTINGTONETWORK}": "[inetd] Connecting to network...", + "{MISC_CONNECTIONSUCCESSFUL}": "[inetd] Connection successful.", + "{MISC_DHCPHANDSHAKEFINISHED}": "[inetd] DHCP handshake finished.", + "{MISC_NONETWORK}": "[inetd] No network access points found.", + "{MISC_SANDBOXMODE}": "[sos] Sandbox Mode initiating...", + "{MISC_ACCEPTINGLOGINS}": "[systemd] Accepting logins on local tty1.", + + //ShiftOS engine strings + "{ENGINE_CANNOTLOADSAVE}": "[sos] Error. Cannot load user save file.", + + //Pre-connection loading messages + "{LOADINGMSG1_0}": "[systemd] The light is so bright...", + "{LOADINGMSG1_1}": "[systemd] Hold your colors...", + "{LOADINGMSG1_2}": "[systemd] Time to shift it my way...", + "{LOADINGMSG1_3}": "[systemd] Does anybody even read this?", + "{LOADINGMSG1_4}": "[systemd] I....just wanna play it right... We're....gonna get there tonight...", + "{LOADINGMSG1_5}": "[systemd] I'm a computer.", + "{LOADINGMSG1_6}": "[systemd] What ya gonna do, what ya gonna do, when DevX comes for you?", + "{LOADINGMSG1_7}": "[systemd] Artificial intelligence do everything now.", + "{LOADINGMSG1_8}": "[systemd] Nobody is really here.", + "{LOADINGMSG1_9}": "[systemd] I so want a giant cake for breakfast.", + + //Post-connection loading messages + "{LOADINGMSG2_0}": "[systemd] It's all yours, Shifter.", + "{LOADINGMSG2_1}": "[systemd] That's a nice network you got there...", + "{LOADINGMSG2_2}": "[systemd] Leave me alone and go earn some Codepoints.", + "{LOADINGMSG2_3}": "[systemd] I'm hungry... Cake would be appreciated.", + "{LOADINGMSG2_4}": "[systemd] SEVERE: CAKE NOT FOUND. PLEASE INSERT CAKE INTO DRIVE 1:/ AND PRESS ANY KEY TO CONTINUE.", + "{LOADINGMSG2_5}": "[systemd] Now SCREAM!", + "{LOADINGMSG2_6}": "[systemd] Yes, I'd like to order a giant 6-mile-long tripple-chocolate ice cream cake?", + "{LOADINGMSG2_7}": "[systemd] There's no antidote...", + "{LOADINGMSG2_8}": "[systemd] Can I at least have a muffin?", + "{LOADINGMSG2_9}": "[systemd] System initiated, but I still want a cake.", + "{LOADINGMSG2_10}": "[sysvinit] Your init system has been upgraded.", + + //Format editor + "{FORMATEDITOR_COMMAND_LOWER}": "command", + "{FORMATEDITOR_ARGUMENT_LOWER}": "argument", + "{FORMATEDITOR_VALUE_LOWER}": "value", + "{FORMATEDITOR_ENTERSYNTAX}": "Enter syntax here", + + //BIOS screen text + "{MISC_BIOSCOPYRIGHT}": "AMIBIOS (C) %year Australian Microtrends, Plc", + "{MISC_BIOSVERSION}": "Shiftsoft 480-L ACPI BIOS Revision 1002", + "{MISC_BIOSCPU}": "CPU : VTC 210-N %cores CPU 1.33GHz", + "{MISC_CLOCKSPEED}": " Speed : 1.337Ghz", + "{MISC_RUNSETUP}": "Press DEL to run Setup", + "{MISC_BBSPOPUP}": "Press <F8> for BBS POPUP", + "{MISC_RAMFREQ}": "DDR3 Frequency 1337MHz, Dual Channel, Linear Mode", + "{MISC_CHECKINGNVRAM}": "Checking NVRAM", + "{MISC_RAM}": "1337 MB OK", +}
\ No newline at end of file diff --git a/ShiftOS.Frontend/ShiftOS.Frontend.csproj b/ShiftOS.Frontend/ShiftOS.Frontend.csproj new file mode 100644 index 0000000..709b7c8 --- /dev/null +++ b/ShiftOS.Frontend/ShiftOS.Frontend.csproj @@ -0,0 +1,185 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$(MSBuildExtensionsPath)\MonoGame\v3.0\MonoGame.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\MonoGame\v3.0\MonoGame.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProductVersion>8.0.30703</ProductVersion> + <SchemaVersion>2.0</SchemaVersion> + <ProjectGuid>{4DFC3088-1B08-4A0E-A9F5-483A7B9811FD}</ProjectGuid> + <OutputType>WinExe</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>ShiftOS.Frontend</RootNamespace> + <AssemblyName>ShiftOS.Frontend</AssemblyName> + <FileAlignment>512</FileAlignment> + <MonoGamePlatform>DesktopGL</MonoGamePlatform> + <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'"> + <DebugSymbols>true</DebugSymbols> + <OutputPath>bin\$(MonoGamePlatform)\$(Platform)\$(Configuration)\</OutputPath> + <DefineConstants>DEBUG;TRACE;LINUX</DefineConstants> + <DebugType>full</DebugType> + <PlatformTarget>AnyCPU</PlatformTarget> + <ErrorReport>prompt</ErrorReport> + <Prefer32Bit>false</Prefer32Bit> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'"> + <OutputPath>bin\$(MonoGamePlatform)\$(Platform)\$(Configuration)\</OutputPath> + <DefineConstants>TRACE;LINUX</DefineConstants> + <Optimize>true</Optimize> + <DebugType>pdbonly</DebugType> + <PlatformTarget>AnyCPU</PlatformTarget> + <ErrorReport>prompt</ErrorReport> + <Prefer32Bit>false</Prefer32Bit> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup> + <ApplicationIcon>Icon.ico</ApplicationIcon> + </PropertyGroup> + <PropertyGroup> + <ApplicationManifest>app.manifest</ApplicationManifest> + </PropertyGroup> + <ItemGroup> + <Compile Include="Apps\CodeShop.cs" /> + <Compile Include="Apps\Pong.cs" /> + <Compile Include="Apps\Terminal.cs" /> + <Compile Include="Commands.cs" /> + <Compile Include="Desktop\Desktop.cs" /> + <Compile Include="Desktop\WindowManager.cs" /> + <Compile Include="GraphicsSubsystem\GraphicsContext.cs" /> + <Compile Include="GraphicsSubsystem\UIManager.cs" /> + <Compile Include="GUI\Button.cs" /> + <Compile Include="GUI\Control.cs" /> + <Compile Include="GUI\ItemGroup.cs" /> + <Compile Include="GUI\ListBox.cs" /> + <Compile Include="GUI\PictureBox.cs" /> + <Compile Include="GUI\ProgressBar.cs" /> + <Compile Include="GUI\TextControl.cs" /> + <Compile Include="GUI\TextInput.cs" /> + <Compile Include="Infobox.cs" /> + <Compile Include="MonoGameLanguageProvider.cs" /> + <Compile Include="Properties\Resources.Designer.cs"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>Resources.resx</DependentUpon> + </Compile> + <Compile Include="ShiftOS.cs" /> + <Compile Include="Program.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + <Compile Include="Window.cs" /> + </ItemGroup> + <ItemGroup> + <Reference Include="MonoGame.Framework"> + <HintPath>$(MonoGameInstallDirectory)\MonoGame\v3.0\Assemblies\DesktopGL\MonoGame.Framework.dll</HintPath> + </Reference> + <Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> + <HintPath>..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="System" /> + <Reference Include="System.Drawing" /> + <Reference Include="System.Windows.Forms" /> + <Reference Include="System.Xml" /> + <Reference Include="TrueTypeSharp, Version=1.0.5.0, Culture=neutral, processorArchitecture=MSIL"> + <HintPath>..\packages\TrueTypeSharp.1.0.5\lib\net20\TrueTypeSharp.dll</HintPath> + <Private>True</Private> + </Reference> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="Icon.ico" /> + <EmbeddedResource Include="Icon.bmp" /> + <EmbeddedResource Include="Properties\Resources.resx"> + <Generator>ResXFileCodeGenerator</Generator> + <LastGenOutput>Resources.Designer.cs</LastGenOutput> + </EmbeddedResource> + </ItemGroup> + <ItemGroup> + <MonoGameContentReference Include="Content\Content.mgcb" /> + <None Include="$(MonoGameInstallDirectory)\MonoGame\v3.0\Assemblies\DesktopGL\x86\SDL2.dll"> + <Link>x86\SDL2.dll</Link> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </None> + <None Include="$(MonoGameInstallDirectory)\MonoGame\v3.0\Assemblies\DesktopGL\x64\SDL2.dll"> + <Link>x64\SDL2.dll</Link> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </None> + <None Include="$(MonoGameInstallDirectory)\MonoGame\v3.0\Assemblies\DesktopGL\x86\soft_oal.dll"> + <Link>x86\soft_oal.dll</Link> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </None> + <None Include="$(MonoGameInstallDirectory)\MonoGame\v3.0\Assemblies\DesktopGL\x64\soft_oal.dll"> + <Link>x64\soft_oal.dll</Link> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </None> + <None Include="$(MonoGameInstallDirectory)\MonoGame\v3.0\Assemblies\DesktopGL\x86\libSDL2-2.0.so.0"> + <Link>x86\libSDL2-2.0.so.0</Link> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </None> + <None Include="$(MonoGameInstallDirectory)\MonoGame\v3.0\Assemblies\DesktopGL\x64\libSDL2-2.0.so.0"> + <Link>x64\libSDL2-2.0.so.0</Link> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </None> + <None Include="$(MonoGameInstallDirectory)\MonoGame\v3.0\Assemblies\DesktopGL\x86\libopenal.so.1"> + <Link>x86\libopenal.so.1</Link> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </None> + <None Include="$(MonoGameInstallDirectory)\MonoGame\v3.0\Assemblies\DesktopGL\x64\libopenal.so.1"> + <Link>x64\libopenal.so.1</Link> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </None> + <None Include="$(MonoGameInstallDirectory)\MonoGame\v3.0\Assemblies\DesktopGL\libSDL2-2.0.0.dylib"> + <Link>libSDL2-2.0.0.dylib</Link> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </None> + <None Include="$(MonoGameInstallDirectory)\MonoGame\v3.0\Assemblies\DesktopGL\libopenal.1.dylib"> + <Link>libopenal.1.dylib</Link> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </None> + <None Include="$(MonoGameInstallDirectory)\MonoGame\v3.0\Assemblies\DesktopGL\MonoGame.Framework.dll.config"> + <Link>MonoGame.Framework.dll.config</Link> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </None> + <None Include="app.config" /> + <None Include="app.manifest" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\ShiftOS.Objects\ShiftOS.Objects.csproj"> + <Project>{a069089a-8962-4607-b2b2-4cf4a371066e}</Project> + <Name>ShiftOS.Objects</Name> + </ProjectReference> + <ProjectReference Include="..\ShiftOS_TheReturn\ShiftOS.Engine.csproj"> + <Project>{7c979b07-0585-4033-a110-e5555b9d6651}</Project> + <Name>ShiftOS.Engine</Name> + </ProjectReference> + </ItemGroup> + <ItemGroup> + <None Include="packages.config" /> + <None Include="Resources\cursor_9x_pointer.png" /> + </ItemGroup> + <ItemGroup> + <None Include="Resources\justthes.png" /> + </ItemGroup> + <ItemGroup> + <None Include="Resources\strings_en.txt" /> + </ItemGroup> + <ItemGroup> + <None Include="Resources\strings_fr.txt" /> + </ItemGroup> + <ItemGroup> + <None Include="Resources\strings_de.txt" /> + </ItemGroup> + <ItemGroup> + <None Include="Resources\Shiftorium.txt" /> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <Import Project="$(MSBuildExtensionsPath)\MonoGame\v3.0\MonoGame.Content.Builder.targets" /> + <!-- To modify your build process, add your task inside one of the targets below and uncomment it. + Other similar extension points exist, see Microsoft.Common.targets. + <Target Name="BeforeBuild"> + </Target> + <Target Name="AfterBuild"> + </Target> + --> +</Project>
\ No newline at end of file diff --git a/ShiftOS.Frontend/ShiftOS.cs b/ShiftOS.Frontend/ShiftOS.cs new file mode 100644 index 0000000..56ffc38 --- /dev/null +++ b/ShiftOS.Frontend/ShiftOS.cs @@ -0,0 +1,308 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; +using Newtonsoft.Json; +using ShiftOS.Engine; +using ShiftOS.Frontend.GraphicsSubsystem; + +namespace ShiftOS.Frontend +{ + /// <summary> + /// This is the main type for your game. + /// </summary> + public class ShiftOS : Game + { + GraphicsDeviceManager GraphicsDevice; + SpriteBatch spriteBatch; + + private bool DisplayDebugInfo = false; + + public ShiftOS() + { + GraphicsDevice = new GraphicsDeviceManager(this); + GraphicsDevice.PreferredBackBufferHeight = 1080; + GraphicsDevice.PreferredBackBufferWidth = 1920; + SkinEngine.SkinLoaded += () => + { + UIManager.ResetSkinTextures(GraphicsDevice.GraphicsDevice); + UIManager.InvalidateAll(); + }; + UIManager.Viewport = new System.Drawing.Size( + GraphicsDevice.PreferredBackBufferWidth, + GraphicsDevice.PreferredBackBufferHeight + ); + + Content.RootDirectory = "Content"; + + + //Make window borderless + Window.IsBorderless = false; + + //Set the title + Window.Title = "ShiftOS"; + + + + //Fullscreen + GraphicsDevice.IsFullScreen = false; + + } + + private GUI.TextControl _titleLabel = null; + private Keys lastKey = Keys.None; + + + /// <summary> + /// Allows the game to perform any initialization it needs to before starting to run. + /// This is where it can query for any required services and load any non-graphic + /// related content. Calling base.Initialize will enumerate through any components + /// and initialize them as well. + /// </summary> + protected override void Initialize() + { + //Before we do ANYTHING, we've got to initiate the ShiftOS engine. + UIManager.GraphicsDevice = GraphicsDevice.GraphicsDevice; + + //Let's get localization going. + Localization.RegisterProvider(new MonoGameLanguageProvider()); + + //First things first, let's initiate the window manager. + AppearanceManager.Initiate(new Desktop.WindowManager()); + //Cool. Now the engine's window management system talks to us. + + //Also initiate the desktop + Engine.Desktop.Init(new Desktop.Desktop()); + + //Now we can initiate the Infobox subsystem + Engine.Infobox.Init(new Infobox()); + + + + //Let's initiate the engine just for a ha. + + TerminalBackend.TerminalRequested += () => + { + AppearanceManager.SetupWindow(new Apps.Terminal()); + }; + + //We'll use sandbox mode + SaveSystem.IsSandbox = false; + Engine.Infobox.Show("Test window", "This is a test window."); + SaveSystem.Begin(true); + + base.Initialize(); + + } + + private double timeSinceLastPurge = 0; + + private Texture2D MouseTexture = null; + + /// <summary> + /// LoadContent will be called once per game and is the place to load + /// all of your content. + /// </summary> + protected override void LoadContent() + { + // Create a new SpriteBatch, which can be used to draw textures. + this.spriteBatch = new SpriteBatch(base.GraphicsDevice); + + UIManager.ResetSkinTextures(GraphicsDevice.GraphicsDevice); + + + // TODO: use this.Content to load your game content here + var bmp = Properties.Resources.cursor_9x_pointer; + var _lock = bmp.LockBits(new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); + byte[] rgb = new byte[Math.Abs(_lock.Stride) * _lock.Height]; + Marshal.Copy(_lock.Scan0, rgb, 0, rgb.Length); + bmp.UnlockBits(_lock); + MouseTexture = new Texture2D(GraphicsDevice.GraphicsDevice, bmp.Width, bmp.Height); + MouseTexture.SetData<byte>(rgb); + rgb = null; + } + + /// <summary> + /// UnloadContent will be called once per game and is the place to unload + /// game-specific content. + /// </summary> + protected override void UnloadContent() + { + MouseTexture = null; + // TODO: Unload any non ContentManager content here + } + + private double kb_elapsedms = 0; + + private MouseState LastMouseState; + /// <summary> + /// Allows the game to run logic such as updating the world, + /// checking for collisions, gathering input, and playing audio. + /// </summary> + /// <param name="gameTime">Provides a snapshot of timing values.</param> + protected override void Update(GameTime gameTime) + { + if (UIManager.CrossThreadOperations.Count > 0) + { + var action = UIManager.CrossThreadOperations.Dequeue(); + action?.Invoke(); + } + + //Let's get the mouse state + var mouseState = Mouse.GetState(this.Window); + LastMouseState = mouseState; + UIManager.ProcessMouseState(LastMouseState); + + //So we have mouse input, and the UI layout system working... + + //But an OS isn't useful without the keyboard! + + //Let's see how keyboard input works. + + //Hmmm... just like the mouse... + var keystate = Keyboard.GetState(); + + //Simple... just iterate through this list and generate some key events? + var keys = keystate.GetPressedKeys(); + if (keys.Length > 0) + { + var key = keys.FirstOrDefault(x => x != Keys.LeftControl && x != Keys.RightControl && x != Keys.LeftShift && x != Keys.RightShift && x != Keys.LeftAlt && x != Keys.RightAlt); + if(lastKey != key) + { + kb_elapsedms = 0; + lastKey = key; + } + } + if (keystate.IsKeyDown(lastKey)) + { + if (kb_elapsedms == 0 || kb_elapsedms >= 500) + { + if (lastKey == Keys.F11) + { + GraphicsDevice.IsFullScreen = !GraphicsDevice.IsFullScreen; + GraphicsDevice.ApplyChanges(); + } + else + { + var shift = keystate.IsKeyDown(Keys.LeftShift) || keystate.IsKeyDown(Keys.RightShift); + var alt = keystate.IsKeyDown(Keys.LeftAlt) || keystate.IsKeyDown(Keys.RightAlt); + var control = keystate.IsKeyDown(Keys.LeftControl) || keystate.IsKeyDown(Keys.RightControl); + + if (control && lastKey == Keys.D) + { + DisplayDebugInfo = !DisplayDebugInfo; + } + else if(control && lastKey == Keys.E) + { + UIManager.ExperimentalEffects = !UIManager.ExperimentalEffects; + } + else + { + var e = new KeyEvent(control, alt, shift, lastKey); + UIManager.ProcessKeyEvent(e); + } + } + } + kb_elapsedms += gameTime.ElapsedGameTime.TotalMilliseconds; + } + else + { + kb_elapsedms = 0; + } + + //Cause layout update on all elements + UIManager.LayoutUpdate(); + + timeSinceLastPurge += gameTime.ElapsedGameTime.TotalSeconds; + + if(timeSinceLastPurge > 30) + { + GraphicsContext.StringCaches.Clear(); + timeSinceLastPurge = 0; + } + + base.Update(gameTime); + } + + private GUI.TextControl framerate = new GUI.TextControl(); + + /// <summary> + /// This is called when the game should draw itself. + /// </summary> + /// <param name="gameTime">Provides a snapshot of timing values.</param> + protected override void Draw(GameTime gameTime) + { + UIManager.DrawControlsToTargets(GraphicsDevice.GraphicsDevice, spriteBatch, 0, 0); + + var rasterizerState = new RasterizerState(); + rasterizerState.CullMode = CullMode.None; + rasterizerState.MultiSampleAntiAlias = true; + + spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.NonPremultiplied, + SamplerState.LinearClamp, DepthStencilState.Default, + rasterizerState); + //Draw the desktop BG. + UIManager.DrawBackgroundLayer(GraphicsDevice.GraphicsDevice, spriteBatch, 640, 480); + + //The desktop is drawn, now we can draw the UI. + UIManager.DrawTArgets(spriteBatch); + + //Draw a mouse cursor + + + + var mousepos = Mouse.GetState(this.Window).Position; + spriteBatch.Draw(MouseTexture, new Rectangle(mousepos.X+1, mousepos.Y+1, MouseTexture.Width, MouseTexture.Height), Color.Black * 0.5f); + spriteBatch.Draw(MouseTexture, new Rectangle(mousepos.X, mousepos.Y, MouseTexture.Width, MouseTexture.Height), Color.White); + + if (DisplayDebugInfo) + { + var gfxContext = new GraphicsContext(GraphicsDevice.GraphicsDevice, spriteBatch, 0, 0, GraphicsDevice.PreferredBackBufferWidth, GraphicsDevice.PreferredBackBufferHeight); + var color = Color.White; + double fps = Math.Round(1 / gameTime.ElapsedGameTime.TotalSeconds); + if (fps <= 20) + color = Color.Red; + gfxContext.DrawString($@"ShiftOS 1.0 Beta 4 +Copyright (c) 2017 Michael VanOverbeek, Rylan Arbour, RogueAI +This is an unstable build. +FPS: {(fps)} +An FPS below 20 can cause glitches in keyboard and mouse handling. It is advised that if you are getting these +framerates, press CTRL+E to disable fancy effects, close any apps you are not using, and try running in windowed mode +or in a lower resolution. + +If all else fails, you can set a breakpoint somewhere in the ShiftOS.Update() or ShiftOS.Draw() methods in the game's source +code and use Visual Studio's debugger to step through the code to find bottlenecks. + +If a method takes more than 30 milliseconds to complete, that is a sign that it is bottlenecking the game and may need to be +optimized. + +Try using the SkinTextures cache when rendering skin elements, and try using the GraphicsContext.DrawString() method when drawing +text. In this build, we are aware that this method causes bottlenecking, we are working on a caching system for fonts so we do not need to +use the System.Drawing.Graphics class to draw text. + +UI render target count: {UIManager.TextureCaches.Count} +Skin texture caches: {UIManager.SkinTextures.Count} +Open windows (excluding dialog boxes): {AppearanceManager.OpenForms.Count} + +Experimental effects enabled: {UIManager.ExperimentalEffects} +Fullscreen: {GraphicsDevice.IsFullScreen} +Game resolution: {GraphicsDevice.PreferredBackBufferWidth}x{GraphicsDevice.PreferredBackBufferHeight}", 0, 0, color, new System.Drawing.Font("Lucida Console", 9F, System.Drawing.FontStyle.Bold)); + } + + spriteBatch.End(); + base.Draw(gameTime); + } + } + + [ShiftoriumProvider] + public class MonoGameShiftoriumProvider : IShiftoriumProvider + { + public List<ShiftoriumUpgrade> GetDefaults() + { + return JsonConvert.DeserializeObject<List<ShiftoriumUpgrade>>(Properties.Resources.Shiftorium); + } + } +} diff --git a/ShiftOS.Frontend/Window.cs b/ShiftOS.Frontend/Window.cs new file mode 100644 index 0000000..4fd08a5 --- /dev/null +++ b/ShiftOS.Frontend/Window.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ShiftOS.Engine; + +namespace ShiftOS.Frontend +{ + public abstract class Window : IShiftOSWindow + { + public void OnLoad() + { + throw new NotImplementedException(); + } + + public void OnSkinLoad() + { + throw new NotImplementedException(); + } + + public bool OnUnload() + { + throw new NotImplementedException(); + } + + public void OnUpgrade() + { + throw new NotImplementedException(); + } + } +} diff --git a/ShiftOS.Frontend/app.config b/ShiftOS.Frontend/app.config new file mode 100644 index 0000000..dde2c3c --- /dev/null +++ b/ShiftOS.Frontend/app.config @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<configuration> + <runtime> + <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> + <dependentAssembly> + <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" /> + <bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" /> + </dependentAssembly> + </assemblyBinding> + </runtime> +</configuration>
\ No newline at end of file diff --git a/ShiftOS.Frontend/app.manifest b/ShiftOS.Frontend/app.manifest new file mode 100644 index 0000000..048d329 --- /dev/null +++ b/ShiftOS.Frontend/app.manifest @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> +<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1"> + <assemblyIdentity version="1.0.0.0" name="ShiftOS.Frontend"/> + <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2"> + <security> + <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3"> + <requestedExecutionLevel level="asInvoker" uiAccess="false" /> + </requestedPrivileges> + </security> + </trustInfo> + + <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> + <application> + <!-- A list of the Windows versions that this application has been tested on and is + is designed to work with. Uncomment the appropriate elements and Windows will + automatically selected the most compatible environment. --> + + <!-- Windows Vista --> + <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" /> + + <!-- Windows 7 --> + <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" /> + + <!-- Windows 8 --> + <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" /> + + <!-- Windows 8.1 --> + <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" /> + + <!-- Windows 10 --> + <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" /> + + </application> + </compatibility> + + <application xmlns="urn:schemas-microsoft-com:asm.v3"> + <windowsSettings> + <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware> + </windowsSettings> + </application> + +</assembly> diff --git a/ShiftOS.Frontend/packages.config b/ShiftOS.Frontend/packages.config new file mode 100644 index 0000000..223a8d3 --- /dev/null +++ b/ShiftOS.Frontend/packages.config @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<packages> + <package id="Newtonsoft.Json" version="10.0.3" targetFramework="net45" /> + <package id="TrueTypeSharp" version="1.0.5" targetFramework="net45" /> +</packages>
\ No newline at end of file |
