make it crash nicely when you inherit from a bogus actor type

This commit is contained in:
Chris Forbes
2011-03-30 20:48:09 +13:00
committed by Chris Forbes
parent cee021ef17
commit 1af23079eb
4 changed files with 420 additions and 403 deletions

View File

@@ -39,9 +39,10 @@ namespace OpenRA
return null; return null;
MiniYaml parent; MiniYaml parent;
allUnits.TryGetValue( inherits.Value, out parent ); allUnits.TryGetValue( inherits.Value, out parent );
if( parent == null ) if (parent == null)
return null; throw new InvalidOperationException(
"Bogus inheritance -- actor type {0} does not exist".F(inherits.Value));
return parent; return parent;
} }

View File

@@ -13,356 +13,369 @@ using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.Linq; using System.Linq;
using OpenRA.FileFormats; using OpenRA.FileFormats;
using OpenRA.Graphics; using OpenRA.Graphics;
namespace OpenRA.Widgets namespace OpenRA.Widgets
{ {
public abstract class Widget public abstract class Widget
{ {
// Info defined in YAML // Info defined in YAML
public string Id = null; public string Id = null;
public string X = "0"; public string X = "0";
public string Y = "0"; public string Y = "0";
public string Width = "0"; public string Width = "0";
public string Height = "0"; public string Height = "0";
public string Delegate = null; public string Delegate = null;
public string EventHandler = null; public string EventHandler = null;
public bool Visible = true; public bool Visible = true;
public readonly List<Widget> Children = new List<Widget>(); public readonly List<Widget> Children = new List<Widget>();
// Calculated internally // Calculated internally
public Rectangle Bounds; public Rectangle Bounds;
public Widget Parent = null; public Widget Parent = null;
public static Stack<Widget> WindowList = new Stack<Widget>(); public static Stack<Widget> WindowList = new Stack<Widget>();
// Common Funcs that most widgets will want // Common Funcs that most widgets will want
public Func<MouseInput, bool> OnMouseDown = mi => false; public Func<MouseInput, bool> OnMouseDown = mi => false;
public Func<MouseInput, bool> OnMouseUp = mi => false; public Func<MouseInput, bool> OnMouseUp = mi => false;
public Action<MouseInput> OnMouseMove = mi => {}; public Action<MouseInput> OnMouseMove = mi => { };
public Func<KeyInput, bool> OnKeyPress = e => false; public Func<KeyInput, bool> OnKeyPress = e => false;
public Func<bool> IsVisible; public Func<bool> IsVisible;
public Widget() { IsVisible = () => Visible; } public Widget() { IsVisible = () => Visible; }
public static Widget RootWidget public static Widget RootWidget
{ {
get { return rootWidget; } get { return rootWidget; }
set { rootWidget = value; } set { rootWidget = value; }
} }
private static Widget rootWidget = new ContainerWidget(); private static Widget rootWidget = new ContainerWidget();
public Widget(Widget widget) public Widget(Widget widget)
{ {
Id = widget.Id; Id = widget.Id;
X = widget.X; X = widget.X;
Y = widget.Y; Y = widget.Y;
Width = widget.Width; Width = widget.Width;
Height = widget.Height; Height = widget.Height;
Delegate = widget.Delegate; Delegate = widget.Delegate;
Visible = widget.Visible; Visible = widget.Visible;
Bounds = widget.Bounds; Bounds = widget.Bounds;
Parent = widget.Parent; Parent = widget.Parent;
OnMouseDown = widget.OnMouseDown; OnMouseDown = widget.OnMouseDown;
OnMouseUp = widget.OnMouseUp; OnMouseUp = widget.OnMouseUp;
OnMouseMove = widget.OnMouseMove; OnMouseMove = widget.OnMouseMove;
OnKeyPress = widget.OnKeyPress; OnKeyPress = widget.OnKeyPress;
IsVisible = widget.IsVisible; IsVisible = widget.IsVisible;
foreach(var child in widget.Children) foreach (var child in widget.Children)
AddChild(child.Clone()); AddChild(child.Clone());
} }
public virtual Widget Clone() public virtual Widget Clone()
{ {
throw new InvalidOperationException("Widget type `{0}` is not cloneable.".F(GetType().Name)); throw new InvalidOperationException("Widget type `{0}` is not cloneable.".F(GetType().Name));
} }
public virtual int2 RenderOrigin public virtual int2 RenderOrigin
{ {
get get
{ {
var offset = (Parent == null) ? int2.Zero : Parent.ChildOrigin; var offset = (Parent == null) ? int2.Zero : Parent.ChildOrigin;
return new int2(Bounds.X, Bounds.Y) + offset; return new int2(Bounds.X, Bounds.Y) + offset;
} }
} }
public virtual int2 ChildOrigin { get { return RenderOrigin; } } public virtual int2 ChildOrigin { get { return RenderOrigin; } }
public virtual Rectangle RenderBounds { get { return new Rectangle(RenderOrigin.X, RenderOrigin.Y, Bounds.Width, Bounds.Height); } } public virtual Rectangle RenderBounds { get { return new Rectangle(RenderOrigin.X, RenderOrigin.Y, Bounds.Width, Bounds.Height); } }
public virtual void Initialize() public virtual void Initialize()
{ {
// Parse the YAML equations to find the widget bounds // Parse the YAML equations to find the widget bounds
var parentBounds = (Parent == null) var parentBounds = (Parent == null)
? new Rectangle(0, 0, Game.viewport.Width, Game.viewport.Height) ? new Rectangle(0, 0, Game.viewport.Width, Game.viewport.Height)
: Parent.Bounds; : Parent.Bounds;
var substitutions = new Dictionary<string, int>(); var substitutions = new Dictionary<string, int>();
substitutions.Add("WINDOW_RIGHT", Game.viewport.Width); substitutions.Add("WINDOW_RIGHT", Game.viewport.Width);
substitutions.Add("WINDOW_BOTTOM", Game.viewport.Height); substitutions.Add("WINDOW_BOTTOM", Game.viewport.Height);
substitutions.Add("PARENT_RIGHT", parentBounds.Width); substitutions.Add("PARENT_RIGHT", parentBounds.Width);
substitutions.Add("PARENT_LEFT", parentBounds.Left); substitutions.Add("PARENT_LEFT", parentBounds.Left);
substitutions.Add("PARENT_TOP", parentBounds.Top); substitutions.Add("PARENT_TOP", parentBounds.Top);
substitutions.Add("PARENT_BOTTOM", parentBounds.Height); substitutions.Add("PARENT_BOTTOM", parentBounds.Height);
int width = Evaluator.Evaluate(Width, substitutions); int width = Evaluator.Evaluate(Width, substitutions);
int height = Evaluator.Evaluate(Height, substitutions); int height = Evaluator.Evaluate(Height, substitutions);
substitutions.Add("WIDTH", width); substitutions.Add("WIDTH", width);
substitutions.Add("HEIGHT", height); substitutions.Add("HEIGHT", height);
Bounds = new Rectangle(Evaluator.Evaluate(X, substitutions), Bounds = new Rectangle(Evaluator.Evaluate(X, substitutions),
Evaluator.Evaluate(Y, substitutions), Evaluator.Evaluate(Y, substitutions),
width, width,
height); height);
} }
public void PostInit(Dictionary<string, object> args) public void PostInit(Dictionary<string, object> args)
{ {
if( Delegate != null ) if (Delegate == null)
{ return;
args["widget"] = this;
Game.modData.ObjectCreator.CreateObject<IWidgetDelegate>(Delegate, args); args["widget"] = this;
args.Remove("widget");
} var iwd = Game.modData.ObjectCreator.CreateObject<IWidgetDelegate>(Delegate, args)
} as IWidgetDelegateEx;
if (iwd != null)
public virtual Rectangle EventBounds { get { return RenderBounds; } } iwd.Init();
public virtual Rectangle GetEventBounds()
{ args.Remove("widget");
return Children }
.Where(c => c.IsVisible())
.Select(c => c.GetEventBounds()) public virtual Rectangle EventBounds { get { return RenderBounds; } }
.Aggregate(EventBounds, Rectangle.Union); public virtual Rectangle GetEventBounds()
} {
return Children
public static Widget SelectedWidget; .Where(c => c.IsVisible())
public bool Focused { get { return SelectedWidget == this; } } .Select(c => c.GetEventBounds())
public virtual bool TakeFocus(MouseInput mi) .Aggregate(EventBounds, Rectangle.Union);
{ }
if (Focused)
return true; public static Widget SelectedWidget;
public bool Focused { get { return SelectedWidget == this; } }
if (SelectedWidget != null && !SelectedWidget.LoseFocus(mi)) public virtual bool TakeFocus(MouseInput mi)
return false; {
SelectedWidget = this; if (Focused)
return true; return true;
}
if (SelectedWidget != null && !SelectedWidget.LoseFocus(mi))
// Remove focus from this widget; return false if you don't want to give it up return false;
public virtual bool LoseFocus(MouseInput mi) SelectedWidget = this;
{ return true;
// Some widgets may need to override focus depending on mouse click }
return LoseFocus();
} // Remove focus from this widget; return false if you don't want to give it up
public virtual bool LoseFocus(MouseInput mi)
public virtual bool LoseFocus() {
{ // Some widgets may need to override focus depending on mouse click
if (SelectedWidget == this) return LoseFocus();
SelectedWidget = null; }
return true; public virtual bool LoseFocus()
} {
if (SelectedWidget == this)
public virtual string GetCursor(int2 pos) { return "default"; } SelectedWidget = null;
public string GetCursorOuter(int2 pos)
{ return true;
// Is the cursor on top of us? }
if (!(IsVisible() && GetEventBounds().Contains(pos)))
return null; public virtual string GetCursor(int2 pos) { return "default"; }
public string GetCursorOuter(int2 pos)
// Do any of our children specify a cursor? {
foreach (var child in Children.OfType<Widget>().Reverse()) // Is the cursor on top of us?
{ if (!(IsVisible() && GetEventBounds().Contains(pos)))
var cc = child.GetCursorOuter(pos); return null;
if (cc != null)
return cc; // Do any of our children specify a cursor?
} foreach (var child in Children.OfType<Widget>().Reverse())
{
return EventBounds.Contains(pos) ? GetCursor(pos) : null; var cc = child.GetCursorOuter(pos);
} if (cc != null)
return cc;
public static bool HandleInput(MouseInput mi) }
{
bool handled = false; return EventBounds.Contains(pos) ? GetCursor(pos) : null;
if (SelectedWidget != null && SelectedWidget.HandleMouseInputOuter(mi)) }
handled = true;
public static bool HandleInput(MouseInput mi)
if (!handled && RootWidget.HandleMouseInputOuter(mi)) {
handled = true; bool handled = false;
if (SelectedWidget != null && SelectedWidget.HandleMouseInputOuter(mi))
if (mi.Event == MouseInputEvent.Move) handled = true;
{
Viewport.LastMousePos = mi.Location; if (!handled && RootWidget.HandleMouseInputOuter(mi))
Viewport.TicksSinceLastMove = 0; handled = true;
}
return handled; if (mi.Event == MouseInputEvent.Move)
} {
Viewport.LastMousePos = mi.Location;
public bool HandleMouseInputOuter(MouseInput mi) Viewport.TicksSinceLastMove = 0;
{ }
// Are we able to handle this event? return handled;
if (!(Focused || (IsVisible() && GetEventBounds().Contains(mi.Location.X,mi.Location.Y)))) }
return false;
public bool HandleMouseInputOuter(MouseInput mi)
// Send the event to the deepest children first and bubble up if unhandled {
foreach (var child in Children.OfType<Widget>().Reverse()) // Are we able to handle this event?
if (child.HandleMouseInputOuter(mi)) if (!(Focused || (IsVisible() && GetEventBounds().Contains(mi.Location.X, mi.Location.Y))))
return true; return false;
return HandleMouseInput(mi); // Send the event to the deepest children first and bubble up if unhandled
} foreach (var child in Children.OfType<Widget>().Reverse())
if (child.HandleMouseInputOuter(mi))
// Hack: Don't eat mouse input that others want return true;
// TODO: Solve this properly
public virtual bool HandleMouseInput(MouseInput mi) return HandleMouseInput(mi);
{ }
// Apply any special logic added by delegates; they return true if they caught the input
if (mi.Event == MouseInputEvent.Down && OnMouseDown(mi)) return true; // Hack: Don't eat mouse input that others want
if (mi.Event == MouseInputEvent.Up && OnMouseUp(mi)) return true; // TODO: Solve this properly
if (mi.Event == MouseInputEvent.Move) public virtual bool HandleMouseInput(MouseInput mi)
OnMouseMove(mi); {
// Apply any special logic added by delegates; they return true if they caught the input
return false; if (mi.Event == MouseInputEvent.Down && OnMouseDown(mi)) return true;
} if (mi.Event == MouseInputEvent.Up && OnMouseUp(mi)) return true;
if (mi.Event == MouseInputEvent.Move)
public virtual bool HandleKeyPressInner(KeyInput e) { return false; } OnMouseMove(mi);
public virtual bool HandleKeyPressOuter(KeyInput e)
{ return false;
if (!IsVisible()) }
return false;
public virtual bool HandleKeyPressInner(KeyInput e) { return false; }
// Can any of our children handle this? public virtual bool HandleKeyPressOuter(KeyInput e)
foreach (var child in Children) {
if (child.HandleKeyPressOuter(e)) if (!IsVisible())
return true; return false;
// Do any widgety behavior (enter text etc) // Can any of our children handle this?
var handled = HandleKeyPressInner(e); foreach (var child in Children)
if (child.HandleKeyPressOuter(e))
// Apply any special logic added by delegates; they return true if they caught the input return true;
if (OnKeyPress(e)) return true;
// Do any widgety behavior (enter text etc)
return handled; var handled = HandleKeyPressInner(e);
}
// Apply any special logic added by delegates; they return true if they caught the input
public static bool HandleKeyPress(KeyInput e) if (OnKeyPress(e)) return true;
{
if (SelectedWidget != null) return handled;
return SelectedWidget.HandleKeyPressOuter(e); }
if (RootWidget.HandleKeyPressOuter(e)) public static bool HandleKeyPress(KeyInput e)
return true; {
return false; if (SelectedWidget != null)
} return SelectedWidget.HandleKeyPressOuter(e);
public abstract void DrawInner(); if (RootWidget.HandleKeyPressOuter(e))
return true;
public virtual void Draw() return false;
{ }
if (IsVisible())
{ public abstract void DrawInner();
DrawInner();
foreach (var child in Children) public virtual void Draw()
child.Draw(); {
} if (IsVisible())
} {
DrawInner();
public virtual void Tick() foreach (var child in Children)
{ child.Draw();
if (IsVisible()) }
foreach (var child in Children) }
child.Tick();
} public virtual void Tick()
{
public virtual void AddChild(Widget child) if (IsVisible())
{ foreach (var child in Children)
child.Parent = this; child.Tick();
Children.Add( child ); }
}
public virtual void RemoveChild(Widget child) { Children.Remove(child); } public virtual void AddChild(Widget child)
public virtual void RemoveChildren() { Children.Clear(); } {
child.Parent = this;
public Widget GetWidget(string id) Children.Add(child);
{ }
if (this.Id == id) public virtual void RemoveChild(Widget child) { Children.Remove(child); }
return this; public virtual void RemoveChildren() { Children.Clear(); }
foreach (var child in Children) public Widget GetWidget(string id)
{ {
var w = child.GetWidget(id); if (this.Id == id)
if (w != null) return this;
return w;
} foreach (var child in Children)
return null; {
} var w = child.GetWidget(id);
if (w != null)
public T GetWidget<T>(string id) where T : Widget return w;
{ }
var widget = GetWidget(id); return null;
return (widget != null)? (T) widget : null; }
}
public T GetWidget<T>(string id) where T : Widget
public static void CloseWindow() {
{ var widget = GetWidget(id);
RootWidget.Children.Remove( WindowList.Pop() ); return (widget != null) ? (T)widget : null;
if( WindowList.Count > 0 ) }
rootWidget.Children.Add( WindowList.Peek() );
} public static void CloseWindow()
{
public static Widget OpenWindow( string id ) RootWidget.Children.Remove(WindowList.Pop());
{ if (WindowList.Count > 0)
return OpenWindow( id, new Dictionary<string, object>() ); rootWidget.Children.Add(WindowList.Peek());
} }
public static Widget OpenWindow(string id, Dictionary<string, object> args ) public static Widget OpenWindow(string id)
{ {
var window = Game.modData.WidgetLoader.LoadWidget( args, rootWidget, id ); return OpenWindow(id, new Dictionary<string, object>());
if( WindowList.Count > 0 ) }
rootWidget.Children.Remove( WindowList.Peek() );
WindowList.Push( window ); public static Widget OpenWindow(string id, Dictionary<string, object> args)
return window; {
} var window = Game.modData.WidgetLoader.LoadWidget(args, rootWidget, id);
if (WindowList.Count > 0)
public static void DoTick() rootWidget.Children.Remove(WindowList.Peek());
{ WindowList.Push(window);
RootWidget.Tick(); return window;
} }
public static void DoDraw() public static void DoTick()
{ {
RootWidget.Draw(); RootWidget.Tick();
} }
}
public static void DoDraw()
public class ContainerWidget : Widget { {
public Func<string> GetBackground; RootWidget.Draw();
public string Background = null; }
}
public ContainerWidget() : base()
{ public class ContainerWidget : Widget
GetBackground = () => Background; {
} public Func<string> GetBackground;
public string Background = null;
public ContainerWidget(ContainerWidget other) : base(other)
{ public ContainerWidget()
Background = other.Background; : base()
GetBackground = other.GetBackground; {
} GetBackground = () => Background;
}
public override void DrawInner()
{ public ContainerWidget(ContainerWidget other)
var bg = GetBackground(); : base(other)
if (bg != null) {
WidgetUtils.DrawPanel(bg, RenderBounds ); Background = other.Background;
} GetBackground = other.GetBackground;
}
public override string GetCursor(int2 pos) { return null; }
public override Widget Clone() { return new ContainerWidget(this); } public override void DrawInner()
} {
public interface IWidgetDelegate { } var bg = GetBackground();
if (bg != null)
WidgetUtils.DrawPanel(bg, RenderBounds);
}
public override string GetCursor(int2 pos) { return null; }
public override Widget Clone() { return new ContainerWidget(this); }
}
public interface IWidgetDelegate { }
public interface IWidgetDelegateEx : IWidgetDelegate
{
void Init();
}
} }

