chat stuff and eas failure

This commit is contained in:
Michael 2017-08-04 18:35:53 -04:00
parent 1f131438c0
commit 6ec4837523
7 changed files with 435 additions and 105 deletions

View file

@ -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<ChatMessage> _messages = new List<ChatMessage>();
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"))

View file

@ -44,6 +44,30 @@ using ShiftOS.Engine;
namespace ShiftOS.Frontend
{
public static class FrontendDebugCommands
{
/// <summary>
/// Debug command to drop a fatal objective/hack failure screen in the form of an emergency alert system-esque screen.
///
/// ...Because WE'RE CANADA.
/// </summary>
[Command("drop_eas")]
[ShellConstraint("shiftos_debug> ")]
[RequiresArgument("id")]
public static void DropEAS(Dictionary<string, object> 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")]

View file

@ -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
/// <param name="gameTime">Provides a snapshot of timing values.</param>
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)

View file

@ -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<Objects.IRCUser>
{
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);
}
}
}

View file

@ -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<IRCUser> OnlineUsers { get; set; }
}
public class IRCUser
{
public string Nickname { get; set; }
public IRCPermission Permission { get; set; }
}
public enum IRCPermission
{
User,
ChanOp,
NetOp,
}
}

View file

@ -52,6 +52,7 @@
<Compile Include="Hacking\Payload.cs" />
<Compile Include="Hacking\Exploit.cs" />
<Compile Include="Hacking\Hackable.cs" />
<Compile Include="IRCNetwork.cs" />
<Compile Include="MudAttributes.cs" />
<Compile Include="Objects.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />

View file

@ -95,6 +95,14 @@ namespace ShiftOS.Engine
public static StoryContext Context { get; private set; }
public static event Action<string> StoryComplete;
public static List<Objective> CurrentObjectives { get; private set; }
public static event Action<string> FailureRequested;
public static void DisplayFailure(string message)
{
FailureRequested?.Invoke(message);
}
public static void PushObjective(string name, string desc, Func<bool> completeFunc, Action onComplete)
{