From 6ec4837523395accbb0ed8c9b206daa6882adc1a Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 4 Aug 2017 18:35:53 -0400 Subject: [PATCH] chat stuff and eas failure --- ShiftOS.Frontend/Apps/ChatClient.cs | 153 +++++++++++- ShiftOS.Frontend/Commands.cs | 24 ++ ShiftOS.Frontend/ShiftOS.cs | 265 ++++++++++++++------- ShiftOS.Frontend/Stories/BeginTutorials.cs | 52 ++++ ShiftOS.Objects/IRCNetwork.cs | 37 +++ ShiftOS.Objects/ShiftOS.Objects.csproj | 1 + ShiftOS_TheReturn/Story.cs | 8 + 7 files changed, 435 insertions(+), 105 deletions(-) create mode 100644 ShiftOS.Objects/IRCNetwork.cs diff --git a/ShiftOS.Frontend/Apps/ChatClient.cs b/ShiftOS.Frontend/Apps/ChatClient.cs index 1f685dc..ab4ea76 100644 --- a/ShiftOS.Frontend/Apps/ChatClient.cs +++ b/ShiftOS.Frontend/Apps/ChatClient.cs @@ -11,6 +11,7 @@ using ShiftOS.Frontend.GraphicsSubsystem; using Microsoft.Xna.Framework; using static ShiftOS.Engine.SkinEngine; using System.Text.RegularExpressions; +using System.Threading; namespace ShiftOS.Frontend.Apps { @@ -23,17 +24,12 @@ namespace ShiftOS.Frontend.Apps private TextInput _input = null; private Button _send = null; private List _messages = new List(); - - + const int usersListWidth = 100; + const int topicBarHeight = 24; + public IRCNetwork NetInfo = null; public ChatClient() { - _messages.Add(new ChatMessage - { - Timestamp = DateTime.Now, - Author = "michael", - Message = "Welcome to ShiftOS IRC! Type in the box below to type a message." - }); _send = new GUI.Button(); _input = new GUI.TextInput(); _sendprompt = new GUI.TextControl(); @@ -81,6 +77,16 @@ namespace ShiftOS.Frontend.Apps } } + public bool ChannelConnected + { + get; private set; + } + + public bool NetworkConnected + { + get; private set; + } + public void SendMessage() { _messages.Add(new Apps.ChatMessage @@ -150,13 +156,16 @@ namespace ShiftOS.Frontend.Apps protected override void OnPaint(GraphicsContext gfx) { + int messagesTop = NetworkConnected ? topicBarHeight : 0; + int messagesFromRight = ChannelConnected ? usersListWidth : 0; + int _bottomseparator = _send.Y - 10; gfx.DrawRectangle(0, _bottomseparator, Width, 1, UIManager.SkinTextures["ControlTextColor"]); int nnGap = 25; int messagebottom = _bottomseparator - 5; foreach (var msg in _messages.OrderByDescending(x=>x.Timestamp)) { - if (Height - messagebottom <= 0) + if (Height - messagebottom <= messagesTop) break; var tsProper = $"[{msg.Timestamp.Hour.ToString("##")}:{msg.Timestamp.Minute.ToString("##")}]"; var nnProper = $"<{msg.Author}>"; @@ -166,16 +175,134 @@ namespace ShiftOS.Frontend.Apps vertSeparatorLeft = (int)Math.Round(Math.Max(vertSeparatorLeft, tsMeasure.X + nnGap + nnMeasure.X+2)); if (old != vertSeparatorLeft) requiresRepaint = true; - var msgMeasure = gfx.MeasureString(msg.Message, LoadedSkin.TerminalFont, Width - vertSeparatorLeft - 4); + var msgMeasure = gfx.MeasureString(msg.Message, LoadedSkin.TerminalFont, (Width - vertSeparatorLeft - 4) - messagesFromRight); messagebottom -= (int)msgMeasure.Y; gfx.DrawString(tsProper, 0, messagebottom, LoadedSkin.ControlTextColor.ToMonoColor(), LoadedSkin.TerminalFont); - var nnColor = (msg.Author == SaveSystem.CurrentSave.Username) ? Color.Red : Color.LightGreen; + var nnColor = Color.LightGreen; + + if (msg.Author == SaveSystem.CurrentSave.Username) + nnColor = Color.Red; + else + { + if (NetInfo != null) + { + if (NetInfo.Channel != null) + { + if (NetInfo.Channel.OnlineUsers != null) + { + var user = NetInfo.Channel.OnlineUsers.FirstOrDefault(x => x.Nickname == msg.Author); + if(user != null) + { + switch(user.Permission) + { + case IRCPermission.ChanOp: + nnColor = Color.Orange; + break; + case IRCPermission.NetOp: + nnColor = Color.Yellow; + break; + } + } + } + } + } + } + gfx.DrawString(nnProper, (int)tsMeasure.X + nnGap, messagebottom, nnColor, LoadedSkin.TerminalFont); - gfx.DrawString(msg.Message, vertSeparatorLeft + 4, messagebottom, LoadedSkin.ControlTextColor.ToMonoColor(), LoadedSkin.TerminalFont, Width - vertSeparatorLeft - 4); + var mcolor = LoadedSkin.ControlTextColor.ToMonoColor(); + if (msg.Message.Contains(SaveSystem.CurrentSave.Username)) + mcolor = Color.Orange; + gfx.DrawString(msg.Message, vertSeparatorLeft + 4, messagebottom, mcolor, LoadedSkin.TerminalFont, (Width - vertSeparatorLeft - 4) - messagesFromRight); } - gfx.DrawRectangle(vertSeparatorLeft, 0, 1, _bottomseparator, UIManager.SkinTextures["ControlTextColor"]); + + string topic = ""; + if (NetworkConnected) + { + topic = $"{NetInfo.FriendlyName}: {NetInfo.MOTD}"; + if (ChannelConnected) + { + topic = $"#{NetInfo.Channel.Tag} | {NetInfo.Channel.Topic}"; + int usersStartY = messagesTop; + foreach(var user in NetInfo.Channel.OnlineUsers.OrderBy(x=>x.Nickname)) + { + var measure = gfx.MeasureString(user.Nickname, LoadedSkin.TerminalFont); + + var nnColor = Color.LightGreen; + if (user.Nickname == SaveSystem.CurrentSave.Username) + nnColor = Color.Red; + else + { + switch (user.Permission) + { + case IRCPermission.ChanOp: + nnColor = Color.Orange; + break; + case IRCPermission.NetOp: + nnColor = Color.Yellow; + break; + } + } + + gfx.DrawString(user.Nickname, Width - messagesFromRight + 2, usersStartY, nnColor, LoadedSkin.TerminalFont); + + usersStartY += (int)measure.Y; + } + gfx.DrawRectangle(Width - messagesFromRight, messagesTop, 1, _bottomseparator - messagesTop, LoadedSkin.ControlTextColor.ToMonoColor()); + } + gfx.DrawString(topic, 0, 0, LoadedSkin.ControlTextColor.ToMonoColor(), LoadedSkin.TerminalFont); + gfx.DrawRectangle(0, messagesTop, Width, 1, LoadedSkin.ControlTextColor.ToMonoColor()); + } + + gfx.DrawRectangle(vertSeparatorLeft, messagesTop, 1, _bottomseparator - messagesTop, UIManager.SkinTextures["ControlTextColor"]); } + public void FakeConnection(IRCNetwork net) + { + NetInfo = net; + var cs = net.Channel.OnlineUsers.FirstOrDefault(x => x.Nickname == "ChanServ"); + if (cs == null) + net.Channel.OnlineUsers.Add(new IRCUser + { + Nickname = "ChanServ", + Permission = IRCPermission.ChanOp + }); + var t = new Thread(() => + { + SendClientMessage("shiftos", $"Looking up {net.SystemName}"); + Thread.Sleep(250); + SendClientMessage("*", $"Connecting to {net.SystemName} ({net.SystemName}:6667)"); + Thread.Sleep(1500); + SendClientMessage("*", "Connected. Now logging in."); + Thread.Sleep(25); + SendClientMessage("*", "*** Looking up your hostname... "); + Thread.Sleep(2000); + SendClientMessage("*", "***Checking Ident"); + Thread.Sleep(10); + SendClientMessage("*", "*** Couldn't look up your hostname"); + Thread.Sleep(10); + SendClientMessage("*", "***No Ident response"); + Thread.Sleep(750); + SendClientMessage("*", "Capabilities supported: account-notify extended-join identify-msg multi-prefix sasl"); + Thread.Sleep(250); + SendClientMessage("*", "Capabilities requested: account-notify extended-join identify-msg multi-prefix"); + Thread.Sleep(250); + SendClientMessage("*", "Capabilities acknowledged: account-notify extended-join identify-msg multi-prefix"); + Thread.Sleep(500); + SendClientMessage("*", $"Welcome to the {net.FriendlyName} {SaveSystem.CurrentSave.Username}"); + NetworkConnected = true; + Thread.Sleep(250); + SendClientMessage("*", $"{SaveSystem.CurrentSave.Username} sets mode +i on {SaveSystem.CurrentSave.Username}"); + Thread.Sleep(300); + SendClientMessage("shiftos", "Joining #" + net.Channel.Tag); + Thread.Sleep(100); + ChannelConnected = true; + SendClientMessage("shiftos", $"{net.Channel.Topic}: {net.Channel.OnlineUsers.Count} users online"); + Thread.Sleep(10); + SendClientMessage("ChanServ", "ChanServ sets mode -v on " + SaveSystem.CurrentSave.Username); + }); + t.Start(); + } + public void OnLoad() { if (System.IO.File.Exists("aicache.dat")) diff --git a/ShiftOS.Frontend/Commands.cs b/ShiftOS.Frontend/Commands.cs index 67bf94f..e557ea2 100644 --- a/ShiftOS.Frontend/Commands.cs +++ b/ShiftOS.Frontend/Commands.cs @@ -44,6 +44,30 @@ using ShiftOS.Engine; namespace ShiftOS.Frontend { + public static class FrontendDebugCommands + { + /// + /// Debug command to drop a fatal objective/hack failure screen in the form of an emergency alert system-esque screen. + /// + /// ...Because WE'RE CANADA. + /// + [Command("drop_eas")] + [ShellConstraint("shiftos_debug> ")] + [RequiresArgument("id")] + public static void DropEAS(Dictionary args) + { + Story.DisplayFailure(args["id"].ToString()); + } + + [Command("loaddefaultskn")] + [ShellConstraint("shiftos_debug> ")] + public static void LoadDefault() + { + Utils.Delete(Paths.GetPath("skin.json")); + SkinEngine.Init(); + } + } + public static class Cowsay { [Command("cowsay")] diff --git a/ShiftOS.Frontend/ShiftOS.cs b/ShiftOS.Frontend/ShiftOS.cs index 597c4ff..5c897d3 100644 --- a/ShiftOS.Frontend/ShiftOS.cs +++ b/ShiftOS.Frontend/ShiftOS.cs @@ -19,10 +19,28 @@ namespace ShiftOS.Frontend internal GraphicsDeviceManager graphicsDevice; SpriteBatch spriteBatch; + private bool isFailing = false; + private double failFadeInMS = 0; + private const double failFadeMaxMS = 500; + private string failMessage = ""; + private string failRealMessage = ""; + private double failFadeOutMS = 0; + private bool failEnded = false; + private double failCharAddMS = 0; + private bool DisplayDebugInfo = false; public ShiftOS() { + Story.FailureRequested += (message) => + { + failMessage = ""; + failRealMessage = message; + isFailing = true; + failFadeInMS = 0; + failFadeOutMS = 0; + failEnded = false; + }; graphicsDevice = new GraphicsDeviceManager(this); var uconf = Objects.UserConfig.Get(); graphicsDevice.PreferredBackBufferHeight = uconf.ScreenHeight; @@ -154,115 +172,163 @@ namespace ShiftOS.Frontend /// Provides a snapshot of timing values. protected override void Update(GameTime gameTime) { - if (UIManager.CrossThreadOperations.Count > 0) + if (isFailing) { - var action = UIManager.CrossThreadOperations.Dequeue(); - action?.Invoke(); - } - - //Let's get the mouse state - var mouseState = Mouse.GetState(this.Window); - LastMouseState = mouseState; - - UIManager.ProcessMouseState(LastMouseState, mouseMS); - if (mouseState.LeftButton == ButtonState.Pressed) - { - mouseMS = 0; - } - else - { - mouseMS += gameTime.ElapsedGameTime.TotalMilliseconds; - - } - //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) + if (failFadeInMS < failFadeMaxMS) + failFadeInMS += gameTime.ElapsedGameTime.TotalMilliseconds; + if(failEnded == false) { - kb_elapsedms = 0; - lastKey = key; - } - } - if (keystate.IsKeyDown(lastKey)) - { - if (kb_elapsedms == 0 || kb_elapsedms >= 500) - { - if (lastKey == Keys.F11) + shroudOpacity = (float)GUI.ProgressBar.linear(failFadeInMS, 0, failFadeMaxMS, 0, 1); + if(shroudOpacity >= 1) { - UIManager.Fullscreen = !UIManager.Fullscreen; - } - 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) + if (failMessage == failRealMessage + "|") { - DisplayDebugInfo = !DisplayDebugInfo; + var keydata = Keyboard.GetState(); + + if (keydata.GetPressedKeys().FirstOrDefault(x => x != Keys.None) != Keys.None) + { + failEnded = true; + } } - else if(control && lastKey == Keys.E) - { - UIManager.ExperimentalEffects = !UIManager.ExperimentalEffects; - } else { - var e = new KeyEvent(control, alt, shift, lastKey); - UIManager.ProcessKeyEvent(e); + failCharAddMS += gameTime.ElapsedGameTime.TotalMilliseconds; + if (failCharAddMS >= 75) + { + failMessage = failRealMessage.Substring(0, failMessage.Length) + "|"; + failCharAddMS = 0; + } } } - } - kb_elapsedms += gameTime.ElapsedGameTime.TotalMilliseconds; - } - else - { - kb_elapsedms = 0; - } - - //Cause layout update on all elements - UIManager.LayoutUpdate(gameTime); - - timeSinceLastPurge += gameTime.ElapsedGameTime.TotalSeconds; - - if(timeSinceLastPurge > 2) - { - GraphicsContext.StringCaches.Clear(); - timeSinceLastPurge = 0; - GC.Collect(); - } - - - //Some hackables have a connection timeout applied to them. - //We must update timeout values here, and disconnect if the timeout - //hits zero. - - if(Hacking.CurrentHackable != null) - { - if (Hacking.CurrentHackable.DoConnectionTimeout) + } + else { - Hacking.CurrentHackable.MillisecondsCountdown -= gameTime.ElapsedGameTime.TotalMilliseconds; - shroudOpacity = (float)GUI.ProgressBar.linear(Hacking.CurrentHackable.MillisecondsCountdown, Hacking.CurrentHackable.TotalConnectionTimeMS, 0, 0, 1); - if (Hacking.CurrentHackable.MillisecondsCountdown <= 0) + if(failFadeOutMS < failFadeMaxMS) { - Hacking.FailHack(); + failFadeOutMS += gameTime.ElapsedGameTime.TotalMilliseconds; + } + + shroudOpacity = 1 - (float)GUI.ProgressBar.linear(failFadeOutMS, 0, failFadeMaxMS, 0, 1); + + if(shroudOpacity <= 0) + { + isFailing = false; } } } else { - shroudOpacity = 0; + 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, mouseMS); + if (mouseState.LeftButton == ButtonState.Pressed) + { + mouseMS = 0; + } + else + { + mouseMS += gameTime.ElapsedGameTime.TotalMilliseconds; + + } + //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) + { + UIManager.Fullscreen = !UIManager.Fullscreen; + } + 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(gameTime); + + timeSinceLastPurge += gameTime.ElapsedGameTime.TotalSeconds; + + if (timeSinceLastPurge > 2) + { + GraphicsContext.StringCaches.Clear(); + timeSinceLastPurge = 0; + GC.Collect(); + } + + + //Some hackables have a connection timeout applied to them. + //We must update timeout values here, and disconnect if the timeout + //hits zero. + + if (Hacking.CurrentHackable != null) + { + if (Hacking.CurrentHackable.DoConnectionTimeout) + { + Hacking.CurrentHackable.MillisecondsCountdown -= gameTime.ElapsedGameTime.TotalMilliseconds; + shroudOpacity = (float)GUI.ProgressBar.linear(Hacking.CurrentHackable.MillisecondsCountdown, Hacking.CurrentHackable.TotalConnectionTimeMS, 0, 0, 1); + if (Hacking.CurrentHackable.MillisecondsCountdown <= 0) + { + Hacking.FailHack(); + } + } + } + else + { + shroudOpacity = 0; + } } + base.Update(gameTime); } @@ -301,6 +367,21 @@ namespace ShiftOS.Frontend spriteBatch.Draw(UIManager.SkinTextures["PureWhite"], new Rectangle(0, 0, UIManager.Viewport.Width, UIManager.Viewport.Height), Color.Red * shroudOpacity); + if(isFailing && failFadeInMS >= failFadeMaxMS) + { + var gfx = new GraphicsContext(graphicsDevice.GraphicsDevice, spriteBatch, 0,0, UIManager.Viewport.Width, UIManager.Viewport.Height); + string objectiveFailed = "- OBJECTIVE FAILURE -"; + string prompt = "[press any key to dismiss this message and return to your sentience]"; + int textMaxWidth = UIManager.Viewport.Width / 3; + var topMeasure = gfx.MeasureString(objectiveFailed, SkinEngine.LoadedSkin.HeaderFont, textMaxWidth); + var msgMeasure = gfx.MeasureString(failMessage, SkinEngine.LoadedSkin.Header3Font, textMaxWidth); + var pMeasure = gfx.MeasureString(prompt, SkinEngine.LoadedSkin.MainFont, textMaxWidth); + + gfx.DrawString(objectiveFailed, (UIManager.Viewport.Width - (int)topMeasure.X) / 2, UIManager.Viewport.Height / 3, Color.White, SkinEngine.LoadedSkin.HeaderFont, textMaxWidth); + gfx.DrawString(failMessage, (UIManager.Viewport.Width - (int)msgMeasure.X) / 2, (UIManager.Viewport.Height - (int)msgMeasure.Y) / 2, Color.White, SkinEngine.LoadedSkin.Header3Font, textMaxWidth); + gfx.DrawString(prompt, (UIManager.Viewport.Width - (int)pMeasure.X) / 2, UIManager.Viewport.Height - (UIManager.Viewport.Height / 3), Color.White, SkinEngine.LoadedSkin.MainFont, textMaxWidth); + } + if(Hacking.CurrentHackable != null) { if (Hacking.CurrentHackable.DoConnectionTimeout) diff --git a/ShiftOS.Frontend/Stories/BeginTutorials.cs b/ShiftOS.Frontend/Stories/BeginTutorials.cs index f5e1905..a023178 100644 --- a/ShiftOS.Frontend/Stories/BeginTutorials.cs +++ b/ShiftOS.Frontend/Stories/BeginTutorials.cs @@ -257,5 +257,57 @@ namespace ShiftOS.Frontend.Stories }); }); } + + [RequiresUpgrade("tutorial_hacking_basics")] + [Mission("the_syndicate", "The Syndicate", "You just exploited a server owned by a group known as ShiftSyndicate. They found out, and they see you as a worthy recruit.", 250, "thejackel")] + public static void TheSyndicateEntry() + { + Story.Context.AutoComplete = false; + var irc = AppearanceManager.OpenForms.FirstOrDefault(x => x.ParentWindow is Apps.ChatClient) as Apps.ChatClient; + if (irc == null) + { + irc = new Apps.ChatClient(); + AppearanceManager.SetupWindow(irc); + } + + irc.FakeConnection(new Objects.IRCNetwork + { + SystemName = "shiftsyndicate_irc", + FriendlyName = "ShiftSyndicate IRC Network", + MOTD = "Welcome to ShiftSyndicate IRC. This network is dedicated to finding out what this Digital Society is and how to break out. Unauthorized users WILL be z-lined.", + Channel = new Objects.IRCChannel + { + Tag = "recruitment", + Topic = "Artificial intelligence do everything now. Nobody is really here.", + OnlineUsers = new List + { + new Objects.IRCUser + { + Nickname = "thejackel", + Permission = Objects.IRCPermission.NetOp + }, + new Objects.IRCUser + { + Nickname = SaveSystem.CurrentSave.Username, + Permission = Objects.IRCPermission.User + } + } + } + }); + while (!irc.ChannelConnected) + Thread.Sleep(10); + SendClientMessage(irc, "thejackel", $"Hello there, {SaveSystem.CurrentSave.Username}. Welcome to our network."); + SendClientMessage(irc, "thejackel", $"I see you breached our File Transfer Protocol server."); + SendClientMessage(irc, "thejackel", $"You may not realize exactly who we are..."); + SendClientMessage(irc, "thejackel", $"We know about you, and we know what you want."); + SendClientMessage(irc, "thejackel", $""); + + } + + public static void SendClientMessage(Apps.ChatClient client, string nick, string message) + { + Thread.Sleep(message.Length * 25); + client.SendClientMessage(nick, message); + } } } diff --git a/ShiftOS.Objects/IRCNetwork.cs b/ShiftOS.Objects/IRCNetwork.cs new file mode 100644 index 0000000..cbf8688 --- /dev/null +++ b/ShiftOS.Objects/IRCNetwork.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ShiftOS.Objects +{ + public class IRCNetwork + { + public string SystemName { get; set; } + public string FriendlyName { get; set; } + public string MOTD { get; set; } + public IRCChannel Channel { get; set; } + } + + public class IRCChannel + { + public string Tag { get; set; } + public string Topic { get; set; } + public List OnlineUsers { get; set; } + + } + + public class IRCUser + { + public string Nickname { get; set; } + public IRCPermission Permission { get; set; } + } + + public enum IRCPermission + { + User, + ChanOp, + NetOp, + } +} diff --git a/ShiftOS.Objects/ShiftOS.Objects.csproj b/ShiftOS.Objects/ShiftOS.Objects.csproj index 65324fb..8117cd9 100644 --- a/ShiftOS.Objects/ShiftOS.Objects.csproj +++ b/ShiftOS.Objects/ShiftOS.Objects.csproj @@ -52,6 +52,7 @@ + diff --git a/ShiftOS_TheReturn/Story.cs b/ShiftOS_TheReturn/Story.cs index c62d3bd..674b782 100644 --- a/ShiftOS_TheReturn/Story.cs +++ b/ShiftOS_TheReturn/Story.cs @@ -95,6 +95,14 @@ namespace ShiftOS.Engine public static StoryContext Context { get; private set; } public static event Action StoryComplete; public static List CurrentObjectives { get; private set; } + public static event Action FailureRequested; + + public static void DisplayFailure(string message) + { + FailureRequested?.Invoke(message); + } + + public static void PushObjective(string name, string desc, Func completeFunc, Action onComplete) {