View File

@@ -29,8 +29,8 @@ namespace OpenRA.Mods.RA.Widgets.Delegates
widget.GetWidget<LabelWidget>("CONNECTING_DESC").GetText = () => widget.GetWidget<LabelWidget>("CONNECTING_DESC").GetText = () =>
"Connecting to {0}:{1}...".F(host, port); "Connecting to {0}:{1}...".F(host, port);
} }
} }
public class ConnectionFailedDelegate : IWidgetDelegate public class ConnectionFailedDelegate : IWidgetDelegate
{ {

View File

@@ -23,7 +23,7 @@ using System.Drawing;
namespace OpenRA.Mods.RA.Widgets.Delegates namespace OpenRA.Mods.RA.Widgets.Delegates
{ {
public class GameInitDelegate : IWidgetDelegate public class GameInitDelegate : IWidgetDelegateEx
{ {
GameInitInfoWidget Info; GameInitInfoWidget Info;
@@ -31,42 +31,45 @@ namespace OpenRA.Mods.RA.Widgets.Delegates
public GameInitDelegate([ObjectCreator.Param] Widget widget) public GameInitDelegate([ObjectCreator.Param] Widget widget)
{ {
Info = (widget as GameInitInfoWidget); Info = (widget as GameInitInfoWidget);
}
Game.ConnectionStateChanged += orderManager =>
{ public void Init()
Widget.CloseWindow(); {
switch( orderManager.Connection.ConnectionState ) Game.ConnectionStateChanged += orderManager =>
{ {
case ConnectionState.PreConnecting: Widget.CloseWindow();
Widget.OpenWindow("MAINMENU_BG"); switch (orderManager.Connection.ConnectionState)
break; {
case ConnectionState.Connecting: case ConnectionState.PreConnecting:
Widget.OpenWindow( "CONNECTING_BG", Widget.OpenWindow("MAINMENU_BG");
new Dictionary<string, object> { { "host", orderManager.Host }, { "port", orderManager.Port } } ); break;
break; case ConnectionState.Connecting:
case ConnectionState.NotConnected: Widget.OpenWindow("CONNECTING_BG",
Widget.OpenWindow( "CONNECTION_FAILED_BG", new Dictionary<string, object> { { "host", orderManager.Host }, { "port", orderManager.Port } });
new Dictionary<string, object> { { "orderManager", orderManager } } ); break;
break; case ConnectionState.NotConnected:
case ConnectionState.Connected: Widget.OpenWindow("CONNECTION_FAILED_BG",
var lobby = Game.OpenWindow(orderManager.world, "SERVER_LOBBY"); new Dictionary<string, object> { { "orderManager", orderManager } });
lobby.GetWidget<ChatDisplayWidget>("CHAT_DISPLAY").ClearChat(); break;
lobby.GetWidget("CHANGEMAP_BUTTON").Visible = true; case ConnectionState.Connected:
lobby.GetWidget("LOCKTEAMS_CHECKBOX").Visible = true; var lobby = Game.OpenWindow(orderManager.world, "SERVER_LOBBY");
lobby.GetWidget("ALLOWCHEATS_CHECKBOX").Visible = true; lobby.GetWidget<ChatDisplayWidget>("CHAT_DISPLAY").ClearChat();
lobby.GetWidget("DISCONNECT_BUTTON").Visible = true; lobby.GetWidget("CHANGEMAP_BUTTON").Visible = true;
break; lobby.GetWidget("LOCKTEAMS_CHECKBOX").Visible = true;
} lobby.GetWidget("ALLOWCHEATS_CHECKBOX").Visible = true;
}; lobby.GetWidget("DISCONNECT_BUTTON").Visible = true;
break;
if (FileSystem.Exists(Info.TestFile)) }
ContinueLoading(widget); };
else
{ if (FileSystem.Exists(Info.TestFile))
MainMenuButtonsDelegate.DisplayModSelector(); ContinueLoading();
ShowInstallMethodDialog(); else
} {
} MainMenuButtonsDelegate.DisplayModSelector();
ShowInstallMethodDialog();
}
}
void ShowInstallMethodDialog() void ShowInstallMethodDialog()
{ {
@@ -113,9 +116,9 @@ namespace OpenRA.Mods.RA.Widgets.Delegates
}; };
Action onComplete = () => Action onComplete = () =>
{ {
if (!error) if (!error)
Game.RunAfterTick(() => ContinueLoading(Info)); Game.RunAfterTick(ContinueLoading);
}; };
if (Info.InstallMode == "ra") if (Info.InstallMode == "ra")
@@ -151,19 +154,19 @@ namespace OpenRA.Mods.RA.Widgets.Delegates
var error = false; var error = false;
Action<string> parseOutput = s => Action<string> parseOutput = s =>
{ {
if (s.Substring(0,5) == "Error") if (s.StartsWith("Error"))
{ {
error = true; error = true;
ShowDownloadError(window, s); ShowDownloadError(window, s);
} }
if (s.Substring(0,6) == "Status") if (s.StartsWith("Status"))
window.GetWidget<LabelWidget>("STATUS").GetText = () => s.Substring(7).Trim(); window.GetWidget<LabelWidget>("STATUS").GetText = () => s.Substring(7).Trim();
}; };
Action onComplete = () => Action onComplete = () =>
{ {
if (!error) if (!error)
Game.RunAfterTick(() => ContinueLoading(Info)); Game.RunAfterTick(ContinueLoading);
}; };
Game.RunAfterTick(() => Game.Utilities.ExtractZipAsync(file, FileSystem.SpecialPackageRoot+Info.PackagePath, parseOutput, onComplete)); Game.RunAfterTick(() => Game.Utilities.ExtractZipAsync(file, FileSystem.SpecialPackageRoot+Info.PackagePath, parseOutput, onComplete));
@@ -185,7 +188,7 @@ namespace OpenRA.Mods.RA.Widgets.Delegates
} }
} }
void ContinueLoading(Widget widget) void ContinueLoading()
{ {
Game.LoadShellMap(); Game.LoadShellMap();
Widget.RootWidget.RemoveChildren(); Widget.RootWidget.RemoveChildren();
@@ -218,6 +221,6 @@ namespace OpenRA.Mods.RA.Widgets.Delegates
wc.CancelAsync(); wc.CancelAsync();
cancelled = true; cancelled = true;
} }
} }
} }
} }