mirror of
https://github.com/Stone-Red-Code/StoneRed.LogicSimulator.git
synced 2026-09-04 09:06:29 +02:00
Add modding support
This commit is contained in:
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
using StoneRed.LogicSimulator.Api.Utilities;
|
||||
|
||||
namespace StoneRed.LogicSimulator.Api.Interfaces;
|
||||
namespace StoneRed.LogicSimulator.Api;
|
||||
#pragma warning disable S112 // General exceptions should never be thrown
|
||||
|
||||
internal abstract class LogicGate
|
||||
@@ -13,12 +13,13 @@
|
||||
</VerticalStackPanel>
|
||||
<Panel GridColumn="2" TransformOrigin="1, 0" Background="#FFA500FF" Id="sidePanel">
|
||||
<VerticalStackPanel Id="settings">
|
||||
<Label Text="Frequency:" />
|
||||
<Label Text="Frequency:" TextColor="#000000FF" />
|
||||
<SpinButton Maximum="1E+09" Minimum="1" HorizontalAlignment="Stretch" Value="100" Integer="True" Id="frequency" />
|
||||
<CheckBox Text="High Performance" Id="highPerformance" />
|
||||
<Label Text="Native components:" Wrap="True" />
|
||||
<CheckBox Text="High Performance" TextColor="#000000FF" Id="highPerformance" />
|
||||
<CheckBox Text="Hide cables behind gates" TextColor="#000000FF" Id="hideCablesBehindGates" />
|
||||
<Label Text="Native components:" Wrap="True" TextColor="#000000FF" />
|
||||
<ListBox HorizontalAlignment="Stretch" Id="nativeComponents" />
|
||||
<Label Text="Custom components:" Wrap="True" />
|
||||
<Label Text="Custom components:" Wrap="True" TextColor="#000000FF" />
|
||||
<ListBox HorizontalAlignment="Stretch">
|
||||
<ListItem Text="This is a test 2" />
|
||||
</ListBox>
|
||||
|
||||
@@ -20,11 +20,6 @@ internal static class Paths
|
||||
return srlsAppDataPath;
|
||||
}
|
||||
|
||||
public static string GetAppDataPath(string fileName)
|
||||
{
|
||||
return Path.Combine(GetAppDataPath(), fileName);
|
||||
}
|
||||
|
||||
public static string GetAppDataPath(params string[] paths)
|
||||
{
|
||||
return Path.Combine(GetAppDataPath(), Path.Combine(paths));
|
||||
@@ -52,7 +47,7 @@ internal static class Paths
|
||||
|
||||
public static string GetWorldSavesPath()
|
||||
{
|
||||
string savesPath = Path.Combine(GetAppDataPath(), "Saves");
|
||||
string savesPath = GetAppDataPath("Saves");
|
||||
|
||||
if (!Directory.Exists(savesPath))
|
||||
{
|
||||
@@ -78,4 +73,33 @@ internal static class Paths
|
||||
{
|
||||
return Path.Combine(GetWorldSaveDirectoryPath(saveName), saveName + ".srls");
|
||||
}
|
||||
|
||||
public static string GetModsPath()
|
||||
{
|
||||
string modsPath = GetAppDataPath("Mods");
|
||||
|
||||
if (!Directory.Exists(modsPath))
|
||||
{
|
||||
_ = Directory.CreateDirectory(modsPath);
|
||||
}
|
||||
|
||||
return modsPath;
|
||||
}
|
||||
|
||||
public static string GetModDirectoryPath(string modName)
|
||||
{
|
||||
string modPath = Path.Combine(GetWorldSavesPath(), modName);
|
||||
|
||||
if (!Directory.Exists(modPath))
|
||||
{
|
||||
_ = Directory.CreateDirectory(modPath);
|
||||
}
|
||||
|
||||
return modPath;
|
||||
}
|
||||
|
||||
public static string GetModFilePath(string modName)
|
||||
{
|
||||
return Path.Combine(GetWorldSaveDirectoryPath(modName), modName + ".dll");
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using StoneRed.LogicSimulator.Api.Interfaces;
|
||||
using StoneRed.LogicSimulator.Api;
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
using StoneRed.LogicSimulator.Api;
|
||||
using StoneRed.LogicSimulator.Api.Attributes;
|
||||
|
||||
namespace StoneRed.LogicSimulator.Simulation.LogicGates;
|
||||
|
||||
[LogicGateName("And Gate")]
|
||||
[LogicGateDescription("A and gate is a gate that returns true if both inputs are true.")]
|
||||
internal class AndGate : LogicGate
|
||||
{
|
||||
public override int InputCount { get; set; } = 2;
|
||||
|
||||
public override int OutputCount { get; set; } = 1;
|
||||
|
||||
protected override void Execute()
|
||||
{
|
||||
SetOutputBit(GetInputBit(0) == 1 && GetInputBit(1) == 1 ? 1 : 0, 0);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
using MonoGame.Extended.Input;
|
||||
|
||||
using StoneRed.LogicSimulator.Api;
|
||||
using StoneRed.LogicSimulator.Api.Attributes;
|
||||
using StoneRed.LogicSimulator.Api.Interfaces;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using MonoGame.Extended.Input;
|
||||
|
||||
using StoneRed.LogicSimulator.Api;
|
||||
using StoneRed.LogicSimulator.Api.Attributes;
|
||||
using StoneRed.LogicSimulator.Api.Interfaces;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
using StoneRed.LogicSimulator.Api;
|
||||
using StoneRed.LogicSimulator.Api.Attributes;
|
||||
using StoneRed.LogicSimulator.Api.Interfaces;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using StoneRed.LogicSimulator.Api.Attributes;
|
||||
using StoneRed.LogicSimulator.Api.Interfaces;
|
||||
using StoneRed.LogicSimulator.Api;
|
||||
using StoneRed.LogicSimulator.Api.Attributes;
|
||||
|
||||
namespace StoneRed.LogicSimulator.Simulation.LogicGates;
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
using StoneRed.LogicSimulator.Api;
|
||||
using StoneRed.LogicSimulator.Api.Attributes;
|
||||
|
||||
namespace StoneRed.LogicSimulator.Simulation.LogicGates;
|
||||
|
||||
[LogicGateName("Or Gate")]
|
||||
[LogicGateDescription("A or gate is a gate that returns true if at least one of the inputs is true.")]
|
||||
internal class OrGate : LogicGate
|
||||
{
|
||||
public override int InputCount { get; set; } = 1;
|
||||
|
||||
public override int OutputCount { get; set; } = 1;
|
||||
|
||||
protected override void Execute()
|
||||
{
|
||||
SetOutputBit(GetInputBit(0), 0);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
using StoneRed.LogicSimulator.Api.Attributes;
|
||||
using StoneRed.LogicSimulator.Api.Interfaces;
|
||||
using StoneRed.LogicSimulator.Api;
|
||||
using StoneRed.LogicSimulator.Api.Attributes;
|
||||
|
||||
namespace StoneRed.LogicSimulator.Simulation.LogicGates;
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
using MonoGame.Extended.Input;
|
||||
|
||||
using StoneRed.LogicSimulator.Api;
|
||||
using StoneRed.LogicSimulator.Api.Attributes;
|
||||
using StoneRed.LogicSimulator.Api.Interfaces;
|
||||
|
||||
|
||||
@@ -3,18 +3,20 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
using StoneRed.LogicSimulator.Api;
|
||||
using StoneRed.LogicSimulator.Api.Attributes;
|
||||
using StoneRed.LogicSimulator.Api.Interfaces;
|
||||
|
||||
using System.Linq;
|
||||
|
||||
namespace StoneRed.LogicSimulator.Simulation.LogicGates;
|
||||
|
||||
[LogicGateName("Test")]
|
||||
internal class TestLogicGate : LogicGate, Api.Interfaces.IDrawable
|
||||
{
|
||||
private readonly Color[] colors = new Color[9];
|
||||
private Color[] colors = new Color[15 * 15];
|
||||
private Texture2D texture = null!;
|
||||
public override int OutputCount { get; set; } = 0;
|
||||
public override int InputCount { get; set; } = 9;
|
||||
public override int InputCount { get; set; } = 7;
|
||||
|
||||
public Texture2D Texture
|
||||
{
|
||||
@@ -27,14 +29,39 @@ internal class TestLogicGate : LogicGate, Api.Interfaces.IDrawable
|
||||
|
||||
protected internal override void Initialize()
|
||||
{
|
||||
texture = CreateTexture(3, 3);
|
||||
colors = Enumerable.Repeat(Color.Gray, 15 * 15).ToArray();
|
||||
texture = CreateTexture(15, 15);
|
||||
}
|
||||
|
||||
protected override void Execute()
|
||||
{
|
||||
for (int i = 0; i < InputCount; i++)
|
||||
// A
|
||||
SetRangeWithSpacing(20, 24, 1, GetInputBit(0) == 1 ? Color.Red : Color.Gray);
|
||||
|
||||
// B
|
||||
SetRangeWithSpacing(40, 100, 15, GetInputBit(1) == 1 ? Color.Red : Color.Gray);
|
||||
|
||||
// C
|
||||
SetRangeWithSpacing(130, 190, 15, GetInputBit(2) == 1 ? Color.Red : Color.Gray);
|
||||
|
||||
// D
|
||||
SetRangeWithSpacing(200, 204, 1, GetInputBit(3) == 1 ? Color.Red : Color.Gray);
|
||||
|
||||
// E
|
||||
SetRangeWithSpacing(124, 184, 15, GetInputBit(4) == 1 ? Color.Red : Color.Gray);
|
||||
|
||||
// F
|
||||
SetRangeWithSpacing(34, 94, 15, GetInputBit(5) == 1 ? Color.Red : Color.Gray);
|
||||
|
||||
// G
|
||||
SetRangeWithSpacing(110, 114, 1, GetInputBit(6) == 1 ? Color.Red : Color.Gray);
|
||||
}
|
||||
|
||||
private void SetRangeWithSpacing(int start, int end, int spacing, Color value)
|
||||
{
|
||||
for (int i = start; i <= end; i += spacing)
|
||||
{
|
||||
colors[i] = GetInputBit(i) == 1 ? Color.Red : Color.Gray;
|
||||
colors[i] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ using MonoGame.Extended.Input;
|
||||
|
||||
using Myra.Graphics2D.UI;
|
||||
|
||||
using StoneRed.LogicSimulator.Api;
|
||||
using StoneRed.LogicSimulator.Api.Interfaces;
|
||||
using StoneRed.LogicSimulator.Simulation;
|
||||
using StoneRed.LogicSimulator.UserInterface.Windows;
|
||||
@@ -33,8 +34,9 @@ internal class WorldScreen : SrlsScreen<Grid>
|
||||
private Label fpsLabel = null!;
|
||||
private Label upsLabel = null!;
|
||||
private Label positionLabel = null!;
|
||||
private SpinButton frequency = null!;
|
||||
private CheckBox highPerformance = null!;
|
||||
private SpinButton frequencySpinButton = null!;
|
||||
private CheckBox highPerformanceCheckBox = null!;
|
||||
private CheckBox hideCablesBehindGatesCheckBox = null!;
|
||||
private ListBox nativeComponentsListBox = null!;
|
||||
|
||||
private float fps;
|
||||
@@ -69,8 +71,9 @@ internal class WorldScreen : SrlsScreen<Grid>
|
||||
positionLabel = infoPanel.FindChildById<Label>("position");
|
||||
|
||||
VerticalStackPanel settingsPanel = Root.FindChildById<VerticalStackPanel>("settings");
|
||||
frequency = settingsPanel.FindChildById<SpinButton>("frequency");
|
||||
highPerformance = settingsPanel.FindChildById<CheckBox>("highPerformance");
|
||||
frequencySpinButton = settingsPanel.FindChildById<SpinButton>("frequency");
|
||||
highPerformanceCheckBox = settingsPanel.FindChildById<CheckBox>("highPerformance");
|
||||
hideCablesBehindGatesCheckBox = settingsPanel.FindChildById<CheckBox>("hideCablesBehindGates");
|
||||
|
||||
nativeComponentsListBox = Root.FindChildById<ListBox>("nativeComponents");
|
||||
|
||||
@@ -139,7 +142,7 @@ internal class WorldScreen : SrlsScreen<Grid>
|
||||
foreach (LogicGateConnection connection in logicGate.LogicGateConnections)
|
||||
{
|
||||
Color lineColor = logicGate.GetCachedOutputBit(connection.OutputIndex) == 1 ? Color.Red : Color.LightBlue;
|
||||
srls.SpriteBatch.DrawLine((logicGate.WorldData.Position * srls.Scale) + (logicGateSize / 2 * srls.Scale), (connection.LogicGate.WorldData.Position * srls.Scale) + (logicGateSize / 2 * srls.Scale), lineColor, 5 * srls.Scale, 0.1f);
|
||||
srls.SpriteBatch.DrawLine((logicGate.WorldData.Position * srls.Scale) + (logicGateSize / 2 * srls.Scale), (connection.LogicGate.WorldData.Position * srls.Scale) + (logicGateSize / 2 * srls.Scale), lineColor, 5 * srls.Scale, hideCablesBehindGatesCheckBox.IsChecked ? 0.5f : 0.1f);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,8 +184,8 @@ internal class WorldScreen : SrlsScreen<Grid>
|
||||
upsLabel.Text = $"FRQ: {FrequencyCalculator.CalculateFrequency(simulator.ActualTicksPerSecond)}/{FrequencyCalculator.CalculateFrequency(simulator.TargetTicksPerSecond)}{(simulator.HighPerformanceClock ? "*" : string.Empty)} {(simulator.ClockCalibrating && simulator.HighPerformanceClock ? $"[Calibrating... {calibrationPercentage}%]" : string.Empty)}";
|
||||
positionLabel.Text = $"X/Y: {(long)Math.Round(camera.Position.X / logicGateSize.X / srls.Scale)}/{(long)Math.Round(camera.Position.Y / logicGateSize.Y / srls.Scale)}";
|
||||
|
||||
simulator.TargetTicksPerSecond = (int)frequency.Value.GetValueOrDefault();
|
||||
simulator.HighPerformanceClock = highPerformance.IsChecked;
|
||||
simulator.TargetTicksPerSecond = (int)frequencySpinButton.Value.GetValueOrDefault();
|
||||
simulator.HighPerformanceClock = highPerformanceCheckBox.IsChecked;
|
||||
|
||||
fpsLabel.Font = srls.FontSystem.GetFont(10 * srls.Scale);
|
||||
upsLabel.Font = srls.FontSystem.GetFont(10 * srls.Scale);
|
||||
|
||||
@@ -22,9 +22,9 @@ internal class LoadWorldWindow : SrlsWindow
|
||||
|
||||
loadButton.Click += LoadButton_Clicked;
|
||||
|
||||
foreach (string directories in Directory.GetDirectories(Paths.GetWorldSavesPath()))
|
||||
foreach (string directory in Directory.GetDirectories(Paths.GetWorldSavesPath()))
|
||||
{
|
||||
string saveName = Path.GetFileName(directories) ?? string.Empty;
|
||||
string saveName = Path.GetFileName(directory) ?? string.Empty;
|
||||
savesListBox.Items.Add(new(saveName));
|
||||
}
|
||||
|
||||
|
||||
@@ -31,9 +31,9 @@ internal class SaveWorldWindow : SrlsWindow
|
||||
newSaveButton.Click += NewSaveButton_Click;
|
||||
|
||||
int index = 0;
|
||||
foreach (string directories in Directory.GetDirectories(Paths.GetWorldSavesPath()))
|
||||
foreach (string directory in Directory.GetDirectories(Paths.GetWorldSavesPath()))
|
||||
{
|
||||
string saveName = Path.GetFileName(directories) ?? string.Empty;
|
||||
string saveName = Path.GetFileName(directory) ?? string.Empty;
|
||||
savesListBox.Items.Add(new(saveName));
|
||||
|
||||
if (saveName == worldData.SaveName)
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
using StoneRed.LogicSimulator.Api;
|
||||
using StoneRed.LogicSimulator.Api.Attributes;
|
||||
using StoneRed.LogicSimulator.Api.Interfaces;
|
||||
using StoneRed.LogicSimulator.Misc;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace StoneRed.LogicSimulator.Utilities;
|
||||
|
||||
@@ -41,9 +44,16 @@ internal class LogicGatesManager
|
||||
|
||||
public void LoadLogicGates()
|
||||
{
|
||||
IEnumerable<Type> logicGateTypes = GetType()
|
||||
.Assembly.GetTypes()
|
||||
.Where(t => t.IsSubclassOf(typeof(LogicGate)) && !t.IsAbstract);
|
||||
IEnumerable<Type> logicGateTypes = GetLogicGateTypesFromAssembly(GetType().Assembly);
|
||||
|
||||
foreach (string directory in Directory.GetDirectories(Paths.GetModsPath()))
|
||||
{
|
||||
string modName = Path.GetFileName(directory) ?? string.Empty;
|
||||
byte[] assemblyBytes = File.ReadAllBytes(Paths.GetModFilePath(modName));
|
||||
|
||||
Assembly assembly = Assembly.Load(assemblyBytes);
|
||||
logicGateTypes = logicGateTypes.Concat(GetLogicGateTypesFromAssembly(assembly));
|
||||
}
|
||||
|
||||
foreach (Type type in logicGateTypes)
|
||||
{
|
||||
@@ -92,6 +102,12 @@ internal class LogicGatesManager
|
||||
logicGate.Initialize();
|
||||
return true;
|
||||
}
|
||||
|
||||
private IEnumerable<Type> GetLogicGateTypesFromAssembly(Assembly assembly)
|
||||
{
|
||||
return assembly.GetTypes()
|
||||
.Where(t => t.IsSubclassOf(typeof(LogicGate)) && !t.IsAbstract);
|
||||
}
|
||||
}
|
||||
|
||||
internal class LogicGateInfo
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using StoneRed.LogicSimulator.Api.Interfaces;
|
||||
|
||||
using StoneRed.LogicSimulator.Api;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace StoneRed.LogicSimulator.WorldSaveSystem;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using FluentResults;
|
||||
|
||||
using StoneRed.LogicSimulator.Api.Interfaces;
|
||||
using StoneRed.LogicSimulator.Api;
|
||||
using StoneRed.LogicSimulator.Misc;
|
||||
|
||||
using System;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using FluentResults;
|
||||
|
||||
using StoneRed.LogicSimulator.Api;
|
||||
using StoneRed.LogicSimulator.Api.Attributes;
|
||||
using StoneRed.LogicSimulator.Api.Interfaces;
|
||||
using StoneRed.LogicSimulator.Misc;
|
||||
using StoneRed.LogicSimulator.Utilities;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user