diff --git a/Artemis/Artemis/Artemis.csproj b/Artemis/Artemis/Artemis.csproj
index 558849b91..1a0103842 100644
--- a/Artemis/Artemis/Artemis.csproj
+++ b/Artemis/Artemis/Artemis.csproj
@@ -414,6 +414,10 @@
+
+
+
+
@@ -637,6 +641,7 @@
Code
+
Designer
@@ -890,7 +895,7 @@
-
+
diff --git a/Artemis/Artemis/ArtemisBootstrapper.cs b/Artemis/Artemis/ArtemisBootstrapper.cs
index 07e5ad234..f513d57d4 100644
--- a/Artemis/Artemis/ArtemisBootstrapper.cs
+++ b/Artemis/Artemis/ArtemisBootstrapper.cs
@@ -10,6 +10,7 @@ using Artemis.Utilities;
using Artemis.Utilities.Converters;
using Artemis.ViewModels;
using Caliburn.Micro;
+using MoonSharp.Interpreter;
using Newtonsoft.Json;
using Ninject;
@@ -80,12 +81,15 @@ namespace Artemis
_kernel.Bind().To().InSingletonScope();
_kernel.Bind().To().InSingletonScope();
+ // Configure JSON.NET
var settings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto,
ContractResolver = _kernel.Get()
};
JsonConvert.DefaultSettings = () => settings;
+ // Configure MoonSharp
+ UserData.RegisterAssembly();
}
protected override void OnExit(object sender, EventArgs e)
diff --git a/Artemis/Artemis/Profiles/Lua/LuaEventsWrapper.cs b/Artemis/Artemis/Profiles/Lua/LuaEventsWrapper.cs
new file mode 100644
index 000000000..68d740b85
--- /dev/null
+++ b/Artemis/Artemis/Profiles/Lua/LuaEventsWrapper.cs
@@ -0,0 +1,9 @@
+using MoonSharp.Interpreter;
+
+namespace Artemis.Profiles.Lua
+{
+ [MoonSharpUserData]
+ public class LuaEventsWrapper
+ {
+ }
+}
\ No newline at end of file
diff --git a/Artemis/Artemis/Profiles/Lua/LuaLayerWrapper.cs b/Artemis/Artemis/Profiles/Lua/LuaLayerWrapper.cs
new file mode 100644
index 000000000..0a5ee5a16
--- /dev/null
+++ b/Artemis/Artemis/Profiles/Lua/LuaLayerWrapper.cs
@@ -0,0 +1,112 @@
+using System.Collections.Generic;
+using System.Linq;
+using Artemis.Profiles.Layers.Models;
+using MoonSharp.Interpreter;
+
+namespace Artemis.Profiles.Lua
+{
+ ///
+ /// Serves as a sandboxed wrapper around the LayerModel
+ ///
+ [MoonSharpUserData]
+ public class LuaLayerWrapper
+ {
+ private readonly LayerModel _layerModel;
+
+ public LuaLayerWrapper(LayerModel layerModel)
+ {
+ _layerModel = layerModel;
+ }
+
+ #region Child methods
+
+ public List GetChildren()
+ {
+ return _layerModel.Children.Select(l => new LuaLayerWrapper(l)).ToList();
+ }
+
+ public LuaLayerWrapper GetChildByName(string name)
+ {
+ var layer = _layerModel.Children.FirstOrDefault(l => l.Name == name);
+ return layer == null ? null : new LuaLayerWrapper(layer);
+ }
+
+ #endregion
+
+ #region General layer properties
+
+ public string Name
+ {
+ get { return _layerModel.Name; }
+ set { _layerModel.Name = value; }
+ }
+
+ public bool Enabled
+ {
+ get { return _layerModel.Enabled; }
+ set { _layerModel.Enabled = value; }
+ }
+
+ public bool IsEvent
+ {
+ get { return _layerModel.IsEvent; }
+ set { _layerModel.IsEvent = value; }
+ }
+
+ public LuaLayerWrapper Parent => new LuaLayerWrapper(_layerModel.Parent);
+
+ #endregion
+
+ #region Advanced layer properties
+
+ public double X
+ {
+ get { return _layerModel.Properties.X; }
+ set { _layerModel.Properties.X = value; }
+ }
+
+ public double Y
+ {
+ get { return _layerModel.Properties.Y; }
+ set { _layerModel.Properties.Y = value; }
+ }
+
+ public double Width
+ {
+ get { return _layerModel.Properties.Width; }
+ set { _layerModel.Properties.Width = value; }
+ }
+
+ public double Height
+ {
+ get { return _layerModel.Properties.Height; }
+ set { _layerModel.Properties.Height = value; }
+ }
+
+ public bool Contain
+ {
+ get { return _layerModel.Properties.Contain; }
+ set { _layerModel.Properties.Contain = value; }
+ }
+
+ public double Opacity
+ {
+ get { return _layerModel.Properties.Opacity; }
+ set { _layerModel.Properties.Opacity = value; }
+ }
+
+ public double AnimationSpeed
+ {
+ get { return _layerModel.Properties.AnimationSpeed; }
+ set { _layerModel.Properties.AnimationSpeed = value; }
+ }
+
+ public double AnimationProgress
+ {
+ get { return _layerModel.Properties.AnimationProgress; }
+ set { _layerModel.Properties.AnimationProgress = value; }
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Artemis/Artemis/Profiles/Lua/LuaProfileWrapper.cs b/Artemis/Artemis/Profiles/Lua/LuaProfileWrapper.cs
new file mode 100644
index 000000000..512cd49ea
--- /dev/null
+++ b/Artemis/Artemis/Profiles/Lua/LuaProfileWrapper.cs
@@ -0,0 +1,41 @@
+using System.Collections.Generic;
+using System.Linq;
+using MoonSharp.Interpreter;
+
+namespace Artemis.Profiles.Lua
+{
+ ///
+ /// Serves as a sandboxed wrapper around the ProfileModel
+ ///
+ [MoonSharpUserData]
+ public class LuaProfileWrapper
+ {
+ private readonly ProfileModel _profileModel;
+
+ public LuaProfileWrapper(ProfileModel profileModel)
+ {
+ _profileModel = profileModel;
+ }
+
+ #region General profile properties
+
+ public string Name => _profileModel.Name;
+
+ #endregion
+
+ #region Layer methods
+
+ public List GetLayers()
+ {
+ return _profileModel.Layers.Select(l => new LuaLayerWrapper(l)).ToList();
+ }
+
+ public LuaLayerWrapper GetLayerByName(string name)
+ {
+ var layer = _profileModel.Layers.FirstOrDefault(l => l.Name == name);
+ return layer == null ? null : new LuaLayerWrapper(layer);
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Artemis/Artemis/Profiles/Lua/LuaWrapper.cs b/Artemis/Artemis/Profiles/Lua/LuaWrapper.cs
new file mode 100644
index 000000000..634db40b0
--- /dev/null
+++ b/Artemis/Artemis/Profiles/Lua/LuaWrapper.cs
@@ -0,0 +1,116 @@
+using System;
+using System.IO;
+using Artemis.Properties;
+using Castle.Core.Internal;
+using MoonSharp.Interpreter;
+using NLog;
+using NuGet;
+
+namespace Artemis.Profiles.Lua
+{
+ public class LuaWrapper
+ {
+ private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
+
+ public LuaWrapper(ProfileModel profileModel)
+ {
+ ProfileModel = profileModel;
+ LuaProfileWrapper = new LuaProfileWrapper(ProfileModel);
+ LuaEventsWrapper = new LuaEventsWrapper();
+
+ SetupLuaScript();
+ }
+
+ public ProfileModel ProfileModel { get; set; }
+
+ public LuaEventsWrapper LuaEventsWrapper { get; set; }
+
+ public LuaProfileWrapper LuaProfileWrapper { get; set; }
+
+ public Script LuaScript { get; set; }
+
+ private void SetupLuaScript()
+ {
+ LuaScript = new Script(CoreModules.Preset_SoftSandbox);
+ LuaScript.Options.DebugPrint = LuaPrint;
+ LuaScript.Globals["Profile"] = LuaProfileWrapper;
+ LuaScript.Globals["Events"] = LuaEventsWrapper;
+
+ if (ProfileModel.LuaScript.IsNullOrEmpty())
+ return;
+
+ try
+ {
+ LuaScript.DoString(ProfileModel.LuaScript);
+ }
+ catch (ScriptRuntimeException e)
+ {
+ Logger.Error(e, "[{0}-LUA]: Error: {1}", ProfileModel.Name, e.DecoratedMessage);
+ }
+ }
+
+ #region Private lua functions
+
+ private void LuaPrint(string s)
+ {
+ Logger.Debug("[{0}-LUA]: {1}", ProfileModel.Name, s);
+ }
+
+ #endregion
+
+ #region Editor
+
+ public void OpenEditor()
+ {
+ // Create a temp file
+ var fileName = Guid.NewGuid() + ".lua";
+ var file = File.Create(Path.GetTempPath() + fileName);
+ file.Dispose();
+
+ // Add instructions to LUA script if it's a new file
+ if (ProfileModel.LuaScript.IsNullOrEmpty())
+ ProfileModel.LuaScript = Resources.lua_placeholder;
+ File.WriteAllText(Path.GetTempPath() + fileName, ProfileModel.LuaScript);
+
+ // Watch the file for changes
+ var watcher = new FileSystemWatcher(Path.GetTempPath(), fileName);
+ watcher.Changed += LuaFileChanged;
+ watcher.EnableRaisingEvents = true;
+
+ // Open the temp file with the default editor
+ System.Diagnostics.Process.Start(Path.GetTempPath() + fileName);
+ }
+
+ private void LuaFileChanged(object sender, FileSystemEventArgs fileSystemEventArgs)
+ {
+ if (fileSystemEventArgs.ChangeType != WatcherChangeTypes.Changed)
+ return;
+
+ using (var fs = new FileStream(fileSystemEventArgs.FullPath,
+ FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
+ {
+ using (var sr = new StreamReader(fs))
+ {
+ ProfileModel.LuaScript = sr.ReadToEnd();
+ }
+ }
+
+ DAL.ProfileProvider.AddOrUpdate(ProfileModel);
+ SetupLuaScript();
+ }
+
+ #endregion
+
+ #region Event triggers
+
+ public void TriggerUpdate()
+ {
+ }
+
+ public void TriggerDraw()
+ {
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Artemis/Artemis/Profiles/ProfileModel.cs b/Artemis/Artemis/Profiles/ProfileModel.cs
index 145eb1917..3b0e01f68 100644
--- a/Artemis/Artemis/Profiles/ProfileModel.cs
+++ b/Artemis/Artemis/Profiles/ProfileModel.cs
@@ -8,6 +8,7 @@ using Artemis.DeviceProviders;
using Artemis.Models.Interfaces;
using Artemis.Profiles.Layers.Interfaces;
using Artemis.Profiles.Layers.Models;
+using Artemis.Profiles.Lua;
using Artemis.Utilities;
using Artemis.Utilities.ParentChild;
using Newtonsoft.Json;
@@ -22,9 +23,10 @@ namespace Artemis.Profiles
public ProfileModel()
{
Layers = new ChildItemCollection(this);
+ LuaWrapper = new LuaWrapper(this);
DrawingVisual = new DrawingVisual();
}
-
+
///
/// Indicates whether the profile is actively being rendered
///
@@ -38,10 +40,14 @@ namespace Artemis.Profiles
public string GameName { get; set; }
public int Width { get; set; }
public int Height { get; set; }
+ public string LuaScript { get; set; }
[JsonIgnore]
public DrawingVisual DrawingVisual { get; set; }
+ [JsonIgnore]
+ public LuaWrapper LuaWrapper { get; set; }
+
public void FixOrder()
{
Layers.Sort(l => l.Order);
diff --git a/Artemis/Artemis/Properties/Resources.Designer.cs b/Artemis/Artemis/Properties/Resources.Designer.cs
index d414ddf78..39ed3e2b9 100644
--- a/Artemis/Artemis/Properties/Resources.Designer.cs
+++ b/Artemis/Artemis/Properties/Resources.Designer.cs
@@ -308,6 +308,15 @@ namespace Artemis.Properties {
}
}
+ ///
+ /// Looks up a localized string similar to .
+ ///
+ internal static string lua_placeholder {
+ get {
+ return ResourceManager.GetString("lua-placeholder", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized resource of type System.Drawing.Bitmap.
///
diff --git a/Artemis/Artemis/Properties/Resources.resx b/Artemis/Artemis/Properties/Resources.resx
index 6bb8fcb3d..555a4ee25 100644
--- a/Artemis/Artemis/Properties/Resources.resx
+++ b/Artemis/Artemis/Properties/Resources.resx
@@ -208,4 +208,7 @@
..\Resources\audio.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+ ..\Resources\lua-placeholder.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252
+
\ No newline at end of file
diff --git a/Artemis/Artemis/Resources/lua-placeholder.txt b/Artemis/Artemis/Resources/lua-placeholder.txt
new file mode 100644
index 000000000..7641d7eff
--- /dev/null
+++ b/Artemis/Artemis/Resources/lua-placeholder.txt
@@ -0,0 +1,12 @@
+--------------------------------------------------------------------------------
+------------------------------- Artemis LUA file -------------------------------
+--------------------------------------------------------------------------------
+
+-- This is a default script to be executed by Artemis.
+-- You do not need to use this if you don't want to script. The default profiles
+-- should provide you with a lot of functionality out of the box.
+-- However, if you wan't to change the way profiles work, this is the ideal way
+-- go about it.
+
+-- Note: You are editing a temporary file. Whenever you save this file the
+-- changes are applied to the profile and the script restarted.
\ No newline at end of file
diff --git a/Artemis/Artemis/ViewModels/Profiles/ProfileEditorViewModel.cs b/Artemis/Artemis/ViewModels/Profiles/ProfileEditorViewModel.cs
index 0fd5e12ff..debe2e2dc 100644
--- a/Artemis/Artemis/ViewModels/Profiles/ProfileEditorViewModel.cs
+++ b/Artemis/Artemis/ViewModels/Profiles/ProfileEditorViewModel.cs
@@ -76,8 +76,8 @@ namespace Artemis.ViewModels.Profiles
public bool EditorEnabled
=>
- SelectedProfile != null && !SelectedProfile.IsDefault &&
- _mainManager.DeviceManager.ActiveKeyboard != null;
+ SelectedProfile != null && !SelectedProfile.IsDefault &&
+ _mainManager.DeviceManager.ActiveKeyboard != null;
public BindableCollection Profiles
{
@@ -669,6 +669,20 @@ namespace Artemis.ViewModels.Profiles
ProfileProvider.ExportProfile(SelectedProfile, dialog.FileName);
}
+ public void EditLua()
+ {
+ try
+ {
+ SelectedProfile?.LuaWrapper.OpenEditor();
+ }
+ catch (Exception e)
+ {
+ DialogService.ShowMessageBox("Couldn't open LUA file",
+ "Please make sure you have a program such as Notepad associated with the .lua extension.\n\n" +
+ "Windows error message: \n" + e.Message);
+ }
+ }
+
private void EditorStateHandler(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName != "SelectedProfile")
diff --git a/Artemis/Artemis/Views/Profiles/ProfileEditorView.xaml b/Artemis/Artemis/Views/Profiles/ProfileEditorView.xaml
index 97c6c1fe2..860787971 100644
--- a/Artemis/Artemis/Views/Profiles/ProfileEditorView.xaml
+++ b/Artemis/Artemis/Views/Profiles/ProfileEditorView.xaml
@@ -115,7 +115,7 @@
Margin="5,5,0,0" Text="Note: To edit a default profile, duplicate it first." FontWeight="Bold" />
-
+
+