Merge branch 'master' into preview
# Conflicts: # Automation/gen_csproj.py # GamecraftModdingAPI/App/AppEngine.cs # GamecraftModdingAPI/App/GameGameEngine.cs # GamecraftModdingAPI/App/GameMenuEngine.cs # GamecraftModdingAPI/Block.cs # GamecraftModdingAPI/Blocks/BlockEngine.cs # GamecraftModdingAPI/Blocks/BlockEngineInit.cs # GamecraftModdingAPI/Blocks/BlockEventsEngine.cs # GamecraftModdingAPI/Blocks/BlockIDs.cs # GamecraftModdingAPI/Blocks/ConsoleBlock.cs # GamecraftModdingAPI/Blocks/DampedSpring.cs # GamecraftModdingAPI/Blocks/LogicGate.cs # GamecraftModdingAPI/Blocks/Motor.cs # GamecraftModdingAPI/Blocks/MusicBlock.cs # GamecraftModdingAPI/Blocks/ObjectIdentifier.cs # GamecraftModdingAPI/Blocks/Piston.cs # GamecraftModdingAPI/Blocks/PlacementEngine.cs # GamecraftModdingAPI/Blocks/Servo.cs # GamecraftModdingAPI/Blocks/SfxBlock.cs # GamecraftModdingAPI/Blocks/SpawnPoint.cs # GamecraftModdingAPI/Blocks/TextBlock.cs # GamecraftModdingAPI/Blocks/Timer.cs # GamecraftModdingAPI/GamecraftModdingAPI.csproj # GamecraftModdingAPI/Inventory/HotbarEngine.cs # GamecraftModdingAPI/Inventory/HotbarSlotSelectionHandlerEnginePatch.cs # GamecraftModdingAPI/Main.cs # GamecraftModdingAPI/Player.cs # GamecraftModdingAPI/Players/PlayerEngine.cs # GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs # TechbloxModdingAPI/BlockGroup.cs # TechbloxModdingAPI/Blocks/Engines/BlueprintEngine.cs # TechbloxModdingAPI/Blocks/Engines/RemovalEngine.cs # TechbloxModdingAPI/Blocks/Engines/SignalEngine.cs # TechbloxModdingAPI/Blueprint.cs # TechbloxModdingAPI/Input/FakeInput.cs
This commit is contained in:
commit
fef66c349d
185 changed files with 9885 additions and 8349 deletions
|
@ -3,15 +3,22 @@
|
|||
import argparse
|
||||
from pathlib import Path, PurePath
|
||||
import re
|
||||
import os
|
||||
|
||||
DLL_EXCLUSIONS_REGEX = r"(System|Microsoft|Mono|IronPython|DiscordRPC)\."
|
||||
|
||||
def getAssemblyReferences(path):
|
||||
asmDir = Path(path)
|
||||
result = list()
|
||||
addedPath = ""
|
||||
if not asmDir.exists():
|
||||
addedPath = "../"
|
||||
asmDir = Path(addedPath + path)
|
||||
for child in asmDir.iterdir():
|
||||
if child.is_file() and re.search(DLL_EXCLUSIONS_REGEX, str(child), re.I) is None and str(child).lower().endswith(".dll"):
|
||||
result.append(str(child).replace("\\", "/"))
|
||||
childstr = str(child)
|
||||
childstr = os.path.relpath(childstr, addedPath).replace("\\", "/")
|
||||
result.append(childstr)
|
||||
return result
|
||||
|
||||
def buildReferencesXml(path):
|
||||
|
@ -27,16 +34,16 @@ def buildReferencesXml(path):
|
|||
return "<!--Start Dependencies-->\n <ItemGroup>\n" + "".join(result) + " </ItemGroup>\n<!--End Dependencies-->"
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Generate GamecraftModdingAPI.csproj")
|
||||
parser = argparse.ArgumentParser(description="Generate TechbloxModdingAPI.csproj")
|
||||
# TODO (maybe?): add params for custom csproj read and write locations
|
||||
args = parser.parse_args()
|
||||
|
||||
print("Building Assembly references")
|
||||
asmXml = buildReferencesXml("../ref/GamecraftPreview_Data/Managed")
|
||||
asmXml = buildReferencesXml("../ref/TechbloxPreview_Data/Managed")
|
||||
# print(asmXml)
|
||||
|
||||
with open("../GamecraftModdingAPI/GamecraftModdingAPI.csproj", "r") as xmlFile:
|
||||
print("Parsing GamecraftModdingAPI.csproj")
|
||||
with open("../TechbloxModdingAPI/TechbloxModdingAPI.csproj", "r") as xmlFile:
|
||||
print("Parsing TechbloxModdingAPI.csproj")
|
||||
fileStr = xmlFile.read()
|
||||
# print(fileStr)
|
||||
depsStart = re.search(r"\<!--\s*Start\s+Dependencies\s*--\>", fileStr)
|
||||
|
@ -45,7 +52,7 @@ if __name__ == "__main__":
|
|||
print("Unable to find dependency XML comments, aborting!")
|
||||
exit(1)
|
||||
newFileStr = fileStr[:depsStart.start()] + "\n" + asmXml + "\n" + fileStr[depsEnd.end() + 1:]
|
||||
with open("../GamecraftModdingAPI/GamecraftModdingAPI.csproj", "w") as xmlFile:
|
||||
with open("../TechbloxModdingAPI/TechbloxModdingAPI.csproj", "w") as xmlFile:
|
||||
print("Writing Assembly references")
|
||||
xmlFile.write(newFileStr)
|
||||
# print(newFileStr)
|
||||
|
|
142
CodeGenerator/BlockClassGenerator.cs
Normal file
142
CodeGenerator/BlockClassGenerator.cs
Normal file
|
@ -0,0 +1,142 @@
|
|||
using System;
|
||||
using System.CodeDom;
|
||||
using System.CodeDom.Compiler;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Gamecraft.Tweaks;
|
||||
using RobocraftX.Common;
|
||||
using Svelto.ECS;
|
||||
using Techblox.EngineBlock;
|
||||
|
||||
namespace CodeGenerator
|
||||
{
|
||||
public class BlockClassGenerator
|
||||
{
|
||||
public void Generate(string name, string group = null, Dictionary<string, string> renames = null, params Type[] types)
|
||||
{
|
||||
if (group is null)
|
||||
{
|
||||
group = GetGroup(name) + "_BLOCK_GROUP";
|
||||
if (typeof(CommonExclusiveGroups).GetFields().All(field => field.Name != group))
|
||||
group = GetGroup(name) + "_BLOCK_BUILD_GROUP";
|
||||
}
|
||||
|
||||
if (!group.Contains("."))
|
||||
group = "CommonExclusiveGroups." + group;
|
||||
|
||||
var codeUnit = new CodeCompileUnit();
|
||||
var ns = new CodeNamespace("TechbloxModdingAPI.Blocks");
|
||||
ns.Imports.Add(new CodeNamespaceImport("RobocraftX.Common"));
|
||||
ns.Imports.Add(new CodeNamespaceImport("Svelto.ECS"));
|
||||
var cl = new CodeTypeDeclaration(name);
|
||||
//cl.BaseTypes.Add(baseClass != null ? new CodeTypeReference(baseClass) : new CodeTypeReference("Block"));
|
||||
cl.BaseTypes.Add(new CodeTypeReference("SignalingBlock"));
|
||||
cl.Members.Add(new CodeConstructor
|
||||
{
|
||||
Parameters = {new CodeParameterDeclarationExpression("EGID", "egid")},
|
||||
Comments =
|
||||
{
|
||||
_start, new CodeCommentStatement($"Constructs a(n) {name} object representing an existing block.", true), _end
|
||||
},
|
||||
BaseConstructorArgs = {new CodeVariableReferenceExpression("egid")},
|
||||
Attributes = MemberAttributes.Public | MemberAttributes.Final
|
||||
});
|
||||
cl.Members.Add(new CodeConstructor
|
||||
{
|
||||
Parameters =
|
||||
{
|
||||
new CodeParameterDeclarationExpression(typeof(uint), "id")
|
||||
},
|
||||
Comments =
|
||||
{
|
||||
_start, new CodeCommentStatement($"Constructs a(n) {name} object representing an existing block.", true), _end
|
||||
},
|
||||
BaseConstructorArgs =
|
||||
{
|
||||
new CodeObjectCreateExpression("EGID", new CodeVariableReferenceExpression("id"),
|
||||
new CodeVariableReferenceExpression(group))
|
||||
},
|
||||
Attributes = MemberAttributes.Public | MemberAttributes.Final
|
||||
});
|
||||
foreach (var type in types)
|
||||
{
|
||||
GenerateProperties(cl, type, name, renames);
|
||||
}
|
||||
ns.Types.Add(cl);
|
||||
codeUnit.Namespaces.Add(ns);
|
||||
|
||||
var provider = CodeDomProvider.CreateProvider("CSharp");
|
||||
var path = $@"..\..\..\TechbloxModdingAPI\Blocks\{name}.cs";
|
||||
using (var sw = new StreamWriter(path))
|
||||
{
|
||||
provider.GenerateCodeFromCompileUnit(codeUnit, sw, new CodeGeneratorOptions {BracingStyle = "C"});
|
||||
}
|
||||
|
||||
File.WriteAllLines(path,
|
||||
File.ReadAllLines(path).SkipWhile(line => line.StartsWith("//") || line.Length == 0));
|
||||
}
|
||||
|
||||
private static string GetGroup(string name)
|
||||
{
|
||||
var ret = "";
|
||||
foreach (var ch in name)
|
||||
{
|
||||
if (char.IsUpper(ch) && ret.Length > 0)
|
||||
ret += "_" + ch;
|
||||
else
|
||||
ret += char.ToUpper(ch);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private void GenerateProperties(CodeTypeDeclaration cl, Type type, string baseClass,
|
||||
Dictionary<string, string> renames)
|
||||
{
|
||||
if (!typeof(IEntityComponent).IsAssignableFrom(type))
|
||||
throw new ArgumentException("Type must be an entity component");
|
||||
foreach (var field in type.GetFields())
|
||||
{
|
||||
var attr = field.GetCustomAttribute<TweakableStatAttribute>();
|
||||
if (renames == null || !renames.TryGetValue(field.Name, out var propName))
|
||||
{
|
||||
propName = field.Name;
|
||||
if (attr != null)
|
||||
propName = attr.propertyName;
|
||||
}
|
||||
|
||||
propName = char.ToUpper(propName[0]) + propName.Substring(1);
|
||||
var structFieldReference = new CodeFieldReferenceExpression(new CodeMethodInvokeExpression(
|
||||
new CodeMethodReferenceExpression(new CodeSnippetExpression("BlockEngine"),
|
||||
"GetBlockInfo", new CodeTypeReference(type)),
|
||||
new CodeThisReferenceExpression()), field.Name);
|
||||
cl.Members.Add(new CodeMemberProperty
|
||||
{
|
||||
Name = propName,
|
||||
HasGet = true,
|
||||
HasSet = true,
|
||||
GetStatements =
|
||||
{
|
||||
new CodeMethodReturnStatement(structFieldReference)
|
||||
},
|
||||
SetStatements =
|
||||
{
|
||||
new CodeAssignStatement(structFieldReference, new CodePropertySetValueReferenceExpression())
|
||||
},
|
||||
Type = new CodeTypeReference(field.FieldType),
|
||||
Attributes = MemberAttributes.Public | MemberAttributes.Final,
|
||||
Comments =
|
||||
{
|
||||
_start, new CodeCommentStatement($"Gets or sets the {baseClass}'s {propName} property." +
|
||||
$" {(attr != null ? "Tweakable stat." : "May not be saved.")}", true), _end
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly CodeCommentStatement _start = new CodeCommentStatement("<summary>", true);
|
||||
private static readonly CodeCommentStatement _end = new CodeCommentStatement("</summary>", true);
|
||||
}
|
||||
}
|
1176
CodeGenerator/CodeGenerator.csproj
Normal file
1176
CodeGenerator/CodeGenerator.csproj
Normal file
File diff suppressed because it is too large
Load diff
42
CodeGenerator/Program.cs
Normal file
42
CodeGenerator/Program.cs
Normal file
|
@ -0,0 +1,42 @@
|
|||
using System.Collections.Generic;
|
||||
using RobocraftX.Blocks;
|
||||
using RobocraftX.PilotSeat;
|
||||
using Techblox.EngineBlock;
|
||||
using Techblox.WheelRigBlock;
|
||||
|
||||
namespace CodeGenerator
|
||||
{
|
||||
internal class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var bcg = new BlockClassGenerator();
|
||||
bcg.Generate("Engine", null, new Dictionary<string, string>
|
||||
{
|
||||
{ "engineOn", "On" }
|
||||
}, typeof(EngineBlockComponent), // Simulation time properties
|
||||
typeof(EngineBlockTweakableComponent), typeof(EngineBlockReadonlyComponent));
|
||||
bcg.Generate("DampedSpring", "DAMPEDSPRING_BLOCK_GROUP", new Dictionary<string, string>
|
||||
{
|
||||
{"maxExtent", "MaxExtension"}
|
||||
},
|
||||
typeof(TweakableJointDampingComponent), typeof(DampedSpringReadOnlyStruct));
|
||||
bcg.Generate("LogicGate", "LOGIC_BLOCK_GROUP");
|
||||
bcg.Generate("Servo", types: typeof(ServoReadOnlyStruct), renames: new Dictionary<string, string>
|
||||
{
|
||||
{"minDeviation", "MinimumAngle"},
|
||||
{"maxDeviation", "MaximumAngle"},
|
||||
{"servoVelocity", "MaximumForce"}
|
||||
});
|
||||
bcg.Generate("WheelRig", "WHEELRIG_BLOCK_BUILD_GROUP", null,
|
||||
typeof(WheelRigTweakableStruct), typeof(WheelRigReadOnlyStruct),
|
||||
typeof(WheelRigSteerableTweakableStruct), typeof(WheelRigSteerableReadOnlyStruct));
|
||||
bcg.Generate("Seat", "RobocraftX.PilotSeat.SeatGroups.PILOTSEAT_BLOCK_BUILD_GROUP", null, typeof(SeatFollowCamComponent), typeof(SeatReadOnlySettingsComponent));
|
||||
bcg.Generate("Piston", null, new Dictionary<string, string>
|
||||
{
|
||||
{"pistonVelocity", "MaximumForce"}
|
||||
}, typeof(PistonReadOnlyStruct));
|
||||
bcg.Generate("Motor", null, null, typeof(MotorReadOnlyStruct));
|
||||
}
|
||||
}
|
||||
}
|
35
CodeGenerator/Properties/AssemblyInfo.cs
Normal file
35
CodeGenerator/Properties/AssemblyInfo.cs
Normal file
|
@ -0,0 +1,35 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("CodeGenerator")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("CodeGenerator")]
|
||||
[assembly: AssemblyCopyright("Copyright © ExMods 2021")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("0EBB6400-95A7-4A3D-B2ED-BF31E364CC10")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
@ -1,64 +0,0 @@
|
|||
using System;
|
||||
|
||||
using RobocraftX.GUI.MyGamesScreen;
|
||||
using RobocraftX.GUI;
|
||||
using Svelto.ECS;
|
||||
|
||||
using GamecraftModdingAPI.Engines;
|
||||
using GamecraftModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftModdingAPI.App
|
||||
{
|
||||
public class AppEngine : IFactoryEngine
|
||||
{
|
||||
public event EventHandler<MenuEventArgs> EnterMenu;
|
||||
|
||||
public event EventHandler<MenuEventArgs> ExitMenu;
|
||||
|
||||
public IEntityFactory Factory { set; private get; }
|
||||
|
||||
public string Name => "GamecraftModdingAPIAppEngine";
|
||||
|
||||
public bool isRemovable => false;
|
||||
|
||||
public EntitiesDB entitiesDB { set; private get; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
IsInMenu = false;
|
||||
ExceptionUtil.InvokeEvent(ExitMenu, this, new MenuEventArgs { });
|
||||
}
|
||||
|
||||
public void Ready()
|
||||
{
|
||||
IsInMenu = true;
|
||||
ExceptionUtil.InvokeEvent(EnterMenu, this, new MenuEventArgs { });
|
||||
}
|
||||
|
||||
// app functionality
|
||||
|
||||
public bool IsInMenu
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
} = false;
|
||||
|
||||
public Game[] GetMyGames()
|
||||
{
|
||||
EntityCollection<MyGameDataEntityStruct> mgsevs = entitiesDB.QueryEntities<MyGameDataEntityStruct>(MyGamesScreenExclusiveGroups.MyGames);
|
||||
var mgsevsB = mgsevs.ToBuffer().buffer;
|
||||
Game[] games = new Game[mgsevs.count];
|
||||
for (int i = 0; i < mgsevs.count; i++)
|
||||
{
|
||||
Utility.Logging.MetaDebugLog($"Found game named {mgsevsB[i].GameName}");
|
||||
games[i] = new Game(mgsevsB[i].ID);
|
||||
}
|
||||
return games;
|
||||
}
|
||||
}
|
||||
|
||||
public struct MenuEventArgs
|
||||
{
|
||||
|
||||
}
|
||||
}
|
|
@ -1,130 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using HarmonyLib;
|
||||
|
||||
using RobocraftX;
|
||||
using RobocraftX.Common;
|
||||
using RobocraftX.Schedulers;
|
||||
using RobocraftX.SimulationModeState;
|
||||
using Svelto.ECS;
|
||||
using Svelto.Tasks;
|
||||
using Svelto.Tasks.Lean;
|
||||
|
||||
using GamecraftModdingAPI.Blocks;
|
||||
using GamecraftModdingAPI.Engines;
|
||||
using GamecraftModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftModdingAPI.App
|
||||
{
|
||||
public class GameGameEngine : IApiEngine
|
||||
{
|
||||
public event EventHandler<GameEventArgs> EnterGame;
|
||||
|
||||
public event EventHandler<GameEventArgs> ExitGame;
|
||||
|
||||
public string Name => "GamecraftModdingAPIGameInfoMenuEngine";
|
||||
|
||||
public bool isRemovable => false;
|
||||
|
||||
public EntitiesDB entitiesDB { set; private get; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
ExceptionUtil.InvokeEvent(ExitGame, this, new GameEventArgs { GameName = GameMode.SaveGameDetails.Name, GamePath = GameMode.SaveGameDetails.Folder });
|
||||
IsInGame = false;
|
||||
}
|
||||
|
||||
public void Ready()
|
||||
{
|
||||
ExceptionUtil.InvokeEvent(EnterGame, this, new GameEventArgs { GameName = GameMode.SaveGameDetails.Name, GamePath = GameMode.SaveGameDetails.Folder });
|
||||
IsInGame = true;
|
||||
}
|
||||
|
||||
// game functionality
|
||||
|
||||
public bool IsInGame
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
} = false;
|
||||
|
||||
public void ExitCurrentGame(bool async = false)
|
||||
{
|
||||
if (async)
|
||||
{
|
||||
ExitCurrentGameAsync().RunOn(Lean.EveryFrameStepRunner_TimeRunningAndStopped);
|
||||
}
|
||||
else
|
||||
{
|
||||
entitiesDB.QueryEntity<GameSceneEntityStruct>(CommonExclusiveGroups.GameSceneEGID).WantsToQuit = true;
|
||||
entitiesDB.PublishEntityChange<GameSceneEntityStruct>(CommonExclusiveGroups.GameSceneEGID);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public IEnumerator<TaskContract> ExitCurrentGameAsync()
|
||||
{
|
||||
/*
|
||||
while (Lean.EveryFrameStepRunner_RUNS_IN_TIME_STOPPED_AND_RUNNING.isStopping) { yield return Yield.It; }
|
||||
AccessTools.Method(typeof(FullGameCompositionRoot), "SwitchToMenu").Invoke(FullGameFields.Instance, new object[0]);*/
|
||||
yield return Yield.It;
|
||||
entitiesDB.QueryEntity<GameSceneEntityStruct>(CommonExclusiveGroups.GameSceneEGID).WantsToQuit = true;
|
||||
entitiesDB.PublishEntityChange<GameSceneEntityStruct>(CommonExclusiveGroups.GameSceneEGID);
|
||||
}
|
||||
|
||||
public void SaveCurrentGame()
|
||||
{
|
||||
ref GameSceneEntityStruct gses = ref entitiesDB.QueryEntity<GameSceneEntityStruct>(CommonExclusiveGroups.GameSceneEGID);
|
||||
gses.LoadAfterSaving = false;
|
||||
gses.SaveNow = true;
|
||||
entitiesDB.PublishEntityChange<GameSceneEntityStruct>(CommonExclusiveGroups.GameSceneEGID);
|
||||
}
|
||||
|
||||
public bool IsTimeRunningMode()
|
||||
{
|
||||
return TimeRunningModeUtil.IsTimeRunningMode(entitiesDB);
|
||||
}
|
||||
|
||||
public bool IsTimeStoppedMode()
|
||||
{
|
||||
return TimeRunningModeUtil.IsTimeStoppedMode(entitiesDB);
|
||||
}
|
||||
|
||||
public void ToggleTimeMode()
|
||||
{
|
||||
TimeRunningModeUtil.ToggleTimeRunningState(entitiesDB);
|
||||
}
|
||||
|
||||
public EGID[] GetAllBlocksInGame(BlockIDs filter = BlockIDs.Invalid)
|
||||
{
|
||||
var allBlocks = entitiesDB.QueryEntities<DBEntityStruct>();
|
||||
List<EGID> blockEGIDs = new List<EGID>();
|
||||
if (filter == BlockIDs.Invalid)
|
||||
{
|
||||
foreach (var (blocks, _) in allBlocks)
|
||||
{
|
||||
var buffer = blocks.ToBuffer().buffer;
|
||||
for (int i = 0; i < buffer.capacity; i++)
|
||||
blockEGIDs.Add(buffer[i].ID);
|
||||
}
|
||||
|
||||
return blockEGIDs.ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var (blocks, _) in allBlocks)
|
||||
{
|
||||
var array = blocks.ToBuffer().buffer;
|
||||
for (var index = 0; index < array.capacity; index++)
|
||||
{
|
||||
var block = array[index];
|
||||
if (block.DBID == (ulong) filter)
|
||||
blockEGIDs.Add(block.ID);
|
||||
}
|
||||
}
|
||||
|
||||
return blockEGIDs.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,141 +0,0 @@
|
|||
using System;
|
||||
using HarmonyLib;
|
||||
|
||||
using RobocraftX;
|
||||
using RobocraftX.Common;
|
||||
using RobocraftX.GUI;
|
||||
using RobocraftX.GUI.MyGamesScreen;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.Experimental;
|
||||
|
||||
using GamecraftModdingAPI.Engines;
|
||||
using GamecraftModdingAPI.Utility;
|
||||
using Svelto.DataStructures;
|
||||
|
||||
namespace GamecraftModdingAPI.App
|
||||
{
|
||||
public class GameMenuEngine : IFactoryEngine
|
||||
{
|
||||
public IEntityFactory Factory { set; private get; }
|
||||
|
||||
public string Name => "GamecraftModdingAPIGameInfoGameEngine";
|
||||
|
||||
public bool isRemovable => false;
|
||||
|
||||
public EntitiesDB entitiesDB { set; private get; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
IsInMenu = false;
|
||||
}
|
||||
|
||||
public void Ready()
|
||||
{
|
||||
IsInMenu = true;
|
||||
}
|
||||
|
||||
// game functionality
|
||||
|
||||
public bool IsInMenu
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
} = false;
|
||||
|
||||
public bool CreateMyGame(EGID id, string path = "", uint thumbnailId = 0, string gameName = "", string creatorName = "", string description = "", long createdDate = 0L)
|
||||
{
|
||||
EntityComponentInitializer eci = Factory.BuildEntity<MyGameDataEntityDescriptor_DamnItFJWhyDidYouMakeThisInternal>(id);
|
||||
eci.Init(new MyGameDataEntityStruct
|
||||
{
|
||||
SavedGamePath = new ECSString(path),
|
||||
ThumbnailId = thumbnailId,
|
||||
GameName = new ECSString(gameName),
|
||||
CreatorName = new ECSString(creatorName),
|
||||
GameDescription = new ECSString(description),
|
||||
CreatedDate = createdDate,
|
||||
});
|
||||
// entitiesDB.PublishEntityChange<MyGameDataEntityStruct>(id); // this will always fail
|
||||
return true;
|
||||
}
|
||||
|
||||
public uint HighestID()
|
||||
{
|
||||
EntityCollection<MyGameDataEntityStruct> games = entitiesDB.QueryEntities<MyGameDataEntityStruct>(MyGamesScreenExclusiveGroups.MyGames);
|
||||
var gamesB = games.ToBuffer().buffer;
|
||||
uint max = 0;
|
||||
for (int i = 0; i < games.count; i++)
|
||||
{
|
||||
if (gamesB[i].ID.entityID > max)
|
||||
{
|
||||
max = gamesB[i].ID.entityID;
|
||||
}
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
public bool EnterGame(EGID id)
|
||||
{
|
||||
if (!ExistsGameInfo(id)) return false;
|
||||
ref MyGameDataEntityStruct mgdes = ref GetGameInfo(id);
|
||||
return EnterGame(mgdes.GameName, mgdes.SavedGamePath);
|
||||
}
|
||||
|
||||
public bool EnterGame(string gameName, string path, ulong workshopId = 0uL, bool autoEnterSim = false)
|
||||
{
|
||||
GameMode.CurrentMode = autoEnterSim ? RCXMode.Play : RCXMode.Build;
|
||||
GameMode.SaveGameDetails = new SaveGameDetails(gameName, path, workshopId);
|
||||
// the private FullGameCompositionRoot.SwitchToGame() method gets passed to menu items for this reason
|
||||
AccessTools.Method(typeof(FullGameCompositionRoot), "SwitchToGame").Invoke(FullGameFields.Instance, new object[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool SetGameName(EGID id, string name)
|
||||
{
|
||||
if (!ExistsGameInfo(id)) return false;
|
||||
GetGameInfo(id).GameName.Set(name);
|
||||
GetGameViewInfo(id).MyGamesSlotComponent.GameName = StringUtil.SanitiseString(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool SetGameDescription(EGID id, string name)
|
||||
{
|
||||
if (!ExistsGameInfo(id)) return false;
|
||||
GetGameInfo(id).GameDescription.Set(name);
|
||||
GetGameViewInfo(id).MyGamesSlotComponent.GameDescription = StringUtil.SanitiseString(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool ExistsGameInfo(EGID id)
|
||||
{
|
||||
return entitiesDB.Exists<MyGameDataEntityStruct>(id);
|
||||
}
|
||||
|
||||
public ref MyGameDataEntityStruct GetGameInfo(EGID id)
|
||||
{
|
||||
return ref GetComponent<MyGameDataEntityStruct>(id);
|
||||
}
|
||||
|
||||
public ref MyGamesSlotEntityViewStruct GetGameViewInfo(EGID id)
|
||||
{
|
||||
EntityCollection<MyGamesSlotEntityViewStruct> entities =
|
||||
entitiesDB.QueryEntities<MyGamesSlotEntityViewStruct>(MyGamesScreenExclusiveGroups.GameSlotGuiEntities);
|
||||
var entitiesB = entities.ToBuffer().buffer;
|
||||
for (int i = 0; i < entities.count; i++)
|
||||
{
|
||||
if (entitiesB[i].ID.entityID == id.entityID)
|
||||
{
|
||||
return ref entitiesB[i];
|
||||
}
|
||||
}
|
||||
MyGamesSlotEntityViewStruct[] defRef = new MyGamesSlotEntityViewStruct[1];
|
||||
return ref defRef[0];
|
||||
}
|
||||
|
||||
public ref T GetComponent<T>(EGID id) where T: unmanaged, IEntityComponent
|
||||
{
|
||||
return ref entitiesDB.QueryEntity<T>(id);
|
||||
}
|
||||
}
|
||||
|
||||
internal class MyGameDataEntityDescriptor_DamnItFJWhyDidYouMakeThisInternal : GenericEntityDescriptor<MyGameDataEntityStruct> { }
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
using RobocraftX.CR.MainGame;
|
||||
using RobocraftX.StateSync;
|
||||
|
||||
using HarmonyLib;
|
||||
|
||||
namespace GamecraftModdingAPI.App
|
||||
{
|
||||
[HarmonyPatch]
|
||||
class StateSyncRegPatch
|
||||
{
|
||||
public static void Postfix(StateSyncRegistrationHelper stateSyncReg)
|
||||
{
|
||||
// register sim/build events engines
|
||||
Game.InitDeterministic(stateSyncReg);
|
||||
}
|
||||
|
||||
[HarmonyTargetMethod]
|
||||
public static MethodBase Target()
|
||||
{
|
||||
return AccessTools.Method(typeof(MainGameCompositionRoot), "DeterministicCompose").MakeGenericMethod(typeof(object));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,501 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
using Gamecraft.Blocks.BlockGroups;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.EntityStructs;
|
||||
using RobocraftX.Common;
|
||||
using RobocraftX.Blocks;
|
||||
using Unity.Mathematics;
|
||||
using Gamecraft.Blocks.GUI;
|
||||
|
||||
using GamecraftModdingAPI.Blocks;
|
||||
using GamecraftModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftModdingAPI
|
||||
{
|
||||
/// <summary>
|
||||
/// A single (perhaps scaled) block. Properties may return default values if the block is removed and then setting them is ignored.
|
||||
/// For specific block type operations, use the specialised block classes in the GamecraftModdingAPI.Blocks namespace.
|
||||
/// </summary>
|
||||
public class Block : IEquatable<Block>, IEquatable<EGID>
|
||||
{
|
||||
protected static readonly PlacementEngine PlacementEngine = new PlacementEngine();
|
||||
protected static readonly MovementEngine MovementEngine = new MovementEngine();
|
||||
protected static readonly RotationEngine RotationEngine = new RotationEngine();
|
||||
protected static readonly RemovalEngine RemovalEngine = new RemovalEngine();
|
||||
protected static readonly SignalEngine SignalEngine = new SignalEngine();
|
||||
protected static readonly BlockEventsEngine BlockEventsEngine = new BlockEventsEngine();
|
||||
protected static readonly ScalingEngine ScalingEngine = new ScalingEngine();
|
||||
|
||||
protected internal static readonly BlockEngine BlockEngine = new BlockEngine();
|
||||
|
||||
/// <summary>
|
||||
/// Place a new block at the given position. If scaled, position means the center of the block. The default block size is 0.2 in terms of position.
|
||||
/// Place blocks next to each other to connect them.
|
||||
/// The placed block will be a complete block with a placement grid and collision which will be saved along with the game.
|
||||
/// </summary>
|
||||
/// <param name="block">The block's type</param>
|
||||
/// <param name="color">The block's color</param>
|
||||
/// <param name="darkness">The block color's darkness (0-9) - 0 is default color</param>
|
||||
/// <param name="position">The block's position - default block size is 0.2</param>
|
||||
/// <param name="rotation">The block's rotation in degrees</param>
|
||||
/// <param name="uscale">The block's uniform scale - default scale is 1 (with 0.2 width)</param>
|
||||
/// <param name="scale">The block's non-uniform scale - 0 means <paramref name="uscale"/> is used</param>
|
||||
/// <param name="player">The player who placed the block</param>
|
||||
/// <returns>The placed block or null if failed</returns>
|
||||
public static Block PlaceNew(BlockIDs block, float3 position,
|
||||
float3 rotation = default, BlockColors color = BlockColors.Default, byte darkness = 0,
|
||||
int uscale = 1, float3 scale = default, Player player = null)
|
||||
{
|
||||
return PlaceNew<Block>(block, position, rotation, color, darkness, uscale, scale, player);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Place a new block at the given position. If scaled, position means the center of the block. The default block size is 0.2 in terms of position.
|
||||
/// Place blocks next to each other to connect them.
|
||||
/// The placed block will be a complete block with a placement grid and collision which will be saved along with the game.
|
||||
/// </summary>
|
||||
/// <param name="block">The block's type</param>
|
||||
/// <param name="color">The block's color</param>
|
||||
/// <param name="darkness">The block color's darkness (0-9) - 0 is default color</param>
|
||||
/// <param name="position">The block's position - default block size is 0.2</param>
|
||||
/// <param name="rotation">The block's rotation in degrees</param>
|
||||
/// <param name="uscale">The block's uniform scale - default scale is 1 (with 0.2 width)</param>
|
||||
/// <param name="scale">The block's non-uniform scale - 0 means <paramref name="uscale"/> is used</param>
|
||||
/// <param name="player">The player who placed the block</param>
|
||||
/// <returns>The placed block or null if failed</returns>
|
||||
public static T PlaceNew<T>(BlockIDs block, float3 position,
|
||||
float3 rotation = default, BlockColors color = BlockColors.Default, byte darkness = 0,
|
||||
int uscale = 1, float3 scale = default, Player player = null) where T : Block
|
||||
{
|
||||
if (PlacementEngine.IsInGame && GameState.IsBuildMode())
|
||||
{
|
||||
var egid = PlacementEngine.PlaceBlock(block, color, darkness,
|
||||
position, uscale, scale, player, rotation, out var initializer);
|
||||
var bl = New<T>(egid.entityID, egid.groupID);
|
||||
bl.InitData.Group = BlockEngine.InitGroup(initializer);
|
||||
Placed += bl.OnPlacedInit;
|
||||
return bl;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the most recently placed block.
|
||||
/// </summary>
|
||||
/// <returns>The block object</returns>
|
||||
public static Block GetLastPlacedBlock()
|
||||
{
|
||||
return New<Block>(BlockIdentifiers.LatestBlockID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An event that fires each time a block is placed.
|
||||
/// </summary>
|
||||
public static event EventHandler<BlockPlacedRemovedEventArgs> Placed
|
||||
{
|
||||
add => BlockEventsEngine.Placed += value;
|
||||
remove => BlockEventsEngine.Placed -= value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An event that fires each time a block is removed.
|
||||
/// </summary>
|
||||
public static event EventHandler<BlockPlacedRemovedEventArgs> Removed
|
||||
{
|
||||
add => BlockEventsEngine.Removed += value;
|
||||
remove => BlockEventsEngine.Removed -= value;
|
||||
}
|
||||
|
||||
private static Dictionary<Type, Func<EGID, Block>> initializers = new Dictionary<Type, Func<EGID, Block>>();
|
||||
|
||||
private static Dictionary<Type, ExclusiveGroupStruct[]> typeToGroup =
|
||||
new Dictionary<Type, ExclusiveGroupStruct[]>
|
||||
{
|
||||
{typeof(ConsoleBlock), new[] {CommonExclusiveGroups.CONSOLE_BLOCK_GROUP}},
|
||||
{typeof(LogicGate), new [] {CommonExclusiveGroups.LOGIC_BLOCK_GROUP}},
|
||||
{typeof(Motor), new[] {CommonExclusiveGroups.MOTOR_BLOCK_GROUP}},
|
||||
{typeof(MusicBlock), new[] {CommonExclusiveGroups.MUSIC_BLOCK_GROUP}},
|
||||
{typeof(ObjectIdentifier), new[]{CommonExclusiveGroups.OBJID_BLOCK_GROUP}},
|
||||
{typeof(Piston), new[] {CommonExclusiveGroups.PISTON_BLOCK_GROUP}},
|
||||
{typeof(Servo), new[] {CommonExclusiveGroups.SERVO_BLOCK_GROUP}},
|
||||
{
|
||||
typeof(SpawnPoint),
|
||||
new[]
|
||||
{
|
||||
CommonExclusiveGroups.SPAWNPOINT_BLOCK_GROUP,
|
||||
CommonExclusiveGroups.BUILDINGSPAWN_BLOCK_GROUP
|
||||
}
|
||||
},
|
||||
{
|
||||
typeof(SfxBlock),
|
||||
new[]
|
||||
{
|
||||
CommonExclusiveGroups.SIMPLESFX_BLOCK_GROUP,
|
||||
CommonExclusiveGroups.LOOPEDSFX_BLOCK_GROUP
|
||||
}
|
||||
},
|
||||
{typeof(DampedSpring), new [] {CommonExclusiveGroups.DAMPEDSPRING_BLOCK_GROUP}},
|
||||
{typeof(TextBlock), new[] {CommonExclusiveGroups.TEXT_BLOCK_GROUP}},
|
||||
{typeof(Timer), new[] {CommonExclusiveGroups.TIMER_BLOCK_GROUP}}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new instance of T with the given ID and group using dynamically created delegates.
|
||||
/// It's equivalent to new T(EGID) with a minimal overhead thanks to caching the created delegates.
|
||||
/// </summary>
|
||||
/// <param name="id">The block ID</param>
|
||||
/// <param name="group">The block group</param>
|
||||
/// <typeparam name="T">The block's type or Block itself</typeparam>
|
||||
/// <returns>An instance of the provided type</returns>
|
||||
/// <exception cref="BlockTypeException">The block group doesn't match or cannot be found</exception>
|
||||
/// <exception cref="MissingMethodException">The block class doesn't have the needed constructor</exception>
|
||||
private static T New<T>(uint id, ExclusiveGroupStruct? group = null) where T : Block
|
||||
{
|
||||
var type = typeof(T);
|
||||
EGID egid;
|
||||
if (!group.HasValue)
|
||||
{
|
||||
if (typeToGroup.TryGetValue(type, out var gr) && gr.Length == 1)
|
||||
egid = new EGID(id, gr[0]);
|
||||
else
|
||||
egid = BlockEngine.FindBlockEGID(id) ?? throw new BlockTypeException("Could not find block group!");
|
||||
}
|
||||
else
|
||||
{
|
||||
egid = new EGID(id, group.Value);
|
||||
if (typeToGroup.TryGetValue(type, out var gr)
|
||||
&& gr.All(egs => egs != group.Value)) //If this subclass has a specific group, then use that - so Block should still work
|
||||
throw new BlockTypeException($"Incompatible block type! Type {type.Name} belongs to group {gr.Select(g => g.ToString()).Aggregate((a, b) => a + ", " + b)} instead of {group.Value}");
|
||||
}
|
||||
|
||||
if (initializers.TryGetValue(type, out var func))
|
||||
{
|
||||
var bl = (T) func(egid);
|
||||
return bl;
|
||||
}
|
||||
|
||||
//https://stackoverflow.com/a/10593806/2703239
|
||||
var ctor = type.GetConstructor(new[] {typeof(EGID)});
|
||||
if (ctor == null)
|
||||
throw new MissingMethodException("There is no constructor with an EGID parameter for this object");
|
||||
DynamicMethod dynamic = new DynamicMethod(string.Empty,
|
||||
type,
|
||||
new[] {typeof(EGID)},
|
||||
type);
|
||||
ILGenerator il = dynamic.GetILGenerator();
|
||||
|
||||
//il.DeclareLocal(type);
|
||||
il.Emit(OpCodes.Ldarg_0); //Load EGID and pass to constructor
|
||||
il.Emit(OpCodes.Newobj, ctor); //Call constructor
|
||||
//il.Emit(OpCodes.Stloc_0); - doesn't seem like we need these
|
||||
//il.Emit(OpCodes.Ldloc_0);
|
||||
il.Emit(OpCodes.Ret);
|
||||
|
||||
func = (Func<EGID, T>) dynamic.CreateDelegate(typeof(Func<EGID, T>));
|
||||
initializers.Add(type, func);
|
||||
var block = (T) func(egid);
|
||||
return block;
|
||||
}
|
||||
|
||||
public Block(EGID id)
|
||||
{
|
||||
Id = id;
|
||||
var type = GetType();
|
||||
if (typeToGroup.TryGetValue(type, out var groups))
|
||||
{
|
||||
if (groups.All(gr => gr != id.groupID))
|
||||
throw new BlockTypeException("The block has the wrong group! The type is " + GetType() +
|
||||
" while the group is " + id.groupID);
|
||||
}
|
||||
else if (type != typeof(Block))
|
||||
Logging.LogWarning($"Unknown block type! Add {type} to the dictionary.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This overload searches for the correct group the block is in.
|
||||
/// It will throw an exception if the block doesn't exist.
|
||||
/// Use the EGID constructor where possible or subclasses of Block as those specify the group.
|
||||
/// </summary>
|
||||
public Block(uint id)
|
||||
{
|
||||
Id = BlockEngine.FindBlockEGID(id) ?? throw new BlockTypeException("Could not find the appropriate group for the block. The block probably doesn't exist or hasn't been submitted.");
|
||||
}
|
||||
|
||||
public EGID Id { get; }
|
||||
|
||||
internal BlockEngine.BlockInitData InitData;
|
||||
|
||||
/// <summary>
|
||||
/// The block's current position or zero if the block no longer exists.
|
||||
/// A block is 0.2 wide by default in terms of position.
|
||||
/// </summary>
|
||||
public float3 Position
|
||||
{
|
||||
get => MovementEngine.GetPosition(Id, InitData);
|
||||
set
|
||||
{
|
||||
MovementEngine.MoveBlock(Id, InitData, value);
|
||||
if (blockGroup != null)
|
||||
blockGroup.PosAndRotCalculated = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The block's current rotation in degrees or zero if the block doesn't exist.
|
||||
/// </summary>
|
||||
public float3 Rotation
|
||||
{
|
||||
get => RotationEngine.GetRotation(Id, InitData);
|
||||
set
|
||||
{
|
||||
RotationEngine.RotateBlock(Id, InitData, value);
|
||||
if (blockGroup != null)
|
||||
blockGroup.PosAndRotCalculated = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The block's non-uniform scale or zero if the block's invalid. Independent of the uniform scaling.
|
||||
/// The default scale of 1 means 0.2 in terms of position.
|
||||
/// </summary>
|
||||
public float3 Scale
|
||||
{
|
||||
get => BlockEngine.GetBlockInfo(this, (ScalingEntityStruct st) => st.scale);
|
||||
set
|
||||
{
|
||||
BlockEngine.SetBlockInfo(this, (ref ScalingEntityStruct st, float3 val) => st.scale = val, value);
|
||||
if (!Exists) return; //UpdateCollision needs the block to exist
|
||||
ScalingEngine.UpdateCollision(Id);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The block's uniform scale or zero if the block's invalid. Also sets the non-uniform scale.
|
||||
/// The default scale of 1 means 0.2 in terms of position.
|
||||
/// </summary>
|
||||
public int UniformScale
|
||||
{
|
||||
get => BlockEngine.GetBlockInfo(this, (UniformBlockScaleEntityStruct st) => st.scaleFactor);
|
||||
set
|
||||
{
|
||||
BlockEngine.SetBlockInfo(this, (ref UniformBlockScaleEntityStruct st, int val) => st.scaleFactor = val,
|
||||
value);
|
||||
Scale = new float3(value, value, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The block's type (ID). Returns BlockIDs.Invalid if the block doesn't exist anymore.
|
||||
/// </summary>
|
||||
public BlockIDs Type
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo(this, (DBEntityStruct st) => (BlockIDs) st.DBID, BlockIDs.Invalid);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The block's color. Returns BlockColors.Default if the block no longer exists.
|
||||
/// </summary>
|
||||
public BlockColor Color
|
||||
{
|
||||
get
|
||||
{
|
||||
byte index = BlockEngine.GetBlockInfo(this, (ColourParameterEntityStruct st) => st.indexInPalette,
|
||||
byte.MaxValue);
|
||||
return new BlockColor(index);
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.SetBlockInfo(this, (ref ColourParameterEntityStruct color, BlockColor val) =>
|
||||
{
|
||||
color.indexInPalette = (byte) (val.Color + val.Darkness * 10);
|
||||
//color.overridePaletteColour = false;
|
||||
//color.needsUpdate = true;
|
||||
color.hasNetworkChange = true;
|
||||
color.paletteColour = BlockEngine.ConvertBlockColor(color.indexInPalette);
|
||||
}, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The block's exact color. Gets reset to the palette color (Color property) after reentering the game.
|
||||
/// </summary>
|
||||
public float4 CustomColor
|
||||
{
|
||||
get => BlockEngine.GetBlockInfo(this, (ColourParameterEntityStruct st) => st.paletteColour);
|
||||
set
|
||||
{
|
||||
BlockEngine.SetBlockInfo(this, (ref ColourParameterEntityStruct color, float4 val) =>
|
||||
{
|
||||
color.paletteColour = val;
|
||||
//color.overridePaletteColour = true;
|
||||
//color.needsUpdate = true;
|
||||
color.hasNetworkChange = true;
|
||||
}, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The text displayed on the block if applicable, or null.
|
||||
/// Setting it is temporary to the session, it won't be saved.
|
||||
/// </summary>
|
||||
public string Label
|
||||
{
|
||||
get => BlockEngine.GetBlockInfoViewStruct(this, (TextLabelEntityViewStruct st) => st.textLabelComponent?.text);
|
||||
set
|
||||
{
|
||||
BlockEngine.SetBlockInfoViewStruct(this, (ref TextLabelEntityViewStruct text, string val) =>
|
||||
{
|
||||
if (text.textLabelComponent != null) text.textLabelComponent.text = val;
|
||||
}, value);
|
||||
}
|
||||
}
|
||||
|
||||
private BlockGroup blockGroup;
|
||||
/// <summary>
|
||||
/// Returns the block group this block is a part of. Block groups can be placed using blueprints.
|
||||
/// Returns null if not part of a group.<br />
|
||||
/// Setting the group after the block has been initialized will not update everything properly.
|
||||
/// You should only set this property on blocks newly placed by your code.
|
||||
/// </summary>
|
||||
public BlockGroup BlockGroup
|
||||
{
|
||||
get
|
||||
{
|
||||
if (blockGroup != null) return blockGroup;
|
||||
return blockGroup = BlockEngine.GetBlockInfo(this,
|
||||
(BlockGroupEntityComponent bgec) =>
|
||||
bgec.currentBlockGroup == -1 ? null : new BlockGroup(bgec.currentBlockGroup, this));
|
||||
}
|
||||
set
|
||||
{
|
||||
blockGroup?.RemoveInternal(this);
|
||||
BlockEngine.SetBlockInfo(this,
|
||||
(ref BlockGroupEntityComponent bgec, BlockGroup val) => bgec.currentBlockGroup = val?.Id ?? -1,
|
||||
value);
|
||||
value?.AddInternal(this);
|
||||
blockGroup = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the block exists. The other properties will return a default value if the block doesn't exist.
|
||||
/// If the block was just placed, then this will also return false but the properties will work correctly.
|
||||
/// </summary>
|
||||
public bool Exists => BlockEngine.BlockExists(Id);
|
||||
|
||||
/// <summary>
|
||||
/// Returns an array of blocks that are connected to this one. Returns an empty array if the block doesn't exist.
|
||||
/// </summary>
|
||||
public Block[] GetConnectedCubes() => BlockEngine.GetConnectedBlocks(Id);
|
||||
|
||||
/// <summary>
|
||||
/// Removes this block.
|
||||
/// </summary>
|
||||
/// <returns>True if the block exists and could be removed.</returns>
|
||||
public bool Remove() => RemovalEngine.RemoveBlock(Id);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the rigid body of the chunk of blocks this one belongs to during simulation.
|
||||
/// Can be used to apply forces or move the block around while the simulation is running.
|
||||
/// </summary>
|
||||
/// <returns>The SimBody of the chunk or null if the block doesn't exist or not in simulation mode.</returns>
|
||||
public SimBody GetSimBody()
|
||||
{
|
||||
return BlockEngine.GetBlockInfo(this,
|
||||
(GridConnectionsEntityStruct st) => st.machineRigidBodyId != uint.MaxValue
|
||||
? new SimBody(st.machineRigidBodyId, st.clusterId)
|
||||
: null);
|
||||
}
|
||||
|
||||
private void OnPlacedInit(object sender, BlockPlacedRemovedEventArgs e)
|
||||
{ //Member method instead of lambda to avoid constantly creating delegates
|
||||
if (e.ID != Id) return;
|
||||
Placed -= OnPlacedInit; //And we can reference it
|
||||
InitData = default; //Remove initializer as it's no longer valid - if the block gets removed it shouldn't be used again
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{nameof(Id)}: {Id}, {nameof(Position)}: {Position}, {nameof(Type)}: {Type}, {nameof(Color)}: {Color}, {nameof(Exists)}: {Exists}";
|
||||
}
|
||||
|
||||
public bool Equals(Block other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
return Id.Equals(other.Id);
|
||||
}
|
||||
|
||||
public bool Equals(EGID other)
|
||||
{
|
||||
return Id.Equals(other);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
return Equals((Block) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Id.GetHashCode();
|
||||
}
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
GameEngineManager.AddGameEngine(PlacementEngine);
|
||||
GameEngineManager.AddGameEngine(MovementEngine);
|
||||
GameEngineManager.AddGameEngine(RotationEngine);
|
||||
GameEngineManager.AddGameEngine(RemovalEngine);
|
||||
GameEngineManager.AddGameEngine(BlockEngine);
|
||||
GameEngineManager.AddGameEngine(BlockEventsEngine);
|
||||
GameEngineManager.AddGameEngine(ScalingEngine);
|
||||
GameEngineManager.AddGameEngine(SignalEngine);
|
||||
Wire.signalEngine = SignalEngine; // requires same functionality, no need to duplicate the engine
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert the block to a specialised block class.
|
||||
/// </summary>
|
||||
/// <returns>The block.</returns>
|
||||
/// <typeparam name="T">The specialised block type.</typeparam>
|
||||
public T Specialise<T>() where T : Block
|
||||
{
|
||||
// What have I gotten myself into?
|
||||
// C# can't cast to a child of Block unless the object was originally that child type
|
||||
// And C# doesn't let me make implicit cast operators for child types
|
||||
// So thanks to Microsoft, we've got this horrible implementation using reflection
|
||||
|
||||
//Lets improve that using delegates
|
||||
var block = New<T>(Id.entityID, Id.groupID);
|
||||
if (this.InitData.Group != null)
|
||||
{
|
||||
block.InitData = this.InitData;
|
||||
Placed += block.OnPlacedInit; //Reset InitData of new object
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
public static EntitiesDB entitiesDB
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetEntitiesDB();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
using System;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace GamecraftModdingAPI.Blocks
|
||||
{
|
||||
public struct BlockColor
|
||||
{
|
||||
public BlockColors Color;
|
||||
public byte Darkness;
|
||||
|
||||
public byte Index => Color == BlockColors.Default
|
||||
? byte.MaxValue
|
||||
: (byte) (Darkness * 10 + Color);
|
||||
|
||||
public BlockColor(byte index)
|
||||
{
|
||||
if (index == byte.MaxValue)
|
||||
{
|
||||
Color = BlockColors.Default;
|
||||
Darkness = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (index > 99)
|
||||
throw new ArgumentOutOfRangeException(nameof(index), "Invalid color index. Must be 0-90 or 255.");
|
||||
Color = (BlockColors) (index % 10);
|
||||
Darkness = (byte) (index / 10);
|
||||
}
|
||||
}
|
||||
|
||||
public BlockColor(BlockColors color, byte darkness)
|
||||
{
|
||||
if (darkness > 9)
|
||||
throw new ArgumentOutOfRangeException(nameof(darkness), "Darkness must be 0-9 where 0 is default.");
|
||||
Color = color;
|
||||
Darkness = darkness;
|
||||
}
|
||||
|
||||
public float4 RGBA => Block.BlockEngine.ConvertBlockColor(Index);
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{nameof(Color)}: {Color}, {nameof(Darkness)}: {Darkness}";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Preset block colours
|
||||
/// </summary>
|
||||
public enum BlockColors
|
||||
{
|
||||
Default = byte.MaxValue,
|
||||
White = 0,
|
||||
Pink,
|
||||
Purple,
|
||||
Blue,
|
||||
Aqua,
|
||||
Green,
|
||||
Lime,
|
||||
Yellow,
|
||||
Orange,
|
||||
Red
|
||||
}
|
||||
}
|
|
@ -1,294 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using Gamecraft.ColourPalette;
|
||||
using Gamecraft.TimeRunning;
|
||||
using Gamecraft.Wires;
|
||||
using RobocraftX.Blocks;
|
||||
using RobocraftX.Common;
|
||||
using RobocraftX.Physics;
|
||||
using RobocraftX.Scene.Simulation;
|
||||
using Svelto.DataStructures;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.Hybrid;
|
||||
using Unity.Mathematics;
|
||||
|
||||
using GamecraftModdingAPI.Engines;
|
||||
|
||||
namespace GamecraftModdingAPI.Blocks
|
||||
{
|
||||
/// <summary>
|
||||
/// Engine for executing general block actions
|
||||
/// </summary>
|
||||
public partial class BlockEngine : IApiEngine
|
||||
{
|
||||
public string Name { get; } = "GamecraftModdingAPIBlockGameEngine";
|
||||
|
||||
public EntitiesDB entitiesDB { set; private get; }
|
||||
|
||||
public bool isRemovable => false;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public void Ready()
|
||||
{
|
||||
}
|
||||
|
||||
public Block[] GetConnectedBlocks(EGID blockID)
|
||||
{
|
||||
if (!BlockExists(blockID)) return new Block[0];
|
||||
Stack<EGID> cubeStack = new Stack<EGID>();
|
||||
FasterList<EGID> cubes = new FasterList<EGID>(10);
|
||||
var coll = entitiesDB.QueryEntities<GridConnectionsEntityStruct>();
|
||||
foreach (var (ecoll, _) in coll)
|
||||
{
|
||||
var ecollB = ecoll.ToBuffer();
|
||||
for(int i = 0; i < ecoll.count; i++)
|
||||
{
|
||||
ref var conn = ref ecollB.buffer[i];
|
||||
conn.isProcessed = false;
|
||||
}
|
||||
}
|
||||
|
||||
ConnectedCubesUtility.TreeTraversal.GetConnectedCubes(entitiesDB, blockID, cubeStack, cubes,
|
||||
(in GridConnectionsEntityStruct g) => { return false; });
|
||||
|
||||
var ret = new Block[cubes.count];
|
||||
for (int i = 0; i < cubes.count; i++)
|
||||
ret[i] = new Block(cubes[i]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public float4 ConvertBlockColor(byte index) => index == byte.MaxValue
|
||||
? new float4(-1f, -1f, -1f, -1f)
|
||||
: entitiesDB.QueryEntity<PaletteEntryEntityStruct>(index,
|
||||
CommonExclusiveGroups.COLOUR_PALETTE_GROUP).Colour;
|
||||
|
||||
public ref T GetBlockInfo<T>(EGID blockID) where T : unmanaged, IEntityComponent
|
||||
{
|
||||
if (entitiesDB.Exists<T>(blockID))
|
||||
return ref entitiesDB.QueryEntity<T>(blockID);
|
||||
T[] structHolder = new T[1]; //Create something that can be referenced
|
||||
return ref structHolder[0]; //Gets a default value automatically
|
||||
}
|
||||
|
||||
public ref T GetBlockInfoViewStruct<T>(EGID blockID) where T : struct, INeedEGID, IEntityViewComponent
|
||||
{
|
||||
if (entitiesDB.Exists<T>(blockID))
|
||||
{
|
||||
// TODO: optimize by using EntitiesDB internal calls instead of iterating over everything
|
||||
BT<MB<T>> entities = entitiesDB.QueryEntities<T>(blockID.groupID).ToBuffer();
|
||||
for (int i = 0; i < entities.count; i++)
|
||||
{
|
||||
if (entities.buffer[i].ID == blockID)
|
||||
{
|
||||
return ref entities.buffer[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
T[] structHolder = new T[1]; //Create something that can be referenced
|
||||
return ref structHolder[0]; //Gets a default value automatically
|
||||
}
|
||||
|
||||
public U GetBlockInfo<T, U>(Block block, Func<T, U> getter,
|
||||
U def = default) where T : unmanaged, IEntityComponent
|
||||
{
|
||||
if (entitiesDB.Exists<T>(block.Id))
|
||||
return getter(entitiesDB.QueryEntity<T>(block.Id));
|
||||
return GetBlockInitInfo(block, getter, def);
|
||||
}
|
||||
|
||||
public U GetBlockInfoViewStruct<T, U>(Block block, Func<T, U> getter,
|
||||
U def = default) where T : struct, IEntityViewComponent
|
||||
{
|
||||
if (entitiesDB.Exists<T>(block.Id))
|
||||
return getter(entitiesDB.QueryEntity<T>(block.Id));
|
||||
return GetBlockInitInfo(block, getter, def);
|
||||
}
|
||||
|
||||
private U GetBlockInitInfo<T, U>(Block block, Func<T, U> getter, U def) where T : struct, IEntityComponent
|
||||
{
|
||||
if (block.InitData.Group == null) return def;
|
||||
var initializer = new EntityComponentInitializer(block.Id, block.InitData.Group);
|
||||
if (initializer.Has<T>())
|
||||
return getter(initializer.Get<T>());
|
||||
return def;
|
||||
}
|
||||
|
||||
public delegate void Setter<T, U>(ref T component, U value) where T : struct, IEntityComponent;
|
||||
|
||||
public void SetBlockInfoViewStruct<T, U>(Block block, Setter<T, U> setter, U value) where T : struct, IEntityViewComponent
|
||||
{
|
||||
if (entitiesDB.Exists<T>(block.Id))
|
||||
setter(ref entitiesDB.QueryEntity<T>(block.Id), value);
|
||||
else
|
||||
SetBlockInitInfo(block, setter, value);
|
||||
}
|
||||
|
||||
public void SetBlockInfo<T, U>(Block block, Setter<T, U> setter, U value) where T : unmanaged, IEntityComponent
|
||||
{
|
||||
if (entitiesDB.Exists<T>(block.Id))
|
||||
setter(ref entitiesDB.QueryEntity<T>(block.Id), value);
|
||||
else
|
||||
SetBlockInitInfo(block, setter, value);
|
||||
}
|
||||
|
||||
private void SetBlockInitInfo<T, U>(Block block, Setter<T, U> setter, U value)
|
||||
where T : struct, IEntityComponent
|
||||
{
|
||||
if (block.InitData.Group != null)
|
||||
{
|
||||
var initializer = new EntityComponentInitializer(block.Id, block.InitData.Group);
|
||||
T component = initializer.Has<T>() ? initializer.Get<T>() : default;
|
||||
ref T structRef = ref component;
|
||||
setter(ref structRef, value);
|
||||
initializer.Init(structRef);
|
||||
}
|
||||
}
|
||||
|
||||
public bool BlockExists(EGID blockID)
|
||||
{
|
||||
return entitiesDB.Exists<DBEntityStruct>(blockID);
|
||||
}
|
||||
|
||||
public bool GetBlockInfoExists<T>(Block block) where T : struct, IEntityComponent
|
||||
{
|
||||
if (entitiesDB.Exists<T>(block.Id))
|
||||
return true;
|
||||
if (block.InitData.Group == null)
|
||||
return false;
|
||||
var init = new EntityComponentInitializer(block.Id, block.InitData.Group);
|
||||
return init.Has<T>();
|
||||
}
|
||||
|
||||
public SimBody[] GetSimBodiesFromID(byte id)
|
||||
{
|
||||
var ret = new FasterList<SimBody>(4);
|
||||
if (!entitiesDB.HasAny<ObjectIdEntityStruct>(CommonExclusiveGroups.OBJID_BLOCK_GROUP))
|
||||
return new SimBody[0];
|
||||
var oids = entitiesDB.QueryEntities<ObjectIdEntityStruct>(CommonExclusiveGroups.OBJID_BLOCK_GROUP).ToBuffer();
|
||||
var connections = entitiesDB.QueryMappedEntities<GridConnectionsEntityStruct>(CommonExclusiveGroups.OBJID_BLOCK_GROUP);
|
||||
for (int i = 0; i < oids.count; i++)
|
||||
{
|
||||
ref ObjectIdEntityStruct oid = ref oids.buffer[i];
|
||||
if (oid.objectId != id) continue;
|
||||
var rid = connections.Entity(oid.ID.entityID).machineRigidBodyId;
|
||||
foreach (var rb in ret)
|
||||
{
|
||||
if (rb.Id.entityID == rid)
|
||||
goto DUPLICATE; //Multiple Object Identifiers on one rigid body
|
||||
}
|
||||
ret.Add(new SimBody(rid));
|
||||
DUPLICATE: ;
|
||||
}
|
||||
return ret.ToArray();
|
||||
}
|
||||
|
||||
public ObjectIdentifier[] GetObjectIDsFromID(byte id, bool sim)
|
||||
{
|
||||
var ret = new FasterList<ObjectIdentifier>(4);
|
||||
if (!entitiesDB.HasAny<ObjectIdEntityStruct>(CommonExclusiveGroups.OBJID_BLOCK_GROUP))
|
||||
return new ObjectIdentifier[0];
|
||||
var oids = entitiesDB.QueryEntities<ObjectIdEntityStruct>(CommonExclusiveGroups.OBJID_BLOCK_GROUP).ToBuffer();
|
||||
for (int i = 0; i < oids.count; i++)
|
||||
{
|
||||
ref ObjectIdEntityStruct oid = ref oids.buffer[i];
|
||||
if (sim ? oid.simObjectId == id : oid.objectId == id)
|
||||
ret.Add(new ObjectIdentifier(oid.ID));
|
||||
}
|
||||
|
||||
return ret.ToArray();
|
||||
}
|
||||
|
||||
public SimBody[] GetConnectedSimBodies(uint id)
|
||||
{
|
||||
var joints = entitiesDB.QueryEntities<JointEntityStruct>(MachineSimulationGroups.JOINTS_GROUP).ToBuffer();
|
||||
var list = new FasterList<SimBody>(4);
|
||||
for (int i = 0; i < joints.count; i++)
|
||||
{
|
||||
ref var joint = ref joints.buffer[i];
|
||||
if (joint.jointState == JointState.Broken) continue;
|
||||
if (joint.connectedEntityA == id) list.Add(new SimBody(joint.connectedEntityB));
|
||||
else if (joint.connectedEntityB == id) list.Add(new SimBody(joint.connectedEntityA));
|
||||
}
|
||||
|
||||
return list.ToArray();
|
||||
}
|
||||
|
||||
public SimBody[] GetClusterBodies(uint cid)
|
||||
{
|
||||
var groups = entitiesDB.QueryEntities<GridConnectionsEntityStruct>();
|
||||
var bodies = new HashSet<uint>();
|
||||
foreach (var (coll, _) in groups)
|
||||
{
|
||||
var array = coll.ToBuffer().buffer;
|
||||
for (var index = 0; index < array.capacity; index++)
|
||||
{
|
||||
var conn = array[index];
|
||||
if (conn.clusterId == cid)
|
||||
bodies.Add(conn.machineRigidBodyId);
|
||||
}
|
||||
}
|
||||
|
||||
return bodies.Select(id => new SimBody(id, cid)).ToArray();
|
||||
}
|
||||
|
||||
public EGID? FindBlockEGID(uint id)
|
||||
{
|
||||
var groups = entitiesDB.FindGroups<DBEntityStruct>();
|
||||
foreach (ExclusiveGroupStruct group in groups)
|
||||
{
|
||||
if (entitiesDB.Exists<DBEntityStruct>(id, group))
|
||||
return new EGID(id, group);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Cluster GetCluster(uint sbid)
|
||||
{
|
||||
var groups = entitiesDB.QueryEntities<GridConnectionsEntityStruct>();
|
||||
foreach (var (coll, _) in groups)
|
||||
{
|
||||
var array = coll.ToBuffer().buffer;
|
||||
for (var index = 0; index < array.capacity; index++)
|
||||
{
|
||||
var conn = array[index];
|
||||
//Static blocks don't have a cluster ID but the cluster destruction manager should have one
|
||||
if (conn.machineRigidBodyId == sbid && conn.clusterId != uint.MaxValue)
|
||||
return new Cluster(conn.clusterId);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Block[] GetBodyBlocks(uint sbid)
|
||||
{
|
||||
var groups = entitiesDB.QueryEntities<GridConnectionsEntityStruct>();
|
||||
var set = new HashSet<Block>();
|
||||
foreach (var (coll, _) in groups)
|
||||
{
|
||||
var array = coll.ToBuffer().buffer;
|
||||
for (var index = 0; index < array.capacity; index++)
|
||||
{
|
||||
var conn = array[index];
|
||||
if (conn.machineRigidBodyId == sbid)
|
||||
set.Add(new Block(conn.ID));
|
||||
}
|
||||
}
|
||||
|
||||
return set.ToArray();
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
public EntitiesDB GetEntitiesDB()
|
||||
{
|
||||
return entitiesDB;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
using System;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
using Svelto.DataStructures;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.Internal;
|
||||
|
||||
namespace GamecraftModdingAPI.Blocks
|
||||
{
|
||||
public partial class BlockEngine
|
||||
{
|
||||
/// <summary>
|
||||
/// Holds information needed to construct a component initializer
|
||||
/// </summary>
|
||||
internal struct BlockInitData
|
||||
{
|
||||
public FasterDictionary<RefWrapperType, ITypeSafeDictionary> Group;
|
||||
}
|
||||
|
||||
internal delegate FasterDictionary<RefWrapperType, ITypeSafeDictionary> GetInitGroup(
|
||||
EntityComponentInitializer initializer);
|
||||
|
||||
/// <summary>
|
||||
/// Accesses the group field of the initializer
|
||||
/// </summary>
|
||||
internal GetInitGroup InitGroup = CreateAccessor<GetInitGroup>("_group");
|
||||
|
||||
//https://stackoverflow.com/questions/55878525/unit-testing-ref-structs-with-private-fields-via-reflection
|
||||
internal static TDelegate CreateAccessor<TDelegate>(string memberName) where TDelegate : Delegate
|
||||
{
|
||||
var invokeMethod = typeof(TDelegate).GetMethod("Invoke");
|
||||
if (invokeMethod == null)
|
||||
throw new InvalidOperationException($"{typeof(TDelegate)} signature could not be determined.");
|
||||
|
||||
var delegateParameters = invokeMethod.GetParameters();
|
||||
if (delegateParameters.Length != 1)
|
||||
throw new InvalidOperationException("Delegate must have a single parameter.");
|
||||
|
||||
var paramType = delegateParameters[0].ParameterType;
|
||||
|
||||
var objParam = Expression.Parameter(paramType, "obj");
|
||||
var memberExpr = Expression.PropertyOrField(objParam, memberName);
|
||||
Expression returnExpr = memberExpr;
|
||||
if (invokeMethod.ReturnType != memberExpr.Type)
|
||||
returnExpr = Expression.ConvertChecked(memberExpr, invokeMethod.ReturnType);
|
||||
|
||||
var lambda =
|
||||
Expression.Lambda<TDelegate>(returnExpr, $"Access{paramType.Name}_{memberName}", new[] {objParam});
|
||||
return lambda.Compile();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,356 +0,0 @@
|
|||
namespace GamecraftModdingAPI.Blocks
|
||||
{
|
||||
/// <summary>
|
||||
/// Possible block types
|
||||
/// </summary>
|
||||
public enum BlockIDs : ushort
|
||||
{
|
||||
/// <summary>
|
||||
/// Called "nothing" in Gamecraft. (DBID.NOTHING)
|
||||
/// </summary>
|
||||
Invalid = ushort.MaxValue,
|
||||
AluminiumCube = 0,
|
||||
AxleS,
|
||||
HingeS = 3,
|
||||
MotorS,
|
||||
HingeM,
|
||||
MotorM,
|
||||
TyreM,
|
||||
AxleM,
|
||||
IronCube,
|
||||
RubberCube,
|
||||
OiledCube,
|
||||
AluminiumConeSegment, //12
|
||||
AluminiumCorner,
|
||||
AluminiumRoundedCorner,
|
||||
AluminiumSlicedCube,
|
||||
AluminiumRoundedSlicedCube,
|
||||
AluminiumCylinder,
|
||||
AluminiumPyramidSegment,
|
||||
AluminiumSlope,
|
||||
AluminiumRoundedSlope,
|
||||
AluminiumSphere,
|
||||
RubberConeSegment, //22
|
||||
RubberCorner,
|
||||
RubberRoundedCorner,
|
||||
RubberSlicedCube,
|
||||
RubberRoundedSlicedCube,
|
||||
RubberCylinder,
|
||||
RubberPyramidSegment,
|
||||
RubberSlope,
|
||||
RubberRoundedSlope,
|
||||
RubberSphere,
|
||||
OiledConeSegment, //32
|
||||
OiledCorner,
|
||||
OiledRoundedCorner,
|
||||
OiledSlicedCube,
|
||||
OiledRoundedSlicedCube,
|
||||
OiledCylinder,
|
||||
OiledPyramidSegment,
|
||||
OiledSlope,
|
||||
OiledRoundedSlope,
|
||||
OiledSphere,
|
||||
IronConeSegment, //42
|
||||
IronCorner,
|
||||
IronRoundedCorner,
|
||||
IronSlicedCube,
|
||||
IronRoundedSlicedCube,
|
||||
IronCylinder,
|
||||
IronPyramidSegment,
|
||||
IronSlope,
|
||||
IronRoundedSlope,
|
||||
IronSphere,
|
||||
GlassCube, //52
|
||||
GlassSlicedCube,
|
||||
GlassSlope,
|
||||
GlassCorner,
|
||||
GlassPyramidSegment,
|
||||
GlassRoundedSlicedCube,
|
||||
GlassRoundedSlope,
|
||||
GlassRoundedCorner,
|
||||
GlassConeSegment,
|
||||
GlassCylinder,
|
||||
GlassSphere,
|
||||
Lever, //63 - two IDs skipped
|
||||
PlayerSpawn = 66, //Crashes without special handling
|
||||
SmallSpawn,
|
||||
MediumSpawn,
|
||||
LargeSpawn,
|
||||
BallJoint,
|
||||
UniversalJoint,
|
||||
ServoAxle,
|
||||
ServoHinge,
|
||||
StepperAxle,
|
||||
StepperHinge,
|
||||
TelescopicJoint,
|
||||
DampedSpring,
|
||||
ServoPiston,
|
||||
StepperPiston,
|
||||
PneumaticPiston,
|
||||
PneumaticHinge,
|
||||
PneumaticAxle, //82
|
||||
PilotSeat = 90, //Might crash
|
||||
PassengerSeat,
|
||||
PilotControls,
|
||||
GrassCube,
|
||||
DirtCube,
|
||||
GrassConeSegment,
|
||||
GrassCorner,
|
||||
GrassRoundedCorner,
|
||||
GrassSlicedCube,
|
||||
GrassRoundedSlicedCube,
|
||||
GrassPyramidSegment,
|
||||
GrassSlope,
|
||||
GrassRoundedSlope,
|
||||
DirtConeSegment,
|
||||
DirtCorner,
|
||||
DirtRoundedCorner,
|
||||
DirtSlicedCube,
|
||||
DirtRoundedSlicedCube,
|
||||
DirtPyramidSegment,
|
||||
DirtSlope,
|
||||
DirtRoundedSlope,
|
||||
RubberHemisphere,
|
||||
AluminiumHemisphere,
|
||||
GrassInnerCornerBulged,
|
||||
DirtInnerCornerBulged,
|
||||
IronHemisphere,
|
||||
OiledHemisphere,
|
||||
GlassHemisphere,
|
||||
TyreS,
|
||||
ThreeWaySwitch,
|
||||
Dial, //120
|
||||
CharacterOnEnterTrigger, //Probably crashes
|
||||
CharacterOnLeaveTrigger,
|
||||
CharacterOnStayTrigger,
|
||||
ObjectOnEnterTrigger,
|
||||
ObjectOnLeaveTrigger,
|
||||
ObjectOnStayTrigger,
|
||||
Button,
|
||||
Switch,
|
||||
TextBlock, //Brings up a screen
|
||||
ConsoleBlock, //Brings up a screen
|
||||
Door,
|
||||
GlassDoor,
|
||||
PoweredDoor,
|
||||
PoweredGlassDoor,
|
||||
AluminiumTubeCorner,
|
||||
IronTubeCorner,
|
||||
WoodCube,
|
||||
WoodSlicedCube,
|
||||
WoodSlope,
|
||||
WoodCorner,
|
||||
WoodPyramidSegment,
|
||||
WoodConeSegment,
|
||||
WoodRoundedSlicedCube,
|
||||
WoodRoundedSlope,
|
||||
WoodRoundedCorner,
|
||||
WoodCylinder,
|
||||
WoodHemisphere,
|
||||
WoodSphere,
|
||||
BrickCube, //149
|
||||
BrickSlicedCube = 151,
|
||||
BrickSlope,
|
||||
BrickCorner,
|
||||
ConcreteCube,
|
||||
ConcreteSlicedCube,
|
||||
ConcreteSlope,
|
||||
ConcreteCorner,
|
||||
RoadCarTyre,
|
||||
OffRoadCarTyre,
|
||||
RacingCarTyre,
|
||||
BicycleTyre,
|
||||
FrontBikeTyre,
|
||||
RearBikeTyre,
|
||||
ChopperBikeTyre,
|
||||
TractorTyre,
|
||||
MonsterTruckTyre,
|
||||
MotocrossBikeTyre,
|
||||
CartTyre, //168
|
||||
ObjectIdentifier,
|
||||
ANDLogicBlock,
|
||||
NANDLogicBlock,
|
||||
NORLogicBlock,
|
||||
NOTLogicBlock,
|
||||
ORLogicBlock,
|
||||
XNORLogicBlock,
|
||||
XORLogicBlock,
|
||||
AbsoluteMathsBlock,
|
||||
AdderMathsBlock,
|
||||
DividerMathsBlock,
|
||||
SignMathsBlock, //180
|
||||
MaxMathsBlock,
|
||||
MinMathsBlock,
|
||||
MultiplierMathsBlock,
|
||||
SubtractorMathsBlock,
|
||||
SimpleConnector,
|
||||
MeanMathsBlock,
|
||||
Bit,
|
||||
Counter,
|
||||
Timer,
|
||||
ObjectFilter,
|
||||
PlayerFilter,
|
||||
TeamFilter,
|
||||
Number2Text, //193
|
||||
DestructionManager = 260,
|
||||
ChunkHealthModifier,
|
||||
ClusterHealthModifier, //262
|
||||
BeachTree1 = 200,
|
||||
BeachTree2,
|
||||
BeachTree3,
|
||||
Rock1,
|
||||
Rock2,
|
||||
Rock3,
|
||||
Rock4,
|
||||
BirchTree1,
|
||||
BirchTree2,
|
||||
BirchTree3,
|
||||
PineTree1,
|
||||
PineTree2,
|
||||
PineTree3,
|
||||
Flower1,
|
||||
Flower2,
|
||||
Flower3,
|
||||
Shrub1,
|
||||
Shrub2,
|
||||
Shrub3,
|
||||
CliffCube,
|
||||
CliffSlicedCorner,
|
||||
CliffCornerA,
|
||||
CliffCornerB,
|
||||
CliffSlopeA,
|
||||
CliffSlopeB,
|
||||
GrassEdge,
|
||||
GrassEdgeInnerCorner,
|
||||
GrassEdgeCorner,
|
||||
GrassEdgeSlope,
|
||||
CentreHUD,
|
||||
ObjectiveHUD,
|
||||
GameStatsHUD, //231
|
||||
GameOverBlock,
|
||||
SFXBlockGameplay = 240,
|
||||
SFXBlock8Bit,
|
||||
SFXBlockInstrument,
|
||||
SFXBlockSciFi,
|
||||
SFXBlockLoops,
|
||||
SFXBlockVocal,
|
||||
MovementConstrainer, //246
|
||||
RotationConstrainer,
|
||||
AdvancedMovementDampener,
|
||||
AdvancedRotationDampener,
|
||||
Mover = 250,
|
||||
Rotator,
|
||||
MovementDampener,
|
||||
RotationDampener,
|
||||
AdvancedMover,
|
||||
AdvancedRotator,
|
||||
MusicBlock, //256
|
||||
PlasmaCannonBlock,
|
||||
QuantumRiflePickup = 300,
|
||||
QuantumRifleAmmoPickup,
|
||||
AluminiumSlicedFraction,
|
||||
AluminiumSlicedSlope,
|
||||
AluminiumHalfPyramidLeft = 305,
|
||||
AluminiumHalfPyramidRight,
|
||||
AluminiumPyramidSliced,
|
||||
AluminiumTubeCross,
|
||||
AluminiumTubeT,
|
||||
AluminiumPlateSquare,
|
||||
AluminiumPlateCircle,
|
||||
AluminiumPlateTriangle, //312
|
||||
OiledSlicedFraction = 314,
|
||||
OiledSlicedSlope,
|
||||
OiledHalfPyramidLeft,
|
||||
OiledHalfPyramidRight,
|
||||
OiledPyramidSliced,
|
||||
GlassSlicedFraction,
|
||||
GlassSlicedSlope,
|
||||
GlassHalfPyramidLeft,
|
||||
GlassHalfPyramidRight,
|
||||
GlassPyramidSliced,
|
||||
RubberSlicedFraction,
|
||||
RubberSlicedSlope,
|
||||
RubberHalfPyramidLeft,
|
||||
RubberHalfPyramidRight,
|
||||
RubberPyramidSliced,
|
||||
WoodSlicedFraction,
|
||||
WoodSlicedSlope, //330
|
||||
WoodHalfPyramidLeft,
|
||||
WoodHalfPyramidRight,
|
||||
WoodPyramidSliced,
|
||||
HexNetSlicedFraction,
|
||||
HexNetSlicedSlope,
|
||||
HexNetHalfPyramidLeft,
|
||||
HexNetHalfPyramidRight,
|
||||
HexNetPyramidSliced,
|
||||
OiledTubeCross,
|
||||
OiledTubeT, //340
|
||||
GlassTubeCross,
|
||||
GlassTubeT,
|
||||
RubberTubeCross,
|
||||
RubberTubeT,
|
||||
WoodTubeCross,
|
||||
WoodTubeT,
|
||||
HexNetTubeCross,
|
||||
HexNetTubeT,
|
||||
BouncyCube,
|
||||
BouncySlicedCube, //350
|
||||
BouncySlope,
|
||||
BouncyCorner,
|
||||
OiledTubeCorner,
|
||||
GlassTubeCorner,
|
||||
RubberTubeCorner,
|
||||
WoodTubeCorner,
|
||||
Basketball,
|
||||
BowlingBall,
|
||||
SoccerBall,
|
||||
GolfBall, //360
|
||||
HockeyPuck,
|
||||
PoolBall,
|
||||
BouncyBall,
|
||||
TennisBall,
|
||||
UnlitCube,
|
||||
IronSlicedFraction,
|
||||
IronSlicedSlope,
|
||||
IronHalfPyramidLeft,
|
||||
IronHalfPyramidRight,
|
||||
IronPyramidSliced, //370
|
||||
IronTubeCross,
|
||||
IronTubeT,
|
||||
SFXBlockMob = 374,
|
||||
PointLight,
|
||||
SpotLight,
|
||||
SunLight,
|
||||
AmbientLight,
|
||||
UnlitGlowCube = 381,
|
||||
PointLightInvisible,
|
||||
SpotLightInvisible,
|
||||
UnlitSlope,
|
||||
UnlitGlowSlope,
|
||||
Fog,
|
||||
Sky,
|
||||
MagmaRockCube = 777,
|
||||
MagmaRockCubeSliced,
|
||||
MagmaRockSlope,
|
||||
MagmaRockCorner,
|
||||
MagmaRockPyramidSegment,
|
||||
MagmaRockConeSegment,
|
||||
MagmaRockSlicedRounded,
|
||||
MagmaRockSlopeRounded,
|
||||
MagmaRockCornerRounded,
|
||||
HexNetCube,
|
||||
HexNetCubeSliced,
|
||||
HexNetSlope,
|
||||
HexNetCorner,
|
||||
HexNetPyramidSegment,
|
||||
HexNetConeSegment,
|
||||
HexNetSlicedRounded,
|
||||
HexNetSlopeRounded,
|
||||
HexNetCornerRounded, //794
|
||||
MagmaRockBulgedInner,
|
||||
HexNetCylinder = 797,
|
||||
HexNetHemisphere,
|
||||
HexNetSphere,
|
||||
HexNetTubeCorner //800
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
using Svelto.ECS;
|
||||
using RobocraftX.Common;
|
||||
|
||||
using HarmonyLib;
|
||||
|
||||
namespace GamecraftModdingAPI.Blocks
|
||||
{
|
||||
/// <summary>
|
||||
/// ExclusiveGroups and IDs used with blocks
|
||||
/// </summary>
|
||||
public static class BlockIdentifiers
|
||||
{
|
||||
/// <summary>
|
||||
/// Blocks placed by the player
|
||||
/// </summary> - TODO
|
||||
//public static ExclusiveGroup OWNED_BLOCKS { get { return CommonExclusiveGroups.REAL_BLOCKS_GROUPS_DON_T_USE_IN_NEW_CODE; } }
|
||||
|
||||
/// <summary>
|
||||
/// Extra parts used in functional blocks
|
||||
/// </summary>
|
||||
public static ExclusiveGroup FUNCTIONAL_BLOCK_PARTS { get { return CommonExclusiveGroups.FUNCTIONAL_BLOCK_PART_GROUP; } }
|
||||
|
||||
/// <summary>
|
||||
/// Blocks which are disabled in Simulation mode
|
||||
/// </summary>
|
||||
public static ExclusiveGroup SIM_BLOCKS_DISABLED { get { return CommonExclusiveGroups.DISABLED_JOINTS_IN_SIM_GROUP; } }
|
||||
|
||||
//public static ExclusiveGroup SPAWN_POINTS { get { return CommonExclusiveGroups.SPAWN_POINTS_GROUP; } }
|
||||
|
||||
//public static ExclusiveGroup SPAWN_POINTS_DISABLED { get { return CommonExclusiveGroups.SPAWN_POINTS_DISABLED_GROUP; } }
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the most recently placed block
|
||||
/// </summary>
|
||||
public static uint LatestBlockID {
|
||||
get
|
||||
{ //Need the private field as the property increments itself
|
||||
return ((uint) AccessTools.Field(typeof(CommonExclusiveGroups), "_nextBlockEntityID").GetValue(null)) - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,124 +0,0 @@
|
|||
using System;
|
||||
|
||||
using Gamecraft.Wires;
|
||||
|
||||
using GamecraftModdingAPI;
|
||||
using GamecraftModdingAPI.Tests;
|
||||
using GamecraftModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftModdingAPI.Blocks
|
||||
{
|
||||
#if TEST
|
||||
/// <summary>
|
||||
/// Block test cases. Not accessible in release versions.
|
||||
/// </summary>
|
||||
[APITestClass]
|
||||
public static class BlockTests
|
||||
{
|
||||
[APITestCase(TestType.EditMode)]
|
||||
public static void TestPlaceNew()
|
||||
{
|
||||
Block newBlock = Block.PlaceNew(BlockIDs.AluminiumCube, Unity.Mathematics.float3.zero);
|
||||
Assert.NotNull(newBlock.Id, "Newly placed block is missing Id. This should be populated when the block is placed.", "Newly placed block Id is not null, block successfully placed.");
|
||||
}
|
||||
|
||||
[APITestCase(TestType.EditMode)]
|
||||
public static void TestInitProperty()
|
||||
{
|
||||
Block newBlock = Block.PlaceNew(BlockIDs.AluminiumCube, Unity.Mathematics.float3.zero + 2);
|
||||
if (!Assert.CloseTo(newBlock.Position, (Unity.Mathematics.float3.zero + 2), $"Newly placed block at {newBlock.Position} is expected at {Unity.Mathematics.float3.zero + 2}.", "Newly placed block position matches.")) return;
|
||||
//Assert.Equal(newBlock.Exists, true, "Newly placed block does not exist, possibly because Sync() skipped/missed/failed.", "Newly placed block exists, Sync() successful.");
|
||||
}
|
||||
|
||||
[APITestCase(TestType.EditMode)]
|
||||
public static void TestTextBlock()
|
||||
{
|
||||
TextBlock textBlock = null; // Note: the assignment operation is a lambda, which slightly confuses the compiler
|
||||
Assert.Errorless(() => { textBlock = Block.PlaceNew<TextBlock>(BlockIDs.TextBlock, Unity.Mathematics.float3.zero + 1); }, "Block.PlaceNew<TextBlock>() raised an exception: ", "Block.PlaceNew<TextBlock>() completed without issue.");
|
||||
if (!Assert.NotNull(textBlock, "Block.PlaceNew<TextBlock>() returned null, possibly because it failed silently.", "Specialized TextBlock is not null.")) return;
|
||||
if (!Assert.NotNull(textBlock.Text, "TextBlock.Text is null, possibly because it failed silently.", "TextBlock.Text is not null.")) return;
|
||||
if (!Assert.NotNull(textBlock.TextBlockId, "TextBlock.TextBlockId is null, possibly because it failed silently.", "TextBlock.TextBlockId is not null.")) return;
|
||||
}
|
||||
|
||||
[APITestCase(TestType.EditMode)]
|
||||
public static void TestMotor()
|
||||
{
|
||||
Block newBlock = Block.PlaceNew(BlockIDs.MotorS, Unity.Mathematics.float3.zero + 1);
|
||||
Motor b = null; // Note: the assignment operation is a lambda, which slightly confuses the compiler
|
||||
Assert.Errorless(() => { b = newBlock.Specialise<Motor>(); }, "Block.Specialize<Motor>() raised an exception: ", "Block.Specialize<Motor>() completed without issue.");
|
||||
if (!Assert.NotNull(b, "Block.Specialize<Motor>() returned null, possibly because it failed silently.", "Specialized Motor is not null.")) return;
|
||||
if (!Assert.CloseTo(b.Torque, 75f, $"Motor.Torque {b.Torque} does not equal default value, possibly because it failed silently.", "Motor.Torque close enough to default.")) return;
|
||||
if (!Assert.CloseTo(b.TopSpeed, 30f, $"Motor.TopSpeed {b.TopSpeed} does not equal default value, possibly because it failed silently.", "Motor.Torque is close enough to default.")) return;
|
||||
if (!Assert.Equal(b.Reverse, false, $"Motor.Reverse {b.Reverse} does not equal default value, possibly because it failed silently.", "Motor.Reverse is default.")) return;
|
||||
}
|
||||
|
||||
[APITestCase(TestType.EditMode)]
|
||||
public static void TestPiston()
|
||||
{
|
||||
Block newBlock = Block.PlaceNew(BlockIDs.PneumaticPiston, Unity.Mathematics.float3.zero + 1);
|
||||
Piston b = null; // Note: the assignment operation is a lambda, which slightly confuses the compiler
|
||||
Assert.Errorless(() => { b = newBlock.Specialise<Piston>(); }, "Block.Specialize<Piston>() raised an exception: ", "Block.Specialize<Piston>() completed without issue.");
|
||||
if (!Assert.NotNull(b, "Block.Specialize<Piston>() returned null, possibly because it failed silently.", "Specialized Piston is not null.")) return;
|
||||
if (!Assert.CloseTo(b.MaximumExtension, 1.01f, $"Piston.MaximumExtension {b.MaximumExtension} does not equal default value, possibly because it failed silently.", "Piston.MaximumExtension is close enough to default.")) return;
|
||||
if (!Assert.CloseTo(b.MaximumForce, 750f, $"Piston.MaximumForce {b.MaximumForce} does not equal default value, possibly because it failed silently.", "Piston.MaximumForce is close enough to default.")) return;
|
||||
}
|
||||
|
||||
[APITestCase(TestType.EditMode)]
|
||||
public static void TestServo()
|
||||
{
|
||||
Block newBlock = Block.PlaceNew(BlockIDs.ServoAxle, Unity.Mathematics.float3.zero + 1);
|
||||
Servo b = null; // Note: the assignment operation is a lambda, which slightly confuses the compiler
|
||||
Assert.Errorless(() => { b = newBlock.Specialise<Servo>(); }, "Block.Specialize<Servo>() raised an exception: ", "Block.Specialize<Servo>() completed without issue.");
|
||||
if (!Assert.NotNull(b, "Block.Specialize<Servo>() returned null, possibly because it failed silently.", "Specialized Servo is not null.")) return;
|
||||
if (!Assert.CloseTo(b.MaximumAngle, 180f, $"Servo.MaximumAngle {b.MaximumAngle} does not equal default value, possibly because it failed silently.", "Servo.MaximumAngle is close enough to default.")) return;
|
||||
if (!Assert.CloseTo(b.MinimumAngle, -180f, $"Servo.MinimumAngle {b.MinimumAngle} does not equal default value, possibly because it failed silently.", "Servo.MinimumAngle is close enough to default.")) return;
|
||||
if (!Assert.CloseTo(b.MaximumForce, 750f, $"Servo.MaximumForce {b.MaximumForce} does not equal default value, possibly because it failed silently.", "Servo.MaximumForce is close enough to default.")) return;
|
||||
}
|
||||
|
||||
[APITestCase(TestType.Game)]
|
||||
public static void TestMusicBlock1()
|
||||
{
|
||||
Block newBlock = Block.PlaceNew(BlockIDs.MusicBlock, Unity.Mathematics.float3.zero + 2);
|
||||
MusicBlock b = null; // Note: the assignment operation is a lambda, which slightly confuses the compiler
|
||||
Assert.Errorless(() => { b = newBlock.Specialise<MusicBlock>(); }, "Block.Specialize<MusicBlock>() raised an exception: ", "Block.Specialize<MusicBlock>() completed without issue.");
|
||||
if (!Assert.NotNull(b, "Block.Specialize<MusicBlock>() returned null, possibly because it failed silently.", "Specialized MusicBlock is not null.")) return;
|
||||
if (!Assert.CloseTo(b.Volume, 100f, $"MusicBlock.Volume {b.Volume} does not equal default value, possibly because it failed silently.", "MusicBlock.Volume is close enough to default.")) return;
|
||||
if (!Assert.Equal(b.TrackIndex, 0, $"MusicBlock.TrackIndex {b.TrackIndex} does not equal default value, possibly because it failed silently.", "MusicBlock.TrackIndex is equal to default.")) return;
|
||||
_musicBlock = b;
|
||||
}
|
||||
|
||||
private static MusicBlock _musicBlock;
|
||||
|
||||
[APITestCase(TestType.EditMode)]
|
||||
public static void TestMusicBlock2()
|
||||
{
|
||||
//Block newBlock = Block.GetLastPlacedBlock();
|
||||
var b = _musicBlock;
|
||||
if (!Assert.NotNull(b, "Block.Specialize<MusicBlock>() returned null, possibly because it failed silently.", "Specialized MusicBlock is not null.")) return;
|
||||
b.IsPlaying = true; // play sfx
|
||||
if (!Assert.Equal(b.IsPlaying, true, $"MusicBlock.IsPlaying {b.IsPlaying} does not equal true, possibly because it failed silently.", "MusicBlock.IsPlaying is set properly.")) return;
|
||||
if (!Assert.Equal(b.ChannelType, ChannelType.None, $"MusicBlock.ChannelType {b.ChannelType} does not equal default value, possibly because it failed silently.", "MusicBlock.ChannelType is equal to default.")) return;
|
||||
//Assert.Log(b.Track.ToString());
|
||||
if (!Assert.Equal(b.Track.ToString(), new Guid("3237ff8f-f5f2-4f84-8144-496ca280f8c0").ToString(), $"MusicBlock.Track {b.Track} does not equal default value, possibly because it failed silently.", "MusicBlock.Track is equal to default.")) return;
|
||||
}
|
||||
|
||||
[APITestCase(TestType.EditMode)]
|
||||
public static void TestLogicGate()
|
||||
{
|
||||
Block newBlock = Block.PlaceNew(BlockIDs.NOTLogicBlock, Unity.Mathematics.float3.zero + 1);
|
||||
LogicGate b = null; // Note: the assignment operation is a lambda, which slightly confuses the compiler
|
||||
Assert.Errorless(() => { b = newBlock.Specialise<LogicGate>(); }, "Block.Specialize<LogicGate>() raised an exception: ", "Block.Specialize<LogicGate>() completed without issue.");
|
||||
if (!Assert.NotNull(b, "Block.Specialize<LogicGate>() returned null, possibly because it failed silently.", "Specialized LogicGate is not null.")) return;
|
||||
if (!Assert.Equal(b.InputCount, 1u, $"LogicGate.InputCount {b.InputCount} does not equal default value, possibly because it failed silently.", "LogicGate.InputCount is default.")) return;
|
||||
if (!Assert.Equal(b.OutputCount, 1u, $"LogicGate.OutputCount {b.OutputCount} does not equal default value, possibly because it failed silently.", "LogicGate.OutputCount is default.")) return;
|
||||
if (!Assert.NotNull(b, "Block.Specialize<LogicGate>() returned null, possibly because it failed silently.", "Specialized LogicGate is not null.")) return;
|
||||
//if (!Assert.Equal(b.PortName(0, true), "Input", $"LogicGate.PortName(0, input:true) {b.PortName(0, true)} does not equal default value, possibly because it failed silently.", "LogicGate.PortName(0, input:true) is close enough to default.")) return;
|
||||
LogicGate target = null;
|
||||
if (!Assert.Errorless(() => { target = Block.PlaceNew<LogicGate>(BlockIDs.ANDLogicBlock, Unity.Mathematics.float3.zero + 2); })) return;
|
||||
Wire newWire = null;
|
||||
if (!Assert.Errorless(() => { newWire = b.Connect(0, target, 0);})) return;
|
||||
if (!Assert.NotNull(newWire, "SignalingBlock.Connect(...) returned null, possible because it failed silently.", "SignalingBlock.Connect(...) returned a non-null value.")) return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
using System;
|
||||
|
||||
using RobocraftX.Blocks;
|
||||
using RobocraftX.Common;
|
||||
using Svelto.ECS;
|
||||
using Unity.Mathematics;
|
||||
|
||||
using GamecraftModdingAPI;
|
||||
using GamecraftModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftModdingAPI.Blocks
|
||||
{
|
||||
public class ConsoleBlock : SignalingBlock
|
||||
{
|
||||
public ConsoleBlock(EGID id): base(id)
|
||||
{
|
||||
}
|
||||
|
||||
public ConsoleBlock(uint id): base(new EGID(id, CommonExclusiveGroups.CONSOLE_BLOCK_GROUP))
|
||||
{
|
||||
}
|
||||
|
||||
// custom console block properties
|
||||
|
||||
/// <summary>
|
||||
/// Setting a nonexistent command will crash the game when switching to simulation
|
||||
/// </summary>
|
||||
public string Command
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo(this, (ConsoleBlockEntityStruct st) => st.commandName);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
BlockEngine.SetBlockInfo(this, (ref ConsoleBlockEntityStruct st, string val) => st.commandName.Set(val),
|
||||
value);
|
||||
}
|
||||
}
|
||||
|
||||
public string Arg1
|
||||
{
|
||||
get => BlockEngine.GetBlockInfo(this, (ConsoleBlockEntityStruct st) => st.arg1);
|
||||
|
||||
set
|
||||
{
|
||||
BlockEngine.SetBlockInfo(this, (ref ConsoleBlockEntityStruct st, string val) => st.arg1.Set(val),
|
||||
value);
|
||||
}
|
||||
}
|
||||
|
||||
public string Arg2
|
||||
{
|
||||
get => BlockEngine.GetBlockInfo(this, (ConsoleBlockEntityStruct st) => st.arg2);
|
||||
|
||||
set
|
||||
{
|
||||
BlockEngine.SetBlockInfo(this, (ref ConsoleBlockEntityStruct st, string val) => st.arg2.Set(val),
|
||||
value);
|
||||
}
|
||||
}
|
||||
|
||||
public string Arg3
|
||||
{
|
||||
get => BlockEngine.GetBlockInfo(this, (ConsoleBlockEntityStruct st) => st.arg3);
|
||||
|
||||
set
|
||||
{
|
||||
BlockEngine.SetBlockInfo(this, (ref ConsoleBlockEntityStruct st, string val) => st.arg3.Set(val),
|
||||
value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
using RobocraftX.Blocks;
|
||||
using RobocraftX.Common;
|
||||
using Svelto.ECS;
|
||||
|
||||
namespace GamecraftModdingAPI.Blocks
|
||||
{
|
||||
public class DampedSpring : Block
|
||||
{
|
||||
public DampedSpring(EGID id) : base(id)
|
||||
{
|
||||
}
|
||||
|
||||
public DampedSpring(uint id) : base(new EGID(id, CommonExclusiveGroups.DAMPEDSPRING_BLOCK_GROUP))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The spring's maximum force. This is known as Stiffness in-game
|
||||
/// </summary>
|
||||
public float MaxForce
|
||||
{
|
||||
get => BlockEngine.GetBlockInfo(this, (DampedSpringReadOnlyStruct dsrs) => dsrs.maxForce);
|
||||
|
||||
set => BlockEngine.SetBlockInfo(this,
|
||||
(ref DampedSpringReadOnlyStruct dsrs, float val) => dsrs.maxForce = val, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Alias of MaxForce.
|
||||
/// </summary>
|
||||
public float Stiffness
|
||||
{
|
||||
get => MaxForce;
|
||||
set => MaxForce = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The spring's maximum damping force.
|
||||
/// </summary>
|
||||
public float Damping
|
||||
{
|
||||
get => BlockEngine.GetBlockInfo(this, (LinearJointForcesReadOnlyStruct ljf) => ljf.dampingForceMagnitude);
|
||||
|
||||
set => BlockEngine.SetBlockInfo(this,
|
||||
(ref LinearJointForcesReadOnlyStruct ljf, float val) => ljf.dampingForceMagnitude = val, value);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
using RobocraftX.Common;
|
||||
using Svelto.ECS;
|
||||
|
||||
namespace GamecraftModdingAPI.Blocks
|
||||
{
|
||||
public class LogicGate : SignalingBlock
|
||||
{
|
||||
public LogicGate(EGID id) : base(id)
|
||||
{
|
||||
}
|
||||
|
||||
public LogicGate(uint id) : base(new EGID(id, CommonExclusiveGroups.LOGIC_BLOCK_GROUP))
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
using System;
|
||||
|
||||
using RobocraftX.Blocks;
|
||||
using RobocraftX.Common;
|
||||
using Svelto.ECS;
|
||||
using Unity.Mathematics;
|
||||
|
||||
using GamecraftModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftModdingAPI.Blocks
|
||||
{
|
||||
public class Motor : SignalingBlock
|
||||
{
|
||||
public Motor(EGID id) : base(id)
|
||||
{
|
||||
}
|
||||
|
||||
public Motor(uint id): base(new EGID(id, CommonExclusiveGroups.MOTOR_BLOCK_GROUP))
|
||||
{
|
||||
}
|
||||
|
||||
// custom motor properties
|
||||
|
||||
/// <summary>
|
||||
/// The motor's maximum rotational velocity.
|
||||
/// </summary>
|
||||
public float TopSpeed
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo(this, (MotorReadOnlyStruct st) => st.maxVelocity);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
BlockEngine.SetBlockInfo(this, (ref MotorReadOnlyStruct st, float val) => st.maxVelocity = val, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The motor's maximum rotational force.
|
||||
/// </summary>
|
||||
public float Torque
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo(this, (MotorReadOnlyStruct st) => st.maxForce);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
BlockEngine.SetBlockInfo(this, (ref MotorReadOnlyStruct st, float val) => st.maxForce = val, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The motor's direction.
|
||||
/// </summary>
|
||||
public bool Reverse
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo(this, (MotorReadOnlyStruct st) => st.reverse);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
BlockEngine.SetBlockInfo(this, (ref MotorReadOnlyStruct st, bool val) => st.reverse = val, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
using RobocraftX.Common;
|
||||
using RobocraftX.UECS;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.EntityStructs;
|
||||
using Unity.Transforms;
|
||||
using Unity.Mathematics;
|
||||
|
||||
using GamecraftModdingAPI.Utility;
|
||||
using GamecraftModdingAPI.Engines;
|
||||
|
||||
namespace GamecraftModdingAPI.Blocks
|
||||
{
|
||||
/// <summary>
|
||||
/// Engine which executes block movement actions
|
||||
/// </summary>
|
||||
public class MovementEngine : IApiEngine
|
||||
{
|
||||
public string Name { get; } = "GamecraftModdingAPIMovementGameEngine";
|
||||
|
||||
public EntitiesDB entitiesDB { set; private get; }
|
||||
|
||||
public bool isRemovable => false;
|
||||
|
||||
public bool IsInGame = false;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
IsInGame = false;
|
||||
}
|
||||
|
||||
public void Ready()
|
||||
{
|
||||
IsInGame = true;
|
||||
}
|
||||
|
||||
// implementations for Movement static class
|
||||
|
||||
internal float3 MoveBlock(EGID blockID, BlockEngine.BlockInitData data, float3 vector)
|
||||
{
|
||||
if (!entitiesDB.Exists<PositionEntityStruct>(blockID))
|
||||
{
|
||||
if (data.Group == null) return float3.zero;
|
||||
var init = new EntityComponentInitializer(blockID, data.Group);
|
||||
init.Init(new PositionEntityStruct {position = vector});
|
||||
init.Init(new GridRotationStruct {position = vector});
|
||||
init.Init(new LocalTransformEntityStruct {position = vector});
|
||||
return vector;
|
||||
}
|
||||
ref PositionEntityStruct posStruct = ref this.entitiesDB.QueryEntity<PositionEntityStruct>(blockID);
|
||||
ref GridRotationStruct gridStruct = ref this.entitiesDB.QueryEntity<GridRotationStruct>(blockID);
|
||||
ref LocalTransformEntityStruct transStruct = ref this.entitiesDB.QueryEntity<LocalTransformEntityStruct>(blockID);
|
||||
ref UECSPhysicsEntityStruct phyStruct = ref this.entitiesDB.QueryEntity<UECSPhysicsEntityStruct>(blockID);
|
||||
// main (persistent) position
|
||||
posStruct.position = vector;
|
||||
// placement grid position
|
||||
gridStruct.position = vector;
|
||||
// rendered position
|
||||
transStruct.position = vector;
|
||||
// collision position
|
||||
FullGameFields._physicsWorld.EntityManager.SetComponentData(phyStruct.uecsEntity, new Translation
|
||||
{
|
||||
Value = posStruct.position
|
||||
});
|
||||
entitiesDB.QueryEntity<GridConnectionsEntityStruct>(blockID).isProcessed = false;
|
||||
return posStruct.position;
|
||||
}
|
||||
|
||||
internal float3 GetPosition(EGID blockID, BlockEngine.BlockInitData data)
|
||||
{
|
||||
if (!entitiesDB.Exists<PositionEntityStruct>(blockID))
|
||||
{
|
||||
if (data.Group == null) return float3.zero;
|
||||
var init = new EntityComponentInitializer(blockID, data.Group);
|
||||
return init.Has<PositionEntityStruct>() ? init.Get<PositionEntityStruct>().position : float3.zero;
|
||||
}
|
||||
ref PositionEntityStruct posStruct = ref this.entitiesDB.QueryEntity<PositionEntityStruct>(blockID);
|
||||
return posStruct.position;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,146 +0,0 @@
|
|||
using System;
|
||||
|
||||
using FMOD.Studio;
|
||||
using FMODUnity;
|
||||
using Gamecraft.Wires;
|
||||
using RobocraftX.Common;
|
||||
using RobocraftX.Blocks;
|
||||
using Svelto.ECS;
|
||||
using Unity.Mathematics;
|
||||
|
||||
using GamecraftModdingAPI;
|
||||
using GamecraftModdingAPI.Tests;
|
||||
using GamecraftModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftModdingAPI.Blocks
|
||||
{
|
||||
public class MusicBlock : SignalingBlock
|
||||
{
|
||||
public MusicBlock(EGID id) : base(id)
|
||||
{
|
||||
}
|
||||
|
||||
public MusicBlock(uint id) : base(new EGID(id, CommonExclusiveGroups.MUSIC_BLOCK_GROUP))
|
||||
{
|
||||
}
|
||||
|
||||
public byte TrackIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo(this, (MusicBlockDataEntityStruct st) => st.trackIndx);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
BlockEngine.SetBlockInfo(this,
|
||||
(ref MusicBlockDataEntityStruct msdes, byte val) => msdes.trackIndx = val, value);
|
||||
}
|
||||
}
|
||||
|
||||
public Guid Track
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo(this,
|
||||
(MusicBlockDataEntityStruct msdes) => msdes.fmod2DEventPaths.Get<Guid>(msdes.trackIndx));
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
BlockEngine.SetBlockInfo(this, (ref MusicBlockDataEntityStruct msdes, Guid val) =>
|
||||
{
|
||||
for (byte i = 0; i < msdes.fmod2DEventPaths.Count<Guid>(); i++)
|
||||
{
|
||||
Guid track = msdes.fmod2DEventPaths.Get<Guid>(i);
|
||||
if (track == val)
|
||||
{
|
||||
msdes.trackIndx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}, value);
|
||||
}
|
||||
}
|
||||
|
||||
public Guid[] Tracks
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo(this, (MusicBlockDataEntityStruct msdes) =>
|
||||
{
|
||||
Guid[] tracks = new Guid[msdes.fmod2DEventPaths.Count<Guid>()];
|
||||
for (byte i = 0; i < tracks.Length; i++)
|
||||
{
|
||||
tracks[i] = msdes.fmod2DEventPaths.Get<Guid>(i);
|
||||
}
|
||||
return tracks;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public float Volume
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo(this, (MusicBlockDataEntityStruct msdes) => msdes.tweakableVolume);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
BlockEngine.SetBlockInfo(this,
|
||||
(ref MusicBlockDataEntityStruct msdes, float val) => msdes.tweakableVolume = val, value);
|
||||
}
|
||||
}
|
||||
|
||||
public ChannelType ChannelType
|
||||
{
|
||||
get
|
||||
{
|
||||
//Assert.Log("Block exists: " + Exists);
|
||||
return BlockEngine.GetBlockInfo(this,
|
||||
(MusicBlockDataEntityStruct msdes) => (ChannelType) msdes.channelType);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
BlockEngine.SetBlockInfo(this,
|
||||
(ref MusicBlockDataEntityStruct msdes, ChannelType val) => msdes.channelType = (byte) val, value);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsPlaying
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo(this,
|
||||
(MusicBlockDataEntityStruct msdes) => msdes.isPlaying);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
BlockEngine.SetBlockInfo(this, (ref MusicBlockDataEntityStruct msdes, bool val) =>
|
||||
{
|
||||
if (msdes.isPlaying == val) return;
|
||||
if (val)
|
||||
{
|
||||
// start playing
|
||||
EventInstance inst = RuntimeManager.CreateInstance(msdes.fmod2DEventPaths.Get<Guid>(msdes.trackIndx));
|
||||
inst.setVolume(msdes.tweakableVolume / 100f);
|
||||
inst.start();
|
||||
msdes.eventHandle = inst.handle;
|
||||
}
|
||||
else
|
||||
{
|
||||
// stop playing
|
||||
EventInstance inst = default(EventInstance);
|
||||
inst.handle = msdes.eventHandle;
|
||||
inst.stop(FMOD.Studio.STOP_MODE.ALLOWFADEOUT);
|
||||
inst.release();
|
||||
}
|
||||
msdes.isPlaying = val;
|
||||
}, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
using Gamecraft.Wires;
|
||||
using RobocraftX.Common;
|
||||
using Svelto.ECS;
|
||||
|
||||
namespace GamecraftModdingAPI.Blocks
|
||||
{
|
||||
public class ObjectIdentifier : Block
|
||||
{
|
||||
public ObjectIdentifier(EGID id) : base(id)
|
||||
{
|
||||
}
|
||||
|
||||
public ObjectIdentifier(uint id) : base(new EGID(id, CommonExclusiveGroups.OBJID_BLOCK_GROUP))
|
||||
{
|
||||
}
|
||||
|
||||
public char Identifier
|
||||
{
|
||||
get => (char) BlockEngine.GetBlockInfo(this, (ObjectIdEntityStruct st) => st.objectId + 'A');
|
||||
set
|
||||
{
|
||||
BlockEngine.SetBlockInfo(this, (ref ObjectIdEntityStruct st, char val) =>
|
||||
{
|
||||
st.objectId = (byte) (val - 'A');
|
||||
Label = val + ""; //The label isn't updated automatically
|
||||
}, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simulation-time ID. Assigned by the game starting from 0.
|
||||
/// </summary>
|
||||
public byte SimID
|
||||
{
|
||||
get => BlockEngine.GetBlockInfo(this, (ObjectIdEntityStruct st) => st.simObjectId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the identifier blocks with the given ID.
|
||||
/// </summary>
|
||||
/// <param name="id">The ID to look for</param>
|
||||
/// <returns>An array that may be empty</returns>
|
||||
public static ObjectIdentifier[] GetByID(char id) => BlockEngine.GetObjectIDsFromID((byte) (id - 'A'), false);
|
||||
|
||||
/// <summary>
|
||||
/// Finds the identifier blocks with the given simulation-time ID. This ID is assigned by the game starting from 0.
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
public static ObjectIdentifier[] GetBySimID(byte id) => BlockEngine.GetObjectIDsFromID(id, true);
|
||||
}
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
using System;
|
||||
|
||||
using RobocraftX.Blocks;
|
||||
using Svelto.ECS;
|
||||
using Unity.Mathematics;
|
||||
|
||||
using GamecraftModdingAPI.Utility;
|
||||
using RobocraftX.Common;
|
||||
|
||||
namespace GamecraftModdingAPI.Blocks
|
||||
{
|
||||
public class Piston : SignalingBlock
|
||||
{
|
||||
public Piston(EGID id) : base(id)
|
||||
{
|
||||
}
|
||||
|
||||
public Piston(uint id) : base(new EGID(id, CommonExclusiveGroups.PISTON_BLOCK_GROUP))
|
||||
{
|
||||
}
|
||||
|
||||
// custom piston properties
|
||||
|
||||
/// <summary>
|
||||
/// The piston's max extension distance.
|
||||
/// </summary>
|
||||
public float MaximumExtension
|
||||
{
|
||||
get => BlockEngine.GetBlockInfo(this, (PistonReadOnlyStruct st) => st.maxDeviation);
|
||||
|
||||
set
|
||||
{
|
||||
BlockEngine.SetBlockInfo(this, (ref PistonReadOnlyStruct st, float val) => st.maxDeviation = val,
|
||||
value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The piston's max extension force.
|
||||
/// </summary>
|
||||
public float MaximumForce
|
||||
{
|
||||
get => BlockEngine.GetBlockInfo(this, (PistonReadOnlyStruct st) => st.maxForce);
|
||||
|
||||
set
|
||||
{
|
||||
BlockEngine.SetBlockInfo(this, (ref PistonReadOnlyStruct st, float val) => st.maxForce = val, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,131 +0,0 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
using DataLoader;
|
||||
using HarmonyLib;
|
||||
using RobocraftX.Blocks;
|
||||
using RobocraftX.Blocks.Scaling;
|
||||
using RobocraftX.Character;
|
||||
using RobocraftX.Common;
|
||||
using RobocraftX.CR.MachineEditing;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.EntityStructs;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
|
||||
using GamecraftModdingAPI.Utility;
|
||||
using GamecraftModdingAPI.Engines;
|
||||
using GamecraftModdingAPI.Players;
|
||||
using RobocraftX.Rendering.GPUI;
|
||||
|
||||
namespace GamecraftModdingAPI.Blocks
|
||||
{
|
||||
/// <summary>
|
||||
/// Engine which executes block placement actions
|
||||
/// </summary>
|
||||
public class PlacementEngine : IApiEngine
|
||||
{
|
||||
public bool IsInGame;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
IsInGame = false;
|
||||
}
|
||||
|
||||
public void Ready()
|
||||
{
|
||||
IsInGame = true;
|
||||
}
|
||||
|
||||
public EntitiesDB entitiesDB { get; set; }
|
||||
private static BlockEntityFactory _blockEntityFactory; //Injected from PlaceBlockEngine
|
||||
|
||||
public EGID PlaceBlock(BlockIDs block, BlockColors color, byte darkness, float3 position, int uscale,
|
||||
float3 scale, Player player, float3 rotation, out EntityComponentInitializer initializer)
|
||||
{ //It appears that only the non-uniform scale has any visible effect, but if that's not given here it will be set to the uniform one
|
||||
if (darkness > 9)
|
||||
throw new Exception("That is too dark. Make sure to use 0-9 as darkness. (0 is default.)");
|
||||
initializer = BuildBlock((ushort) block, (byte) (color + darkness * 10), position, uscale, scale, rotation,
|
||||
(player ?? new Player(PlayerType.Local)).Id);
|
||||
return initializer.EGID;
|
||||
}
|
||||
|
||||
private EntityComponentInitializer BuildBlock(ushort block, byte color, float3 position, int uscale, float3 scale, float3 rot, uint playerId)
|
||||
{
|
||||
if (_blockEntityFactory == null)
|
||||
throw new BlockException("The factory is null.");
|
||||
if (uscale < 1)
|
||||
throw new BlockException("Scale needs to be at least 1");
|
||||
if (scale.x < 4e-5) scale.x = uscale;
|
||||
if (scale.y < 4e-5) scale.y = uscale;
|
||||
if (scale.z < 4e-5) scale.z = uscale;
|
||||
uint dbid = block;
|
||||
if (!PrefabsID.HasPrefabRegistered(dbid, 0))
|
||||
throw new BlockException("Block with ID " + dbid + " not found!");
|
||||
//RobocraftX.CR.MachineEditing.PlaceBlockEngine
|
||||
ScalingEntityStruct scaling = new ScalingEntityStruct {scale = scale};
|
||||
Quaternion rotQ = Quaternion.Euler(rot);
|
||||
RotationEntityStruct rotation = new RotationEntityStruct {rotation = rotQ};
|
||||
GridRotationStruct gridRotation = new GridRotationStruct
|
||||
{position = position, rotation = rotQ};
|
||||
DBEntityStruct dbEntity = new DBEntityStruct {DBID = dbid};
|
||||
BlockPlacementScaleEntityStruct placementScale = new BlockPlacementScaleEntityStruct
|
||||
{
|
||||
blockPlacementHeight = uscale, blockPlacementWidth = uscale, desiredScaleFactor = uscale
|
||||
};
|
||||
EquippedColourStruct colour = new EquippedColourStruct {indexInPalette = color};
|
||||
|
||||
EntityComponentInitializer
|
||||
structInitializer =
|
||||
_blockEntityFactory.Build(CommonExclusiveGroups.nextBlockEntityID, dbid); //The ghost block index is only used for triggers
|
||||
if (colour.indexInPalette != byte.MaxValue)
|
||||
structInitializer.Init(new ColourParameterEntityStruct
|
||||
{
|
||||
indexInPalette = colour.indexInPalette,
|
||||
hasNetworkChange = true
|
||||
});
|
||||
uint prefabId = PrefabsID.GetPrefabId(dbid, 0);
|
||||
structInitializer.Init(new GFXPrefabEntityStructGPUI(prefabId));
|
||||
structInitializer.Init(new PhysicsPrefabEntityStruct(prefabId));
|
||||
structInitializer.Init(dbEntity);
|
||||
structInitializer.Init(new PositionEntityStruct {position = position});
|
||||
structInitializer.Init(rotation);
|
||||
structInitializer.Init(scaling);
|
||||
structInitializer.Init(gridRotation);
|
||||
structInitializer.Init(new UniformBlockScaleEntityStruct
|
||||
{
|
||||
scaleFactor = placementScale.desiredScaleFactor
|
||||
});
|
||||
structInitializer.Init(new BlockPlacementInfoStruct()
|
||||
{
|
||||
loadedFromDisk = false,
|
||||
placedBy = playerId
|
||||
});
|
||||
PrimaryRotationUtility.InitialisePrimaryDirection(rotation.rotation, ref structInitializer);
|
||||
EGID playerEGID = new EGID(playerId, CharacterExclusiveGroups.OnFootGroup);
|
||||
ref PickedBlockExtraDataStruct pickedBlock = ref entitiesDB.QueryEntity<PickedBlockExtraDataStruct>(playerEGID);
|
||||
pickedBlock.placedBlockEntityID = structInitializer.EGID;
|
||||
pickedBlock.placedBlockWasAPickedBlock = false;
|
||||
return structInitializer;
|
||||
}
|
||||
|
||||
public string Name { get; } = "GamecraftModdingAPIPlacementGameEngine";
|
||||
|
||||
public bool isRemovable => false;
|
||||
|
||||
[HarmonyPatch]
|
||||
public class FactoryObtainerPatch
|
||||
{
|
||||
static void Postfix(BlockEntityFactory blockEntityFactory)
|
||||
{
|
||||
_blockEntityFactory = blockEntityFactory;
|
||||
Logging.MetaDebugLog("Block entity factory injected.");
|
||||
}
|
||||
|
||||
static MethodBase TargetMethod(Harmony instance)
|
||||
{
|
||||
return AccessTools.TypeByName("RobocraftX.CR.MachineEditing.PlaceBlockEngine").GetConstructors()[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,90 +0,0 @@
|
|||
using RobocraftX.Common;
|
||||
using RobocraftX.UECS;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.EntityStructs;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
|
||||
using GamecraftModdingAPI.Utility;
|
||||
using GamecraftModdingAPI.Engines;
|
||||
|
||||
namespace GamecraftModdingAPI.Blocks
|
||||
{
|
||||
/// <summary>
|
||||
/// Engine which executes block movement actions
|
||||
/// </summary>
|
||||
public class RotationEngine : IApiEngine
|
||||
{
|
||||
public string Name { get; } = "GamecraftModdingAPIRotationGameEngine";
|
||||
|
||||
public EntitiesDB entitiesDB { set; private get; }
|
||||
|
||||
public bool isRemovable => false;
|
||||
|
||||
public bool IsInGame = false;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
IsInGame = false;
|
||||
}
|
||||
|
||||
public void Ready()
|
||||
{
|
||||
IsInGame = true;
|
||||
}
|
||||
|
||||
// implementations for Rotation static class
|
||||
|
||||
internal float3 RotateBlock(EGID blockID, BlockEngine.BlockInitData data, Vector3 vector)
|
||||
{
|
||||
if (!entitiesDB.Exists<RotationEntityStruct>(blockID))
|
||||
{
|
||||
if (data.Group == null) return float3.zero;
|
||||
var init = new EntityComponentInitializer(blockID, data.Group);
|
||||
init.Init(new RotationEntityStruct {rotation = new Quaternion {eulerAngles = vector}});
|
||||
init.Init(new GridRotationStruct {rotation = new Quaternion {eulerAngles = vector}});
|
||||
init.Init(new LocalTransformEntityStruct {rotation = new Quaternion {eulerAngles = vector}});
|
||||
return vector;
|
||||
}
|
||||
ref RotationEntityStruct rotStruct = ref this.entitiesDB.QueryEntity<RotationEntityStruct>(blockID);
|
||||
ref GridRotationStruct gridStruct = ref this.entitiesDB.QueryEntity<GridRotationStruct>(blockID);
|
||||
ref LocalTransformEntityStruct transStruct = ref this.entitiesDB.QueryEntity<LocalTransformEntityStruct>(blockID);
|
||||
ref UECSPhysicsEntityStruct phyStruct = ref this.entitiesDB.QueryEntity<UECSPhysicsEntityStruct>(blockID);
|
||||
// main (persistent) position
|
||||
Quaternion newRotation = rotStruct.rotation;
|
||||
newRotation.eulerAngles = vector;
|
||||
rotStruct.rotation = newRotation;
|
||||
// placement grid rotation
|
||||
Quaternion newGridRotation = gridStruct.rotation;
|
||||
newGridRotation.eulerAngles = vector;
|
||||
gridStruct.rotation = newGridRotation;
|
||||
// rendered position
|
||||
Quaternion newTransRotation = rotStruct.rotation;
|
||||
newTransRotation.eulerAngles = vector;
|
||||
transStruct.rotation = newTransRotation;
|
||||
// collision position
|
||||
FullGameFields._physicsWorld.EntityManager.SetComponentData(phyStruct.uecsEntity, new Unity.Transforms.Rotation
|
||||
{
|
||||
Value = rotStruct.rotation
|
||||
});
|
||||
entitiesDB.QueryEntity<GridConnectionsEntityStruct>(blockID).isProcessed = false;
|
||||
return ((Quaternion)rotStruct.rotation).eulerAngles;
|
||||
|
||||
}
|
||||
|
||||
internal float3 GetRotation(EGID blockID, BlockEngine.BlockInitData data)
|
||||
{
|
||||
if (!entitiesDB.Exists<RotationEntityStruct>(blockID))
|
||||
{
|
||||
if (data.Group == null) return float3.zero;
|
||||
var init = new EntityComponentInitializer(blockID, data.Group);
|
||||
return init.Has<RotationEntityStruct>()
|
||||
? (float3) ((Quaternion) init.Get<RotationEntityStruct>().rotation).eulerAngles
|
||||
: float3.zero;
|
||||
}
|
||||
|
||||
ref RotationEntityStruct rotStruct = ref entitiesDB.QueryEntity<RotationEntityStruct>(blockID);
|
||||
return ((Quaternion) rotStruct.rotation).eulerAngles;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
using System;
|
||||
|
||||
using RobocraftX.Blocks;
|
||||
using RobocraftX.Common;
|
||||
using Svelto.ECS;
|
||||
using Unity.Mathematics;
|
||||
|
||||
using GamecraftModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftModdingAPI.Blocks
|
||||
{
|
||||
public class Servo : SignalingBlock
|
||||
{
|
||||
public Servo(EGID id) : base(id)
|
||||
{
|
||||
}
|
||||
|
||||
public Servo(uint id) : base(new EGID(id, CommonExclusiveGroups.SERVO_BLOCK_GROUP))
|
||||
{
|
||||
}
|
||||
|
||||
// custom servo properties
|
||||
|
||||
/// <summary>
|
||||
/// The servo's minimum angle.
|
||||
/// </summary>
|
||||
public float MinimumAngle
|
||||
{
|
||||
get => BlockEngine.GetBlockInfo(this, (ServoReadOnlyStruct st) => st.minDeviation);
|
||||
|
||||
set
|
||||
{
|
||||
BlockEngine.SetBlockInfo(this, (ref ServoReadOnlyStruct st, float val) => st.minDeviation = val, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The servo's maximum angle.
|
||||
/// </summary>
|
||||
public float MaximumAngle
|
||||
{
|
||||
get => BlockEngine.GetBlockInfo(this, (ServoReadOnlyStruct st) => st.maxDeviation);
|
||||
|
||||
set
|
||||
{
|
||||
BlockEngine.SetBlockInfo(this, (ref ServoReadOnlyStruct st, float val) => st.maxDeviation = val, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The servo's maximum force.
|
||||
/// </summary>
|
||||
public float MaximumForce
|
||||
{
|
||||
get => BlockEngine.GetBlockInfo(this, (ServoReadOnlyStruct st) => st.maxForce);
|
||||
|
||||
set
|
||||
{
|
||||
BlockEngine.SetBlockInfo(this, (ref ServoReadOnlyStruct st, float val) => st.maxForce = val, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The servo's direction.
|
||||
/// </summary>
|
||||
public bool Reverse
|
||||
{
|
||||
get => BlockEngine.GetBlockInfo(this, (ServoReadOnlyStruct st) => st.reverse);
|
||||
|
||||
set
|
||||
{
|
||||
BlockEngine.SetBlockInfo(this, (ref ServoReadOnlyStruct st, bool val) => st.reverse = val, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,209 +0,0 @@
|
|||
using System;
|
||||
using FMOD.Studio;
|
||||
using FMODUnity;
|
||||
using Gamecraft.Wires;
|
||||
using RobocraftX.Blocks;
|
||||
using RobocraftX.Common;
|
||||
using Svelto.ECS;
|
||||
|
||||
namespace GamecraftModdingAPI.Blocks
|
||||
{
|
||||
public class SfxBlock : SignalingBlock
|
||||
{
|
||||
public SfxBlock(EGID id) : base(id)
|
||||
{
|
||||
}
|
||||
|
||||
public SfxBlock(uint id) : base(new EGID(id, CommonExclusiveGroups.SIMPLESFX_BLOCK_GROUP /* This could also be BUILD_LOOPEDSFX_BLOCK_GROUP */))
|
||||
{
|
||||
}
|
||||
|
||||
public float Volume
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo(this, (SoundSfxBlockDataEntityStruct obj) => obj.tweakableVolume);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
BlockEngine.SetBlockInfo(this,
|
||||
(ref SoundSfxBlockDataEntityStruct obj, float val) => obj.tweakableVolume = val, value);
|
||||
}
|
||||
}
|
||||
|
||||
public float Pitch
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo(this, (SoundSfxBlockDataEntityStruct obj) => obj.tweakablePitch);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
BlockEngine.SetBlockInfo(this,
|
||||
(ref SoundSfxBlockDataEntityStruct obj, float val) => obj.tweakablePitch = val, value);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Is3D
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo(this, (SoundSfxBlockDataEntityStruct obj) => obj.is3D);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
BlockEngine.SetBlockInfo(this,
|
||||
(ref SoundSfxBlockDataEntityStruct obj, bool val) => obj.is3D = val, value);
|
||||
}
|
||||
}
|
||||
|
||||
public ChannelType ChannelType
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo(this, (SoundSfxBlockDataEntityStruct obj) => (ChannelType)obj.channelType);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
BlockEngine.SetBlockInfo(this,
|
||||
(ref SoundSfxBlockDataEntityStruct obj, ChannelType val) => obj.tweakableVolume = (byte) val, value);
|
||||
}
|
||||
}
|
||||
|
||||
public byte TrackIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo(this, (SoundSfxBlockDataEntityStruct obj) => obj.soundEffectIndex);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
BlockEngine.SetBlockInfo(this,
|
||||
(ref SoundSfxBlockDataEntityStruct obj, byte val) => obj.soundEffectIndex = val, value);
|
||||
}
|
||||
}
|
||||
|
||||
// track
|
||||
public Guid Track
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo(this,
|
||||
(SoundSfxBlockDataEntityStruct obj) => obj.is3D ? obj.fmod3DEventPaths.Get<Guid>(obj.soundEffectIndex) : obj.fmod2DEventPaths.Get<Guid>(obj.soundEffectIndex));
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
BlockEngine.SetBlockInfo(this, (ref SoundSfxBlockDataEntityStruct obj, Guid val) =>
|
||||
{
|
||||
for (byte i = 0; i < obj.fmod2DEventPaths.Count<Guid>(); i++)
|
||||
{
|
||||
Guid track = obj.fmod2DEventPaths.Get<Guid>(i);
|
||||
if (track == val)
|
||||
{
|
||||
obj.soundEffectIndex = i;
|
||||
obj.is3D = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (byte i = 0; i < obj.fmod3DEventPaths.Count<Guid>(); i++)
|
||||
{
|
||||
Guid track = obj.fmod3DEventPaths.Get<Guid>(i);
|
||||
if (track == val)
|
||||
{
|
||||
obj.soundEffectIndex = i;
|
||||
obj.is3D = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}, value);
|
||||
}
|
||||
}
|
||||
|
||||
// all tracks
|
||||
public Guid[] Tracks2D
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo(this, (SoundSfxBlockDataEntityStruct obj) =>
|
||||
{
|
||||
Guid[] tracks = new Guid[obj.fmod2DEventPaths.Count<Guid>()];
|
||||
for (byte i = 0; i < tracks.Length; i++)
|
||||
{
|
||||
tracks[i] = obj.fmod2DEventPaths.Get<Guid>(i);
|
||||
}
|
||||
return tracks;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public Guid[] Tracks3D
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo(this, (SoundSfxBlockDataEntityStruct obj) =>
|
||||
{
|
||||
Guid[] tracks = new Guid[obj.fmod3DEventPaths.Count<Guid>()];
|
||||
for (byte i = 0; i < tracks.Length; i++)
|
||||
{
|
||||
tracks[i] = obj.fmod2DEventPaths.Get<Guid>(i);
|
||||
}
|
||||
return tracks;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsLooped
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo(this, (SoundSfxBlockDataEntityStruct obj) => obj.isLoopedBlock);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
BlockEngine.SetBlockInfo(this,
|
||||
(ref SoundSfxBlockDataEntityStruct obj, bool val) => obj.isLoopedBlock = val, value);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsPlaying
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo(this,
|
||||
(SoundSfxBlockDataEntityStruct obj) => obj.isPlaying);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
BlockEngine.SetBlockInfo(this, (ref SoundSfxBlockDataEntityStruct obj, bool val) =>
|
||||
{
|
||||
if (obj.isPlaying == val) return;
|
||||
if (val)
|
||||
{
|
||||
// start playing
|
||||
EventInstance inst = RuntimeManager.CreateInstance(obj.is3D ? obj.fmod3DEventPaths.Get<Guid>(obj.soundEffectIndex) : obj.fmod2DEventPaths.Get<Guid>(obj.soundEffectIndex));
|
||||
inst.setVolume(obj.tweakableVolume / 100f);
|
||||
inst.start();
|
||||
obj.eventHandle = inst.handle;
|
||||
}
|
||||
else
|
||||
{
|
||||
// stop playing
|
||||
EventInstance inst = default(EventInstance);
|
||||
inst.handle = obj.eventHandle;
|
||||
inst.stop(FMOD.Studio.STOP_MODE.ALLOWFADEOUT);
|
||||
inst.release();
|
||||
}
|
||||
obj.isPlaying = val;
|
||||
}, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
using System;
|
||||
|
||||
using RobocraftX.Blocks;
|
||||
using RobocraftX.Common;
|
||||
using Gamecraft.CharacterVulnerability;
|
||||
using Svelto.ECS;
|
||||
using Unity.Mathematics;
|
||||
|
||||
using GamecraftModdingAPI;
|
||||
using GamecraftModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftModdingAPI.Blocks
|
||||
{
|
||||
public class SpawnPoint : Block
|
||||
{
|
||||
public SpawnPoint(EGID id) : base(id)
|
||||
{
|
||||
}
|
||||
|
||||
public SpawnPoint(uint id) : base(new EGID(id, CommonExclusiveGroups.SPAWNPOINT_BLOCK_GROUP))
|
||||
{
|
||||
}
|
||||
|
||||
// custom spawn point properties
|
||||
|
||||
/// <summary>
|
||||
/// The lives the player spawns in with.
|
||||
/// </summary>
|
||||
public uint Lives
|
||||
{
|
||||
get => BlockEngine.GetBlockInfo(this, (SpawnPointStatsEntityStruct st) => st.lives);
|
||||
|
||||
set
|
||||
{
|
||||
BlockEngine.SetBlockInfo(this, (ref SpawnPointStatsEntityStruct st, uint val) => st.lives = val, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the spawned player can take damage.
|
||||
/// </summary>
|
||||
public bool Damageable
|
||||
{
|
||||
get => BlockEngine.GetBlockInfo(this, (SpawnPointStatsEntityStruct st) => st.canTakeDamage);
|
||||
|
||||
set
|
||||
{
|
||||
BlockEngine.SetBlockInfo(this, (ref SpawnPointStatsEntityStruct st, bool val) => st.canTakeDamage = val, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the game over screen will be displayed
|
||||
/// </summary>
|
||||
public bool GameOverEnabled
|
||||
{
|
||||
get => BlockEngine.GetBlockInfo(this, (SpawnPointStatsEntityStruct st) => st.gameOverScreen);
|
||||
|
||||
set
|
||||
{
|
||||
BlockEngine.SetBlockInfo(this, (ref SpawnPointStatsEntityStruct st, bool val) => st.gameOverScreen = val, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The team id for players who spawn here.
|
||||
/// </summary>
|
||||
public byte Team
|
||||
{
|
||||
get => BlockEngine.GetBlockInfo(this, (SpawnPointIdsEntityStruct st) => st.teamId);
|
||||
|
||||
set
|
||||
{
|
||||
BlockEngine.SetBlockInfo(this, (ref SpawnPointIdsEntityStruct st, byte val) => st.teamId = val, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
using System;
|
||||
|
||||
using Gamecraft.Blocks.GUI;
|
||||
using RobocraftX.Common;
|
||||
using Svelto.ECS;
|
||||
using Unity.Mathematics;
|
||||
|
||||
using GamecraftModdingAPI;
|
||||
using GamecraftModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftModdingAPI.Blocks
|
||||
{
|
||||
public class TextBlock : SignalingBlock
|
||||
{
|
||||
public TextBlock(EGID id) : base(id)
|
||||
{
|
||||
}
|
||||
|
||||
public TextBlock(uint id) : base(new EGID(id, CommonExclusiveGroups.TEXT_BLOCK_GROUP))
|
||||
{
|
||||
}
|
||||
|
||||
// custom text block properties
|
||||
|
||||
/// <summary>
|
||||
/// The text block's current text.
|
||||
/// </summary>
|
||||
public string Text
|
||||
{
|
||||
get => BlockEngine.GetBlockInfo(this, (TextBlockDataStruct st) => st.textCurrent);
|
||||
|
||||
set
|
||||
{
|
||||
if (value == null) value = "";
|
||||
BlockEngine.SetBlockInfo(this, (ref TextBlockDataStruct tbds, string val) =>
|
||||
{
|
||||
if (val == null) val = "";
|
||||
tbds.textCurrent.Set(val);
|
||||
tbds.textStored.Set(val);
|
||||
}, value);
|
||||
BlockEngine.SetBlockInfo(this,
|
||||
(ref TextBlockNetworkDataStruct st, string val) => st.newTextBlockStringContent.Set(val), value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The text block's current text block ID (used in ChangeTextBlockCommand).
|
||||
/// </summary>
|
||||
public string TextBlockId
|
||||
{
|
||||
get => BlockEngine.GetBlockInfo(this, (TextBlockDataStruct st) => st.textBlockID);
|
||||
|
||||
set
|
||||
{
|
||||
if (value == null) value = "";
|
||||
BlockEngine.SetBlockInfo(this, (ref TextBlockDataStruct tbds, string val) =>
|
||||
tbds.textBlockID.Set(val), value);
|
||||
BlockEngine.SetBlockInfo(this,
|
||||
(ref TextBlockNetworkDataStruct st, string val) => st.newTextBlockID.Set(val), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
using System;
|
||||
|
||||
using RobocraftX.Blocks;
|
||||
using RobocraftX.Common;
|
||||
using Gamecraft.Blocks.TimerBlock;
|
||||
using Svelto.ECS;
|
||||
using Unity.Mathematics;
|
||||
|
||||
using GamecraftModdingAPI;
|
||||
using GamecraftModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftModdingAPI.Blocks
|
||||
{
|
||||
public class Timer : SignalingBlock
|
||||
{
|
||||
public Timer(EGID id) : base(id)
|
||||
{
|
||||
}
|
||||
|
||||
public Timer(uint id) : base(new EGID(id, CommonExclusiveGroups.TIMER_BLOCK_GROUP))
|
||||
{
|
||||
}
|
||||
|
||||
// custom timer properties
|
||||
|
||||
/// <summary>
|
||||
/// The player-specified start time.
|
||||
/// </summary>
|
||||
public float Start
|
||||
{
|
||||
get => BlockEngine.GetBlockInfo(this, (TimerBlockDataStruct st) => st.startTime);
|
||||
|
||||
set
|
||||
{
|
||||
BlockEngine.SetBlockInfo(this, (ref TimerBlockDataStruct tbds, float val) => tbds.startTime = val,
|
||||
value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The player-specified end time.
|
||||
/// </summary>
|
||||
public float End
|
||||
{
|
||||
get => BlockEngine.GetBlockInfo(this, (TimerBlockDataStruct st) => st.endTime);
|
||||
|
||||
set
|
||||
{
|
||||
BlockEngine.SetBlockInfo(this, (ref TimerBlockDataStruct tbds, float val) => tbds.endTime = val,
|
||||
value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether to display time with millisecond precision.
|
||||
/// </summary>
|
||||
public bool DisplayMilliseconds
|
||||
{
|
||||
get => BlockEngine.GetBlockInfo(this, (TimerBlockDataStruct st) => st.outputFormatHasMS);
|
||||
|
||||
set
|
||||
{
|
||||
BlockEngine.SetBlockInfo(this, (ref TimerBlockDataStruct tbds, bool val) => tbds.outputFormatHasMS = val,
|
||||
value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Current time (as of the last video frame), in milliseconds.
|
||||
/// </summary>
|
||||
public int CurrentTime
|
||||
{
|
||||
get => BlockEngine.GetBlockInfo(this, (TimerBlockLabelCacheEntityStruct st) => st.timeLastRenderFrameMS);
|
||||
|
||||
set
|
||||
{
|
||||
BlockEngine.SetBlockInfo(this, (ref TimerBlockLabelCacheEntityStruct tbds, int val) => tbds.timeLastRenderFrameMS = val,
|
||||
value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,355 +0,0 @@
|
|||
using System;
|
||||
|
||||
using Gamecraft.Wires;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.Experimental;
|
||||
|
||||
using GamecraftModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftModdingAPI.Blocks
|
||||
{
|
||||
public class Wire
|
||||
{
|
||||
internal static SignalEngine signalEngine;
|
||||
|
||||
protected EGID startPortEGID;
|
||||
|
||||
protected EGID endPortEGID;
|
||||
|
||||
protected EGID startBlockEGID;
|
||||
|
||||
protected EGID endBlockEGID;
|
||||
|
||||
protected EGID wireEGID;
|
||||
|
||||
protected bool inputToOutput;
|
||||
|
||||
protected byte startPort;
|
||||
|
||||
protected byte endPort;
|
||||
|
||||
public static Wire Connect(SignalingBlock start, byte startPort, SignalingBlock end, byte endPort)
|
||||
{
|
||||
WireEntityStruct wire = signalEngine.CreateNewWire(start.Id, startPort, end.Id, endPort);
|
||||
return new Wire(wire, start, end);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An existing wire connection ending at the specified input.
|
||||
/// If multiple exist, this will return the first one found.
|
||||
/// </summary>
|
||||
/// <param name="end">Destination block.</param>
|
||||
/// <param name="endPort">Port number.</param>
|
||||
/// <returns>The wire, where the end of the wire is the block port specified, or null if does not exist.</returns>
|
||||
public static Wire ConnectedToInputPort(SignalingBlock end, byte endPort)
|
||||
{
|
||||
EGID port = signalEngine.MatchBlockInputToPort(end, endPort, out bool exists);
|
||||
if (!exists) return null;
|
||||
WireEntityStruct wire = signalEngine.MatchPortToWire(port, end.Id, out exists);
|
||||
if (exists)
|
||||
{
|
||||
return new Wire(new Block(wire.sourceBlockEGID), end, wire.sourcePortUsage, endPort);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An existing wire connection starting at the specified output.
|
||||
/// If multiple exist, this will return the first one found.
|
||||
/// </summary>
|
||||
/// <param name="start">Source block entity ID.</param>
|
||||
/// <param name="startPort">Port number.</param>
|
||||
/// <returns>The wire, where the start of the wire is the block port specified, or null if does not exist.</returns>
|
||||
public static Wire ConnectedToOutputPort(SignalingBlock start, byte startPort)
|
||||
{
|
||||
EGID port = signalEngine.MatchBlockOutputToPort(start, startPort, out bool exists);
|
||||
if (!exists) return null;
|
||||
WireEntityStruct wire = signalEngine.MatchPortToWire(port, start.Id, out exists);
|
||||
if (exists)
|
||||
{
|
||||
return new Wire(start, new Block(wire.destinationBlockEGID), startPort, wire.destinationPortUsage);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a wire object from an existing connection.
|
||||
/// </summary>
|
||||
/// <param name="start">Starting block ID.</param>
|
||||
/// <param name="end">Ending block ID.</param>
|
||||
/// <param name="startPort">Starting port number, or guess if omitted.</param>
|
||||
/// <param name="endPort">Ending port number, or guess if omitted.</param>
|
||||
/// <exception cref="WireInvalidException">Guessing failed or wire does not exist.</exception>
|
||||
public Wire(Block start, Block end, byte startPort = Byte.MaxValue, byte endPort = Byte.MaxValue)
|
||||
{
|
||||
startBlockEGID = start.Id;
|
||||
endBlockEGID = end.Id;
|
||||
// find block ports
|
||||
WireEntityStruct wire = signalEngine.MatchBlocksToWire(start.Id, end.Id, out bool exists, startPort, endPort);
|
||||
if (exists)
|
||||
{
|
||||
wireEGID = wire.ID;
|
||||
endPortEGID = signalEngine.MatchBlockInputToPort(end, wire.destinationPortUsage, out exists);
|
||||
if (!exists) throw new WireInvalidException("Wire end port not found");
|
||||
startPortEGID = signalEngine.MatchBlockOutputToPort(start, wire.sourcePortUsage, out exists);
|
||||
if (!exists) throw new WireInvalidException("Wire start port not found");
|
||||
inputToOutput = false;
|
||||
endPort = wire.destinationPortUsage;
|
||||
startPort = wire.sourcePortUsage;
|
||||
}
|
||||
else
|
||||
{
|
||||
// flip I/O around and try again
|
||||
wire = signalEngine.MatchBlocksToWire(end.Id, start.Id, out exists, endPort, startPort);
|
||||
if (exists)
|
||||
{
|
||||
wireEGID = wire.ID;
|
||||
endPortEGID = signalEngine.MatchBlockOutputToPort(end, wire.sourcePortUsage, out exists);
|
||||
if (!exists) throw new WireInvalidException("Wire end port not found");
|
||||
startPortEGID = signalEngine.MatchBlockInputToPort(start, wire.destinationPortUsage, out exists);
|
||||
if (!exists) throw new WireInvalidException("Wire start port not found");
|
||||
inputToOutput = true; // end is actually the source
|
||||
// NB: start and end are handled exactly as they're received as params.
|
||||
// This makes wire traversal easier, but makes logic in this class a bit more complex
|
||||
endPort = wire.sourcePortUsage;
|
||||
startPort = wire.destinationPortUsage;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new WireInvalidException("Wire not found");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a wire object from an existing wire connection.
|
||||
/// </summary>
|
||||
/// <param name="start">Starting block ID.</param>
|
||||
/// <param name="end">Ending block ID.</param>
|
||||
/// <param name="startPort">Starting port number.</param>
|
||||
/// <param name="endPort">Ending port number.</param>
|
||||
/// <param name="wire">The wire ID.</param>
|
||||
/// <param name="inputToOutput">Whether the wire direction goes input -> output (true) or output -> input (false, preferred).</param>
|
||||
public Wire(Block start, Block end, byte startPort, byte endPort, EGID wire, bool inputToOutput)
|
||||
{
|
||||
this.startBlockEGID = start.Id;
|
||||
this.endBlockEGID = end.Id;
|
||||
this.inputToOutput = inputToOutput;
|
||||
this.wireEGID = wire;
|
||||
if (inputToOutput)
|
||||
{
|
||||
endPortEGID = signalEngine.MatchBlockOutputToPort(start, startPort, out bool exists);
|
||||
if (!exists) throw new WireInvalidException("Wire end port not found");
|
||||
startPortEGID = signalEngine.MatchBlockInputToPort(end, endPort, out exists);
|
||||
if (!exists) throw new WireInvalidException("Wire start port not found");
|
||||
}
|
||||
else
|
||||
{
|
||||
endPortEGID = signalEngine.MatchBlockInputToPort(end, endPort, out bool exists);
|
||||
if (!exists) throw new WireInvalidException("Wire end port not found");
|
||||
startPortEGID = signalEngine.MatchBlockOutputToPort(start, startPort, out exists);
|
||||
if (!exists) throw new WireInvalidException("Wire start port not found");
|
||||
}
|
||||
this.startPort = startPort;
|
||||
this.endPort = endPort;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a wire object from an existing wire connection.
|
||||
/// </summary>
|
||||
/// <param name="wireEgid">The wire ID.</param>
|
||||
public Wire(EGID wireEgid)
|
||||
{
|
||||
this.wireEGID = wireEgid;
|
||||
WireEntityStruct wire = signalEngine.GetWire(wireEGID);
|
||||
this.startBlockEGID = wire.sourceBlockEGID;
|
||||
this.endBlockEGID = wire.destinationBlockEGID;
|
||||
this.inputToOutput = false;
|
||||
endPortEGID = signalEngine.MatchBlockInputToPort(wire.destinationBlockEGID, wire.destinationPortUsage, out bool exists);
|
||||
if (!exists) throw new WireInvalidException("Wire end port not found");
|
||||
startPortEGID = signalEngine.MatchBlockOutputToPort(wire.sourceBlockEGID, wire.sourcePortUsage, out exists);
|
||||
if (!exists) throw new WireInvalidException("Wire start port not found");
|
||||
this.endPort = wire.destinationPortUsage;
|
||||
this.startPort = wire.sourcePortUsage;
|
||||
}
|
||||
|
||||
internal Wire(WireEntityStruct wire, SignalingBlock src, SignalingBlock dest)
|
||||
{
|
||||
this.wireEGID = wire.ID;
|
||||
this.startBlockEGID = wire.sourceBlockEGID;
|
||||
this.endBlockEGID = wire.destinationBlockEGID;
|
||||
inputToOutput = false;
|
||||
endPortEGID = signalEngine.MatchBlockInputToPort(dest, wire.destinationPortUsage, out bool exists);
|
||||
if (!exists) throw new WireInvalidException("Wire end port not found");
|
||||
startPortEGID = signalEngine.MatchBlockOutputToPort(src, wire.sourcePortUsage, out exists);
|
||||
if (!exists) throw new WireInvalidException("Wire start port not found");
|
||||
this.endPort = wire.destinationPortUsage;
|
||||
this.startPort = wire.sourcePortUsage;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The wire's in-game id.
|
||||
/// </summary>
|
||||
public EGID Id
|
||||
{
|
||||
get => wireEGID;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The wire's signal value, as a float.
|
||||
/// </summary>
|
||||
public float Float
|
||||
{
|
||||
get
|
||||
{
|
||||
ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
|
||||
if (!exists) return 0f;
|
||||
return cds.valueAsFloat;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
|
||||
if (!exists) return;
|
||||
cds.valueAsFloat = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The wire's string signal.
|
||||
/// </summary>
|
||||
public string String
|
||||
{
|
||||
get
|
||||
{
|
||||
ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
|
||||
if (!exists) return "";
|
||||
return cds.valueAsEcsString;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
|
||||
if (!exists) return;
|
||||
cds.valueAsEcsString.Set(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The wire's raw string signal.
|
||||
/// </summary>
|
||||
public ECSString ECSString
|
||||
{
|
||||
get
|
||||
{
|
||||
ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
|
||||
if (!exists) return default;
|
||||
return cds.valueAsEcsString;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
|
||||
if (!exists) return;
|
||||
cds.valueAsEcsString = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The wire's signal id.
|
||||
/// I'm 50% sure this is useless.
|
||||
/// </summary>
|
||||
public uint SignalId
|
||||
{
|
||||
get
|
||||
{
|
||||
ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
|
||||
if (!exists) return uint.MaxValue;
|
||||
return cds.valueAsID;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists);
|
||||
if (!exists) return;
|
||||
cds.valueAsID = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The block at the beginning of the wire.
|
||||
/// </summary>
|
||||
public SignalingBlock Start
|
||||
{
|
||||
get => new SignalingBlock(startBlockEGID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The port number that the beginning of the wire connects to.
|
||||
/// </summary>
|
||||
public byte StartPort
|
||||
{
|
||||
get => startPort;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The block at the end of the wire.
|
||||
/// </summary>
|
||||
public SignalingBlock End
|
||||
{
|
||||
get => new SignalingBlock(endBlockEGID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The port number that the end of the wire connects to.
|
||||
/// </summary>
|
||||
public byte EndPort
|
||||
{
|
||||
get => endPort;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a copy of the wire object where the direction of the wire is guaranteed to be from a block output to a block input.
|
||||
/// This is simply a different memory configuration and does not affect the in-game wire (which is always output -> input).
|
||||
/// </summary>
|
||||
/// <returns>A copy of the wire object.</returns>
|
||||
public Wire OutputToInputCopy()
|
||||
{
|
||||
return new Wire(wireEGID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert the wire object to the direction the signal flows.
|
||||
/// Signals on wires always flow from a block output port to a block input port.
|
||||
/// This is simply a different memory configuration and does not affect the in-game wire (which is always output -> input).
|
||||
/// </summary>
|
||||
public void OutputToInputInPlace()
|
||||
{
|
||||
if (inputToOutput)
|
||||
{
|
||||
inputToOutput = false;
|
||||
// swap inputs and outputs
|
||||
EGID temp = endBlockEGID;
|
||||
endBlockEGID = startBlockEGID;
|
||||
startBlockEGID = temp;
|
||||
temp = endPortEGID;
|
||||
endPortEGID = startPortEGID;
|
||||
startPortEGID = temp;
|
||||
byte tempPortNumber = endPort;
|
||||
endPort = startPort;
|
||||
startPort = tempPortNumber;
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (signalEngine.Exists<WireEntityStruct>(wireEGID))
|
||||
{
|
||||
return $"{nameof(Id)}: {Id}, Start{nameof(Start.Id)}: {Start.Id}, End{nameof(End.Id)}: {End.Id}, ({Start.Type}::{StartPort} aka {Start.PortName(StartPort, inputToOutput)}) -> ({End.Type}::{EndPort} aka {End.PortName(EndPort, !inputToOutput)})";
|
||||
}
|
||||
return $"{nameof(Id)}: {Id}, Start{nameof(Start.Id)}: {Start.Id}, End{nameof(End.Id)}: {End.Id}, ({Start.Type}::{StartPort} -> {End.Type}::{EndPort})";
|
||||
}
|
||||
|
||||
internal static void Init() { }
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Reflection;
|
||||
|
||||
using HarmonyLib;
|
||||
using Svelto.Context;
|
||||
using Svelto.ECS;
|
||||
using RobocraftX;
|
||||
|
||||
using GamecraftModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftModdingAPI.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Patch of RobocraftX.GUI.CommandLine.CommandLineCompositionRoot.Compose<T>()
|
||||
/// </summary>
|
||||
// TODO: fix
|
||||
[HarmonyPatch]
|
||||
//[HarmonyPatch(typeof(RobocraftX.GUI.CommandLine.CommandLineCompositionRoot))]
|
||||
//[HarmonyPatch("Compose")]
|
||||
//[HarmonyPatch("Compose", new Type[] { typeof(UnityContext<FullGameCompositionRoot>), typeof(EnginesRoot), typeof(World), typeof(Action), typeof(MultiplayerInitParameters), typeof(StateSyncRegistrationHelper)})]
|
||||
static class CommandPatch
|
||||
{
|
||||
public static void Postfix(EnginesRoot enginesRoot)
|
||||
{
|
||||
// When a game is loaded, register the command engines
|
||||
CommandManager.RegisterEngines(enginesRoot);
|
||||
}
|
||||
|
||||
public static MethodBase TargetMethod(Harmony instance)
|
||||
{
|
||||
return typeof(RobocraftX.GUI.CommandLine.CommandLineCompositionRoot).GetMethod("Compose").MakeGenericMethod(typeof(object));
|
||||
//return func.Method;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
using System;
|
||||
|
||||
using uREPL;
|
||||
|
||||
namespace GamecraftModdingAPI.Commands
|
||||
{
|
||||
public static class ExistingCommands
|
||||
{
|
||||
public static void Call(string commandName)
|
||||
{
|
||||
RuntimeCommands.Call(commandName);
|
||||
}
|
||||
|
||||
public static void Call<Arg0>(string commandName, Arg0 arg0)
|
||||
{
|
||||
RuntimeCommands.Call<Arg0>(commandName, arg0);
|
||||
}
|
||||
|
||||
public static void Call<Arg0, Arg1>(string commandName, Arg0 arg0, Arg1 arg1)
|
||||
{
|
||||
RuntimeCommands.Call<Arg0, Arg1>(commandName, arg0, arg1);
|
||||
}
|
||||
|
||||
public static void Call<Arg0, Arg1, Arg2>(string commandName, Arg0 arg0, Arg1 arg1, Arg2 arg2)
|
||||
{
|
||||
RuntimeCommands.Call<Arg0, Arg1, Arg2>(commandName, arg0, arg1, arg2);
|
||||
}
|
||||
|
||||
public static bool Exists(string commandName)
|
||||
{
|
||||
return RuntimeCommands.HasRegistered(commandName);
|
||||
}
|
||||
|
||||
public static string[] GetCommandNames()
|
||||
{
|
||||
var keys = RuntimeCommands.table.Keys;
|
||||
string[] res = new string[keys.Count];
|
||||
keys.CopyTo(res, 0);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using HarmonyLib;
|
||||
using Svelto.ECS;
|
||||
using RobocraftX.Common;
|
||||
using RobocraftX.StateSync;
|
||||
|
||||
using GamecraftModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftModdingAPI.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Patch of RobocraftX.StateSync.DeterministicStepCompositionRoot.ComposeEnginesGroups(...)
|
||||
/// </summary>
|
||||
//[HarmonyPatch(typeof(DeterministicStepCompositionRoot), "DeterministicCompose")]
|
||||
[Obsolete]
|
||||
[HarmonyPatch]
|
||||
class GameHostTransitionDeterministicGroupEnginePatch
|
||||
{
|
||||
|
||||
public static readonly GameStateBuildEmitterEngine buildEngine = new GameStateBuildEmitterEngine();
|
||||
|
||||
public static readonly GameStateSimulationEmitterEngine simEngine = new GameStateSimulationEmitterEngine();
|
||||
|
||||
public static void Postfix()
|
||||
{
|
||||
//stateSyncReg.buildModeInitializationEngines.Add(buildEngine);
|
||||
//stateSyncReg.simulationModeInitializationEngines.Add(simEngine);
|
||||
//enginesRoot.AddEngine(buildEngine);
|
||||
//enginesRoot.AddEngine(simEngine);
|
||||
buildEngine.EmitIfBuildMode();
|
||||
simEngine.EmitIfSimMode();
|
||||
}
|
||||
|
||||
[HarmonyTargetMethod]
|
||||
public static MethodBase TargetMethod(Harmony harmonyInstance)
|
||||
{
|
||||
return AccessTools.Method(AccessTools.TypeByName("RobocraftX.StateSync.GameHostTransitionDeterministicGroupEngine"), "EndTransition");
|
||||
//.MakeGenericMethod(typeof(CosmeticEnginesSequenceBuildOrder), typeof(CosmeticEnginesSequenceSimOrder), typeof(DeterministicToCosmeticSyncBuildOrder), typeof(DeterministicToCosmeticSyncSimOrder));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,106 +0,0 @@
|
|||
using System;
|
||||
|
||||
using Svelto.ECS;
|
||||
|
||||
namespace GamecraftModdingAPI.Events
|
||||
{
|
||||
[Obsolete]
|
||||
public class EmitterBuilder
|
||||
{
|
||||
private string name;
|
||||
|
||||
private int? type;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new event emitter builder.
|
||||
/// </summary>
|
||||
public EmitterBuilder()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new event emitter builder.
|
||||
/// This is equivalent to new <code>EmitterBuilder().Name(name)</code>
|
||||
/// </summary>
|
||||
/// <param name="name">The emitter name.</param>
|
||||
public EmitterBuilder(string name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create and return an event emitter builder.
|
||||
/// </summary>
|
||||
/// <returns>The builder.</returns>
|
||||
public static EmitterBuilder Builder()
|
||||
{
|
||||
return new EmitterBuilder();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create and return an event emitter builder.
|
||||
/// This is equivalent to <code>Builder().Name(name)</code>
|
||||
/// </summary>
|
||||
/// <returns>The builder.</returns>
|
||||
/// <param name="name">The emitter name.</param>
|
||||
public static EmitterBuilder Builder(string name)
|
||||
{
|
||||
return new EmitterBuilder(name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Name the event emitter.
|
||||
/// </summary>
|
||||
/// <returns>The builder.</returns>
|
||||
/// <param name="name">The event emitter name.</param>
|
||||
public EmitterBuilder Name(string name)
|
||||
{
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the type of event to handle.
|
||||
/// </summary>
|
||||
/// <returns>The builder.</returns>
|
||||
/// <param name="eventType">The event type.</param>
|
||||
public EmitterBuilder Handle(EventType eventType)
|
||||
{
|
||||
return Handle((int)eventType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the type of event to handle.
|
||||
/// </summary>
|
||||
/// <returns>The builder.</returns>
|
||||
/// <param name="eventType">The event type.</param>
|
||||
public EmitterBuilder Handle(int eventType)
|
||||
{
|
||||
this.type = eventType;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Build the event emitter.
|
||||
/// </summary>
|
||||
/// <returns>The event emitter.</returns>
|
||||
/// <param name="register">Automatically register the event emitter with EventManager.AddEventemitter().</param>
|
||||
public IEventEmitterEngine Build(bool register = true)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
throw new EventParameterMissingException("Event emitter name must be defined before Build() is called");
|
||||
}
|
||||
if (!type.HasValue)
|
||||
{
|
||||
throw new EventParameterMissingException("Event emitter event type must be defined before Build() is called");
|
||||
}
|
||||
SimpleEventEmitterEngine result = new SimpleEventEmitterEngine(type.Value, name);
|
||||
if (register)
|
||||
{
|
||||
EventManager.AddEventEmitter(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Svelto.ECS;
|
||||
|
||||
namespace GamecraftModdingAPI.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Convenient factories for mod event engines
|
||||
/// </summary>
|
||||
[Obsolete]
|
||||
public static class EventEngineFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Factory method which automatically adds the SimpleEventHandlerEngine to the Manager
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the engine</param>
|
||||
/// <param name="type">The type of event to handle</param>
|
||||
/// <param name="onActivated">The operation to do when the event is created</param>
|
||||
/// <param name="onDestroyed">The operation to do when the event is destroyed (if applicable)</param>
|
||||
/// <returns>The created object</returns>
|
||||
public static SimpleEventHandlerEngine CreateAddSimpleHandler(string name, int type, Action onActivated, Action onDestroyed)
|
||||
{
|
||||
var engine = new SimpleEventHandlerEngine(onActivated, onDestroyed, type, name);
|
||||
EventManager.AddEventHandler(engine);
|
||||
return engine;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Factory method which automatically adds the SimpleEventHandlerEngine to the Manager
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the engine</param>
|
||||
/// <param name="type">The type of event to handle</param>
|
||||
/// <param name="onActivated">The operation to do when the event is created</param>
|
||||
/// <param name="onDestroyed">The operation to do when the event is destroyed (if applicable)</param>
|
||||
/// <returns>The created object</returns>
|
||||
public static SimpleEventHandlerEngine CreateAddSimpleHandler(string name, int type, Action<EntitiesDB> onActivated, Action<EntitiesDB> onDestroyed)
|
||||
{
|
||||
var engine = new SimpleEventHandlerEngine(onActivated, onDestroyed, type, name);
|
||||
EventManager.AddEventHandler(engine);
|
||||
return engine;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Factory method which automatically adds the SimpleEventEmitterEngine to the Manager
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the engine</param>
|
||||
/// <param name="type">The type of event to emit</param>
|
||||
/// <param name="isRemovable">Will removing this engine not break your code?</param>
|
||||
/// <returns>The created object</returns>
|
||||
public static SimpleEventEmitterEngine CreateAddSimpleEmitter(string name, int type, bool isRemovable = true)
|
||||
{
|
||||
var engine = new SimpleEventEmitterEngine(type, name, isRemovable);
|
||||
EventManager.AddEventEmitter(engine);
|
||||
return engine;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
using System;
|
||||
namespace GamecraftModdingAPI.Events
|
||||
{
|
||||
public class EventException : GamecraftModdingAPIException
|
||||
{
|
||||
public EventException()
|
||||
{
|
||||
}
|
||||
|
||||
public EventException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public EventException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class EventNotFoundException : EventException
|
||||
{
|
||||
public EventNotFoundException()
|
||||
{
|
||||
}
|
||||
|
||||
public EventNotFoundException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class EventAlreadyExistsException : EventException
|
||||
{
|
||||
public EventAlreadyExistsException()
|
||||
{
|
||||
}
|
||||
|
||||
public EventAlreadyExistsException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class EventRuntimeException : EventException
|
||||
{
|
||||
public EventRuntimeException()
|
||||
{
|
||||
}
|
||||
|
||||
public EventRuntimeException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public EventRuntimeException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class EventParameterMissingException : EventException
|
||||
{
|
||||
public EventParameterMissingException()
|
||||
{
|
||||
}
|
||||
|
||||
public EventParameterMissingException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,129 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Svelto.ECS;
|
||||
|
||||
using GamecraftModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftModdingAPI.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Keeps track of event handlers and emitters.
|
||||
/// This is used to add, remove and get API event handlers and emitters.
|
||||
/// </summary>
|
||||
[Obsolete("This will be removed in an upcoming update. Use the new C# event architecture from GamecraftModdingAPI.App")]
|
||||
public static class EventManager
|
||||
{
|
||||
private static Dictionary<string, IEventEmitterEngine> _eventEmitters = new Dictionary<string, IEventEmitterEngine>();
|
||||
|
||||
private static Dictionary<string, IEventHandlerEngine> _eventHandlers = new Dictionary<string, IEventHandlerEngine>();
|
||||
|
||||
private static EnginesRoot _lastEngineRoot;
|
||||
|
||||
// event handler management
|
||||
|
||||
public static void AddEventHandler(IEventHandlerEngine engine)
|
||||
{
|
||||
if (ExistsEventHandler(engine))
|
||||
{
|
||||
throw new EventAlreadyExistsException($"IEventHandlerEngine {engine.Name} already exists");
|
||||
}
|
||||
_eventHandlers[engine.Name] = engine;
|
||||
if (_lastEngineRoot != null)
|
||||
{
|
||||
Logging.MetaDebugLog($"Registering IEventHandlerEngine {engine.Name}");
|
||||
_lastEngineRoot.AddEngine(engine);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool ExistsEventHandler(string name)
|
||||
{
|
||||
return _eventHandlers.ContainsKey(name);
|
||||
}
|
||||
|
||||
public static bool ExistsEventHandler(IEventHandlerEngine engine)
|
||||
{
|
||||
return ExistsEventHandler(engine.Name);
|
||||
}
|
||||
|
||||
public static IEventHandlerEngine GetEventHandler(string name)
|
||||
{
|
||||
return _eventHandlers[name];
|
||||
}
|
||||
|
||||
public static string[] GetEventHandlerNames()
|
||||
{
|
||||
return _eventHandlers.Keys.ToArray();
|
||||
}
|
||||
|
||||
public static void RemoveEventHandler(string name)
|
||||
{
|
||||
_eventHandlers.Remove(name);
|
||||
}
|
||||
|
||||
// event emitter management
|
||||
|
||||
public static void AddEventEmitter(IEventEmitterEngine engine)
|
||||
{
|
||||
if (ExistsEventEmitter(engine))
|
||||
{
|
||||
throw new EventAlreadyExistsException($"IEventEmitterEngine {engine.Name} already exists");
|
||||
}
|
||||
_eventEmitters[engine.Name] = engine;
|
||||
if (_lastEngineRoot != null)
|
||||
{
|
||||
Logging.MetaDebugLog($"Registering IEventEmitterEngine {engine.Name}");
|
||||
_lastEngineRoot.AddEngine(engine);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool ExistsEventEmitter(string name)
|
||||
{
|
||||
return _eventEmitters.ContainsKey(name);
|
||||
}
|
||||
|
||||
public static bool ExistsEventEmitter(IEventEmitterEngine engine)
|
||||
{
|
||||
return ExistsEventEmitter(engine.Name);
|
||||
}
|
||||
|
||||
public static IEventEmitterEngine GetEventEmitter(string name)
|
||||
{
|
||||
return _eventEmitters[name];
|
||||
}
|
||||
|
||||
public static string[] GetEventEmitterNames()
|
||||
{
|
||||
return _eventEmitters.Keys.ToArray();
|
||||
}
|
||||
|
||||
public static void RemoveEventEmitter(string name)
|
||||
{
|
||||
if (_eventEmitters[name].isRemovable)
|
||||
{
|
||||
_eventEmitters.Remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
public static void RegisterEngines(EnginesRoot enginesRoot)
|
||||
{
|
||||
_lastEngineRoot = enginesRoot;
|
||||
// Register handlers before emitters so no events are missed
|
||||
var entityFactory = enginesRoot.GenerateEntityFactory();
|
||||
foreach (var key in _eventHandlers.Keys)
|
||||
{
|
||||
Logging.MetaDebugLog($"Registering IEventHandlerEngine {_eventHandlers[key].Name}");
|
||||
enginesRoot.AddEngine(_eventHandlers[key]);
|
||||
}
|
||||
foreach (var key in _eventEmitters.Keys)
|
||||
{
|
||||
Logging.MetaDebugLog($"Registering IEventEmitterEngine {_eventEmitters[key].Name}");
|
||||
_eventEmitters[key].Factory = entityFactory;
|
||||
enginesRoot.AddEngine(_eventEmitters[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GamecraftModdingAPI.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Built-in event types.
|
||||
/// These are configured to fire when the API is initialized.
|
||||
/// </summary>
|
||||
public enum EventType
|
||||
{
|
||||
ApplicationInitialized,
|
||||
Menu,
|
||||
MenuSwitchedTo,
|
||||
Game,
|
||||
GameReloaded,
|
||||
GameSwitchedTo,
|
||||
SimulationSwitchedTo,
|
||||
BuildSwitchedTo
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using HarmonyLib;
|
||||
using RobocraftX.CR.MainGame;
|
||||
using Svelto.ECS;
|
||||
using Unity.Entities;
|
||||
|
||||
using GamecraftModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftModdingAPI.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Patch of RobocraftX.FullGameCompositionRoot.ActivateGame()
|
||||
/// </summary>
|
||||
[Obsolete]
|
||||
[HarmonyPatch]
|
||||
class GameActivatedComposePatch
|
||||
{
|
||||
public static bool IsGameSwitching = false;
|
||||
|
||||
public static bool IsGameReloading = false;
|
||||
|
||||
public static void Postfix(ref object contextHolder, ref EnginesRoot enginesRoot, World physicsWorld)
|
||||
{
|
||||
// register custom game engines
|
||||
GameEngineManager.RegisterEngines(enginesRoot);
|
||||
// initialize AsyncUtils
|
||||
AsyncUtils.Setup(enginesRoot);
|
||||
// A new EnginesRoot is always created when ActivateGame is called
|
||||
// so all event emitters and handlers must be re-registered.
|
||||
EventManager.RegisterEngines(enginesRoot);
|
||||
Logging.Log("Dispatching Game Activated event");
|
||||
EventManager.GetEventEmitter("GamecraftModdingAPIGameActivatedEventEmitter").Emit();
|
||||
if (IsGameSwitching)
|
||||
{
|
||||
IsGameSwitching = false;
|
||||
Logging.Log("Dispatching Game Switched To event");
|
||||
EventManager.GetEventEmitter("GamecraftModdingAPIGameSwitchedToEventEmitter").Emit();
|
||||
}
|
||||
if (IsGameReloading)
|
||||
{
|
||||
IsGameReloading = false;
|
||||
Logging.Log("Dispatching Game Reloaded event");
|
||||
EventManager.GetEventEmitter("GamecraftModdingAPIGameReloadedEventEmitter").Emit();
|
||||
}
|
||||
}
|
||||
|
||||
public static MethodBase TargetMethod()
|
||||
{
|
||||
return typeof(MainGameCompositionRoot).GetMethods().First(m => m.Name == "Compose")
|
||||
.MakeGenericMethod(typeof(object));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using HarmonyLib;
|
||||
using RobocraftX;
|
||||
|
||||
using GamecraftModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftModdingAPI.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Patch of RobocraftX.FullGameCompositionRoot.ReloadGame()
|
||||
/// </summary>
|
||||
[Obsolete]
|
||||
[HarmonyPatch(typeof(FullGameCompositionRoot), "ReloadGame")]
|
||||
class GameReloadedPatch
|
||||
{
|
||||
public static void Postfix()
|
||||
{
|
||||
GameActivatedComposePatch.IsGameReloading = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
using System;
|
||||
|
||||
using Unity.Jobs;
|
||||
using RobocraftX.SimulationModeState;
|
||||
using RobocraftX.StateSync;
|
||||
using Svelto.ECS;
|
||||
|
||||
using GamecraftModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftModdingAPI.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Event emitter engine for switching to to build mode.
|
||||
/// </summary>
|
||||
[Obsolete]
|
||||
public class GameStateBuildEmitterEngine : IEventEmitterEngine, IUnorderedInitializeOnTimeStoppedModeEntered
|
||||
{
|
||||
public string Name { get; } = "GamecraftModdingAPIGameStateBuildEventEmitter" ;
|
||||
|
||||
public EntitiesDB entitiesDB { set; private get; }
|
||||
|
||||
public int type { get; } = (int)EventType.BuildSwitchedTo;
|
||||
|
||||
public bool isRemovable { get; } = false;
|
||||
|
||||
public IEntityFactory Factory { set; private get; }
|
||||
|
||||
public void Dispose() { }
|
||||
|
||||
public void Emit()
|
||||
{
|
||||
Logging.Log("Dispatching Build Switched To event");
|
||||
if (Factory == null) { return; }
|
||||
Factory.BuildEntity<ModEventEntityDescriptor>(ApiExclusiveGroups.eventID++, ApiExclusiveGroups.eventsExclusiveGroup)
|
||||
.Init(new ModEventEntityStruct { type = type });
|
||||
}
|
||||
|
||||
public void EmitIfBuildMode()
|
||||
{
|
||||
//Logging.MetaDebugLog($"nextSimulationMode: {entitiesDB.QueryUniqueEntity<SimulationModeStateEntityStruct>(SimulationModeStateExclusiveGroups.GAME_STATE_GROUP).nextSimulationMode}");
|
||||
if (entitiesDB.QueryUniqueEntity<SimulationModeStateEntityStruct>(SimulationModeStateExclusiveGroups.GAME_STATE_GROUP).nextSimulationMode == SimulationMode.TimeStopped)
|
||||
{
|
||||
Emit();
|
||||
}
|
||||
}
|
||||
|
||||
public JobHandle OnInitializeTimeStoppedMode(JobHandle inputDeps)
|
||||
{
|
||||
Emit();
|
||||
return inputDeps;
|
||||
}
|
||||
|
||||
public void Ready() { }
|
||||
}
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
using System;
|
||||
|
||||
using Unity.Jobs;
|
||||
using RobocraftX.SimulationModeState;
|
||||
using RobocraftX.StateSync;
|
||||
using Svelto.ECS;
|
||||
|
||||
using GamecraftModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftModdingAPI.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Event emitter engine for switching to simulation mode.
|
||||
/// </summary>
|
||||
[Obsolete]
|
||||
public class GameStateSimulationEmitterEngine : IEventEmitterEngine, IUnorderedInitializeOnTimeRunningModeEntered
|
||||
{
|
||||
public string Name { get; } = "GamecraftModdingAPIGameStateSimulationEventEmitter" ;
|
||||
|
||||
public EntitiesDB entitiesDB { set; private get; }
|
||||
|
||||
public int type { get; } = (int)EventType.SimulationSwitchedTo;
|
||||
|
||||
public bool isRemovable { get; } = false;
|
||||
|
||||
public IEntityFactory Factory { set; private get; }
|
||||
|
||||
public void Dispose() { }
|
||||
|
||||
public void Emit()
|
||||
{
|
||||
Logging.Log("Dispatching Simulation Switched To event");
|
||||
if (Factory == null) { return; }
|
||||
Factory.BuildEntity<ModEventEntityDescriptor>(ApiExclusiveGroups.eventID++, ApiExclusiveGroups.eventsExclusiveGroup)
|
||||
.Init(new ModEventEntityStruct { type = type });
|
||||
}
|
||||
|
||||
public void EmitIfSimMode()
|
||||
{
|
||||
if (entitiesDB.QueryUniqueEntity<SimulationModeStateEntityStruct>(SimulationModeStateExclusiveGroups.GAME_STATE_GROUP).nextSimulationMode == SimulationMode.TimeRunning)
|
||||
{
|
||||
Emit();
|
||||
}
|
||||
}
|
||||
|
||||
public JobHandle OnInitializeTimeRunningMode(JobHandle inputDeps)
|
||||
{
|
||||
Emit();
|
||||
return inputDeps;
|
||||
}
|
||||
|
||||
public void Ready() { }
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Reflection;
|
||||
|
||||
using HarmonyLib;
|
||||
using RobocraftX;
|
||||
using RobocraftX.CR.MainGame;
|
||||
using Svelto.ECS;
|
||||
|
||||
using GamecraftModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftModdingAPI.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Patch of RobocraftX.FullGameCompositionRoot.ActivateGame()
|
||||
/// (scheduled for execution during RobocraftX.FullGameCompositionRoot.SwitchToGame())
|
||||
/// </summary>
|
||||
[Obsolete]
|
||||
[HarmonyPatch(typeof(FullGameCompositionRoot), "SwitchToGame")]
|
||||
class GameSwitchedToPatch
|
||||
{
|
||||
public static void Prefix()
|
||||
{
|
||||
GameActivatedComposePatch.IsGameSwitching = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,166 +0,0 @@
|
|||
using System;
|
||||
|
||||
using Svelto.ECS;
|
||||
|
||||
namespace GamecraftModdingAPI.Events
|
||||
{
|
||||
[Obsolete]
|
||||
public class HandlerBuilder
|
||||
{
|
||||
private string name;
|
||||
|
||||
private int? type;
|
||||
|
||||
private Action<EntitiesDB> activated;
|
||||
|
||||
private Action<EntitiesDB> destroyed;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new event handler builder.
|
||||
/// </summary>
|
||||
public HandlerBuilder()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new event handler builder.
|
||||
/// This is equivalent to new <code>HandlerBuilder().Name(name)</code>
|
||||
/// </summary>
|
||||
/// <param name="name">The handler name.</param>
|
||||
public HandlerBuilder(string name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create and return an event handler builder.
|
||||
/// </summary>
|
||||
/// <returns>The builder.</returns>
|
||||
public static HandlerBuilder Builder()
|
||||
{
|
||||
return new HandlerBuilder();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create and return an event handler builder.
|
||||
/// This is equivalent to <code>Builder().Name(name)</code>
|
||||
/// </summary>
|
||||
/// <returns>The builder.</returns>
|
||||
/// <param name="name">The handler name.</param>
|
||||
public static HandlerBuilder Builder(string name)
|
||||
{
|
||||
return new HandlerBuilder(name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Name the event handler.
|
||||
/// </summary>
|
||||
/// <returns>The builder.</returns>
|
||||
/// <param name="name">The event handler name.</param>
|
||||
public HandlerBuilder Name(string name)
|
||||
{
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the action to perform on when the activated event occurs.
|
||||
/// </summary>
|
||||
/// <returns>The builder.</returns>
|
||||
/// <param name="action">The activated event action.</param>
|
||||
public HandlerBuilder OnActivation(Action action)
|
||||
{
|
||||
return OnActivation((_) => { action(); });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the action to perform on when the activated event occurs.
|
||||
/// </summary>
|
||||
/// <returns>The builder.</returns>
|
||||
/// <param name="action">The activated event action.</param>
|
||||
public HandlerBuilder OnActivation(Action<EntitiesDB> action)
|
||||
{
|
||||
this.activated = action;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the action to perform when the destroyed event occurs.
|
||||
/// </summary>
|
||||
/// <returns>The builder.</returns>
|
||||
/// <param name="action">The destroyed event action.</param>
|
||||
public HandlerBuilder OnDestruction(Action action)
|
||||
{
|
||||
return OnDestruction((_) => { action(); });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the action to perform when the destroyed event occurs.
|
||||
/// </summary>
|
||||
/// <returns>The builder.</returns>
|
||||
/// <param name="action">The destroyed event action.</param>
|
||||
public HandlerBuilder OnDestruction(Action<EntitiesDB> action)
|
||||
{
|
||||
this.destroyed = action;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the type of event to handle.
|
||||
/// </summary>
|
||||
/// <returns>The builder.</returns>
|
||||
/// <param name="eventType">The event type.</param>
|
||||
public HandlerBuilder Handle(EventType eventType)
|
||||
{
|
||||
return Handle((int)eventType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the type of event to handle.
|
||||
/// </summary>
|
||||
/// <returns>The builder.</returns>
|
||||
/// <param name="eventType">The event type.</param>
|
||||
public HandlerBuilder Handle(int eventType)
|
||||
{
|
||||
this.type = eventType;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Build the event handler.
|
||||
/// </summary>
|
||||
/// <returns>The event handler.</returns>
|
||||
/// <param name="register">Automatically register the event handler with EventManager.AddEventHandler().</param>
|
||||
public IEventHandlerEngine Build(bool register = true)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
throw new EventParameterMissingException("Event handler name must be defined before Build() is called");
|
||||
}
|
||||
if (activated == null && destroyed == null)
|
||||
{
|
||||
throw new EventParameterMissingException("Event handler destruction or activated event action must be defined before Build() is called");
|
||||
}
|
||||
if (!type.HasValue)
|
||||
{
|
||||
throw new EventParameterMissingException("Event handler event type must be defined before Build() is called");
|
||||
}
|
||||
Action<EntitiesDB> validActivated = activated;
|
||||
if (validActivated == null)
|
||||
{
|
||||
validActivated = (_) => { };
|
||||
}
|
||||
Action<EntitiesDB> validDestroyed = destroyed;
|
||||
if (validDestroyed == null)
|
||||
{
|
||||
validDestroyed = (_) => { };
|
||||
}
|
||||
SimpleEventHandlerEngine result = new SimpleEventHandlerEngine(validActivated, validDestroyed, type.Value, name);
|
||||
if (register)
|
||||
{
|
||||
EventManager.AddEventHandler(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Svelto.ECS;
|
||||
|
||||
using GamecraftModdingAPI.Engines;
|
||||
|
||||
namespace GamecraftModdingAPI.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Engine interface to create a ModEventEntityStruct in entitiesDB when a specific event occurs.
|
||||
/// </summary>
|
||||
[Obsolete]
|
||||
public interface IEventEmitterEngine : IFactoryEngine
|
||||
{
|
||||
/// <summary>
|
||||
/// Emit the event. (Optional)
|
||||
/// </summary>
|
||||
void Emit();
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.Internal;
|
||||
|
||||
using GamecraftModdingAPI.Engines;
|
||||
|
||||
namespace GamecraftModdingAPI.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Engine interface to handle ModEventEntityStruct events emitted by IEventEmitterEngines.
|
||||
/// </summary>
|
||||
[Obsolete]
|
||||
public interface IEventHandlerEngine : IReactionaryEngine<ModEventEntityStruct>
|
||||
{
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using HarmonyLib;
|
||||
using RobocraftX;
|
||||
using Svelto.ECS;
|
||||
|
||||
using GamecraftModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftModdingAPI.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Patch of RobocraftX.FullGameCompositionRoot.ActivateMenu()
|
||||
/// </summary>
|
||||
[Obsolete]
|
||||
[HarmonyPatch(typeof(FullGameCompositionRoot), "ActivateMenu")]
|
||||
class MenuActivatedPatch
|
||||
{
|
||||
|
||||
private static bool firstLoad = true;
|
||||
public static void Postfix(ref EnginesRoot ____frontEndEnginesRoot, FullGameCompositionRoot __instance)
|
||||
{
|
||||
// register custom menu engines
|
||||
MenuEngineManager.RegisterEngines(____frontEndEnginesRoot);
|
||||
// A new EnginesRoot is always created when ActivateMenu is called
|
||||
// so all event emitters and handlers must be re-registered.
|
||||
EventManager.RegisterEngines(____frontEndEnginesRoot);
|
||||
if (firstLoad)
|
||||
{
|
||||
firstLoad = false;
|
||||
FullGameFields.Init(__instance);
|
||||
//Application.Application.SetFullGameCompositionRoot(__instance);
|
||||
Logging.Log("Dispatching App Init event");
|
||||
EventManager.GetEventEmitter("GamecraftModdingAPIApplicationInitializedEventEmitter").Emit();
|
||||
}
|
||||
Logging.Log("Dispatching Menu Activated event");
|
||||
EventManager.GetEventEmitter("GamecraftModdingAPIMenuActivatedEventEmitter").Emit();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using HarmonyLib;
|
||||
using RobocraftX;
|
||||
using Svelto.ECS;
|
||||
|
||||
using GamecraftModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftModdingAPI.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Patch of RobocraftX.FullGameCompositionRoot.SwitchToMenu()
|
||||
/// </summary>
|
||||
[Obsolete]
|
||||
[HarmonyPatch(typeof(FullGameCompositionRoot), "SwitchToMenu")]
|
||||
class MenuSwitchedToPatch
|
||||
{
|
||||
public static void Postfix()
|
||||
{
|
||||
// Event emitters and handlers should already be registered by MenuActivated event
|
||||
Logging.Log("Dispatching Menu Switched To event");
|
||||
EventManager.GetEventEmitter("GamecraftModdingAPIMenuSwitchedToEventEmitter").Emit();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Svelto.ECS;
|
||||
|
||||
namespace GamecraftModdingAPI.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// EntityDescriptor for creating ModEventEntityStructs
|
||||
/// </summary>
|
||||
public class ModEventEntityDescriptor : GenericEntityDescriptor<ModEventEntityStruct>
|
||||
{
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Svelto.ECS;
|
||||
|
||||
namespace GamecraftModdingAPI.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// The event entity struct
|
||||
/// </summary>
|
||||
public struct ModEventEntityStruct : IEntityComponent, INeedEGID
|
||||
{
|
||||
/// <summary>
|
||||
/// The type of event that has been emitted
|
||||
/// </summary>
|
||||
public int type;
|
||||
|
||||
public EGID ID { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Svelto.ECS;
|
||||
using GamecraftModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftModdingAPI.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// A simple implementation of IEventEmitterEngine sufficient for most uses
|
||||
/// </summary>
|
||||
[Obsolete]
|
||||
public class SimpleEventEmitterEngine : IEventEmitterEngine
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public int type { get; set; }
|
||||
|
||||
public bool isRemovable { get; }
|
||||
|
||||
public IEntityFactory Factory { private get; set; }
|
||||
|
||||
public EntitiesDB entitiesDB { set; private get; }
|
||||
|
||||
public void Ready() { }
|
||||
|
||||
/// <summary>
|
||||
/// Emit the event
|
||||
/// </summary>
|
||||
public void Emit()
|
||||
{
|
||||
Factory.BuildEntity<ModEventEntityDescriptor>(ApiExclusiveGroups.eventID++, ApiExclusiveGroups.eventsExclusiveGroup)
|
||||
.Init(new ModEventEntityStruct { type = type });
|
||||
}
|
||||
|
||||
public void Dispose() { }
|
||||
|
||||
/// <summary>
|
||||
/// Construct the engine
|
||||
/// </summary>
|
||||
/// <param name="type">The EventType to use for ModEventEntityStruct.type</param>
|
||||
/// <param name="name">The name of this engine</param>
|
||||
/// <param name="isRemovable">Will removing this engine not break your code?</param>
|
||||
public SimpleEventEmitterEngine(EventType type, string name, bool isRemovable = true)
|
||||
{
|
||||
this.type = (int)type;
|
||||
this.Name = name;
|
||||
this.isRemovable = isRemovable;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct the engine
|
||||
/// </summary>
|
||||
/// <param name="type">The object to use for ModEventEntityStruct.type</param>
|
||||
/// <param name="name">The name of this engine</param>
|
||||
/// <param name="isRemovable">Will removing this engine not break your code?</param>
|
||||
public SimpleEventEmitterEngine(int type, string name, bool isRemovable = true)
|
||||
{
|
||||
this.type = type;
|
||||
this.Name = name;
|
||||
this.isRemovable = isRemovable;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,146 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Svelto.ECS;
|
||||
|
||||
using GamecraftModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftModdingAPI.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// A simple implementation of IEventHandlerEngine sufficient for most uses
|
||||
/// </summary>
|
||||
[Obsolete]
|
||||
public class SimpleEventHandlerEngine : IEventHandlerEngine
|
||||
{
|
||||
public int type { get; set; }
|
||||
public string Name { get; set; }
|
||||
|
||||
private bool isActivated = false;
|
||||
|
||||
private bool jankActivateFix = false;
|
||||
|
||||
private bool jankDestroyFix = false;
|
||||
|
||||
private readonly Action<EntitiesDB> onActivated;
|
||||
|
||||
private readonly Action<EntitiesDB> onDestroyed;
|
||||
|
||||
public EntitiesDB entitiesDB { set; private get; }
|
||||
|
||||
public bool isRemovable => true;
|
||||
|
||||
public void Add(ref ModEventEntityStruct entityView, EGID egid)
|
||||
{
|
||||
if (entityView.type.Equals(this.type))
|
||||
{
|
||||
jankActivateFix = !jankActivateFix;
|
||||
if (jankActivateFix) return;
|
||||
isActivated = true;
|
||||
onActivatedInvokeCatchError(entitiesDB);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Manually activate the EventHandler.
|
||||
/// Once activated, the next remove event will not be ignored.
|
||||
/// </summary>
|
||||
/// <param name="handle">Whether to invoke the activated action</param>
|
||||
public void Activate(bool handle = false)
|
||||
{
|
||||
isActivated = true;
|
||||
if (handle && entitiesDB != null)
|
||||
{
|
||||
onActivatedInvokeCatchError(entitiesDB);
|
||||
}
|
||||
}
|
||||
|
||||
public void Deactivate()
|
||||
{
|
||||
isActivated = false;
|
||||
}
|
||||
|
||||
public void Ready() { }
|
||||
|
||||
public void Remove(ref ModEventEntityStruct entityView, EGID egid)
|
||||
{
|
||||
if (entityView.type.Equals(this.type) && isActivated)
|
||||
{
|
||||
jankDestroyFix = !jankDestroyFix;
|
||||
if (jankDestroyFix) return;
|
||||
isActivated = false;
|
||||
onDestroyedInvokeCatchError(entitiesDB);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (isActivated)
|
||||
{
|
||||
isActivated = false;
|
||||
onDestroyedInvokeCatchError(entitiesDB);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct the engine
|
||||
/// </summary>
|
||||
/// <param name="activated">The operation to do when the event is created</param>
|
||||
/// <param name="removed">The operation to do when the event is destroyed (if applicable)</param>
|
||||
/// <param name="type">The type of event to handle</param>
|
||||
/// <param name="name">The name of the engine</param>
|
||||
/// <param name="simple">A useless parameter to use to avoid Python overload resolution errors</param>
|
||||
public SimpleEventHandlerEngine(Action activated, Action removed, int type, string name, bool simple = true)
|
||||
: this((EntitiesDB _) => { activated.Invoke(); }, (EntitiesDB _) => { removed.Invoke(); }, type, name) { }
|
||||
|
||||
/// <summary>
|
||||
/// Construct the engine
|
||||
/// </summary>
|
||||
/// <param name="activated">The operation to do when the event is created</param>
|
||||
/// <param name="removed">The operation to do when the event is destroyed (if applicable)</param>
|
||||
/// <param name="type">The type of event to handler</param>
|
||||
/// <param name="name">The name of the engine</param>
|
||||
public SimpleEventHandlerEngine(Action<EntitiesDB> activated, Action<EntitiesDB> removed, int type, string name)
|
||||
{
|
||||
this.type = type;
|
||||
this.Name = name;
|
||||
this.onActivated = activated;
|
||||
this.onDestroyed = removed;
|
||||
}
|
||||
|
||||
private void onActivatedInvokeCatchError(EntitiesDB _entitiesDB)
|
||||
{
|
||||
try
|
||||
{
|
||||
onActivated.Invoke(_entitiesDB);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
EventRuntimeException wrappedException = new EventRuntimeException($"EventHandler {Name} threw an exception when activated", e);
|
||||
Logging.LogWarning(wrappedException.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private void onDestroyedInvokeCatchError(EntitiesDB _entitiesDB)
|
||||
{
|
||||
try
|
||||
{
|
||||
onDestroyed.Invoke(_entitiesDB);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
EventRuntimeException wrappedException = new EventRuntimeException($"EventHandler {Name} threw an exception when destroyed", e);
|
||||
Logging.LogWarning(wrappedException.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public SimpleEventHandlerEngine(Action activated, Action removed, EventType type, string name, bool simple = true)
|
||||
: this((EntitiesDB _) => { activated.Invoke(); }, (EntitiesDB _) => { removed.Invoke(); }, (int)type, name) { }
|
||||
|
||||
public SimpleEventHandlerEngine(Action<EntitiesDB> activated, Action<EntitiesDB> removed, EventType type, string name, bool simple = true)
|
||||
: this(activated, removed, (int)type, name) { }
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,24 +0,0 @@
|
|||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace GamecraftModdingAPI
|
||||
{
|
||||
public class GamecraftModdingAPIException : Exception
|
||||
{
|
||||
public GamecraftModdingAPIException()
|
||||
{
|
||||
}
|
||||
|
||||
public GamecraftModdingAPIException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public GamecraftModdingAPIException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
protected GamecraftModdingAPIException(SerializationInfo info, StreamingContext context) : base(info, context)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
using System;
|
||||
|
||||
using RobocraftX.Common.Input;
|
||||
using RobocraftX.Players;
|
||||
using Svelto.ECS;
|
||||
|
||||
using GamecraftModdingAPI.Utility;
|
||||
using GamecraftModdingAPI.Engines;
|
||||
|
||||
namespace GamecraftModdingAPI.Input
|
||||
{
|
||||
public class FakeInputEngine : IApiEngine
|
||||
{
|
||||
public string Name { get; } = "GamecraftModdingAPIFakeInputEngine";
|
||||
|
||||
public EntitiesDB entitiesDB { set; private get; }
|
||||
|
||||
public bool isRemovable => false;
|
||||
|
||||
public bool IsReady = false;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
IsReady = false;
|
||||
}
|
||||
|
||||
public void Ready()
|
||||
{
|
||||
IsReady = true;
|
||||
}
|
||||
|
||||
public bool SendCustomInput(LocalInputEntityStruct input, uint playerID, bool remote = false)
|
||||
{
|
||||
EGID egid = new EGID(playerID, remote ? InputExclusiveGroups.RemotePlayers : InputExclusiveGroups.LocalPlayers);
|
||||
if (entitiesDB.Exists<LocalInputEntityStruct>(egid))
|
||||
{
|
||||
ref LocalInputEntityStruct ies = ref entitiesDB.QueryEntity<LocalInputEntityStruct>(egid);
|
||||
ies = input;
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
|
||||
public LocalInputEntityStruct GetInput(uint playerID, bool remote = false)
|
||||
{
|
||||
EGID egid = new EGID(playerID, remote ? InputExclusiveGroups.RemotePlayers : InputExclusiveGroups.LocalPlayers);
|
||||
if (entitiesDB.Exists<LocalInputEntityStruct>(egid))
|
||||
{
|
||||
return entitiesDB.QueryEntity<LocalInputEntityStruct>(egid);
|
||||
}
|
||||
else return default(LocalInputEntityStruct);
|
||||
}
|
||||
|
||||
public ref LocalInputEntityStruct GetInputRef(uint playerID, bool remote = false)
|
||||
{
|
||||
EGID egid = new EGID(playerID, remote ? InputExclusiveGroups.RemotePlayers : InputExclusiveGroups.LocalPlayers);
|
||||
return ref entitiesDB.QueryEntity<LocalInputEntityStruct>(egid);
|
||||
}
|
||||
|
||||
public uint GetLocalPlayerID()
|
||||
{
|
||||
return LocalPlayerIDUtility.GetLocalPlayerID(entitiesDB);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
using System;
|
||||
|
||||
using RobocraftX.Common.Input;
|
||||
using RobocraftX.Multiplayer.Input;
|
||||
|
||||
using GamecraftModdingAPI.Blocks;
|
||||
using GamecraftModdingAPI.Utility;
|
||||
using HarmonyLib;
|
||||
|
||||
namespace GamecraftModdingAPI.Inventory
|
||||
{
|
||||
public static class Hotbar
|
||||
{
|
||||
private static readonly HotbarEngine hotbarEngine = new HotbarEngine();
|
||||
|
||||
/// <summary>
|
||||
/// Switch the block in the player's hand
|
||||
/// </summary>
|
||||
/// <param name="block">The block to switch to.</param>
|
||||
/// <param name="playerID">The player. Omit this to use the local player.</param>
|
||||
public static void EquipBlock(BlockIDs block, uint playerID = uint.MaxValue)
|
||||
{
|
||||
if (playerID == uint.MaxValue)
|
||||
{
|
||||
playerID = hotbarEngine.GetLocalPlayerID();
|
||||
}
|
||||
hotbarEngine.SelectBlock((int) block, playerID);
|
||||
// cubeSelectedByPick = true will crash the game
|
||||
// (this would be equivalent to mouse middle click pick block action)
|
||||
// reason: the game expects a Dictionary entry for the tweaked stats
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the block in the player's hand
|
||||
/// </summary>
|
||||
/// <returns>The equipped block.</returns>
|
||||
public static BlockIDs GetEquippedBlock()
|
||||
{
|
||||
return HotbarSlotSelectionHandlerEnginePatch.EquippedPartID;
|
||||
}
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
GameEngineManager.AddGameEngine(hotbarEngine);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
using System;
|
||||
|
||||
using RobocraftX.Character;
|
||||
using RobocraftX.GUI.Hotbar;
|
||||
using RobocraftX.Players;
|
||||
using RobocraftX.Common;
|
||||
using RobocraftX.Common.Input;
|
||||
using RobocraftX.Common.Players;
|
||||
using Svelto.ECS;
|
||||
|
||||
using GamecraftModdingAPI.Blocks;
|
||||
using GamecraftModdingAPI.Utility;
|
||||
using GamecraftModdingAPI.Engines;
|
||||
|
||||
namespace GamecraftModdingAPI.Inventory
|
||||
{
|
||||
public class HotbarEngine : IApiEngine
|
||||
{
|
||||
public string Name { get; } = "GamecraftModdingAPIHotbarGameEngine";
|
||||
|
||||
public EntitiesDB entitiesDB { set; private get; }
|
||||
|
||||
public bool isRemovable => false;
|
||||
|
||||
public bool IsInGame = false;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
IsInGame = false;
|
||||
}
|
||||
|
||||
public void Ready()
|
||||
{
|
||||
IsInGame = true;
|
||||
}
|
||||
|
||||
public bool SelectBlock(int block, uint playerID, bool cubeSelectedByPick = false)
|
||||
{
|
||||
var inputs = entitiesDB.QueryEntities<LocalInputEntityStruct>(InputExclusiveGroups.LocalPlayers).ToBuffer();
|
||||
if (inputs.count == 0) return false;
|
||||
for (int i = 0; i < inputs.count; i++)
|
||||
{
|
||||
if (inputs.buffer[i].ID.entityID == playerID) {
|
||||
inputs.buffer[i].cubeSelectedByPick = cubeSelectedByPick;
|
||||
inputs.buffer[i].selectedCube = block;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// TODO: expose the rest of the input functionality
|
||||
return false;
|
||||
}
|
||||
|
||||
public uint GetLocalPlayerID()
|
||||
{
|
||||
return LocalPlayerIDUtility.GetLocalPlayerID(entitiesDB);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,446 +0,0 @@
|
|||
using System;
|
||||
using Gamecraft.GUI.Blueprints;
|
||||
using Unity.Mathematics;
|
||||
using RobocraftX.Common;
|
||||
using RobocraftX.Common.Players;
|
||||
using Svelto.ECS;
|
||||
|
||||
using GamecraftModdingAPI.Players;
|
||||
using GamecraftModdingAPI.Blocks;
|
||||
|
||||
namespace GamecraftModdingAPI
|
||||
{
|
||||
/// <summary>
|
||||
/// An in-game player character. Any Leo you see is a player.
|
||||
/// </summary>
|
||||
public class Player : IEquatable<Player>, IEquatable<EGID>
|
||||
{
|
||||
// static functionality
|
||||
private static PlayerEngine playerEngine = new PlayerEngine();
|
||||
private static Player localPlayer;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the specified player exists.
|
||||
/// </summary>
|
||||
/// <returns>Whether the player exists.</returns>
|
||||
/// <param name="player">Player type.</param>
|
||||
public static bool Exists(PlayerType player)
|
||||
{
|
||||
switch (player)
|
||||
{
|
||||
case PlayerType.Remote:
|
||||
return playerEngine.GetRemotePlayer() != uint.MaxValue;
|
||||
case PlayerType.Local:
|
||||
return playerEngine.GetLocalPlayer() != uint.MaxValue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the specified player exists.
|
||||
/// </summary>
|
||||
/// <returns>Whether the player exists.</returns>
|
||||
/// <param name="player">The player's unique identifier.</param>
|
||||
public static bool Exists(uint player)
|
||||
{
|
||||
return playerEngine.ExistsById(player);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The amount of Players in the current game.
|
||||
/// </summary>
|
||||
/// <returns>The count.</returns>
|
||||
public static uint Count()
|
||||
{
|
||||
return (uint) playerEngine.GetAllPlayerCount();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current player belonging to this client.
|
||||
/// </summary>
|
||||
public static Player LocalPlayer
|
||||
{
|
||||
get
|
||||
{
|
||||
if (localPlayer == null || localPlayer.Id != playerEngine.GetLocalPlayer())
|
||||
localPlayer = new Player(PlayerType.Local);
|
||||
return localPlayer;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:GamecraftModdingAPI.Player"/> class.
|
||||
/// </summary>
|
||||
/// <param name="id">The player's unique identifier.</param>
|
||||
public Player(uint id)
|
||||
{
|
||||
this.Id = id;
|
||||
if (!Exists(id))
|
||||
{
|
||||
throw new PlayerNotFoundException($"No player with id {id} exists");
|
||||
}
|
||||
this.Type = playerEngine.GetLocalPlayer() == id ? PlayerType.Local : PlayerType.Remote;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:GamecraftModdingAPI.Player"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player type. Chooses the first available player matching the criteria.</param>
|
||||
public Player(PlayerType player)
|
||||
{
|
||||
switch (player)
|
||||
{
|
||||
case PlayerType.Local:
|
||||
this.Id = playerEngine.GetLocalPlayer();
|
||||
break;
|
||||
case PlayerType.Remote:
|
||||
this.Id = playerEngine.GetRemotePlayer();
|
||||
break;
|
||||
}
|
||||
if (this.Id == uint.MaxValue)
|
||||
{
|
||||
throw new PlayerNotFoundException($"No player of {player} type exists");
|
||||
}
|
||||
this.Type = player;
|
||||
}
|
||||
|
||||
// object fields & properties
|
||||
|
||||
/// <summary>
|
||||
/// The player's type.
|
||||
/// The player type is always relative to the current client, not the game host.
|
||||
/// </summary>
|
||||
/// <value>The enumerated player type.</value>
|
||||
public PlayerType Type { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The player's unique identifier.
|
||||
/// </summary>
|
||||
/// <value>The identifier.</value>
|
||||
public uint Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The player's current position.
|
||||
/// </summary>
|
||||
/// <value>The position.</value>
|
||||
public float3 Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return playerEngine.GetLocation(Id);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
playerEngine.SetLocation(Id, value, false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The player's current rotation.
|
||||
/// </summary>
|
||||
/// <value>The rotation.</value>
|
||||
public float3 Rotation
|
||||
{
|
||||
get
|
||||
{
|
||||
return playerEngine.GetRotation(Id);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
playerEngine.SetRotation(Id, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The player's current velocity.
|
||||
/// </summary>
|
||||
/// <value>The velocity.</value>
|
||||
public float3 Velocity
|
||||
{
|
||||
get
|
||||
{
|
||||
return playerEngine.GetLinearVelocity(Id);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
playerEngine.SetLinearVelocity(Id, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The player's current angular velocity.
|
||||
/// </summary>
|
||||
/// <value>The angular velocity.</value>
|
||||
public float3 AngularVelocity
|
||||
{
|
||||
get
|
||||
{
|
||||
return playerEngine.GetAngularVelocity(Id);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
playerEngine.SetAngularVelocity(Id, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The player's mass.
|
||||
/// </summary>
|
||||
/// <value>The mass.</value>
|
||||
public float Mass
|
||||
{
|
||||
get
|
||||
{
|
||||
return 1f / playerEngine.GetMass(Id).InverseMass;
|
||||
}
|
||||
|
||||
// FIXME: Setting mass doesn't do anything
|
||||
/*set
|
||||
{
|
||||
playerEngine.SetInverseMass(Id, 1f / value);
|
||||
}*/
|
||||
}
|
||||
|
||||
private float _ping = -1f;
|
||||
|
||||
/// <summary>
|
||||
/// The player's latest network ping time.
|
||||
/// </summary>
|
||||
/// <value>The ping (s).</value>
|
||||
public float Ping
|
||||
{
|
||||
get
|
||||
{
|
||||
float? temp = playerEngine.GetLastPingTime(Id, Type);
|
||||
if (temp.HasValue)
|
||||
{
|
||||
_ping = temp.Value;
|
||||
}
|
||||
return _ping;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The player's initial health when entering Simulation (aka Time Running) mode.
|
||||
/// </summary>
|
||||
/// <value>The initial health.</value>
|
||||
public float InitialHealth
|
||||
{
|
||||
get => playerEngine.GetInitialHealth(Id);
|
||||
|
||||
set
|
||||
{
|
||||
playerEngine.SetInitialHealth(Id, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The player's current health in Simulation (aka Time Running) mode.
|
||||
/// </summary>
|
||||
/// <value>The current health.</value>
|
||||
public float CurrentHealth
|
||||
{
|
||||
get => playerEngine.GetCurrentHealth(Id);
|
||||
|
||||
set
|
||||
{
|
||||
playerEngine.DamagePlayer(Id, CurrentHealth - value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether this <see cref="T:GamecraftModdingAPI.Player"/> is damageable.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if damageable; otherwise, <c>false</c>.</value>
|
||||
public bool Damageable
|
||||
{
|
||||
get => playerEngine.GetDamageable(Id);
|
||||
|
||||
set
|
||||
{
|
||||
playerEngine.SetDamageable(Id, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The player's lives when initially entering Simulation (aka Time Running) mode.
|
||||
/// </summary>
|
||||
/// <value>The initial lives.</value>
|
||||
public uint InitialLives
|
||||
{
|
||||
get => playerEngine.GetInitialLives(Id);
|
||||
|
||||
set => playerEngine.SetInitialLives(Id, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The player's current lives in Simulation (aka Time Running) mode.
|
||||
/// </summary>
|
||||
/// <value>The current lives.</value>
|
||||
public uint CurrentLives
|
||||
{
|
||||
get => playerEngine.GetCurrentLives(Id);
|
||||
|
||||
set => playerEngine.SetCurrentLives(Id, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the Game Over screen is displayed for the player.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if game over; otherwise, <c>false</c>.</value>
|
||||
public bool GameOver
|
||||
{
|
||||
get => playerEngine.GetGameOverScreen(Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the player is dead.
|
||||
/// If <c>true</c>, hopefully it was quick.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if dead; otherwise, <c>false</c>.</value>
|
||||
public bool Dead
|
||||
{
|
||||
get => playerEngine.IsDead(Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The player's selected block ID in their hand.
|
||||
/// </summary>
|
||||
/// <value>The selected block.</value>
|
||||
public BlockIDs SelectedBlock
|
||||
{
|
||||
get
|
||||
{
|
||||
return (BlockIDs)playerEngine.GetSelectedBlock(Id);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The player's selected block color in their hand.
|
||||
/// </summary>
|
||||
/// <value>The selected block's color.</value>
|
||||
public BlockColor SelectedColor
|
||||
{
|
||||
get
|
||||
{
|
||||
return new BlockColor(playerEngine.GetSelectedColor(Id));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The player's selected block colour in their hand.
|
||||
/// </summary>
|
||||
/// <value>The selected block's colour.</value>
|
||||
public BlockColor SelectedColour
|
||||
{
|
||||
get
|
||||
{
|
||||
return new BlockColor(playerEngine.GetSelectedColor(Id));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The player's selected blueprint in their hand. Set to null to clear. Dispose after usage.
|
||||
/// </summary>
|
||||
public Blueprint SelectedBlueprint
|
||||
{
|
||||
get => playerEngine.GetPlayerStruct(Id, out BlueprintInventoryItemEntityStruct biies)
|
||||
? new Blueprint(biies.blueprintResourceId)
|
||||
: null;
|
||||
set => BlockGroup._engine.SelectBlueprint(value?.Id ?? uint.MaxValue);
|
||||
}
|
||||
|
||||
// object methods
|
||||
|
||||
/// <summary>
|
||||
/// Teleport the player to the specified coordinates.
|
||||
/// </summary>
|
||||
/// <param name="x">The x coordinate.</param>
|
||||
/// <param name="y">The y coordinate.</param>
|
||||
/// <param name="z">The z coordinate.</param>
|
||||
/// <param name="relative">If set to <c>true</c> teleport relative to the player's current position.</param>
|
||||
/// <param name="exitSeat">If set to <c>true</c> exit any seat the player is in.</param>
|
||||
public void Teleport(float x, float y, float z, bool relative = true, bool exitSeat = true)
|
||||
{
|
||||
float3 location = new float3(x, y, z);
|
||||
if (relative)
|
||||
{
|
||||
location += playerEngine.GetLocation(Id);
|
||||
}
|
||||
playerEngine.SetLocation(Id, location, exitSeat: exitSeat);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the block the player is currently looking at in build mode.
|
||||
/// </summary>
|
||||
/// <param name="maxDistance">The maximum distance from the player (default is the player's building reach)</param>
|
||||
/// <returns>The block or null if not found</returns>
|
||||
public Block GetBlockLookedAt(float maxDistance = -1f)
|
||||
{
|
||||
var egid = playerEngine.GetThingLookedAt(Id, maxDistance);
|
||||
return egid.HasValue && egid.Value.groupID != CommonExclusiveGroups.SIMULATION_BODIES_GROUP
|
||||
? new Block(egid.Value)
|
||||
: null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the rigid body the player is currently looking at during simulation.
|
||||
/// </summary>
|
||||
/// <param name="maxDistance">The maximum distance from the player (default is the player's building reach)</param>
|
||||
/// <returns>The block or null if not found</returns>
|
||||
public SimBody GetSimBodyLookedAt(float maxDistance = -1f)
|
||||
{
|
||||
var egid = playerEngine.GetThingLookedAt(Id, maxDistance);
|
||||
return egid.HasValue && egid.Value.groupID == CommonExclusiveGroups.SIMULATION_BODIES_GROUP
|
||||
? new SimBody(egid.Value)
|
||||
: null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the blocks that are in the player's current selection.
|
||||
/// </summary>
|
||||
/// <returns>An array of blocks or an empty array</returns>
|
||||
public Block[] GetSelectedBlocks()
|
||||
{
|
||||
return playerEngine.GetSelectedBlocks(Id);
|
||||
}
|
||||
|
||||
public bool Equals(Player other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
return Id == other.Id;
|
||||
}
|
||||
|
||||
public bool Equals(EGID other)
|
||||
{
|
||||
return Id == other.entityID && other.groupID == (Type == PlayerType.Local
|
||||
? PlayersExclusiveGroups.LocalPlayers
|
||||
: PlayersExclusiveGroups.RemotePlayers);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
return Equals((Player) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return (int) Id;
|
||||
}
|
||||
|
||||
// internal methods
|
||||
|
||||
internal static void Init()
|
||||
{
|
||||
Utility.GameEngineManager.AddGameEngine(playerEngine);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,479 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
using RobocraftX.Character;
|
||||
using RobocraftX.Character.Movement;
|
||||
using RobocraftX.Common.Players;
|
||||
using RobocraftX.Common.Input;
|
||||
using RobocraftX.CR.MachineEditing.BoxSelect;
|
||||
using RobocraftX.Physics;
|
||||
using RobocraftX.Blocks.Ghost;
|
||||
using RobocraftX.Character.Camera;
|
||||
using RobocraftX.Character.Factories;
|
||||
using Gamecraft.GUI.HUDFeedbackBlocks;
|
||||
using Svelto.ECS;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Physics;
|
||||
using UnityEngine;
|
||||
|
||||
using GamecraftModdingAPI.Engines;
|
||||
using HarmonyLib;
|
||||
using RobocraftX.Common;
|
||||
using Svelto.ECS.DataStructures;
|
||||
|
||||
namespace GamecraftModdingAPI.Players
|
||||
{
|
||||
internal class PlayerEngine : IApiEngine, IFactoryEngine
|
||||
{
|
||||
public string Name { get; } = "GamecraftModdingAPIPlayerGameEngine";
|
||||
|
||||
public EntitiesDB entitiesDB { set; private get; }
|
||||
|
||||
public bool isRemovable => false;
|
||||
|
||||
public IEntityFactory Factory { set; private get; }
|
||||
|
||||
private bool isReady = false;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
isReady = false;
|
||||
}
|
||||
|
||||
public void Ready()
|
||||
{
|
||||
isReady = true;
|
||||
}
|
||||
|
||||
public uint GetLocalPlayer()
|
||||
{
|
||||
if (!isReady) return uint.MaxValue;
|
||||
var localPlayers = entitiesDB.QueryEntities<PlayerIDStruct>(PlayersExclusiveGroups.LocalPlayers).ToBuffer();
|
||||
if (localPlayers.count > 0)
|
||||
{
|
||||
return localPlayers.buffer[0].ID.entityID;
|
||||
}
|
||||
return uint.MaxValue;
|
||||
}
|
||||
|
||||
public uint GetRemotePlayer()
|
||||
{
|
||||
if (!isReady) return uint.MaxValue;
|
||||
var localPlayers = entitiesDB.QueryEntities<PlayerIDStruct>(PlayersExclusiveGroups.RemotePlayers).ToBuffer();
|
||||
if (localPlayers.count > 0)
|
||||
{
|
||||
return localPlayers.buffer[0].ID.entityID;
|
||||
}
|
||||
return uint.MaxValue;
|
||||
}
|
||||
|
||||
public long GetAllPlayerCount()
|
||||
{
|
||||
if (entitiesDB == null) return 0;
|
||||
long count = 0;
|
||||
foreach (ExclusiveGroupStruct eg in PlayersExclusiveGroups.AllPlayers)
|
||||
{
|
||||
count += entitiesDB.Count<PlayerIDStruct>(eg);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
public long GetLocalPlayerCount()
|
||||
{
|
||||
if (entitiesDB == null) return 0;
|
||||
return entitiesDB.Count<PlayerIDStruct>(PlayersExclusiveGroups.LocalPlayers);
|
||||
}
|
||||
|
||||
public long GetRemotePlayerCount()
|
||||
{
|
||||
if (entitiesDB == null) return 0;
|
||||
return entitiesDB.Count<PlayerIDStruct>(PlayersExclusiveGroups.RemotePlayers);
|
||||
}
|
||||
|
||||
public bool ExistsById(uint playerId)
|
||||
{
|
||||
if (entitiesDB == null) return false;
|
||||
return entitiesDB.Exists<PlayerIDStruct>(playerId, PlayersExclusiveGroups.LocalPlayers)
|
||||
|| entitiesDB.Exists<PlayerIDStruct>(playerId, PlayersExclusiveGroups.RemotePlayers);
|
||||
}
|
||||
|
||||
public float3 GetLocation(uint playerId)
|
||||
{
|
||||
if (entitiesDB == null) return float3.zero;
|
||||
ref var rbes = ref GetCharacterStruct<RigidBodyEntityStruct>(playerId, out bool exists);
|
||||
if (exists)
|
||||
{
|
||||
return rbes.position;
|
||||
}
|
||||
return float3.zero;
|
||||
}
|
||||
|
||||
public bool SetLocation(uint playerId, float3 location, bool exitSeat = true)
|
||||
{
|
||||
if (entitiesDB == null) return false;
|
||||
var characterGroups = CharacterExclusiveGroups.AllCharacters;
|
||||
for (int i = 0; i < characterGroups.count; i++)
|
||||
{
|
||||
EGID egid = new EGID(playerId, characterGroups[i]);
|
||||
if (entitiesDB.Exists<RigidBodyEntityStruct>(egid))
|
||||
{
|
||||
ref RigidBodyEntityStruct rbes = ref entitiesDB.QueryEntity<RigidBodyEntityStruct>(egid);
|
||||
if (characterGroups[i] == CharacterExclusiveGroups.InPilotSeatGroup && exitSeat)
|
||||
{
|
||||
entitiesDB.QueryEntity<CharacterPilotSeatEntityStruct>(egid).instantExit = true;
|
||||
entitiesDB.PublishEntityChange<CharacterPilotSeatEntityStruct>(egid);
|
||||
}
|
||||
rbes.position = location;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public float3 GetRotation(uint playerId)
|
||||
{
|
||||
if (entitiesDB == null) return float3.zero;
|
||||
ref var rbes = ref GetCharacterStruct<RigidBodyEntityStruct>(playerId, out bool exists);
|
||||
if (exists)
|
||||
{
|
||||
return ((Quaternion) rbes.rotation).eulerAngles;
|
||||
}
|
||||
return default(float3);
|
||||
}
|
||||
|
||||
public bool SetRotation(uint playerId, float3 value)
|
||||
{
|
||||
if (entitiesDB == null) return false;
|
||||
ref var rbes = ref GetCharacterStruct<RigidBodyEntityStruct>(playerId, out bool exists);
|
||||
if (exists)
|
||||
{
|
||||
Quaternion q = rbes.rotation;
|
||||
q.eulerAngles = value;
|
||||
rbes.rotation = q;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public float3 GetLinearVelocity(uint playerId)
|
||||
{
|
||||
if (entitiesDB == null) return float3.zero;
|
||||
ref var rbes = ref GetCharacterStruct<RigidBodyEntityStruct>(playerId, out bool exists);
|
||||
if (exists)
|
||||
{
|
||||
return rbes.velocity;
|
||||
}
|
||||
return float3.zero;
|
||||
}
|
||||
|
||||
public bool SetLinearVelocity(uint playerId, float3 value)
|
||||
{
|
||||
if (entitiesDB == null) return false;
|
||||
ref var rbes = ref GetCharacterStruct<RigidBodyEntityStruct>(playerId, out bool exists);
|
||||
if (exists)
|
||||
{
|
||||
rbes.velocity = value;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public float3 GetAngularVelocity(uint playerId)
|
||||
{
|
||||
if (entitiesDB == null) return float3.zero;
|
||||
ref var rbes = ref GetCharacterStruct<RigidBodyEntityStruct>(playerId, out bool exists);
|
||||
if (exists)
|
||||
{
|
||||
return rbes.angularVelocity;
|
||||
}
|
||||
return float3.zero;
|
||||
}
|
||||
|
||||
public bool SetAngularVelocity(uint playerId, float3 value)
|
||||
{
|
||||
if (entitiesDB == null) return false;
|
||||
ref var rbes = ref GetCharacterStruct<RigidBodyEntityStruct>(playerId, out bool exists);
|
||||
if (exists)
|
||||
{
|
||||
rbes.angularVelocity = value;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public PhysicsMass GetMass(uint playerId)
|
||||
{
|
||||
if (entitiesDB == null) return default(PhysicsMass);
|
||||
ref var rbes = ref GetCharacterStruct<RigidBodyEntityStruct>(playerId, out bool exists);
|
||||
if (exists)
|
||||
{
|
||||
return rbes.physicsMass;
|
||||
}
|
||||
return default(PhysicsMass);
|
||||
}
|
||||
|
||||
public bool SetInverseMass(uint playerId, float inverseMass)
|
||||
{
|
||||
if (entitiesDB == null) return false;
|
||||
ref var rbes = ref GetCharacterStruct<RigidBodyEntityStruct>(playerId, out bool exists);
|
||||
if (exists)
|
||||
{
|
||||
rbes.physicsMass.InverseInertia = inverseMass;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public float? GetLastPingTime(uint playerId, PlayerType type)
|
||||
{
|
||||
if (entitiesDB == null) return null;
|
||||
EGID egid = new EGID(playerId, PlayerGroupFromEnum(type));
|
||||
if (entitiesDB.Exists<PlayerNetworkStatsEntityStruct>(egid))
|
||||
{
|
||||
return entitiesDB.QueryEntity<PlayerNetworkStatsEntityStruct>(egid).lastPingTimeSinceLevelLoad;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public float GetInitialHealth(uint playerId)
|
||||
{
|
||||
if (entitiesDB == null) return 0;
|
||||
ref var c = ref GetCharacterStruct<CharacterHealthEntityStruct>(playerId, out bool exists);
|
||||
if (exists)
|
||||
{
|
||||
return c.initialHealth;
|
||||
}
|
||||
return -1f;
|
||||
}
|
||||
|
||||
public bool SetInitialHealth(uint playerId, float val)
|
||||
{
|
||||
if (entitiesDB == null) return false;
|
||||
ref var c = ref GetCharacterStruct<CharacterHealthEntityStruct>(playerId, out bool exists);
|
||||
if (exists)
|
||||
{
|
||||
c.initialHealth = val;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public float GetCurrentHealth(uint playerId)
|
||||
{
|
||||
if (entitiesDB == null) return 0;
|
||||
ref var c = ref GetCharacterStruct<CharacterHealthEntityStruct>(playerId, out bool exists);
|
||||
if (exists)
|
||||
{
|
||||
return c.currentHealth;
|
||||
}
|
||||
return -1f;
|
||||
}
|
||||
|
||||
public bool SetCurrentHealth(uint playerId, float val)
|
||||
{
|
||||
if (entitiesDB == null) return false;
|
||||
ref var c = ref GetCharacterStruct<CharacterHealthEntityStruct>(playerId, out bool exists);
|
||||
if (exists)
|
||||
{
|
||||
c.currentHealth = val;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool DamagePlayer(uint playerId, float amount)
|
||||
{
|
||||
if (entitiesDB == null) return false;
|
||||
return SetCurrentHealth(playerId, GetCurrentHealth(playerId) - amount);
|
||||
}
|
||||
|
||||
public bool GetDamageable(uint playerId)
|
||||
{
|
||||
if (entitiesDB == null) return false;
|
||||
ref var c = ref GetCharacterStruct<CharacterHealthEntityStruct>(playerId, out bool exists);
|
||||
if (exists)
|
||||
{
|
||||
return c.canTakeDamageStat;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool SetDamageable(uint playerId, bool val)
|
||||
{
|
||||
if (entitiesDB == null) return false;
|
||||
ref var ches = ref GetCharacterStruct<CharacterHealthEntityStruct>(playerId, out bool exists);
|
||||
if (exists)
|
||||
{
|
||||
ches.canTakeDamage = val;
|
||||
ches.canTakeDamage = val;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public uint GetInitialLives(uint playerId)
|
||||
{
|
||||
if (entitiesDB == null) return 0;
|
||||
ref var c = ref GetCharacterStruct<CharacterLivesEntityComponent>(playerId, out bool exists);
|
||||
if (exists)
|
||||
{
|
||||
return c.initialLives;
|
||||
}
|
||||
return uint.MaxValue;
|
||||
}
|
||||
|
||||
public bool SetInitialLives(uint playerId, uint val)
|
||||
{
|
||||
if (entitiesDB == null) return false;
|
||||
ref var c = ref GetCharacterStruct<CharacterLivesEntityComponent>(playerId, out bool exists);
|
||||
if (exists)
|
||||
{
|
||||
c.initialLives = val;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public uint GetCurrentLives(uint playerId)
|
||||
{
|
||||
if (entitiesDB == null) return 0;
|
||||
ref var c = ref GetCharacterStruct<CharacterLivesEntityComponent>(playerId, out bool exists);
|
||||
if (exists)
|
||||
{
|
||||
return c.currentLives;
|
||||
}
|
||||
return uint.MaxValue;
|
||||
}
|
||||
|
||||
public bool SetCurrentLives(uint playerId, uint val)
|
||||
{
|
||||
if (entitiesDB == null) return false;
|
||||
ref var c = ref GetCharacterStruct<CharacterLivesEntityComponent>(playerId, out bool exists);
|
||||
if (exists)
|
||||
{
|
||||
c.currentLives = val;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool GetGameOverScreen(uint playerId)
|
||||
{
|
||||
if (entitiesDB == null) return false;
|
||||
ref HudActivatedBlocksEntityStruct habes = ref entitiesDB.QueryEntity<HudActivatedBlocksEntityStruct>(HUDFeedbackBlocksGUIExclusiveGroups.GameOverHudEgid);
|
||||
NativeDynamicArrayCast<EGID> nativeDynamicArrayCast = new NativeDynamicArrayCast<EGID>(habes.activatedBlocksOrdered);
|
||||
return nativeDynamicArrayCast.count > 0;
|
||||
}
|
||||
|
||||
public bool IsDead(uint playerId)
|
||||
{
|
||||
if (entitiesDB == null) return true;
|
||||
return entitiesDB.Exists<RigidBodyEntityStruct>(playerId, CharacterExclusiveGroups.DeadCharacters);
|
||||
}
|
||||
|
||||
public int GetSelectedBlock(uint playerId)
|
||||
{
|
||||
if (entitiesDB == null) return 0;
|
||||
ref var c = ref GetCharacterStruct<EquippedPartStruct>(playerId, out bool exists);
|
||||
if (exists)
|
||||
{
|
||||
return c.SelectedDBPartID;
|
||||
}
|
||||
return ushort.MaxValue;
|
||||
}
|
||||
|
||||
public byte GetSelectedColor(uint playerId)
|
||||
{
|
||||
if (entitiesDB == null) return 0;
|
||||
ref var c = ref GetCharacterStruct<EquippedColourStruct>(playerId, out bool exists);
|
||||
if (exists)
|
||||
{
|
||||
return c.indexInPalette;
|
||||
}
|
||||
return 255;
|
||||
}
|
||||
|
||||
// reusable methods
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private ExclusiveGroup PlayerGroupFromEnum(PlayerType type)
|
||||
{
|
||||
return type == PlayerType.Local ? PlayersExclusiveGroups.LocalPlayers : PlayersExclusiveGroups.RemotePlayers;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ref T GetCharacterStruct<T>(uint playerId, out bool exists) where T : unmanaged, IEntityComponent
|
||||
{
|
||||
var characterGroups = CharacterExclusiveGroups.AllCharacters;
|
||||
for (int i = 0; i < characterGroups.count; i++)
|
||||
{
|
||||
EGID egid = new EGID(playerId, characterGroups[i]);
|
||||
if (entitiesDB.Exists<T>(egid))
|
||||
{
|
||||
exists = true;
|
||||
return ref entitiesDB.QueryEntity<T>(egid);
|
||||
}
|
||||
}
|
||||
|
||||
exists = false;
|
||||
T[] arr = new T[1];
|
||||
return ref arr[0]; //Return default value
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool GetPlayerStruct<T>(uint playerId, out T s) where T : unmanaged, IEntityComponent
|
||||
{
|
||||
var playerGroups = PlayersExclusiveGroups.AllPlayers;
|
||||
for (int i = 0; i < playerGroups.count; i++)
|
||||
{
|
||||
EGID egid = new EGID(playerId, playerGroups[i]);
|
||||
if (entitiesDB.Exists<T>(egid))
|
||||
{
|
||||
s = entitiesDB.QueryEntity<T>(egid);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
s = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public EGID? GetThingLookedAt(uint playerId, float maxDistance = -1f)
|
||||
{
|
||||
if (!entitiesDB.TryQueryMappedEntities<CharacterCameraRayCastEntityStruct>(
|
||||
CameraExclusiveGroups.CameraGroup, out var mapper))
|
||||
return null;
|
||||
mapper.TryGetEntity(playerId, out CharacterCameraRayCastEntityStruct rayCast);
|
||||
float distance = maxDistance < 0
|
||||
? GhostBlockUtils.GetBuildInteractionDistance(entitiesDB, rayCast,
|
||||
GhostBlockUtils.GhostCastMethod.GhostCastProportionalToBlockSize)
|
||||
: maxDistance;
|
||||
if (rayCast.hit && rayCast.distance <= distance)
|
||||
return rayCast.hitEgid;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public unsafe Block[] GetSelectedBlocks(uint playerid)
|
||||
{
|
||||
if (!entitiesDB.Exists<BoxSelectStateEntityStruct>(playerid,
|
||||
BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup))
|
||||
return new Block[0];
|
||||
var state = entitiesDB.QueryEntity<BoxSelectStateEntityStruct>(playerid,
|
||||
BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup);
|
||||
var blocks = entitiesDB.QueryEntity<SelectedBlocksStruct>(playerid,
|
||||
BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup);
|
||||
if (!state.active) return new Block[0];
|
||||
var pointer = (EGID*) blocks.selectedBlocks.ToPointer();
|
||||
var ret = new Block[blocks.count];
|
||||
for (int j = 0; j < blocks.count; j++)
|
||||
{
|
||||
var egid = pointer[j];
|
||||
ret[j] = new Block(egid);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,497 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.Text;
|
||||
|
||||
using HarmonyLib;
|
||||
using IllusionInjector;
|
||||
// test
|
||||
using GPUInstancer;
|
||||
using Svelto.ECS;
|
||||
using RobocraftX.Blocks;
|
||||
using RobocraftX.Common;
|
||||
using RobocraftX.SimulationModeState;
|
||||
using RobocraftX.FrontEnd;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
|
||||
using GamecraftModdingAPI.Commands;
|
||||
using GamecraftModdingAPI.Events;
|
||||
using GamecraftModdingAPI.Utility;
|
||||
using GamecraftModdingAPI.Blocks;
|
||||
using GamecraftModdingAPI.Players;
|
||||
using EventType = GamecraftModdingAPI.Events.EventType;
|
||||
|
||||
namespace GamecraftModdingAPI.Tests
|
||||
{
|
||||
#if DEBUG
|
||||
// unused by design
|
||||
/// <summary>
|
||||
/// Modding API implemented as a standalone IPA Plugin.
|
||||
/// Ideally, GamecraftModdingAPI should be loaded by another mod; not itself
|
||||
/// </summary>
|
||||
public class GamecraftModdingAPIPluginTest : IllusionPlugin.IEnhancedPlugin
|
||||
{
|
||||
|
||||
private static Harmony harmony { get; set; }
|
||||
|
||||
public override string Name { get; } = Assembly.GetExecutingAssembly().GetName().Name;
|
||||
|
||||
public override string Version { get; } = Assembly.GetExecutingAssembly().GetName().Version.ToString();
|
||||
|
||||
public string HarmonyID { get; } = "org.git.exmods.modtainers.gamecraftmoddingapi";
|
||||
|
||||
public override void OnApplicationQuit()
|
||||
{
|
||||
GamecraftModdingAPI.Main.Shutdown();
|
||||
}
|
||||
|
||||
public override void OnApplicationStart()
|
||||
{
|
||||
FileLog.Reset();
|
||||
Harmony.DEBUG = true;
|
||||
GamecraftModdingAPI.Main.Init();
|
||||
Logging.MetaDebugLog($"Version group id {(uint)ApiExclusiveGroups.versionGroup}");
|
||||
// in case Steam is not installed/running
|
||||
// this will crash the game slightly later during startup
|
||||
//SteamInitPatch.ForcePassSteamCheck = true;
|
||||
// in case running in a VM
|
||||
//MinimumSpecsCheckPatch.ForcePassMinimumSpecCheck = true;
|
||||
// disable some Gamecraft analytics
|
||||
//AnalyticsDisablerPatch.DisableAnalytics = true;
|
||||
// disable background music
|
||||
Logging.MetaDebugLog("Audio Mixers: " + string.Join(",", AudioTools.GetMixers()));
|
||||
//AudioTools.SetVolume(0.0f, "Music"); // The game now sets this from settings again after this is called :(
|
||||
|
||||
//Utility.VersionTracking.Enable();//(very) unstable
|
||||
|
||||
// debug/test handlers
|
||||
#pragma warning disable 0612
|
||||
HandlerBuilder.Builder()
|
||||
.Name("appinit API debug")
|
||||
.Handle(EventType.ApplicationInitialized)
|
||||
.OnActivation(() => { Logging.Log("App Inited event!"); })
|
||||
.Build();
|
||||
|
||||
HandlerBuilder.Builder("menuact API debug")
|
||||
.Handle(EventType.Menu)
|
||||
.OnActivation(() => { Logging.Log("Menu Activated event!"); })
|
||||
.OnDestruction(() => { Logging.Log("Menu Destroyed event!"); })
|
||||
.Build();
|
||||
|
||||
HandlerBuilder.Builder("menuswitch API debug")
|
||||
.Handle(EventType.MenuSwitchedTo)
|
||||
.OnActivation(() => { Logging.Log("Menu Switched To event!"); })
|
||||
.Build();
|
||||
|
||||
HandlerBuilder.Builder("gameact API debug")
|
||||
.Handle(EventType.Menu)
|
||||
.OnActivation(() => { Logging.Log("Game Activated event!"); })
|
||||
.OnDestruction(() => { Logging.Log("Game Destroyed event!"); })
|
||||
.Build();
|
||||
|
||||
HandlerBuilder.Builder("gamerel API debug")
|
||||
.Handle(EventType.GameReloaded)
|
||||
.OnActivation(() => { Logging.Log("Game Reloaded event!"); })
|
||||
.Build();
|
||||
|
||||
HandlerBuilder.Builder("gameswitch API debug")
|
||||
.Handle(EventType.GameSwitchedTo)
|
||||
.OnActivation(() => { Logging.Log("Game Switched To event!"); })
|
||||
.Build();
|
||||
|
||||
HandlerBuilder.Builder("simulationswitch API debug")
|
||||
.Handle(EventType.SimulationSwitchedTo)
|
||||
.OnActivation(() => { Logging.Log("Game Mode Simulation Switched To event!"); })
|
||||
.Build();
|
||||
|
||||
HandlerBuilder.Builder("buildswitch API debug")
|
||||
.Handle(EventType.BuildSwitchedTo)
|
||||
.OnActivation(() => { Logging.Log("Game Mode Build Switched To event!"); })
|
||||
.Build();
|
||||
|
||||
HandlerBuilder.Builder("menu activated API error thrower test")
|
||||
.Handle(EventType.Menu)
|
||||
.OnActivation(() => { throw new Exception("Event Handler always throws an exception!"); })
|
||||
.Build();
|
||||
#pragma warning restore 0612
|
||||
/*HandlerBuilder.Builder("enter game from menu test")
|
||||
.Handle(EventType.Menu)
|
||||
.OnActivation(() =>
|
||||
{
|
||||
Tasks.Scheduler.Schedule(new Tasks.Repeatable(enterGame, shouldRetry, 0.2f));
|
||||
})
|
||||
.Build();*/
|
||||
|
||||
// debug/test commands
|
||||
if (Dependency.Hell("ExtraCommands"))
|
||||
{
|
||||
CommandBuilder.Builder()
|
||||
.Name("Exit")
|
||||
.Description("Close Gamecraft immediately, without any prompts")
|
||||
.Action(() => { UnityEngine.Application.Quit(); })
|
||||
.Build();
|
||||
|
||||
CommandBuilder.Builder()
|
||||
.Name("SetFOV")
|
||||
.Description("Set the player camera's field of view")
|
||||
.Action((float d) => { UnityEngine.Camera.main.fieldOfView = d; })
|
||||
.Build();
|
||||
|
||||
CommandBuilder.Builder()
|
||||
.Name("MoveLastBlock")
|
||||
.Description("Move the most-recently-placed block, and any connected blocks by the given offset")
|
||||
.Action((float x, float y, float z) =>
|
||||
{
|
||||
if (GameState.IsBuildMode())
|
||||
foreach (var block in Block.GetLastPlacedBlock().GetConnectedCubes())
|
||||
block.Position += new Unity.Mathematics.float3(x, y, z);
|
||||
else
|
||||
GamecraftModdingAPI.Utility.Logging.CommandLogError("Blocks can only be moved in Build mode!");
|
||||
}).Build();
|
||||
|
||||
CommandBuilder.Builder()
|
||||
.Name("PlaceAluminium")
|
||||
.Description("Place a block of aluminium at the given coordinates")
|
||||
.Action((float x, float y, float z) =>
|
||||
{
|
||||
var block = Block.PlaceNew(BlockIDs.AluminiumCube, new float3(x, y, z));
|
||||
Logging.CommandLog("Block placed with type: " + block.Type);
|
||||
})
|
||||
.Build();
|
||||
|
||||
CommandBuilder.Builder()
|
||||
.Name("PlaceAluminiumLots")
|
||||
.Description("Place a lot of blocks of aluminium at the given coordinates")
|
||||
.Action((float x, float y, float z) =>
|
||||
{
|
||||
Logging.CommandLog("Starting...");
|
||||
var sw = Stopwatch.StartNew();
|
||||
for (int i = 0; i < 100; i++)
|
||||
for (int j = 0; j < 100; j++)
|
||||
Block.PlaceNew(BlockIDs.AluminiumCube, new float3(x + i, y, z + j));
|
||||
//Block.Sync();
|
||||
sw.Stop();
|
||||
Logging.CommandLog("Finished in " + sw.ElapsedMilliseconds + "ms");
|
||||
})
|
||||
.Build();
|
||||
|
||||
Block b = null;
|
||||
CommandBuilder.Builder("moveBlockInSim", "Run in build mode first while looking at a block, then in sim to move it up")
|
||||
.Action(() =>
|
||||
{
|
||||
if (b == null)
|
||||
{
|
||||
b = new Player(PlayerType.Local).GetBlockLookedAt();
|
||||
Logging.CommandLog("Block saved: " + b);
|
||||
}
|
||||
else
|
||||
Logging.CommandLog("Block moved to: " + (b.GetSimBody().Position += new float3(0, 2, 0)));
|
||||
}).Build();
|
||||
|
||||
CommandBuilder.Builder("Error", "Throw an error to make sure SimpleCustomCommandEngine's wrapper catches it.")
|
||||
.Action(() => { throw new Exception("Error Command always throws an error"); })
|
||||
.Build();
|
||||
|
||||
CommandBuilder.Builder("ColorBlock",
|
||||
"Change color of the block looked at if there's any.")
|
||||
.Action<string>(str =>
|
||||
{
|
||||
if (!Enum.TryParse(str, out BlockColors color))
|
||||
{
|
||||
Logging.CommandLog("Color " + str + " not found! Interpreting as 4 color values.");
|
||||
var s = str.Split(' ');
|
||||
new Player(PlayerType.Local).GetBlockLookedAt().CustomColor = new float4(float.Parse(s[0]),
|
||||
float.Parse(s[1]), float.Parse(s[2]), float.Parse(s[3]));
|
||||
return;
|
||||
}
|
||||
new Player(PlayerType.Local).GetBlockLookedAt().Color =
|
||||
new BlockColor { Color = color };
|
||||
Logging.CommandLog("Colored block to " + color);
|
||||
|
||||
}).Build();
|
||||
|
||||
CommandBuilder.Builder("GetBlockByID", "Gets a block based on its object identifier and teleports it up.")
|
||||
.Action<char>(ch =>
|
||||
{
|
||||
foreach (var body in SimBody.GetFromObjectID(ch))
|
||||
{
|
||||
Logging.CommandLog("SimBody: " + body);
|
||||
body.Position += new float3(0, 10, 0);
|
||||
foreach (var bodyConnectedBody in body.GetConnectedBodies())
|
||||
{
|
||||
Logging.CommandLog("Moving " + bodyConnectedBody);
|
||||
bodyConnectedBody.Position += new float3(0, 10, 0);
|
||||
}
|
||||
}
|
||||
}).Build();
|
||||
|
||||
CommandBuilder.Builder()
|
||||
.Name("PlaceConsole")
|
||||
.Description("Place a bunch of console block with a given text - entering simulation with them crashes the game as the cmd doesn't exist")
|
||||
.Action((float x, float y, float z) =>
|
||||
{
|
||||
Stopwatch sw = new Stopwatch();
|
||||
sw.Start();
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
for (int j = 0; j < 100; j++)
|
||||
{
|
||||
var block = Block.PlaceNew<ConsoleBlock>(BlockIDs.ConsoleBlock,
|
||||
new float3(x + i, y, z + j));
|
||||
block.Command = "test_command";
|
||||
}
|
||||
}
|
||||
|
||||
sw.Stop();
|
||||
Logging.CommandLog($"Blocks placed in {sw.ElapsedMilliseconds} ms");
|
||||
})
|
||||
.Build();
|
||||
|
||||
CommandBuilder.Builder()
|
||||
.Name("WireTest")
|
||||
.Description("Place two blocks and then wire them together")
|
||||
.Action(() =>
|
||||
{
|
||||
LogicGate notBlock = Block.PlaceNew<LogicGate>(BlockIDs.NOTLogicBlock, new float3(1, 2, 0));
|
||||
LogicGate andBlock = Block.PlaceNew<LogicGate>(BlockIDs.ANDLogicBlock, new float3(2, 2, 0));
|
||||
// connect NOT Gate output to AND Gate input #2 (ports are zero-indexed, so 1 is 2nd position and 0 is 1st position)
|
||||
Wire conn = notBlock.Connect(0, andBlock, 1);
|
||||
Logging.CommandLog(conn.ToString());
|
||||
})
|
||||
.Build();
|
||||
|
||||
CommandBuilder.Builder("TestChunkHealth", "Sets the chunk looked at to the given health.")
|
||||
.Action((float val, float max) =>
|
||||
{
|
||||
var body = new Player(PlayerType.Local).GetSimBodyLookedAt();
|
||||
if (body == null) return;
|
||||
body.CurrentHealth = val;
|
||||
body.InitialHealth = max;
|
||||
Logging.CommandLog("Health set to: " + val);
|
||||
}).Build();
|
||||
|
||||
CommandBuilder.Builder("placeBlockGroup", "Places some blocks in a group")
|
||||
.Action((float x, float y, float z) =>
|
||||
{
|
||||
var pos = new float3(x, y, z);
|
||||
var group = BlockGroup.Create(Block.PlaceNew(BlockIDs.AluminiumCube, pos,
|
||||
color: BlockColors.Aqua));
|
||||
Block.PlaceNew(BlockIDs.AluminiumCube, pos += new float3(1, 0, 0), color: BlockColors.Blue)
|
||||
.BlockGroup = group;
|
||||
Block.PlaceNew(BlockIDs.AluminiumCube, pos += new float3(1, 0, 0), color: BlockColors.Green)
|
||||
.BlockGroup = group;
|
||||
Block.PlaceNew(BlockIDs.AluminiumCube, pos += new float3(1, 0, 0), color: BlockColors.Lime)
|
||||
.BlockGroup = group;
|
||||
}).Build();
|
||||
|
||||
GameClient.SetDebugInfo("InstalledMods", InstalledMods);
|
||||
Block.Placed += (sender, args) =>
|
||||
Logging.MetaDebugLog("Placed block " + args.Type + " with ID " + args.ID);
|
||||
Block.Removed += (sender, args) =>
|
||||
Logging.MetaDebugLog("Removed block " + args.Type + " with ID " + args.ID);
|
||||
|
||||
/*
|
||||
CommandManager.AddCommand(new SimpleCustomCommandEngine<float>((float d) => { UnityEngine.Camera.main.fieldOfView = d; },
|
||||
"SetFOV", "Set the player camera's field of view"));
|
||||
CommandManager.AddCommand(new SimpleCustomCommandEngine<float, float, float>(
|
||||
(x, y, z) => {
|
||||
bool success = GamecraftModdingAPI.Blocks.Movement.MoveConnectedBlocks(
|
||||
GamecraftModdingAPI.Blocks.BlockIdentifiers.LatestBlockID,
|
||||
new Unity.Mathematics.float3(x, y, z));
|
||||
if (!success)
|
||||
{
|
||||
GamecraftModdingAPI.Utility.Logging.CommandLogError("Blocks can only be moved in Build mode!");
|
||||
}
|
||||
}, "MoveLastBlock", "Move the most-recently-placed block, and any connected blocks by the given offset"));
|
||||
CommandManager.AddCommand(new SimpleCustomCommandEngine<float, float, float>(
|
||||
(x, y, z) => { Blocks.Placement.PlaceBlock(Blocks.BlockIDs.AluminiumCube, new Unity.Mathematics.float3(x, y, z)); },
|
||||
"PlaceAluminium", "Place a block of aluminium at the given coordinates"));
|
||||
System.Random random = new System.Random(); // for command below
|
||||
CommandManager.AddCommand(new SimpleCustomCommandEngine(
|
||||
() => {
|
||||
if (!GameState.IsSimulationMode())
|
||||
{
|
||||
Logging.CommandLogError("You must be in simulation mode for this to work!");
|
||||
return;
|
||||
}
|
||||
Tasks.Repeatable task = new Tasks.Repeatable(() => {
|
||||
uint count = 0;
|
||||
EGID[] eBlocks = Blocks.Signals.GetElectricBlocks();
|
||||
for (uint i = 0u; i < eBlocks.Length; i++)
|
||||
{
|
||||
uint[] ids = Blocks.Signals.GetSignalIDs(eBlocks[i]);
|
||||
for (uint j = 0u; j < ids.Length; j++)
|
||||
{
|
||||
Blocks.Signals.SetSignalByID(ids[j], (float)random.NextDouble());
|
||||
count++;
|
||||
}
|
||||
}
|
||||
Logging.MetaDebugLog($"Did the thing on {count} inputs");
|
||||
},
|
||||
() => { return GameState.IsSimulationMode(); });
|
||||
Tasks.Scheduler.Schedule(task);
|
||||
}, "RandomizeSignalsInputs", "Do the thing"));
|
||||
*/
|
||||
}
|
||||
|
||||
// dependency test
|
||||
if (Dependency.Hell("GamecraftScripting", new Version("0.0.1.0")))
|
||||
{
|
||||
Logging.LogWarning("You're in GamecraftScripting dependency hell");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logging.Log("Compatible GamecraftScripting detected");
|
||||
}
|
||||
|
||||
#if TEST
|
||||
TestRoot.RunTests();
|
||||
#endif
|
||||
}
|
||||
|
||||
private string modsString;
|
||||
private string InstalledMods()
|
||||
{
|
||||
if (modsString != null) return modsString;
|
||||
StringBuilder sb = new StringBuilder("Installed mods:");
|
||||
foreach (var plugin in PluginManager.Plugins)
|
||||
sb.Append("\n" + plugin.Name + " - " + plugin.Version);
|
||||
return modsString = sb.ToString();
|
||||
}
|
||||
|
||||
private bool retry = true;
|
||||
|
||||
private bool shouldRetry()
|
||||
{
|
||||
return retry;
|
||||
}
|
||||
|
||||
private void enterGame()
|
||||
{
|
||||
App.Client app = new App.Client();
|
||||
App.Game[] myGames = app.MyGames;
|
||||
Logging.MetaDebugLog($"MyGames count {myGames.Length}");
|
||||
if (myGames.Length != 0)
|
||||
{
|
||||
Logging.MetaDebugLog($"MyGames[0] EGID {myGames[0].EGID}");
|
||||
retry = false;
|
||||
try
|
||||
{
|
||||
//myGames[0].Description = "test msg pls ignore"; // make sure game exists first
|
||||
Logging.MetaDebugLog($"Entering game {myGames[0].Name}");
|
||||
myGames[0].EnterGame();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.MetaDebugLog($"Failed to enter game; exception: {e}");
|
||||
retry = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logging.MetaDebugLog("MyGames not populated yet :(");
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch]
|
||||
public class MinimumSpecsPatch
|
||||
{
|
||||
public static bool Prefix()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public static MethodInfo TargetMethod()
|
||||
{
|
||||
return ((Action) MinimumSpecsCheck.CheckRequirementsMet).Method;
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch]
|
||||
public class BugHuntPatch
|
||||
{
|
||||
public static MethodInfo method =
|
||||
SymbolExtensions.GetMethodInfo<string>(str => Console.WriteLine(str));
|
||||
|
||||
public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
|
||||
{
|
||||
int i = 0;
|
||||
foreach (var instruction in instructions)
|
||||
{
|
||||
i++;
|
||||
yield return instruction; //Return the instruction first
|
||||
//stloc, dup, callvirt
|
||||
if (instruction.opcode.Name.ToLower().StartsWith("stloc")
|
||||
|| instruction.opcode == OpCodes.Dup
|
||||
|| instruction.opcode == OpCodes.Callvirt)
|
||||
{
|
||||
yield return new CodeInstruction(OpCodes.Ldstr,
|
||||
"Just ran the " + i + ". instruction ending with " + instruction.opcode.Name);
|
||||
yield return new CodeInstruction(OpCodes.Call, method);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static MethodInfo TargetMethod()
|
||||
{
|
||||
return AccessTools.Method("RobocraftX.CR.MachineEditing.BoxSelect.CopySelectionEngine:GenerateThumbnail");
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch]
|
||||
public class BugHuntPatch2
|
||||
{
|
||||
public static void Prefix(int width, float fieldOfView, Vector3 cameraDirection, Vector3 lightDirection)
|
||||
{
|
||||
Console.WriteLine("TakeThumbnail invoked with parameters: " + width + ", " + fieldOfView + ", " +
|
||||
cameraDirection + ", " + lightDirection);
|
||||
|
||||
GPUInstancerManager manager = GPUInstancerAPI.GetActiveManagers().Find(m => m is GPUInstancerPrefabManager);
|
||||
Bounds instancesBounds = manager.ComputeInstancesBounds(2);
|
||||
Console.WriteLine("Bounds: " + instancesBounds);
|
||||
Console.WriteLine("Size: " + instancesBounds.size);
|
||||
Console.WriteLine("Size.x < 0: " + (instancesBounds.size.x < 0));
|
||||
}
|
||||
|
||||
public static void Postfix(Texture2D __result)
|
||||
{
|
||||
Console.WriteLine("TakeThumbnail returned: " + (__result == null ? null : __result.name));
|
||||
}
|
||||
|
||||
private delegate Texture2D TakeThumbnailDel(int width, float fieldOfView, Vector3 cameraDirection,
|
||||
Vector3 lightDirection);
|
||||
|
||||
public static MethodInfo TargetMethod()
|
||||
{
|
||||
return ((TakeThumbnailDel) ThumbnailUtility.TakeThumbnail).Method;
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch]
|
||||
public class BugHuntPatch3
|
||||
{
|
||||
public static void Prefix(int width, int filterLayerMask, GPUInstancerManager manager,
|
||||
Vector3 cameraPosition, Quaternion cameraRotation, float cameraFov, Vector3 lightDirection,
|
||||
int cullingLayer)
|
||||
{
|
||||
Console.WriteLine("Inner TakeThumbnail invoked with parameters: " + width + ", " + filterLayerMask +
|
||||
", " + (manager != null ? manager.name : null) + ", " + cameraPosition + ", " +
|
||||
cameraRotation + ", " + cameraFov + ", " + lightDirection + ", " + cullingLayer);
|
||||
}
|
||||
|
||||
private delegate Texture2D TakeThumbnailDel(int width, int filterLayerMask, GPUInstancerManager manager,
|
||||
Vector3 cameraPosition, Quaternion cameraRotation, float cameraFov, Vector3 lightDirection,
|
||||
int cullingLayer);
|
||||
|
||||
public static MethodInfo TargetMethod()
|
||||
{
|
||||
return ((TakeThumbnailDel) ThumbnailUtility.TakeThumbnail).Method;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
using Svelto.ECS;
|
||||
|
||||
namespace GamecraftModdingAPI.Utility
|
||||
{
|
||||
public static class AsyncUtils
|
||||
{
|
||||
private static AsyncUtilsEngine gameEngine = new AsyncUtilsEngine();
|
||||
|
||||
/// <summary>
|
||||
/// Waits for entity submission asynchronously.
|
||||
/// Use after placing a block or otherwise creating things in the game to access their properties.
|
||||
/// </summary>
|
||||
public static async Task WaitForSubmission()
|
||||
{
|
||||
await gameEngine.WaitForSubmission();
|
||||
}
|
||||
|
||||
public static async Task WaitForNextFrame()
|
||||
{
|
||||
await gameEngine.WaitForNextFrame();
|
||||
}
|
||||
|
||||
public static void Setup(EnginesRoot enginesRoot)
|
||||
{
|
||||
gameEngine.Setup(enginesRoot.GenerateEntityFunctions(), enginesRoot.GenerateEntityFactory());
|
||||
}
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
GameEngineManager.AddGameEngine(gameEngine);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
using System.Collections;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using RobocraftX.Schedulers;
|
||||
using Svelto.ECS;
|
||||
using Svelto.Tasks.ExtraLean;
|
||||
|
||||
using GamecraftModdingAPI.Engines;
|
||||
|
||||
namespace GamecraftModdingAPI.Utility
|
||||
{
|
||||
public class AsyncUtilsEngine : IApiEngine
|
||||
{
|
||||
private IEntityFunctions _efu;
|
||||
private IEntityFactory _efa;
|
||||
private IEnumerator WaitForSubmissionInternal(IEntityFunctions efu, IEntityFactory efa,
|
||||
EntitiesDB entitiesDB, TaskCompletionSource<object> task)
|
||||
{
|
||||
var waitEnumerator = new WaitForSubmissionEnumerator(efu, efa, entitiesDB);
|
||||
while (waitEnumerator.MoveNext())
|
||||
yield return null;
|
||||
task.SetResult(null);
|
||||
}
|
||||
|
||||
private IEnumerator WaitForNextFrameInternal(TaskCompletionSource<object> task)
|
||||
{
|
||||
yield return null;
|
||||
task.SetResult(null);
|
||||
}
|
||||
|
||||
public Task WaitForSubmission()
|
||||
{
|
||||
var task = new TaskCompletionSource<object>();
|
||||
WaitForSubmissionInternal(_efu, _efa, entitiesDB, task).RunOn(ExtraLean.EveryFrameStepRunner_TimeStopped);
|
||||
return task.Task;
|
||||
}
|
||||
|
||||
public Task WaitForNextFrame()
|
||||
{
|
||||
var task = new TaskCompletionSource<object>();
|
||||
WaitForNextFrameInternal(task).RunOn(ExtraLean.EveryFrameStepRunner_TimeStopped);
|
||||
return task.Task;
|
||||
}
|
||||
|
||||
public void Setup(IEntityFunctions efu, IEntityFactory efa)
|
||||
{
|
||||
_efu = efu;
|
||||
_efa = efa;
|
||||
}
|
||||
|
||||
public void Ready()
|
||||
{
|
||||
}
|
||||
|
||||
public EntitiesDB entitiesDB { get; set; }
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public string Name { get; } = "GamecraftModdingAPIAsyncUtilsGameEngine";
|
||||
public bool isRemovable { get; } = false;
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
using System;
|
||||
using GamecraftModdingAPI.Events;
|
||||
|
||||
namespace GamecraftModdingAPI.Utility
|
||||
{
|
||||
public static class ExceptionUtil
|
||||
{
|
||||
/// <summary>
|
||||
/// Invokes an event in a try-catch block to avoid propagating exceptions.
|
||||
/// </summary>
|
||||
/// <param name="handler">The event to emit, can be null</param>
|
||||
/// <param name="sender">Event sender</param>
|
||||
/// <param name="args">Event arguments</param>
|
||||
/// <typeparam name="T">Type of the event arguments</typeparam>
|
||||
public static void InvokeEvent<T>(EventHandler<T> handler, object sender, T args)
|
||||
{
|
||||
try
|
||||
{
|
||||
handler?.Invoke(sender, args);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
EventRuntimeException wrappedException =
|
||||
new EventRuntimeException($"EventHandler with arg type {typeof(T).Name} threw an exception", e);
|
||||
Logging.LogWarning(wrappedException.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
using System;
|
||||
using GamecraftModdingAPI.Blocks;
|
||||
|
||||
namespace GamecraftModdingAPI.Utility
|
||||
{
|
||||
public static class GameClient
|
||||
{
|
||||
private static DebugInterfaceEngine _engine = new DebugInterfaceEngine();
|
||||
|
||||
/// <summary>
|
||||
/// Saves the extra information to be displayed on the debug view.
|
||||
/// The provided getter function is called each time the view updates so make sure it returns quickly.
|
||||
/// </summary>
|
||||
/// <param name="id">A global ID for the custom information</param>
|
||||
/// <param name="contentGetter">A function that returns the current information</param>
|
||||
public static void SetDebugInfo(string id, Func<string> contentGetter) => _engine.SetInfo(id, contentGetter);
|
||||
|
||||
/// <summary>
|
||||
/// Removes an information provided by a plugin.
|
||||
/// </summary>
|
||||
/// <param name="id">The ID of the custom information</param>
|
||||
/// <returns></returns>
|
||||
public static bool RemoveDebugInfo(string id) => _engine.RemoveInfo(id);
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
GameEngineManager.AddGameEngine(_engine);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,116 +0,0 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
using RobocraftX.Common;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.Serialization;
|
||||
|
||||
using GamecraftModdingAPI.Persistence;
|
||||
using GamecraftModdingAPI.Events;
|
||||
|
||||
namespace GamecraftModdingAPI.Utility
|
||||
{
|
||||
/// <summary>
|
||||
/// Tracks the API version the current game was built for.
|
||||
/// For compatibility reasons, this must be enabled before it will work.
|
||||
/// </summary>
|
||||
[Obsolete]
|
||||
public static class VersionTracking
|
||||
{
|
||||
private static readonly VersionTrackingEngine versionEngine = new VersionTrackingEngine();
|
||||
|
||||
private static bool isEnabled = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the API version saved in the current game.
|
||||
/// </summary>
|
||||
/// <returns>The version.</returns>
|
||||
public static uint GetVersion()
|
||||
{
|
||||
if (!isEnabled) return 0u;
|
||||
return versionEngine.GetGameVersion();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enable API version tracking.
|
||||
/// </summary>
|
||||
public static void Enable()
|
||||
{
|
||||
if (!SerializerManager.ExistsSerializer(typeof(ModVersionStruct).FullName))
|
||||
{
|
||||
SerializerManager.AddSerializer<ModVersionDescriptor>(new SimpleEntitySerializer<ModVersionDescriptor>(
|
||||
(_) => { return new EGID[1] { new EGID(0u, ApiExclusiveGroups.versionGroup) }; }
|
||||
));
|
||||
}
|
||||
EventManager.AddEventEmitter(versionEngine);
|
||||
isEnabled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disable API version tracking.
|
||||
/// </summary>
|
||||
public static void Disable()
|
||||
{
|
||||
EventManager.AddEventEmitter(versionEngine);
|
||||
isEnabled = false;
|
||||
}
|
||||
|
||||
public static void Init() { }
|
||||
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
internal class VersionTrackingEngine : IEventEmitterEngine
|
||||
{
|
||||
public string Name { get; } = "GamecraftModdingAPIVersionTrackingGameEngine";
|
||||
|
||||
public EntitiesDB entitiesDB { set; private get; }
|
||||
|
||||
public int type => -1;
|
||||
|
||||
public bool isRemovable => false;
|
||||
|
||||
public IEntityFactory Factory { set; private get; }
|
||||
|
||||
public void Dispose() { }
|
||||
|
||||
public void Ready()
|
||||
{
|
||||
EGID egid = new EGID(0u, ApiExclusiveGroups.versionGroup);
|
||||
if (!entitiesDB.Exists<ModVersionStruct>(egid))
|
||||
{
|
||||
Version currentVersion = Assembly.GetExecutingAssembly().GetName().Version;
|
||||
int v = (currentVersion.Major * 1000) + (currentVersion.Minor);
|
||||
Factory.BuildEntity<ModVersionDescriptor>(egid).Init<ModVersionStruct>(new ModVersionStruct
|
||||
{
|
||||
version = (uint)v
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public uint GetGameVersion()
|
||||
{
|
||||
return entitiesDB.QueryUniqueEntity<ModVersionStruct>(ApiExclusiveGroups.versionGroup).version;
|
||||
}
|
||||
|
||||
public void Emit() { }
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public struct ModVersionStruct : IEntityComponent
|
||||
{
|
||||
public uint version;
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public class ModVersionDescriptor: SerializableEntityDescriptor<ModVersionDescriptor._ModVersionDescriptor>
|
||||
{
|
||||
[HashName("GamecraftModdingAPIVersionV0")]
|
||||
public class _ModVersionDescriptor : IEntityDescriptor
|
||||
{
|
||||
public IComponentBuilder[] componentsToBuild { get; } = new IComponentBuilder[]{
|
||||
new SerializableComponentBuilder<SerializationType, ModVersionStruct>(((int)SerializationType.Network, new DefaultSerializer<ModVersionStruct>()), ((int)SerializationType.Storage, new DefaultSerializer<ModVersionStruct>())),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
22
README.md
22
README.md
|
@ -1,12 +1,12 @@
|
|||
# GamecraftModdingAPI
|
||||
# TechbloxModdingAPI
|
||||
|
||||
Unofficial Gamecraft modding API for interfacing Gamecraft from mods.
|
||||
Unofficial Techblox modding API for interfacing Techblox from mods.
|
||||
|
||||
The GamecraftModdingAPI aims to simplify the mods in two ways:
|
||||
The TechbloxModdingAPI aims to simplify the mods in two ways:
|
||||
- *Ease-of-Use* The API provides convenient ways to do common tasks such as moving blocks and adding commands.
|
||||
All of the Harmony patching is done for you, so you can focus on writing your mod instead of reading swathes of undocumented code.
|
||||
- *Stability* The API aims to be reliable and consistent between versions.
|
||||
This means your code won't break when the GamecraftModdingAPI or Gamecraft updates.
|
||||
This means your code won't break when the TechbloxModdingAPI or Techblox updates.
|
||||
|
||||
For more info, please check out the [official documentation](https://mod.exmods.org).
|
||||
|
||||
|
@ -16,16 +16,16 @@ For more support, join the ExMods [Discord](https://discord.exmods.org).
|
|||
[Please follow the official mod installation guide](https://www.exmods.org/guides/install.html) or use GCMM.
|
||||
|
||||
## Development
|
||||
To get started, create a symbolic link called `ref` in the root of the project, or one folder higher, linking to the Gamecraft install folder.
|
||||
This will allow your IDE to resolve references to Gamecraft files for building and IDE tools.
|
||||
To get started, create a symbolic link called `ref` in the root of the project, or one folder higher, linking to the Techblox install folder.
|
||||
This will allow your IDE to resolve references to Techblox files for building and IDE tools.
|
||||
|
||||
GamecraftModdingAPI version numbers follow the [Semantic Versioning guidelines](https://semver.org/).
|
||||
TechbloxModdingAPI version numbers follow the [Semantic Versioning guidelines](https://semver.org/).
|
||||
|
||||
## External Libraries
|
||||
GamecraftModdingAPI includes [Harmony](https://github.com/pardeike/Harmony) to modify the behaviour of existing Gamecraft code.
|
||||
TechbloxModdingAPI includes [Harmony](https://github.com/pardeike/Harmony) to modify the behaviour of existing Techblox code.
|
||||
|
||||
# Disclaimer
|
||||
This API is an unofficial modification of Gamecraft software, and is not endorsed or supported by FreeJam or Gamecraft.
|
||||
The GamecraftModdingAPI developer(s) claim no rights on the Gamecraft code referenced within this project.
|
||||
All code contained in this project is licensed under the [GNU Public License v3](https://git.exmods.org/modtainers/GamecraftModdingAPI/src/branch/master/LICENSE).
|
||||
This API is an unofficial modification of Techblox software, and is not endorsed or supported by FreeJam or Techblox.
|
||||
The TechbloxModdingAPI developer(s) claim no rights on the Techblox code referenced within this project.
|
||||
All code contained in this project is licensed under the [GNU Public License v3](https://git.exmods.org/modtainers/TechbloxModdingAPI/src/branch/master/LICENSE).
|
||||
|
||||
|
|
|
@ -3,7 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
|||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.29411.108
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GamecraftModdingAPI", "GamecraftModdingAPI\GamecraftModdingAPI.csproj", "{7FD5A7D8-4F3E-426A-B07D-7DC70442A4DF}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TechbloxModdingAPI", "TechbloxModdingAPI\TechbloxModdingAPI.csproj", "{7FD5A7D8-4F3E-426A-B07D-7DC70442A4DF}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeGenerator", "CodeGenerator\CodeGenerator.csproj", "{0EBB6400-95A7-4A3D-B2ED-BF31E364CC10}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
@ -18,6 +20,12 @@ Global
|
|||
{7FD5A7D8-4F3E-426A-B07D-7DC70442A4DF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7FD5A7D8-4F3E-426A-B07D-7DC70442A4DF}.Test|Any CPU.ActiveCfg = Test|Any CPU
|
||||
{7FD5A7D8-4F3E-426A-B07D-7DC70442A4DF}.Test|Any CPU.Build.0 = Test|Any CPU
|
||||
{0EBB6400-95A7-4A3D-B2ED-BF31E364CC10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0EBB6400-95A7-4A3D-B2ED-BF31E364CC10}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0EBB6400-95A7-4A3D-B2ED-BF31E364CC10}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0EBB6400-95A7-4A3D-B2ED-BF31E364CC10}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{0EBB6400-95A7-4A3D-B2ED-BF31E364CC10}.Test|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0EBB6400-95A7-4A3D-B2ED-BF31E364CC10}.Test|Any CPU.Build.0 = Debug|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
|
@ -1,8 +1,8 @@
|
|||
using System;
|
||||
|
||||
using GamecraftModdingAPI.Tests;
|
||||
using TechbloxModdingAPI.Tests;
|
||||
|
||||
namespace GamecraftModdingAPI.App
|
||||
namespace TechbloxModdingAPI.App
|
||||
{
|
||||
#if TEST
|
||||
/// <summary>
|
44
TechbloxModdingAPI/App/AppEngine.cs
Normal file
44
TechbloxModdingAPI/App/AppEngine.cs
Normal file
|
@ -0,0 +1,44 @@
|
|||
using System;
|
||||
|
||||
using RobocraftX.GUI.MyGamesScreen;
|
||||
using Svelto.ECS;
|
||||
using TechbloxModdingAPI.Engines;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
namespace TechbloxModdingAPI.App
|
||||
{
|
||||
public class AppEngine : IFactoryEngine
|
||||
{
|
||||
public WrappedHandler<MenuEventArgs> EnterMenu;
|
||||
|
||||
public WrappedHandler<MenuEventArgs> ExitMenu;
|
||||
|
||||
public IEntityFactory Factory { set; private get; }
|
||||
|
||||
public string Name => "TechbloxModdingAPIAppEngine";
|
||||
|
||||
public bool isRemovable => false;
|
||||
|
||||
public EntitiesDB entitiesDB { set; private get; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
IsInMenu = false;
|
||||
ExitMenu.Invoke(this, new MenuEventArgs { });
|
||||
}
|
||||
|
||||
public void Ready()
|
||||
{
|
||||
IsInMenu = true;
|
||||
EnterMenu.Invoke(this, new MenuEventArgs { });
|
||||
}
|
||||
|
||||
// app functionality
|
||||
|
||||
public bool IsInMenu
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
} = false;
|
||||
}
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace GamecraftModdingAPI.App
|
||||
namespace TechbloxModdingAPI.App
|
||||
{
|
||||
public class AppException : GamecraftModdingAPIException
|
||||
public class AppException : TechbloxModdingAPIException
|
||||
{
|
||||
public AppException()
|
||||
{
|
|
@ -4,33 +4,29 @@ using HarmonyLib;
|
|||
|
||||
using RobocraftX.Services;
|
||||
using UnityEngine;
|
||||
|
||||
using GamecraftModdingAPI.Utility;
|
||||
using RobocraftX.Common;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftModdingAPI.App
|
||||
namespace TechbloxModdingAPI.App
|
||||
{
|
||||
/// <summary>
|
||||
/// The Gamecraft application that is running this code right now.
|
||||
/// The Techblox application that is running this code right now.
|
||||
/// </summary>
|
||||
public class Client
|
||||
{
|
||||
// extensible engine
|
||||
protected static AppEngine appEngine = new AppEngine();
|
||||
|
||||
protected static Func<object> ErrorHandlerInstanceGetter;
|
||||
protected static Func<object> ErrorHandlerInstanceGetter;
|
||||
|
||||
protected static Action<object, Error> EnqueueError;
|
||||
|
||||
protected static Action<object> HandleErrorClosed;
|
||||
|
||||
/// <summary>
|
||||
/// <summary>
|
||||
/// An event that fires whenever the main menu is loaded.
|
||||
/// </summary>
|
||||
public static event EventHandler<MenuEventArgs> EnterMenu
|
||||
{
|
||||
add => appEngine.EnterMenu += value;
|
||||
remove => appEngine.EnterMenu -= value;
|
||||
{
|
||||
add => Game.menuEngine.EnterMenu += value;
|
||||
remove => Game.menuEngine.EnterMenu -= value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -38,12 +34,12 @@ namespace GamecraftModdingAPI.App
|
|||
/// </summary>
|
||||
public static event EventHandler<MenuEventArgs> ExitMenu
|
||||
{
|
||||
add => appEngine.ExitMenu += value;
|
||||
remove => appEngine.ExitMenu -= value;
|
||||
add => Game.menuEngine.ExitMenu += value;
|
||||
remove => Game.menuEngine.ExitMenu -= value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gamecraft build version string.
|
||||
/// Techblox build version string.
|
||||
/// Usually this is in the form YYYY.mm.DD.HH.MM.SS
|
||||
/// </summary>
|
||||
/// <value>The version.</value>
|
||||
|
@ -70,23 +66,23 @@ namespace GamecraftModdingAPI.App
|
|||
{
|
||||
get
|
||||
{
|
||||
if (!appEngine.IsInMenu) return new Game[0];
|
||||
return appEngine.GetMyGames();
|
||||
if (!Game.menuEngine.IsInMenu) return Array.Empty<Game>();
|
||||
return Game.menuEngine.GetMyGames();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether Gamecraft is in the Main Menu
|
||||
/// Whether Techblox is in the Main Menu
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if in menu; <c>false</c> when loading or in a game.</value>
|
||||
public bool InMenu
|
||||
{
|
||||
get => appEngine.IsInMenu;
|
||||
get => Game.menuEngine.IsInMenu;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open a popup which prompts the user to click a button.
|
||||
/// This reuses Gamecraft's error dialog popup
|
||||
/// This reuses Techblox's error dialog popup
|
||||
/// </summary>
|
||||
/// <param name="popup">The popup to display. Use an instance of SingleChoicePrompt or DualChoicePrompt.</param>
|
||||
public void PromptUser(Error popup)
|
||||
|
@ -111,17 +107,15 @@ namespace GamecraftModdingAPI.App
|
|||
// this would have been so much simpler if this didn't involve a bunch of internal fields & classes
|
||||
Type errorHandler = AccessTools.TypeByName("RobocraftX.Services.ErrorHandler");
|
||||
Type errorHandle = AccessTools.TypeByName("RobocraftX.Services.ErrorHandle");
|
||||
ErrorHandlerInstanceGetter = (Func<object>) AccessTools.Method("GamecraftModdingAPI.App.Client:GenInstanceGetter")
|
||||
ErrorHandlerInstanceGetter = (Func<object>) AccessTools.Method("TechbloxModdingAPI.App.Client:GenInstanceGetter")
|
||||
.MakeGenericMethod(errorHandler)
|
||||
.Invoke(null, new object[0]);
|
||||
EnqueueError = (Action<object, Error>) AccessTools.Method("GamecraftModdingAPI.App.Client:GenEnqueueError")
|
||||
EnqueueError = (Action<object, Error>) AccessTools.Method("TechbloxModdingAPI.App.Client:GenEnqueueError")
|
||||
.MakeGenericMethod(errorHandler, errorHandle)
|
||||
.Invoke(null, new object[0]);
|
||||
/*HandleErrorClosed = (Action<object>) AccessTools.Method("GamecraftModdingAPI.App.Client:GenHandlePopupClosed")
|
||||
/*HandleErrorClosed = (Action<object>) AccessTools.Method("TechbloxModdingAPI.App.Client:GenHandlePopupClosed")
|
||||
.MakeGenericMethod(errorHandler)
|
||||
.Invoke(null, new object[0]);*/
|
||||
// register engines
|
||||
MenuEngineManager.AddMenuEngine(appEngine);
|
||||
}
|
||||
|
||||
// Creating delegates once is faster than reflection every time
|
|
@ -3,9 +3,9 @@ using HarmonyLib;
|
|||
|
||||
using RobocraftX.Services;
|
||||
|
||||
using GamecraftModdingAPI.Tests;
|
||||
using TechbloxModdingAPI.Tests;
|
||||
|
||||
namespace GamecraftModdingAPI.App
|
||||
namespace TechbloxModdingAPI.App
|
||||
{
|
||||
#if TEST
|
||||
/// <summary>
|
23
TechbloxModdingAPI/App/CurrentGameMode.cs
Normal file
23
TechbloxModdingAPI/App/CurrentGameMode.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
namespace TechbloxModdingAPI.App
|
||||
{
|
||||
public enum CurrentGameMode
|
||||
{
|
||||
None,
|
||||
/// <summary>
|
||||
/// Building a game
|
||||
/// </summary>
|
||||
Build,
|
||||
/// <summary>
|
||||
/// Playing a game
|
||||
/// </summary>
|
||||
Play,
|
||||
/// <summary>
|
||||
/// Viewing a prefab
|
||||
/// </summary>
|
||||
View,
|
||||
/// <summary>
|
||||
/// Viewing a tutorial
|
||||
/// </summary>
|
||||
Tutorial
|
||||
}
|
||||
}
|
|
@ -7,12 +7,12 @@ using RobocraftX.GUI.MyGamesScreen;
|
|||
using RobocraftX.StateSync;
|
||||
using Svelto.ECS;
|
||||
|
||||
using GamecraftModdingAPI;
|
||||
using GamecraftModdingAPI.Blocks;
|
||||
using GamecraftModdingAPI.Tasks;
|
||||
using GamecraftModdingAPI.Utility;
|
||||
using TechbloxModdingAPI;
|
||||
using TechbloxModdingAPI.Blocks;
|
||||
using TechbloxModdingAPI.Tasks;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftModdingAPI.App
|
||||
namespace TechbloxModdingAPI.App
|
||||
{
|
||||
/// <summary>
|
||||
/// An in-game save.
|
||||
|
@ -23,7 +23,7 @@ namespace GamecraftModdingAPI.App
|
|||
{
|
||||
// extensible engines
|
||||
protected static GameGameEngine gameEngine = new GameGameEngine();
|
||||
protected static GameMenuEngine menuEngine = new GameMenuEngine();
|
||||
protected internal static GameMenuEngine menuEngine = new GameMenuEngine();
|
||||
protected static DebugInterfaceEngine debugOverlayEngine = new DebugInterfaceEngine();
|
||||
protected static GameBuildSimEventEngine buildSimEventEngine = new GameBuildSimEventEngine();
|
||||
|
||||
|
@ -33,7 +33,7 @@ namespace GamecraftModdingAPI.App
|
|||
private bool hasId = false;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:GamecraftModdingAPI.App.Game"/> class.
|
||||
/// Initializes a new instance of the <see cref="T:TechbloxModdingAPI.App.Game"/> class.
|
||||
/// </summary>
|
||||
/// <param name="id">Menu identifier.</param>
|
||||
public Game(uint id) : this(new EGID(id, MyGamesScreenExclusiveGroups.MyGames))
|
||||
|
@ -41,7 +41,7 @@ namespace GamecraftModdingAPI.App
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:GamecraftModdingAPI.App.Game"/> class.
|
||||
/// Initializes a new instance of the <see cref="T:TechbloxModdingAPI.App.Game"/> class.
|
||||
/// </summary>
|
||||
/// <param name="id">Menu identifier.</param>
|
||||
public Game(EGID id)
|
||||
|
@ -54,7 +54,7 @@ namespace GamecraftModdingAPI.App
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:GamecraftModdingAPI.App.Game"/> class without id.
|
||||
/// Initializes a new instance of the <see cref="T:TechbloxModdingAPI.App.Game"/> class without id.
|
||||
/// This is assumed to be the current game.
|
||||
/// </summary>
|
||||
public Game()
|
||||
|
@ -118,7 +118,7 @@ namespace GamecraftModdingAPI.App
|
|||
|
||||
/// <summary>
|
||||
/// An event that fires right before a game returns to the main menu.
|
||||
/// At this point, Gamecraft is transitioning state so many things are invalid/unstable here.
|
||||
/// At this point, Techblox is transitioning state so many things are invalid/unstable here.
|
||||
/// </summary>
|
||||
public static event EventHandler<GameEventArgs> Exit
|
||||
{
|
||||
|
@ -165,7 +165,7 @@ namespace GamecraftModdingAPI.App
|
|||
{
|
||||
if (!VerifyMode()) return null;
|
||||
if (menuMode) return menuEngine.GetGameInfo(EGID).GameName;
|
||||
return GameMode.SaveGameDetails.Name;
|
||||
return gameEngine.GetGameData().saveName;
|
||||
}
|
||||
|
||||
set
|
||||
|
@ -174,11 +174,7 @@ namespace GamecraftModdingAPI.App
|
|||
if (menuMode)
|
||||
{
|
||||
menuEngine.SetGameName(EGID, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
GameMode.SaveGameDetails.Name = value;
|
||||
}
|
||||
} // Save details are directly saved from user input or not changed at all when in game
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -201,11 +197,7 @@ namespace GamecraftModdingAPI.App
|
|||
if (menuMode)
|
||||
{
|
||||
menuEngine.SetGameDescription(EGID, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
// No description exists in-game
|
||||
}
|
||||
} // No description exists in-game
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -219,7 +211,7 @@ namespace GamecraftModdingAPI.App
|
|||
{
|
||||
if (!VerifyMode()) return null;
|
||||
if (menuMode) return menuEngine.GetGameInfo(EGID).SavedGamePath;
|
||||
return GameMode.SaveGameDetails.Folder;
|
||||
return gameEngine.GetGameData().gameID;
|
||||
}
|
||||
|
||||
set
|
||||
|
@ -229,11 +221,6 @@ namespace GamecraftModdingAPI.App
|
|||
{
|
||||
menuEngine.GetGameInfo(EGID).SavedGamePath.Set(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
// this likely breaks things
|
||||
GameMode.SaveGameDetails = new SaveGameDetails(GameMode.SaveGameDetails.Name, value, GameMode.SaveGameDetails.WorkshopId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -242,28 +229,16 @@ namespace GamecraftModdingAPI.App
|
|||
/// In most cases this is invalid and returns 0, so this can be ignored.
|
||||
/// </summary>
|
||||
/// <value>The workshop identifier.</value>
|
||||
[Obsolete]
|
||||
public ulong WorkshopId
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!VerifyMode()) return 0uL;
|
||||
if (menuMode) return 0uL; // MyGames don't have workshop IDs
|
||||
return GameMode.SaveGameDetails.WorkshopId;
|
||||
return 0uL; // Not supported anymore
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
VerifyMode();
|
||||
if (menuMode)
|
||||
{
|
||||
// MyGames don't have workshop IDs
|
||||
// menuEngine.GetGameInfo(EGID).GameName.Set(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
// this likely breaks things
|
||||
GameMode.SaveGameDetails = new SaveGameDetails(GameMode.SaveGameDetails.Name, GameMode.SaveGameDetails.Folder, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -335,6 +310,18 @@ namespace GamecraftModdingAPI.App
|
|||
gameEngine.ToggleTimeMode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The mode of the game.
|
||||
/// </summary>
|
||||
public CurrentGameMode Mode
|
||||
{
|
||||
get
|
||||
{
|
||||
if (menuMode || !VerifyMode()) return CurrentGameMode.None;
|
||||
return (CurrentGameMode) GameMode.CurrentMode;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load the game save.
|
||||
/// This happens asynchronously, so when this method returns the game not loaded yet.
|
||||
|
@ -384,19 +371,20 @@ namespace GamecraftModdingAPI.App
|
|||
|
||||
/// <summary>
|
||||
/// Add information to the in-game debug display.
|
||||
/// When this object is garbage collected, this debug info is automatically removed.
|
||||
/// When this object is garbage collected, this debug info is automatically removed.
|
||||
/// The provided getter function is called each frame so make sure it returns quickly.
|
||||
/// </summary>
|
||||
/// <param name="id">Debug info identifier.</param>
|
||||
/// <param name="contentGetter">Content getter.</param>
|
||||
public void AddDebugInfo(string id, Func<string> contentGetter)
|
||||
/// <param name="contentGetter">A function that returns the current information.</param>
|
||||
public void AddDebugInfo(string id, Func<string> contentGetter)
|
||||
{
|
||||
if (!VerifyMode()) return;
|
||||
if (menuMode)
|
||||
{
|
||||
throw new GameNotFoundException("Game object references a menu item but AddDebugInfo only works on the currently-loaded game");
|
||||
}
|
||||
debugOverlayEngine.SetInfo(id, contentGetter);
|
||||
debugIds.Add(id);
|
||||
if (!VerifyMode()) return;
|
||||
if (menuMode)
|
||||
{
|
||||
throw new GameNotFoundException("Game object references a menu item but AddDebugInfo only works on the currently-loaded game");
|
||||
}
|
||||
debugOverlayEngine.SetInfo("game_" + id, contentGetter);
|
||||
debugIds.Add(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -406,14 +394,36 @@ namespace GamecraftModdingAPI.App
|
|||
/// <param name="id">Debug info identifier.</param>
|
||||
public bool RemoveDebugInfo(string id)
|
||||
{
|
||||
if (!VerifyMode()) return false;
|
||||
if (menuMode)
|
||||
{
|
||||
throw new GameNotFoundException("Game object references a menu item but RemoveDebugInfo only works on the currently-loaded game");
|
||||
}
|
||||
if (!debugIds.Contains(id)) return false;
|
||||
debugOverlayEngine.RemoveInfo(id);
|
||||
return debugIds.Remove(id);
|
||||
if (!VerifyMode()) return false;
|
||||
if (menuMode)
|
||||
{
|
||||
throw new GameNotFoundException("Game object references a menu item but RemoveDebugInfo only works on the currently-loaded game");
|
||||
}
|
||||
if (!debugIds.Contains(id)) return false;
|
||||
debugOverlayEngine.RemoveInfo("game_" + id);
|
||||
return debugIds.Remove(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add information to the in-game debug display.
|
||||
/// This debug info will be present for all games until it is manually removed.
|
||||
/// The provided getter function is called each frame so make sure it returns quickly.
|
||||
/// </summary>
|
||||
/// <param name="id">Debug info identifier.</param>
|
||||
/// <param name="contentGetter">A function that returns the current information.</param>
|
||||
public static void AddPersistentDebugInfo(string id, Func<string> contentGetter)
|
||||
{
|
||||
debugOverlayEngine.SetInfo("persistent_" + id, contentGetter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove persistent information from the in-game debug display.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c>, if debug info was removed, <c>false</c> otherwise.</returns>
|
||||
/// <param name="id">Debug info identifier.</param>
|
||||
public static bool RemovePersistentDebugInfo(string id)
|
||||
{
|
||||
return debugOverlayEngine.RemoveInfo("persistent_" + id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -433,11 +443,20 @@ namespace GamecraftModdingAPI.App
|
|||
Block[] blocks = new Block[blockEGIDs.Length];
|
||||
for (int b = 0; b < blockEGIDs.Length; b++)
|
||||
{
|
||||
blocks[b] = new Block(blockEGIDs[b]);
|
||||
blocks[b] = Block.New(blockEGIDs[b]);
|
||||
}
|
||||
return blocks;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enable the screenshot taker for updating the game's screenshot. Breaks the pause menu in a new save.
|
||||
/// </summary>
|
||||
public void EnableScreenshotTaker()
|
||||
{
|
||||
if (!VerifyMode()) return;
|
||||
gameEngine.EnableScreenshotTaker();
|
||||
}
|
||||
|
||||
~Game()
|
||||
{
|
||||
foreach (string id in debugIds)
|
||||
|
@ -466,12 +485,8 @@ namespace GamecraftModdingAPI.App
|
|||
{
|
||||
GameEngineManager.AddGameEngine(gameEngine);
|
||||
GameEngineManager.AddGameEngine(debugOverlayEngine);
|
||||
GameEngineManager.AddGameEngine(buildSimEventEngine);
|
||||
MenuEngineManager.AddMenuEngine(menuEngine);
|
||||
}
|
||||
|
||||
internal static void InitDeterministic(StateSyncRegistrationHelper stateSyncReg)
|
||||
{
|
||||
stateSyncReg.AddDeterministicEngine(buildSimEventEngine);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,19 +4,18 @@ using RobocraftX.Common;
|
|||
using RobocraftX.StateSync;
|
||||
using Svelto.ECS;
|
||||
using Unity.Jobs;
|
||||
using TechbloxModdingAPI.Engines;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
using GamecraftModdingAPI.Engines;
|
||||
using GamecraftModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftModdingAPI.App
|
||||
namespace TechbloxModdingAPI.App
|
||||
{
|
||||
public class GameBuildSimEventEngine : IApiEngine, IUnorderedInitializeOnTimeRunningModeEntered, IUnorderedInitializeOnTimeStoppedModeEntered
|
||||
{
|
||||
public event EventHandler<GameEventArgs> SimulationMode;
|
||||
public WrappedHandler<GameEventArgs> SimulationMode;
|
||||
|
||||
public event EventHandler<GameEventArgs> BuildMode;
|
||||
public WrappedHandler<GameEventArgs> BuildMode;
|
||||
|
||||
public string Name => "GamecraftModdingAPIBuildSimEventGameEngine";
|
||||
public string Name => "TechbloxModdingAPIBuildSimEventGameEngine";
|
||||
|
||||
public bool isRemovable => false;
|
||||
|
||||
|
@ -28,13 +27,13 @@ namespace GamecraftModdingAPI.App
|
|||
|
||||
public JobHandle OnInitializeTimeRunningMode(JobHandle inputDeps)
|
||||
{
|
||||
ExceptionUtil.InvokeEvent(SimulationMode, this, new GameEventArgs { GameName = GameMode.SaveGameDetails.Name, GamePath = GameMode.SaveGameDetails.Folder });
|
||||
SimulationMode.Invoke(this, new GameEventArgs { GameName = "", GamePath = "" }); // TODO
|
||||
return inputDeps;
|
||||
}
|
||||
|
||||
public JobHandle OnInitializeTimeStoppedMode(JobHandle inputDeps)
|
||||
{
|
||||
ExceptionUtil.InvokeEvent(BuildMode, this, new GameEventArgs { GameName = GameMode.SaveGameDetails.Name, GamePath = GameMode.SaveGameDetails.Folder });
|
||||
BuildMode.Invoke(this, new GameEventArgs { GameName = "", GamePath = "" });
|
||||
return inputDeps;
|
||||
}
|
||||
}
|
150
TechbloxModdingAPI/App/GameGameEngine.cs
Normal file
150
TechbloxModdingAPI/App/GameGameEngine.cs
Normal file
|
@ -0,0 +1,150 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
using RobocraftX.Common;
|
||||
using RobocraftX.Schedulers;
|
||||
using RobocraftX.SimulationModeState;
|
||||
using Svelto.ECS;
|
||||
using Svelto.Tasks;
|
||||
using Svelto.Tasks.Lean;
|
||||
using RobocraftX.Blocks;
|
||||
using RobocraftX.Common.Loading;
|
||||
using RobocraftX.ScreenshotTaker;
|
||||
using Techblox.Environment.Transition;
|
||||
using Techblox.GameSelection;
|
||||
using TechbloxModdingAPI.Blocks;
|
||||
using TechbloxModdingAPI.Engines;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
namespace TechbloxModdingAPI.App
|
||||
{
|
||||
public class GameGameEngine : IApiEngine, IReactOnAddAndRemove<LoadingActionEntityStruct>
|
||||
{
|
||||
public WrappedHandler<GameEventArgs> EnterGame;
|
||||
|
||||
public WrappedHandler<GameEventArgs> ExitGame;
|
||||
|
||||
public string Name => "TechbloxModdingAPIGameInfoMenuEngine";
|
||||
|
||||
public bool isRemovable => false;
|
||||
|
||||
public EntitiesDB entitiesDB { set; private get; }
|
||||
|
||||
private bool enteredGame;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
ExitGame.Invoke(this, new GameEventArgs { GameName = GetGameData().saveName, GamePath = GetGameData().gameID });
|
||||
IsInGame = false;
|
||||
}
|
||||
|
||||
public void Ready()
|
||||
{
|
||||
enteredGame = true;
|
||||
}
|
||||
|
||||
// game functionality
|
||||
|
||||
public bool IsInGame
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
} = false;
|
||||
|
||||
public void ExitCurrentGame(bool async = false)
|
||||
{
|
||||
if (async)
|
||||
{
|
||||
ExitCurrentGameAsync().RunOn(Lean.EveryFrameStepRunner_TimeRunningAndStopped);
|
||||
}
|
||||
else
|
||||
{
|
||||
entitiesDB.QueryEntity<GameSceneEntityStruct>(CommonExclusiveGroups.GameSceneEGID).WantsToQuit = true;
|
||||
entitiesDB.PublishEntityChange<GameSceneEntityStruct>(CommonExclusiveGroups.GameSceneEGID);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public IEnumerator<TaskContract> ExitCurrentGameAsync()
|
||||
{
|
||||
/*
|
||||
while (Lean.EveryFrameStepRunner_RUNS_IN_TIME_STOPPED_AND_RUNNING.isStopping) { yield return Yield.It; }
|
||||
AccessTools.Method(typeof(FullGameCompositionRoot), "SwitchToMenu").Invoke(FullGameFields.Instance, new object[0]);*/
|
||||
yield return Yield.It;
|
||||
entitiesDB.QueryEntity<GameSceneEntityStruct>(CommonExclusiveGroups.GameSceneEGID).WantsToQuit = true;
|
||||
entitiesDB.PublishEntityChange<GameSceneEntityStruct>(CommonExclusiveGroups.GameSceneEGID);
|
||||
}
|
||||
|
||||
public void SaveCurrentGame()
|
||||
{
|
||||
ref GameSceneEntityStruct gses = ref entitiesDB.QueryEntity<GameSceneEntityStruct>(CommonExclusiveGroups.GameSceneEGID);
|
||||
gses.LoadAfterSaving = false;
|
||||
gses.SaveNow = true;
|
||||
entitiesDB.PublishEntityChange<GameSceneEntityStruct>(CommonExclusiveGroups.GameSceneEGID);
|
||||
}
|
||||
|
||||
public bool IsTimeRunningMode()
|
||||
{
|
||||
return TimeRunningModeUtil.IsTimeRunningMode(entitiesDB);
|
||||
}
|
||||
|
||||
public bool IsTimeStoppedMode()
|
||||
{
|
||||
return TimeRunningModeUtil.IsTimeStoppedMode(entitiesDB);
|
||||
}
|
||||
|
||||
public void ToggleTimeMode()
|
||||
{
|
||||
if (!entitiesDB.FoundInGroups<BlockTagEntityStruct>())
|
||||
throw new AppStateException("At least one block must exist in the world to enter simulation");
|
||||
SwitchAnimationUtil.Start(entitiesDB);
|
||||
}
|
||||
|
||||
public EGID[] GetAllBlocksInGame(BlockIDs filter = BlockIDs.Invalid)
|
||||
{
|
||||
var allBlocks = entitiesDB.QueryEntities<BlockTagEntityStruct>();
|
||||
List<EGID> blockEGIDs = new List<EGID>();
|
||||
foreach (var (blocks, _) in allBlocks)
|
||||
{
|
||||
var (buffer, count) = blocks.ToBuffer();
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
uint dbid;
|
||||
if (filter == BlockIDs.Invalid)
|
||||
dbid = (uint)filter;
|
||||
else
|
||||
dbid = entitiesDB.QueryEntity<DBEntityStruct>(buffer[i].ID).DBID;
|
||||
if (dbid == (ulong)filter)
|
||||
blockEGIDs.Add(buffer[i].ID);
|
||||
}
|
||||
}
|
||||
|
||||
return blockEGIDs.ToArray();
|
||||
}
|
||||
|
||||
public void EnableScreenshotTaker()
|
||||
{
|
||||
ref var local = ref entitiesDB.QueryEntity<ScreenshotModeEntityStruct>(ScreenshotTakerEgids.ScreenshotTaker);
|
||||
if (local.enabled)
|
||||
return;
|
||||
local.enabled = true;
|
||||
entitiesDB.PublishEntityChange<ScreenshotModeEntityStruct>(ScreenshotTakerEgids.ScreenshotTaker);
|
||||
}
|
||||
|
||||
public GameSelectionComponent GetGameData()
|
||||
{
|
||||
return entitiesDB.QueryEntity<GameSelectionComponent>(GameSelectionConstants.GameSelectionEGID);
|
||||
}
|
||||
|
||||
public void Add(ref LoadingActionEntityStruct entityComponent, EGID egid)
|
||||
{
|
||||
}
|
||||
|
||||
public void Remove(ref LoadingActionEntityStruct entityComponent, EGID egid)
|
||||
{ // Finished loading
|
||||
if (!enteredGame) return;
|
||||
EnterGame.Invoke(this, new GameEventArgs { GameName = GetGameData().saveName, GamePath = GetGameData().gameID });
|
||||
IsInGame = true;
|
||||
enteredGame = false;
|
||||
}
|
||||
}
|
||||
}
|
204
TechbloxModdingAPI/App/GameMenuEngine.cs
Normal file
204
TechbloxModdingAPI/App/GameMenuEngine.cs
Normal file
|
@ -0,0 +1,204 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using HarmonyLib;
|
||||
|
||||
using RobocraftX;
|
||||
using RobocraftX.Common;
|
||||
using RobocraftX.FrontEnd;
|
||||
using RobocraftX.GUI;
|
||||
using RobocraftX.GUI.MyGamesScreen;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.Experimental;
|
||||
using Techblox.GameSelection;
|
||||
using TechbloxModdingAPI.Engines;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
using GameMode = RobocraftX.Common.GameMode;
|
||||
|
||||
namespace TechbloxModdingAPI.App
|
||||
{
|
||||
public class GameMenuEngine : IFactoryEngine
|
||||
{
|
||||
public WrappedHandler<MenuEventArgs> EnterMenu;
|
||||
|
||||
public WrappedHandler<MenuEventArgs> ExitMenu;
|
||||
public IEntityFactory Factory { set; private get; }
|
||||
|
||||
public string Name => "TechbloxModdingAPIGameInfoGameEngine";
|
||||
|
||||
public bool isRemovable => false;
|
||||
|
||||
public EntitiesDB entitiesDB { set; private get; }
|
||||
|
||||
public GameMenuEngine()
|
||||
{
|
||||
MenuEnteredEnginePatch.EnteredExitedMenu = () =>
|
||||
{
|
||||
if (IsInMenu)
|
||||
EnterMenu.Invoke(this, new MenuEventArgs { });
|
||||
else
|
||||
ExitMenu.Invoke(this, new MenuEventArgs { });
|
||||
};
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public void Ready()
|
||||
{
|
||||
MenuEnteredEnginePatch.IsInMenu = true; // At first it uses ActivateMenu(), then GoToMenu() which is patched
|
||||
MenuEnteredEnginePatch.EnteredExitedMenu();
|
||||
}
|
||||
|
||||
// game functionality
|
||||
|
||||
public bool IsInMenu => MenuEnteredEnginePatch.IsInMenu;
|
||||
|
||||
public Game[] GetMyGames()
|
||||
{
|
||||
EntityCollection<MyGameDataEntityStruct> mgsevs = entitiesDB.QueryEntities<MyGameDataEntityStruct>(MyGamesScreenExclusiveGroups.MyGames);
|
||||
var mgsevsB = mgsevs.ToBuffer().buffer;
|
||||
Game[] games = new Game[mgsevs.count];
|
||||
for (int i = 0; i < mgsevs.count; i++)
|
||||
{
|
||||
Utility.Logging.MetaDebugLog($"Found game named {mgsevsB[i].GameName}");
|
||||
games[i] = new Game(mgsevsB[i].ID);
|
||||
}
|
||||
return games;
|
||||
}
|
||||
|
||||
public bool CreateMyGame(EGID id, string path = "", uint thumbnailId = 0, string gameName = "", string creatorName = "", string description = "", long createdDate = 0L)
|
||||
{
|
||||
EntityInitializer eci = Factory.BuildEntity<MyGameDataEntityDescriptor_DamnItFJWhyDidYouMakeThisInternal>(id);
|
||||
eci.Init(new MyGameDataEntityStruct
|
||||
{
|
||||
SavedGamePath = new ECSString(path),
|
||||
ThumbnailId = thumbnailId,
|
||||
GameName = new ECSString(gameName),
|
||||
CreatorName = new ECSString(creatorName),
|
||||
GameDescription = new ECSString(description),
|
||||
CreatedDate = createdDate,
|
||||
});
|
||||
// entitiesDB.PublishEntityChange<MyGameDataEntityStruct>(id); // this will always fail
|
||||
return true;
|
||||
}
|
||||
|
||||
public uint HighestID()
|
||||
{
|
||||
EntityCollection<MyGameDataEntityStruct> games = entitiesDB.QueryEntities<MyGameDataEntityStruct>(MyGamesScreenExclusiveGroups.MyGames);
|
||||
var gamesB = games.ToBuffer().buffer;
|
||||
uint max = 0;
|
||||
for (int i = 0; i < games.count; i++)
|
||||
{
|
||||
if (gamesB[i].ID.entityID > max)
|
||||
{
|
||||
max = gamesB[i].ID.entityID;
|
||||
}
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
public bool EnterGame(EGID id)
|
||||
{
|
||||
if (!ExistsGameInfo(id)) return false;
|
||||
ref MyGameDataEntityStruct mgdes = ref GetGameInfo(id);
|
||||
return EnterGame(mgdes.GameName, mgdes.FileId);
|
||||
}
|
||||
|
||||
public bool EnterGame(string gameName, string fileId, bool autoEnterSim = false)
|
||||
{
|
||||
GameMode.CurrentMode = autoEnterSim ? RCXMode.Play : RCXMode.Build;
|
||||
var data = new GameSelectionData
|
||||
{
|
||||
gameMode = Techblox.GameSelection.GameMode.PlayGame,
|
||||
gameType = GameType.MachineEditor,
|
||||
saveName = gameName,
|
||||
saveType = SaveType.ExistingSave,
|
||||
gameID = "GAMEID_Road_Track", //TODO: Expose to the API
|
||||
userContentID = fileId
|
||||
};
|
||||
// the private FullGameCompositionRoot.SwitchToGame() method gets passed to menu items for this reason
|
||||
AccessTools.Method(typeof(FullGameCompositionRoot), "SwitchToGame").Invoke(FullGameFields.Instance, new object[]{data});
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool SetGameName(EGID id, string name)
|
||||
{
|
||||
if (!ExistsGameInfo(id)) return false;
|
||||
GetGameInfo(id).GameName.Set(name);
|
||||
GetGameViewInfo(id).MyGamesSlotComponent.GameName = StringUtil.SanitiseString(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool SetGameDescription(EGID id, string name)
|
||||
{
|
||||
if (!ExistsGameInfo(id)) return false;
|
||||
GetGameInfo(id).GameDescription.Set(name);
|
||||
GetGameViewInfo(id).MyGamesSlotComponent.GameDescription = StringUtil.SanitiseString(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool ExistsGameInfo(EGID id)
|
||||
{
|
||||
return entitiesDB.Exists<MyGameDataEntityStruct>(id);
|
||||
}
|
||||
|
||||
public ref MyGameDataEntityStruct GetGameInfo(EGID id)
|
||||
{
|
||||
return ref GetComponent<MyGameDataEntityStruct>(id);
|
||||
}
|
||||
|
||||
public dynamic GetGameViewInfo(EGID id)
|
||||
{
|
||||
dynamic structOptional = AccessTools.Method("TechbloxModdingAPI.Utility.NativeApiExtensions:QueryEntityOptional", new []{typeof(EntitiesDB), typeof(EGID)})
|
||||
.MakeGenericMethod(AccessTools.TypeByName("RobocraftX.GUI.MyGamesScreen.MyGamesSlotEntityViewStruct"))
|
||||
.Invoke(null, new object[] {entitiesDB, new EGID(id.entityID, MyGamesScreenExclusiveGroups.GameSlotGuiEntities)});
|
||||
if (structOptional == null) throw new Exception("Could not get game slot entity");
|
||||
return structOptional ? structOptional : null;
|
||||
}
|
||||
|
||||
public ref T GetComponent<T>(EGID id) where T: unmanaged, IEntityComponent
|
||||
{
|
||||
return ref entitiesDB.QueryEntity<T>(id);
|
||||
}
|
||||
}
|
||||
|
||||
internal class MyGameDataEntityDescriptor_DamnItFJWhyDidYouMakeThisInternal : GenericEntityDescriptor<MyGameDataEntityStruct> { }
|
||||
|
||||
[HarmonyPatch]
|
||||
static class MenuEnteredEnginePatch
|
||||
{
|
||||
internal static bool IsInMenu;
|
||||
internal static Action EnteredExitedMenu;
|
||||
public static void Postfix()
|
||||
{
|
||||
IsInMenu = true;
|
||||
EnteredExitedMenu();
|
||||
}
|
||||
|
||||
public static MethodBase TargetMethod()
|
||||
{
|
||||
return AccessTools.Method(typeof(FullGameCompositionRoot), "GoToMenu");
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch]
|
||||
static class MenuExitedEnginePatch
|
||||
{
|
||||
public static void Prefix()
|
||||
{
|
||||
MenuEnteredEnginePatch.IsInMenu = false;
|
||||
MenuEnteredEnginePatch.EnteredExitedMenu();
|
||||
}
|
||||
|
||||
public static MethodBase TargetMethod()
|
||||
{
|
||||
return AccessTools.Method(typeof(FullGameCompositionRoot), "SwitchToGame");
|
||||
}
|
||||
}
|
||||
|
||||
public struct MenuEventArgs
|
||||
{
|
||||
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@ using HarmonyLib;
|
|||
|
||||
using RobocraftX.Services;
|
||||
|
||||
namespace GamecraftModdingAPI.App
|
||||
namespace TechbloxModdingAPI.App
|
||||
{
|
||||
public class DualChoicePrompt : MultiChoiceError
|
||||
{
|
495
TechbloxModdingAPI/Block.cs
Normal file
495
TechbloxModdingAPI/Block.cs
Normal file
|
@ -0,0 +1,495 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using DataLoader;
|
||||
using Gamecraft.Blocks.BlockGroups;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.EntityStructs;
|
||||
using RobocraftX.Common;
|
||||
using RobocraftX.Blocks;
|
||||
using Unity.Mathematics;
|
||||
using Gamecraft.Blocks.GUI;
|
||||
using HarmonyLib;
|
||||
using RobocraftX.PilotSeat;
|
||||
|
||||
using TechbloxModdingAPI.Blocks;
|
||||
using TechbloxModdingAPI.Blocks.Engines;
|
||||
using TechbloxModdingAPI.Tests;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
namespace TechbloxModdingAPI
|
||||
{
|
||||
/// <summary>
|
||||
/// A single (perhaps scaled) block. Properties may return default values if the block is removed and then setting them is ignored.
|
||||
/// For specific block type operations, use the specialised block classes in the TechbloxModdingAPI.Blocks namespace.
|
||||
/// </summary>
|
||||
public class Block : EcsObjectBase, IEquatable<Block>, IEquatable<EGID>
|
||||
{
|
||||
protected static readonly PlacementEngine PlacementEngine = new PlacementEngine();
|
||||
protected static readonly MovementEngine MovementEngine = new MovementEngine();
|
||||
protected static readonly RotationEngine RotationEngine = new RotationEngine();
|
||||
protected static readonly RemovalEngine RemovalEngine = new RemovalEngine();
|
||||
protected static readonly SignalEngine SignalEngine = new SignalEngine();
|
||||
protected static readonly BlockEventsEngine BlockEventsEngine = new BlockEventsEngine();
|
||||
protected static readonly ScalingEngine ScalingEngine = new ScalingEngine();
|
||||
protected static readonly BlockCloneEngine BlockCloneEngine = new BlockCloneEngine();
|
||||
|
||||
protected internal static readonly BlockEngine BlockEngine = new BlockEngine();
|
||||
|
||||
/// <summary>
|
||||
/// Place a new block at the given position. If scaled, position means the center of the block. The default block size is 0.2 in terms of position.
|
||||
/// Place blocks next to each other to connect them.
|
||||
/// The placed block will be a complete block with a placement grid and collision which will be saved along with the game.
|
||||
/// </summary>
|
||||
/// <param name="block">The block's type</param>
|
||||
/// <param name="position">The block's position - default block size is 0.2</param>
|
||||
/// <param name="autoWire">Whether the block should be auto-wired (if functional)</param>
|
||||
/// <param name="player">The player who placed the block</param>
|
||||
/// <param name="force"></param>
|
||||
/// <returns>The placed block or null if failed</returns>
|
||||
public static Block PlaceNew(BlockIDs block, float3 position, bool autoWire = false, Player player = null,
|
||||
bool force = false)
|
||||
{
|
||||
if (PlacementEngine.IsInGame && (GameState.IsBuildMode() || force))
|
||||
{
|
||||
var initializer = PlacementEngine.PlaceBlock(block, position, player, autoWire);
|
||||
var egid = initializer.EGID;
|
||||
var bl = New(egid);
|
||||
bl.InitData = initializer;
|
||||
Placed += bl.OnPlacedInit;
|
||||
return bl;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the most recently placed block.
|
||||
/// </summary>
|
||||
/// <returns>The block object or null if doesn't exist</returns>
|
||||
public static Block GetLastPlacedBlock()
|
||||
{
|
||||
uint lastBlockID = (uint) AccessTools.Field(typeof(CommonExclusiveGroups), "_nextBlockEntityID").GetValue(null) - 1;
|
||||
EGID? egid = BlockEngine.FindBlockEGID(lastBlockID);
|
||||
return egid.HasValue ? New(egid.Value) : null;
|
||||
}
|
||||
|
||||
/*public static Block CreateGhostBlock()
|
||||
{
|
||||
return BlockGroup._engine.BuildGhostChild();
|
||||
}*/
|
||||
|
||||
/// <summary>
|
||||
/// An event that fires each time a block is placed.
|
||||
/// </summary>
|
||||
public static event EventHandler<BlockPlacedRemovedEventArgs> Placed
|
||||
{ //TODO: Rename and add instance version in 3.0
|
||||
add => BlockEventsEngine.Placed += value;
|
||||
remove => BlockEventsEngine.Placed -= value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An event that fires each time a block is removed.
|
||||
/// </summary>
|
||||
public static event EventHandler<BlockPlacedRemovedEventArgs> Removed
|
||||
{
|
||||
add => BlockEventsEngine.Removed += value;
|
||||
remove => BlockEventsEngine.Removed -= value;
|
||||
}
|
||||
|
||||
private static readonly Dictionary<ExclusiveBuildGroup, (Func<EGID, Block> Constructor, Type Type)> GroupToConstructor =
|
||||
new Dictionary<ExclusiveBuildGroup, (Func<EGID, Block>, Type)>
|
||||
{
|
||||
{CommonExclusiveGroups.DAMPEDSPRING_BLOCK_GROUP, (id => new DampedSpring(id), typeof(DampedSpring))},
|
||||
{CommonExclusiveGroups.ENGINE_BLOCK_BUILD_GROUP, (id => new Engine(id), typeof(Engine))},
|
||||
{CommonExclusiveGroups.LOGIC_BLOCK_GROUP, (id => new LogicGate(id), typeof(LogicGate))},
|
||||
{CommonExclusiveGroups.PISTON_BLOCK_GROUP, (id => new Piston(id), typeof(Piston))},
|
||||
{CommonExclusiveGroups.SERVO_BLOCK_GROUP, (id => new Servo(id), typeof(Servo))},
|
||||
{CommonExclusiveGroups.WHEELRIG_BLOCK_BUILD_GROUP, (id => new WheelRig(id), typeof(WheelRig))}
|
||||
};
|
||||
|
||||
static Block()
|
||||
{
|
||||
foreach (var group in SeatGroups.SEATS_BLOCK_GROUPS) // Adds driver and passenger seats, occupied and unoccupied
|
||||
GroupToConstructor.Add(group, (id => new Seat(id), typeof(Seat)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a correctly typed instance of this block. The instances are shared for a specific block.
|
||||
/// If an instance is no longer referenced a new instance is returned.
|
||||
/// </summary>
|
||||
/// <param name="egid">The EGID of the block</param>
|
||||
/// <param name="signaling">Whether the block is definitely a signaling block</param>
|
||||
/// <returns></returns>
|
||||
internal static Block New(EGID egid, bool signaling = false)
|
||||
{
|
||||
if (egid == default) return null;
|
||||
if (GroupToConstructor.ContainsKey(egid.groupID))
|
||||
{
|
||||
var (constructor, type) = GroupToConstructor[egid.groupID];
|
||||
return GetInstance(egid, constructor, type);
|
||||
}
|
||||
|
||||
return signaling
|
||||
? GetInstance(egid, e => new SignalingBlock(e))
|
||||
: GetInstance(egid, e => new Block(e));
|
||||
}
|
||||
|
||||
public Block(EGID id) : base(id)
|
||||
{
|
||||
Type expectedType;
|
||||
if (GroupToConstructor.ContainsKey(id.groupID) &&
|
||||
!GetType().IsAssignableFrom(expectedType = GroupToConstructor[id.groupID].Type))
|
||||
throw new BlockSpecializationException($"Incorrect block type! Expected: {expectedType} Actual: {GetType()}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This overload searches for the correct group the block is in.
|
||||
/// It will throw an exception if the block doesn't exist.
|
||||
/// Use the EGID constructor where possible or subclasses of Block as those specify the group.
|
||||
/// </summary>
|
||||
public Block(uint id) : this(BlockEngine.FindBlockEGID(id)
|
||||
?? throw new BlockTypeException(
|
||||
"Could not find the appropriate group for the block." +
|
||||
" The block probably doesn't exist or hasn't been submitted."))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Places a new block in the world.
|
||||
/// </summary>
|
||||
/// <param name="type">The block's type</param>
|
||||
/// <param name="position">The block's position (a block is 0.2 wide in terms of position)</param>
|
||||
/// <param name="autoWire">Whether the block should be auto-wired (if functional)</param>
|
||||
/// <param name="player">The player who placed the block</param>
|
||||
/// <param name="force">Place even if not in build mode</param>
|
||||
public Block(BlockIDs type, float3 position, bool autoWire = false, Player player = null, bool force = false)
|
||||
: base(block =>
|
||||
{
|
||||
if (!PlacementEngine.IsInGame || !GameState.IsBuildMode() && !force)
|
||||
throw new BlockException("Blocks can only be placed in build mode.");
|
||||
var initializer = PlacementEngine.PlaceBlock(type, position, player, autoWire);
|
||||
block.InitData = initializer;
|
||||
Placed += ((Block)block).OnPlacedInit;
|
||||
return initializer.EGID;
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
private EGID copiedFrom;
|
||||
|
||||
/// <summary>
|
||||
/// The block's current position or zero if the block no longer exists.
|
||||
/// A block is 0.2 wide by default in terms of position.
|
||||
/// </summary>
|
||||
public float3 Position
|
||||
{
|
||||
get => MovementEngine.GetPosition(this);
|
||||
set
|
||||
{
|
||||
MovementEngine.MoveBlock(this, value);
|
||||
if (blockGroup != null)
|
||||
blockGroup.PosAndRotCalculated = false;
|
||||
BlockEngine.UpdateDisplayedBlock(Id);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The block's current rotation in degrees or zero if the block doesn't exist.
|
||||
/// </summary>
|
||||
public float3 Rotation
|
||||
{
|
||||
get => RotationEngine.GetRotation(this);
|
||||
set
|
||||
{
|
||||
RotationEngine.RotateBlock(this, value);
|
||||
if (blockGroup != null)
|
||||
blockGroup.PosAndRotCalculated = false;
|
||||
BlockEngine.UpdateDisplayedBlock(Id);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The block's non-uniform scale or zero if the block's invalid. Independent of the uniform scaling.
|
||||
/// The default scale of 1 means 0.2 in terms of position.
|
||||
/// </summary>
|
||||
public float3 Scale
|
||||
{
|
||||
get => BlockEngine.GetBlockInfo<ScalingEntityStruct>(this).scale;
|
||||
set
|
||||
{
|
||||
int uscale = UniformScale;
|
||||
if (value.x < 4e-5) value.x = uscale;
|
||||
if (value.y < 4e-5) value.y = uscale;
|
||||
if (value.z < 4e-5) value.z = uscale;
|
||||
BlockEngine.GetBlockInfo<ScalingEntityStruct>(this).scale = value;
|
||||
//BlockEngine.GetBlockInfo<GridScaleStruct>(this).gridScale = value - (int3) value + 1;
|
||||
if (!Exists) return; //UpdateCollision needs the block to exist
|
||||
ScalingEngine.UpdateCollision(Id);
|
||||
BlockEngine.UpdateDisplayedBlock(Id);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The block's uniform scale or zero if the block's invalid. Also sets the non-uniform scale.
|
||||
/// The default scale of 1 means 0.2 in terms of position.
|
||||
/// </summary>
|
||||
public int UniformScale
|
||||
{
|
||||
get => BlockEngine.GetBlockInfo<UniformBlockScaleEntityStruct>(this).scaleFactor;
|
||||
set
|
||||
{
|
||||
if (value < 1) value = 1;
|
||||
BlockEngine.GetBlockInfo<UniformBlockScaleEntityStruct>(this).scaleFactor = value;
|
||||
Scale = new float3(value, value, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the block is flipped.
|
||||
*/
|
||||
public bool Flipped
|
||||
{
|
||||
get => BlockEngine.GetBlockInfo<ScalingEntityStruct>(this).scale.x < 0;
|
||||
set
|
||||
{
|
||||
ref var st = ref BlockEngine.GetBlockInfo<ScalingEntityStruct>(this);
|
||||
st.scale.x = math.abs(st.scale.x) * (value ? -1 : 1);
|
||||
BlockEngine.UpdatePrefab(this, (byte) Material, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The block's type (ID). Returns BlockIDs.Invalid if the block doesn't exist anymore.
|
||||
/// </summary>
|
||||
public BlockIDs Type
|
||||
{
|
||||
get
|
||||
{
|
||||
var opt = BlockEngine.GetBlockInfoOptional<DBEntityStruct>(this);
|
||||
return opt ? (BlockIDs) opt.Get().DBID : BlockIDs.Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The block's color. Returns BlockColors.Default if the block no longer exists.
|
||||
/// </summary>
|
||||
public BlockColor Color
|
||||
{
|
||||
get
|
||||
{
|
||||
var opt = BlockEngine.GetBlockInfoOptional<ColourParameterEntityStruct>(this);
|
||||
return new BlockColor(opt ? opt.Get().indexInPalette : byte.MaxValue);
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value.Color == BlockColors.Default)
|
||||
value = new BlockColor(FullGameFields._dataDb.TryGetValue((int) Type, out CubeListData cld)
|
||||
? cld.DefaultColour
|
||||
: throw new BlockTypeException("Unknown block type! Could not set default color."));
|
||||
ref var color = ref BlockEngine.GetBlockInfo<ColourParameterEntityStruct>(this);
|
||||
color.indexInPalette = value.Index;
|
||||
color.hasNetworkChange = true;
|
||||
color.paletteColour = BlockEngine.ConvertBlockColor(color.indexInPalette); //Setting to 255 results in black
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The block's exact color. Gets reset to the palette color (Color property) after reentering the game.
|
||||
/// </summary>
|
||||
public float4 CustomColor
|
||||
{
|
||||
get => BlockEngine.GetBlockInfo<ColourParameterEntityStruct>(this).paletteColour;
|
||||
set
|
||||
{
|
||||
ref var color = ref BlockEngine.GetBlockInfo<ColourParameterEntityStruct>(this);
|
||||
color.paletteColour = value;
|
||||
color.hasNetworkChange = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The block's material.
|
||||
*/
|
||||
public BlockMaterial Material
|
||||
{
|
||||
get
|
||||
{
|
||||
var opt = BlockEngine.GetBlockInfoOptional<CubeMaterialStruct>(this);
|
||||
return opt ? (BlockMaterial) opt.Get().materialId : BlockMaterial.Default;
|
||||
}
|
||||
set
|
||||
{
|
||||
byte val = (byte) value;
|
||||
if (value == BlockMaterial.Default)
|
||||
val = FullGameFields._dataDb.TryGetValue((int) Type, out CubeListData cld)
|
||||
? cld.DefaultMaterialID
|
||||
: throw new BlockTypeException("Unknown block type! Could not set default material.");
|
||||
if (!FullGameFields._dataDb.ContainsKey<MaterialPropertiesData>(val))
|
||||
throw new BlockException($"Block material {value} does not exist!");
|
||||
ref var comp = ref BlockEngine.GetBlockInfo<CubeMaterialStruct>(this);
|
||||
if (comp.materialId == val)
|
||||
return;
|
||||
comp.materialId = val;
|
||||
BlockEngine.UpdatePrefab(this, val, Flipped); //The default causes the screen to go black
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The text displayed on the block if applicable, or null.
|
||||
/// Setting it is temporary to the session, it won't be saved.
|
||||
/// </summary>
|
||||
[TestValue(null)]
|
||||
public string Label
|
||||
{
|
||||
get => BlockEngine.GetBlockInfoViewComponent<TextLabelEntityViewStruct>(this).textLabelComponent?.text;
|
||||
set
|
||||
{
|
||||
var comp = BlockEngine.GetBlockInfoViewComponent<TextLabelEntityViewStruct>(this).textLabelComponent;
|
||||
if (comp != null) comp.text = value;
|
||||
}
|
||||
}
|
||||
|
||||
private BlockGroup blockGroup;
|
||||
/// <summary>
|
||||
/// Returns the block group this block is a part of. Block groups can also be placed using blueprints.
|
||||
/// Returns null if not part of a group, although all blocks should have their own by default.<br />
|
||||
/// Setting the group after the block has been initialized will not update everything properly,
|
||||
/// so you can only set this property on blocks newly placed by your code.<br />
|
||||
/// To set it for existing blocks, you can use the Copy() method and set the property on the resulting block
|
||||
/// (and remove this block).
|
||||
/// </summary>
|
||||
public BlockGroup BlockGroup
|
||||
{
|
||||
get
|
||||
{
|
||||
if (blockGroup != null) return blockGroup;
|
||||
var bgec = BlockEngine.GetBlockInfo<BlockGroupEntityComponent>(this);
|
||||
return blockGroup = bgec.currentBlockGroup == -1 ? null : new BlockGroup(bgec.currentBlockGroup, this);
|
||||
}
|
||||
set
|
||||
{
|
||||
if (Exists)
|
||||
{
|
||||
Logging.LogWarning("Attempted to set group of existing block. This is not supported."
|
||||
+ " Copy the block and set the group of the resulting block.");
|
||||
return;
|
||||
}
|
||||
blockGroup?.RemoveInternal(this);
|
||||
if (!InitData.Valid)
|
||||
return;
|
||||
BlockEngine.GetBlockInfo<BlockGroupEntityComponent>(this).currentBlockGroup = (int?) value?.Id.entityID ?? -1;
|
||||
value?.AddInternal(this);
|
||||
blockGroup = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the block should be static in simulation. If set, it cannot be moved. The effect is temporary, it will not be saved with the block.
|
||||
/// </summary>
|
||||
public bool Static
|
||||
{
|
||||
get => BlockEngine.GetBlockInfo<BlockStaticComponent>(this).isStatic;
|
||||
set => BlockEngine.GetBlockInfo<BlockStaticComponent>(this).isStatic = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the block exists. The other properties will return a default value if the block doesn't exist.
|
||||
/// If the block was just placed, then this will also return false but the properties will work correctly.
|
||||
/// </summary>
|
||||
public bool Exists => BlockEngine.BlockExists(Id);
|
||||
|
||||
/// <summary>
|
||||
/// Returns an array of blocks that are connected to this one. Returns an empty array if the block doesn't exist.
|
||||
/// </summary>
|
||||
public Block[] GetConnectedCubes() => BlockEngine.GetConnectedBlocks(Id);
|
||||
|
||||
/// <summary>
|
||||
/// Removes this block.
|
||||
/// </summary>
|
||||
/// <returns>True if the block exists and could be removed.</returns>
|
||||
public bool Remove() => RemovalEngine.RemoveBlock(Id);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the rigid body of the chunk of blocks this one belongs to during simulation.
|
||||
/// Can be used to apply forces or move the block around while the simulation is running.
|
||||
/// </summary>
|
||||
/// <returns>The SimBody of the chunk or null if the block doesn't exist or not in simulation mode.</returns>
|
||||
public SimBody GetSimBody()
|
||||
{
|
||||
var st = BlockEngine.GetBlockInfo<GridConnectionsEntityStruct>(this);
|
||||
return st.machineRigidBodyId != uint.MaxValue
|
||||
? new SimBody(st.machineRigidBodyId, st.clusterId)
|
||||
: null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a copy of the block in the game with the same properties, stats and wires.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Block Copy()
|
||||
{
|
||||
var block = PlaceNew(Type, Position);
|
||||
block.Rotation = Rotation;
|
||||
block.Color = Color;
|
||||
block.Material = Material;
|
||||
block.UniformScale = UniformScale;
|
||||
block.Scale = Scale;
|
||||
block.copiedFrom = Id;
|
||||
return block;
|
||||
}
|
||||
|
||||
private void OnPlacedInit(object sender, BlockPlacedRemovedEventArgs e)
|
||||
{ //Member method instead of lambda to avoid constantly creating delegates
|
||||
if (e.ID != Id) return;
|
||||
Placed -= OnPlacedInit; //And we can reference it
|
||||
InitData = default; //Remove initializer as it's no longer valid - if the block gets removed it shouldn't be used again
|
||||
if (copiedFrom != default)
|
||||
BlockCloneEngine.CopyBlockStats(copiedFrom, Id);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{nameof(Id)}: {Id}, {nameof(Position)}: {Position}, {nameof(Type)}: {Type}, {nameof(Color)}: {Color}, {nameof(Exists)}: {Exists}";
|
||||
}
|
||||
|
||||
public bool Equals(Block other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
return Id.Equals(other.Id);
|
||||
}
|
||||
|
||||
public bool Equals(EGID other)
|
||||
{
|
||||
return Id.Equals(other);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
return Equals((Block) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Id.GetHashCode();
|
||||
}
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
GameEngineManager.AddGameEngine(PlacementEngine);
|
||||
GameEngineManager.AddGameEngine(MovementEngine);
|
||||
GameEngineManager.AddGameEngine(RotationEngine);
|
||||
GameEngineManager.AddGameEngine(RemovalEngine);
|
||||
GameEngineManager.AddGameEngine(BlockEngine);
|
||||
GameEngineManager.AddGameEngine(BlockEventsEngine);
|
||||
GameEngineManager.AddGameEngine(ScalingEngine);
|
||||
GameEngineManager.AddGameEngine(SignalEngine);
|
||||
GameEngineManager.AddGameEngine(BlockCloneEngine);
|
||||
Wire.signalEngine = SignalEngine; // requires same functionality, no need to duplicate the engine
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,39 +1,62 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Gamecraft.Blocks.BlockGroups;
|
||||
using Svelto.ECS;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
|
||||
using GamecraftModdingAPI.Blocks;
|
||||
using GamecraftModdingAPI.Utility;
|
||||
using TechbloxModdingAPI.Blocks;
|
||||
using TechbloxModdingAPI.Blocks.Engines;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftModdingAPI
|
||||
namespace TechbloxModdingAPI
|
||||
{
|
||||
/// <summary>
|
||||
/// A group of blocks that can be selected together. The placed version of blueprints.
|
||||
/// A group of blocks that can be selected together. The placed version of blueprints. Dispose after usage.
|
||||
/// </summary>
|
||||
public class BlockGroup : ICollection<Block>
|
||||
public class BlockGroup : EcsObjectBase, ICollection<Block>, IDisposable
|
||||
{
|
||||
internal static BlueprintEngine _engine = new BlueprintEngine();
|
||||
public int Id { get; }
|
||||
private readonly Block sourceBlock;
|
||||
private readonly List<Block> blocks;
|
||||
private float3 position, rotation;
|
||||
internal bool PosAndRotCalculated;
|
||||
|
||||
internal BlockGroup(int id, Block block)
|
||||
internal BlockGroup(int id, Block block) : base(new EGID((uint)id,
|
||||
BlockGroupExclusiveGroups.BlockGroupEntityGroup))
|
||||
{
|
||||
if (id == BlockGroupUtility.GROUP_UNASSIGNED)
|
||||
throw new BlockException("Cannot create a block group for blocks without a group!");
|
||||
Id = id;
|
||||
sourceBlock = block;
|
||||
blocks = new List<Block>(GetBlocks());
|
||||
Block.Removed += OnBlockRemoved;
|
||||
}
|
||||
|
||||
|
||||
private void OnBlockRemoved(object sender, BlockPlacedRemovedEventArgs e)
|
||||
{
|
||||
//blocks.RemoveAll(block => block.Id == e.ID); - Allocation heavy
|
||||
int index = -1;
|
||||
for (int i = 0; i < blocks.Count; i++)
|
||||
{
|
||||
if (blocks[i].Id == e.ID)
|
||||
{
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (index != -1) blocks.RemoveAt(index);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Block.Removed -= OnBlockRemoved;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The position of the block group (center). Recalculated if blocks have been added/removed since the last query.
|
||||
/// The position of the block group (center). Can only be used after initialization is complete.
|
||||
/// </summary>
|
||||
public float3 Position
|
||||
{
|
||||
|
@ -56,7 +79,7 @@ namespace GamecraftModdingAPI
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// The rotation of the block group. Recalculated if blocks have been added/removed since the last query.
|
||||
/// The rotation of the block group. Can only be used after initialization is complete.
|
||||
/// </summary>
|
||||
public float3 Rotation
|
||||
{
|
||||
|
@ -93,13 +116,15 @@ namespace GamecraftModdingAPI
|
|||
/// <summary>
|
||||
/// Creates a new block group consisting of a single block.
|
||||
/// You can add more blocks using the Add() method or by setting the BlockGroup property of the blocks.<br />
|
||||
/// Note that only newly placed blocks should be added to groups.
|
||||
/// Note that only newly placed blocks can be added to groups.
|
||||
/// </summary>
|
||||
/// <param name="block">The block to add</param>
|
||||
/// <returns>A new block group containing the given block</returns>
|
||||
public static BlockGroup Create(Block block)
|
||||
{
|
||||
return new BlockGroup(_engine.CreateBlockGroup(default, default), block);
|
||||
var bg = new BlockGroup(_engine.CreateBlockGroup(block.Position, Quaternion.Euler(block.Rotation)), block);
|
||||
block.BlockGroup = bg;
|
||||
return bg;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -131,7 +156,7 @@ namespace GamecraftModdingAPI
|
|||
IEnumerator IEnumerable.GetEnumerator() => blocks.GetEnumerator();
|
||||
|
||||
/// <summary>
|
||||
/// Adds a block to the group. You should only add newly placed blocks
|
||||
/// Adds a block to the group. You can only add newly placed blocks
|
||||
/// so that the game initializes the group membership properly.
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
|
@ -142,11 +167,15 @@ namespace GamecraftModdingAPI
|
|||
item.BlockGroup = this; //Calls AddInternal
|
||||
}
|
||||
|
||||
internal void AddInternal(Block item) => blocks.Add(item);
|
||||
internal void AddInternal(Block item)
|
||||
{
|
||||
blocks.Add(item);
|
||||
_engine.AddBlockToGroup(item.Id, (int) Id.entityID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all blocks from this group.
|
||||
/// You should not remove blocks that have been initialized, only those that you placed recently.
|
||||
/// You cannot remove blocks that have been initialized, only those that you placed recently.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
|
@ -159,7 +188,7 @@ namespace GamecraftModdingAPI
|
|||
|
||||
/// <summary>
|
||||
/// Removes a block from this group.
|
||||
/// You should not remove blocks that have been initialized, only those that you placed recently.
|
||||
/// You cannot remove blocks that have been initialized, only those that you placed recently.
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <returns></returns>
|
||||
|
@ -179,5 +208,10 @@ namespace GamecraftModdingAPI
|
|||
public bool IsReadOnly { get; } = false;
|
||||
|
||||
public Block this[int index] => blocks[index]; //Setting is not supported, since the order doesn't matter
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{nameof(Id)}: {Id}, {nameof(Position)}: {Position}, {nameof(Rotation)}: {Rotation}, {nameof(Count)}: {Count}";
|
||||
}
|
||||
}
|
||||
}
|
64
TechbloxModdingAPI/Blocks/BlockColor.cs
Normal file
64
TechbloxModdingAPI/Blocks/BlockColor.cs
Normal file
|
@ -0,0 +1,64 @@
|
|||
using System;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace TechbloxModdingAPI.Blocks
|
||||
{
|
||||
public struct BlockColor
|
||||
{
|
||||
public BlockColors Color => Index == byte.MaxValue
|
||||
? BlockColors.Default
|
||||
: (BlockColors) (Index % 10);
|
||||
|
||||
public byte Darkness => (byte) (Index == byte.MaxValue
|
||||
? 0
|
||||
: Index / 10);
|
||||
|
||||
public byte Index { get; }
|
||||
|
||||
public BlockColor(byte index)
|
||||
{
|
||||
if (index > 99 && index != byte.MaxValue)
|
||||
throw new ArgumentOutOfRangeException(nameof(index), "Invalid color index. Must be 0-90 or 255.");
|
||||
Index = index;
|
||||
}
|
||||
|
||||
public BlockColor(BlockColors color, byte darkness = 0)
|
||||
{
|
||||
if (darkness > 9)
|
||||
throw new ArgumentOutOfRangeException(nameof(darkness), "Darkness must be 0-9 where 0 is default.");
|
||||
if (color > BlockColors.Red && color != BlockColors.Default) //Last valid color
|
||||
throw new ArgumentOutOfRangeException(nameof(color), "Invalid color!");
|
||||
Index = (byte) (darkness * 10 + (byte) color);
|
||||
}
|
||||
|
||||
public static implicit operator BlockColor(BlockColors color)
|
||||
{
|
||||
return new BlockColor(color);
|
||||
}
|
||||
|
||||
public float4 RGBA => Block.BlockEngine.ConvertBlockColor(Index);
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{nameof(Color)}: {Color}, {nameof(Darkness)}: {Darkness}";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Preset block colours
|
||||
/// </summary>
|
||||
public enum BlockColors : byte
|
||||
{
|
||||
Default = byte.MaxValue,
|
||||
White = 0,
|
||||
Pink,
|
||||
Purple,
|
||||
Blue,
|
||||
Aqua,
|
||||
Green,
|
||||
Lime,
|
||||
Yellow,
|
||||
Orange,
|
||||
Red
|
||||
}
|
||||
}
|
|
@ -1,10 +1,10 @@
|
|||
using System;
|
||||
|
||||
using GamecraftModdingAPI;
|
||||
using TechbloxModdingAPI;
|
||||
|
||||
namespace GamecraftModdingAPI.Blocks
|
||||
namespace TechbloxModdingAPI.Blocks
|
||||
{
|
||||
public class BlockException : GamecraftModdingAPIException
|
||||
public class BlockException : TechbloxModdingAPIException
|
||||
{
|
||||
public BlockException()
|
||||
{
|
278
TechbloxModdingAPI/Blocks/BlockIDs.cs
Normal file
278
TechbloxModdingAPI/Blocks/BlockIDs.cs
Normal file
|
@ -0,0 +1,278 @@
|
|||
namespace TechbloxModdingAPI.Blocks
|
||||
{
|
||||
/// <summary>
|
||||
/// Possible block types
|
||||
/// </summary>
|
||||
public enum BlockIDs : ushort
|
||||
{
|
||||
/// <summary>
|
||||
/// Called "nothing" in Techblox. (DBID.NOTHING)
|
||||
/// </summary>
|
||||
Invalid = ushort.MaxValue,
|
||||
Cube = 0,
|
||||
Wedge,
|
||||
QuarterPyramid,
|
||||
Tetrahedron,
|
||||
RoundedWedge,
|
||||
RoundedQuarterPyramid,
|
||||
RoundedTetrahedron,
|
||||
NegativeQuarterPyramid,
|
||||
NegativeTetrahedron,
|
||||
RoundedNegativeQuarterPyramid,
|
||||
RoundedNegativeTetrahedron,
|
||||
Plate,
|
||||
PlateWedge,
|
||||
PlateQuarterPyramid,
|
||||
PlateTetrahedron,
|
||||
Sphere,
|
||||
CarWheelArch = 47,
|
||||
CarArchSmallFlare,
|
||||
CarArchFlare,
|
||||
CarArchExtrudedFlare,
|
||||
Axle = 100,
|
||||
Hinge,
|
||||
BallJoint,
|
||||
UniversalJoint,
|
||||
TelescopicJoint,
|
||||
DampedHingeSpring,
|
||||
DampedAxleSpring,
|
||||
DampedSpring,
|
||||
WheelRigNoSteering,
|
||||
WheelRigWithSteering,
|
||||
PlateTriangle = 130,
|
||||
PlateCircle,
|
||||
PlateQuarterCircle,
|
||||
PlateRoundedWedge,
|
||||
PlateRoundedTetrahedron,
|
||||
Cone,
|
||||
ConeSegment,
|
||||
DoubleSliced,
|
||||
HalfDoubleSliced,
|
||||
EighthPyramid,
|
||||
Hemisphere,
|
||||
WideCylinder,
|
||||
WideCylinderBend,
|
||||
WideCylinderT,
|
||||
WideCylinderCross,
|
||||
WideCylinderCorner,
|
||||
NarrowCylinder,
|
||||
NarrowCylinderBend,
|
||||
NarrowCylinderT,
|
||||
NarrowCylinderCross,
|
||||
DriverSeat,
|
||||
PassengerSeat,
|
||||
Engine,
|
||||
NarrowCylinderCorner,
|
||||
PlateWideCylinder,
|
||||
PlateNarrowCylinder,
|
||||
PlateNegativeTetrahedron,
|
||||
PlateNegativeQuarterPyramid,
|
||||
PlateRoundedNegativeTetrahedron,
|
||||
PlateRoundedNegativeQuarterPyramid,
|
||||
HeadlampSquare,
|
||||
HeadlampCircle,
|
||||
HeadlampWedge,
|
||||
WideCylinderDiagonal,
|
||||
NarrowCylinderDiagonal,
|
||||
HeadlampTetrahedron,
|
||||
GoKartEngine,
|
||||
Screen5X2Y2Z,
|
||||
Screen5X2Y3Z,
|
||||
Screen5X2Y5Z,
|
||||
Screen9X2Y2Z,
|
||||
Screen9X3Y2Z,
|
||||
Screen9X2Y3Z,
|
||||
Screen9X3Y3Z,
|
||||
Screen9X2Y5Z,
|
||||
Screen9X3Y5Z,
|
||||
Screen11X3Y2Z,
|
||||
Screen11X3Y3Z,
|
||||
Screen11X3Y5Z,
|
||||
Window6X2Y2Z,
|
||||
Window6X3Y2Z,
|
||||
Window6X2Y2ZS1,
|
||||
Window6X3Y2ZS1,
|
||||
Window6X2Y2ZS2,
|
||||
Window6X3Y2ZS2,
|
||||
Window6X2Y2ZS4,
|
||||
Window6X3Y2ZS4,
|
||||
FrameSquare,
|
||||
FrameSkewedSquare,
|
||||
FrameTriangle,
|
||||
FrameSkewedTriangle,
|
||||
GlassFrameSquare,
|
||||
GlassFrameSkewedSquare,
|
||||
GlassFrameTriangle,
|
||||
GlassFrameSkewedTriangle,
|
||||
GlassPlate,
|
||||
GlassPlateTriangle,
|
||||
GoKartWheelRigNoSteering,
|
||||
GoKartWheelRigWithSteering,
|
||||
GoKartSeat,
|
||||
CarWheelWideProfile,
|
||||
CarWheel,
|
||||
GoKartWheelWideProfile,
|
||||
GoKartWheel,
|
||||
ANDLogicGate,
|
||||
ORLogicGate,
|
||||
NOTLogicGate,
|
||||
NANDLogicGate,
|
||||
NORLogicGate,
|
||||
XORLogicGate,
|
||||
XNORLogicGate,
|
||||
AdderMathBlock,
|
||||
SubtractorMathBlock,
|
||||
MultiplierMathBlock,
|
||||
DividerMathBlock,
|
||||
InverterMathBlock,
|
||||
AverageMathBlock,
|
||||
AbsoluteMathBlock,
|
||||
MinMathBlock,
|
||||
MaxMathBlock,
|
||||
SimpleConnector,
|
||||
Motor,
|
||||
AxleServo,
|
||||
HingeServo,
|
||||
Piston,
|
||||
Button,
|
||||
Switch,
|
||||
Dial,
|
||||
Lever,
|
||||
ThreeWaySwitch,
|
||||
EqualsMathBlock,
|
||||
LessThanMathBlock,
|
||||
LessThanOrEqualMathBlock,
|
||||
GreaterThanMathBlock,
|
||||
GreaterThanOrEqualMathBlock,
|
||||
HatchbackWheelRigNoSteering,
|
||||
HatchbackWheelRigWithSteering,
|
||||
HatchbackEngine,
|
||||
HatchbackWheel,
|
||||
HatchbackWheelArch,
|
||||
HatchbackArchSmallFlare,
|
||||
HatchbackArchFlare,
|
||||
CeilingStripLight,
|
||||
CardboardBox,
|
||||
BarrierRail,
|
||||
BarrierRailEnd,
|
||||
TruckWheel,
|
||||
HatchbackWheelWideProfile,
|
||||
TruckWheelRigWithSteering = 249,
|
||||
TruckWheelRigNoSteering,
|
||||
HatchbackDriverSeat,
|
||||
HatchbackPassengerSeat,
|
||||
FormulaEngine,
|
||||
SmallGrass,
|
||||
SmallGrassRoad,
|
||||
GrassBridge,
|
||||
SmallGrassTurn,
|
||||
MediumGrassTurn,
|
||||
LargeGrassTurn,
|
||||
ExtraLargeGrassTurn,
|
||||
TruckWheelDouble,
|
||||
TruckWheelArch,
|
||||
TruckArchSingleFlare,
|
||||
WoodenDoorWithWindow,
|
||||
TyreBarrierCorner,
|
||||
TyreBarrierEdge,
|
||||
TyreBarrierCenter,
|
||||
AppleTree,
|
||||
AppleForestTree,
|
||||
FormulaWheel,
|
||||
FormulaWheelRear,
|
||||
AppleSapling,
|
||||
GrassHill,
|
||||
GrassHillInnerCorner,
|
||||
GrassHillOuterCorner,
|
||||
GrassRoadHill,
|
||||
FormulaSeat,
|
||||
SmallDirt,
|
||||
SmallDirtRoad,
|
||||
SmallDirtTurn,
|
||||
MediumDirtTurn,
|
||||
LargeDirtTurn,
|
||||
ExtraLargeDirtTurn,
|
||||
SmallGrid,
|
||||
MonsterTruckWheel,
|
||||
SmallGrassGridStart,
|
||||
SmallGrassRumbleStripRoad,
|
||||
SmallGrassRumbleStripEndRoad,
|
||||
SmallGrassStartLine,
|
||||
MonsterTruckEngine,
|
||||
DirtHill,
|
||||
DirtHillInnerCorner,
|
||||
DirtHillOuterCorner,
|
||||
BuildingWindowEdge,
|
||||
BuildingWindowCorner,
|
||||
BuildingWindowStraight,
|
||||
BuildingWindowTJunction,
|
||||
BuildingWindowCross,
|
||||
BuildingWindowEdgeSill,
|
||||
BuildingWindowCornerSill,
|
||||
BuildingWindowTJunctionSill,
|
||||
Broadleaf,
|
||||
ForestBroadleaf,
|
||||
AzaleaBush,
|
||||
AzaleaFlowers1,
|
||||
AzaleaFlowers2,
|
||||
TreeStump1,
|
||||
TreeStump2,
|
||||
FieldJuniper,
|
||||
ForestJuniper,
|
||||
JuniperSapling,
|
||||
JuniperSeedling,
|
||||
FieldRedMaple,
|
||||
RedMapleForest1,
|
||||
RedMapleForest2,
|
||||
RedMapleSapling,
|
||||
FieldWhiteSpruce,
|
||||
ForestWhiteSpruce,
|
||||
WhiteSpruceSapling,
|
||||
GirderBase,
|
||||
GirderStraight,
|
||||
GirderDiagonal,
|
||||
GirderCorner,
|
||||
PostBase,
|
||||
PostStraight,
|
||||
PostLShape,
|
||||
PostTJunction,
|
||||
PostCross,
|
||||
PostCorner,
|
||||
PostDiagonal,
|
||||
DirtRock1,
|
||||
DirtRock2,
|
||||
DirtRock3,
|
||||
DirtRock4,
|
||||
DirtRoadHill,
|
||||
WoodenPalette,
|
||||
ElderberryBush,
|
||||
BarrelCactus,
|
||||
KnapweedFlower,
|
||||
MarigoldFlowers,
|
||||
TrampledBushyBluestep,
|
||||
RoughGrass,
|
||||
DogRose,
|
||||
WesternSwordFern,
|
||||
BackyardGrass,
|
||||
ThickGrass,
|
||||
FireExtinguisher,
|
||||
DirtLowRamp,
|
||||
DirtTabletopRamp,
|
||||
MonsterTruckWheelRigNoSteering,
|
||||
MonsterTruckWheelRigWithSteering,
|
||||
MeadowCloudyDayAtmosphere,
|
||||
BarrierRailDiagonal,
|
||||
DirtHighRamp,
|
||||
GrassRock1,
|
||||
GrassRock2,
|
||||
GrassRock3,
|
||||
GrassRock4,
|
||||
GreenFieldsSunnyDayAtmosphere,
|
||||
RedMountainsDawnAtmosphere,
|
||||
HighFantasySunriseAtmosphere,
|
||||
/// <summary>
|
||||
/// The grid block used by the world editor, named Small Grid like the other one
|
||||
/// </summary>
|
||||
SmallGridInWorldEditor
|
||||
}
|
||||
}
|
35
TechbloxModdingAPI/Blocks/BlockMaterial.cs
Normal file
35
TechbloxModdingAPI/Blocks/BlockMaterial.cs
Normal file
|
@ -0,0 +1,35 @@
|
|||
namespace TechbloxModdingAPI.Blocks
|
||||
{
|
||||
public enum BlockMaterial : byte
|
||||
{
|
||||
Default = byte.MaxValue,
|
||||
SteelBodywork = 0,
|
||||
RigidSteel,
|
||||
CarbonFiber,
|
||||
Plastic,
|
||||
Wood = 6,
|
||||
RigidSteelPainted,
|
||||
RigidSteelRustedPaint,
|
||||
RigidSteelHeavyRust,
|
||||
SteelBodyworkMetallicPaint,
|
||||
SteelBodyworkRustedPaint,
|
||||
SteelBodyworkHeavyRust,
|
||||
WoodVarnishedDark,
|
||||
Chrome,
|
||||
FenceChainLink,
|
||||
ConcreteUnpainted,
|
||||
Grid9x9,
|
||||
CeramicTileFloor,
|
||||
PlasticBumpy,
|
||||
PlasticDustySmeared,
|
||||
AluminiumGarageDoor,
|
||||
SteelRigidScratched,
|
||||
AluminiumBrushedTinted,
|
||||
AluminiumSheetStained,
|
||||
ConcretePaintedGrooves,
|
||||
PlasticSpecklySatin,
|
||||
SteelBodyworkPaintedChipped,
|
||||
WoodPainted,
|
||||
WoodRoughGrungy,
|
||||
}
|
||||
}
|
251
TechbloxModdingAPI/Blocks/BlockTests.cs
Normal file
251
TechbloxModdingAPI/Blocks/BlockTests.cs
Normal file
|
@ -0,0 +1,251 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
using DataLoader;
|
||||
using Svelto.Tasks;
|
||||
using Svelto.Tasks.Enumerators;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
|
||||
using TechbloxModdingAPI.App;
|
||||
using TechbloxModdingAPI.Tests;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
namespace TechbloxModdingAPI.Blocks
|
||||
{
|
||||
#if TEST
|
||||
/// <summary>
|
||||
/// Block test cases. Not accessible in release versions.
|
||||
/// </summary>
|
||||
[APITestClass]
|
||||
public static class BlockTests
|
||||
{
|
||||
[APITestCase(TestType.Game)] //At least one block must be placed for simulation to work
|
||||
public static void TestPlaceNew()
|
||||
{
|
||||
Block newBlock = Block.PlaceNew(BlockIDs.Cube, float3.zero);
|
||||
Assert.NotNull(newBlock.Id, "Newly placed block is missing Id. This should be populated when the block is placed.", "Newly placed block Id is not null, block successfully placed.");
|
||||
}
|
||||
|
||||
[APITestCase(TestType.EditMode)]
|
||||
public static void TestInitProperty()
|
||||
{
|
||||
Block newBlock = Block.PlaceNew(BlockIDs.Cube, float3.zero + 2);
|
||||
if (!Assert.CloseTo(newBlock.Position, (float3.zero + 2), $"Newly placed block at {newBlock.Position} is expected at {Unity.Mathematics.float3.zero + 2}.", "Newly placed block position matches.")) return;
|
||||
//Assert.Equal(newBlock.Exists, true, "Newly placed block does not exist, possibly because Sync() skipped/missed/failed.", "Newly placed block exists, Sync() successful.");
|
||||
}
|
||||
|
||||
[APITestCase(TestType.EditMode)]
|
||||
public static void TestBlockIDCoverage()
|
||||
{
|
||||
Assert.Equal(
|
||||
FullGameFields._dataDb.GetValues<CubeListData>().Keys.Select(ushort.Parse).OrderBy(id => id)
|
||||
.SequenceEqual(Enum.GetValues(typeof(BlockIDs)).Cast<ushort>().OrderBy(id => id)
|
||||
.Except(new[] {(ushort) BlockIDs.Invalid})), true,
|
||||
"Block ID enum is different than the known block types, update needed.",
|
||||
"Block ID enum matches the known block types.");
|
||||
}
|
||||
|
||||
private static Block[] blocks; // Store placed blocks as some blocks are already present as the workshop and the game save
|
||||
[APITestCase(TestType.EditMode)]
|
||||
public static void TestBlockIDs()
|
||||
{
|
||||
float3 pos = new float3();
|
||||
var values = Enum.GetValues(typeof(BlockIDs));
|
||||
blocks = new Block[values.Length - 1]; // Minus the invalid ID
|
||||
int i = 0;
|
||||
foreach (BlockIDs id in values)
|
||||
{
|
||||
if (id == BlockIDs.Invalid) continue;
|
||||
try
|
||||
{
|
||||
blocks[i++] = Block.PlaceNew(id, pos);
|
||||
pos += 0.2f;
|
||||
}
|
||||
catch (Exception e)
|
||||
{ //Only print failed case
|
||||
Assert.Fail($"Failed to place block type {id}: {e}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Assert.Pass("Placing all possible block types succeeded.");
|
||||
}
|
||||
|
||||
[APITestCase(TestType.EditMode)]
|
||||
public static IEnumerator<TaskContract> TestBlockProperties()
|
||||
{ //Uses the result of the previous test case
|
||||
yield return Yield.It;
|
||||
if (blocks is null)
|
||||
yield break;
|
||||
for (var index = 0; index < blocks.Length; index++)
|
||||
{
|
||||
if (index % 50 == 0) yield return Yield.It; //The material or flipped status can only be changed 130 times per submission
|
||||
var block = blocks[index];
|
||||
if (!block.Exists) continue;
|
||||
foreach (var property in block.GetType().GetProperties())
|
||||
{
|
||||
if (property.Name == "Material" || property.Name == "Flipped") continue; // TODO: Crashes in game
|
||||
if (property.Name == "Material" || property.Name == "Flipped")
|
||||
{
|
||||
Console.WriteLine("Block type: "+block.Type);
|
||||
Console.WriteLine("Will set " + property.Name);
|
||||
yield return new WaitForSecondsEnumerator(1).Continue();
|
||||
}
|
||||
//Includes specialised block properties
|
||||
if (property.SetMethod == null) continue;
|
||||
var testValues = new (Type, object, Predicate<object>)[]
|
||||
{
|
||||
//(type, default value, predicate or null for equality)
|
||||
(typeof(long), 3, null),
|
||||
(typeof(int), 4, null),
|
||||
(typeof(double), 5.2f, obj => Math.Abs((double) obj - 5.2f) < float.Epsilon),
|
||||
(typeof(float), 5.2f, obj => Math.Abs((float) obj - 5.2f) < float.Epsilon),
|
||||
(typeof(bool), true, obj => (bool) obj),
|
||||
(typeof(string), "Test", obj => (string) obj == "Test"), //String equality check
|
||||
(typeof(float3), (float3) 2, obj => math.all((float3) obj - 2 < (float3) float.Epsilon)),
|
||||
(typeof(BlockColor), new BlockColor(BlockColors.Aqua, 2), null),
|
||||
(typeof(float4), (float4) 5, obj => math.all((float4) obj - 5 < (float4) float.Epsilon))
|
||||
};
|
||||
var propType = property.PropertyType;
|
||||
if (!propType.IsValueType) continue;
|
||||
(object valueToUse, Predicate<object> predicateToUse) = (null, null);
|
||||
foreach (var (type, value, predicate) in testValues)
|
||||
{
|
||||
if (type.IsAssignableFrom(propType))
|
||||
{
|
||||
valueToUse = value;
|
||||
predicateToUse = predicate ?? (obj => Equals(obj, value));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (propType.IsEnum)
|
||||
{
|
||||
var values = propType.GetEnumValues();
|
||||
valueToUse = values.GetValue(values.Length / 2);
|
||||
predicateToUse = val => Equals(val, valueToUse);
|
||||
}
|
||||
|
||||
if (valueToUse == null)
|
||||
{
|
||||
Assert.Fail($"Property {block.GetType().Name}.{property.Name} has an unknown type {propType}, test needs fixing.");
|
||||
yield break;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
property.SetValue(block, valueToUse);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Assert.Fail($"Failed to set property {block.GetType().Name}.{property.Name} to {valueToUse}\n{e}");
|
||||
}
|
||||
object got;
|
||||
try
|
||||
{
|
||||
got = property.GetValue(block);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Assert.Fail($"Failed to get property {block.GetType().Name}.{property.Name}\n{e}");
|
||||
continue;
|
||||
}
|
||||
var attr = property.GetCustomAttribute<TestValueAttribute>();
|
||||
if (!predicateToUse(got) && (attr == null || !Equals(attr.PossibleValue, got)))
|
||||
{
|
||||
Assert.Fail($"Property {block.GetType().Name}.{property.Name} value {got} does not equal {valueToUse} for block {block}.");
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Assert.Pass("Setting all possible properties of all registered API block types succeeded.");
|
||||
}
|
||||
|
||||
[APITestCase(TestType.EditMode)]
|
||||
public static IEnumerator<TaskContract> TestDefaultValue()
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
{ //Tests shared defaults
|
||||
var block = Block.PlaceNew(BlockIDs.Cube, 1);
|
||||
while (!block.Exists)
|
||||
yield return Yield.It;
|
||||
block.Remove();
|
||||
while (block.Exists)
|
||||
yield return Yield.It;
|
||||
if(!Assert.Equal(block.Position, default,
|
||||
$"Block position default value {block.Position} is incorrect, should be 0.",
|
||||
$"Block position default value {block.Position} matches default."))
|
||||
yield break;
|
||||
block.Position = 4;
|
||||
}
|
||||
}
|
||||
|
||||
[APITestCase(TestType.EditMode)]
|
||||
public static void TestDampedSpring()
|
||||
{
|
||||
Block newBlock = Block.PlaceNew(BlockIDs.DampedSpring, Unity.Mathematics.float3.zero + 1);
|
||||
DampedSpring b = null; // Note: the assignment operation is a lambda, which slightly confuses the compiler
|
||||
Assert.Errorless(() => { b = (DampedSpring) newBlock; }, "Casting block to DampedSpring raised an exception: ", "Casting block to DampedSpring completed without issue.");
|
||||
if (!Assert.CloseTo(b.Stiffness, 1f, $"DampedSpring.Stiffness {b.Stiffness} does not equal default value, possibly because it failed silently.", "DampedSpring.Stiffness is close enough to default.")) return;
|
||||
if (!Assert.CloseTo(b.Damping, 0.1f, $"DampedSpring.Damping {b.Damping} does not equal default value, possibly because it failed silently.", "DampedSpring.Damping is close enough to default.")) return;
|
||||
if (!Assert.CloseTo(b.MaxExtension, 0.3f, $"DampedSpring.MaxExtension {b.MaxExtension} does not equal default value, possibly because it failed silently.", "DampedSpring.MaxExtension is close enough to default.")) return;
|
||||
}
|
||||
|
||||
/*[APITestCase(TestType.Game)]
|
||||
public static void TestMusicBlock1()
|
||||
{
|
||||
Block newBlock = Block.PlaceNew(BlockIDs.MusicBlock, Unity.Mathematics.float3.zero + 2);
|
||||
MusicBlock b = null; // Note: the assignment operation is a lambda, which slightly confuses the compiler
|
||||
Assert.Errorless(() => { b = newBlock.Specialise<MusicBlock>(); }, "Block.Specialize<MusicBlock>() raised an exception: ", "Block.Specialize<MusicBlock>() completed without issue.");
|
||||
if (!Assert.NotNull(b, "Block.Specialize<MusicBlock>() returned null, possibly because it failed silently.", "Specialized MusicBlock is not null.")) return;
|
||||
if (!Assert.CloseTo(b.Volume, 100f, $"MusicBlock.Volume {b.Volume} does not equal default value, possibly because it failed silently.", "MusicBlock.Volume is close enough to default.")) return;
|
||||
if (!Assert.Equal(b.TrackIndex, 0, $"MusicBlock.TrackIndex {b.TrackIndex} does not equal default value, possibly because it failed silently.", "MusicBlock.TrackIndex is equal to default.")) return;
|
||||
_musicBlock = b;
|
||||
}
|
||||
|
||||
private static MusicBlock _musicBlock;
|
||||
|
||||
[APITestCase(TestType.EditMode)]
|
||||
public static void TestMusicBlock2()
|
||||
{
|
||||
//Block newBlock = Block.GetLastPlacedBlock();
|
||||
var b = _musicBlock;
|
||||
if (!Assert.NotNull(b, "Block.Specialize<MusicBlock>() returned null, possibly because it failed silently.", "Specialized MusicBlock is not null.")) return;
|
||||
b.IsPlaying = true; // play sfx
|
||||
if (!Assert.Equal(b.IsPlaying, true, $"MusicBlock.IsPlaying {b.IsPlaying} does not equal true, possibly because it failed silently.", "MusicBlock.IsPlaying is set properly.")) return;
|
||||
if (!Assert.Equal(b.ChannelType, ChannelType.None, $"MusicBlock.ChannelType {b.ChannelType} does not equal default value, possibly because it failed silently.", "MusicBlock.ChannelType is equal to default.")) return;
|
||||
//Assert.Log(b.Track.ToString());
|
||||
if (!Assert.Equal(b.Track.ToString(), new Guid("3237ff8f-f5f2-4f84-8144-496ca280f8c0").ToString(), $"MusicBlock.Track {b.Track} does not equal default value, possibly because it failed silently.", "MusicBlock.Track is equal to default.")) return;
|
||||
}
|
||||
|
||||
[APITestCase(TestType.EditMode)]
|
||||
public static void TestLogicGate()
|
||||
{
|
||||
Block newBlock = Block.PlaceNew(BlockIDs.NOTLogicBlock, Unity.Mathematics.float3.zero + 1);
|
||||
LogicGate b = null; // Note: the assignment operation is a lambda, which slightly confuses the compiler
|
||||
Assert.Errorless(() => { b = newBlock.Specialise<LogicGate>(); }, "Block.Specialize<LogicGate>() raised an exception: ", "Block.Specialize<LogicGate>() completed without issue.");
|
||||
if (!Assert.NotNull(b, "Block.Specialize<LogicGate>() returned null, possibly because it failed silently.", "Specialized LogicGate is not null.")) return;
|
||||
if (!Assert.Equal(b.InputCount, 1u, $"LogicGate.InputCount {b.InputCount} does not equal default value, possibly because it failed silently.", "LogicGate.InputCount is default.")) return;
|
||||
if (!Assert.Equal(b.OutputCount, 1u, $"LogicGate.OutputCount {b.OutputCount} does not equal default value, possibly because it failed silently.", "LogicGate.OutputCount is default.")) return;
|
||||
if (!Assert.NotNull(b, "Block.Specialize<LogicGate>() returned null, possibly because it failed silently.", "Specialized LogicGate is not null.")) return;
|
||||
//if (!Assert.Equal(b.PortName(0, true), "Input", $"LogicGate.PortName(0, input:true) {b.PortName(0, true)} does not equal default value, possibly because it failed silently.", "LogicGate.PortName(0, input:true) is close enough to default.")) return;
|
||||
LogicGate target = null;
|
||||
if (!Assert.Errorless(() => { target = Block.PlaceNew<LogicGate>(BlockIDs.ANDLogicBlock, Unity.Mathematics.float3.zero + 2); })) return;
|
||||
Wire newWire = null;
|
||||
if (!Assert.Errorless(() => { newWire = b.Connect(0, target, 0);})) return;
|
||||
if (!Assert.NotNull(newWire, "SignalingBlock.Connect(...) returned null, possible because it failed silently.", "SignalingBlock.Connect(...) returned a non-null value.")) return;
|
||||
}*/
|
||||
|
||||
/*[APITestCase(TestType.EditMode)]
|
||||
public static void TestSpecialiseError()
|
||||
{
|
||||
Block newBlock = Block.PlaceNew(BlockIDs.Bench, new float3(1, 1, 1));
|
||||
if (Assert.Errorful<BlockTypeException>(() => newBlock.Specialise<MusicBlock>(), "Block.Specialise<MusicBlock>() was expected to error on a bench block.", "Block.Specialise<MusicBlock>() errored as expected for a bench block.")) return;
|
||||
}*/
|
||||
}
|
||||
#endif
|
||||
}
|
71
TechbloxModdingAPI/Blocks/DampedSpring.cs
Normal file
71
TechbloxModdingAPI/Blocks/DampedSpring.cs
Normal file
|
@ -0,0 +1,71 @@
|
|||
namespace TechbloxModdingAPI.Blocks
|
||||
{
|
||||
using RobocraftX.Common;
|
||||
using Svelto.ECS;
|
||||
|
||||
|
||||
public class DampedSpring : SignalingBlock
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a(n) DampedSpring object representing an existing block.
|
||||
/// </summary>
|
||||
public DampedSpring(EGID egid) :
|
||||
base(egid)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a(n) DampedSpring object representing an existing block.
|
||||
/// </summary>
|
||||
public DampedSpring(uint id) :
|
||||
base(new EGID(id, CommonExclusiveGroups.DAMPEDSPRING_BLOCK_GROUP))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the DampedSpring's Stiffness property. Tweakable stat.
|
||||
/// </summary>
|
||||
public float Stiffness
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<RobocraftX.Blocks.TweakableJointDampingComponent>(this).stiffness;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<RobocraftX.Blocks.TweakableJointDampingComponent>(this).stiffness = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the DampedSpring's Damping property. Tweakable stat.
|
||||
/// </summary>
|
||||
public float Damping
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<RobocraftX.Blocks.TweakableJointDampingComponent>(this).damping;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<RobocraftX.Blocks.TweakableJointDampingComponent>(this).damping = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the DampedSpring's MaxExtension property. Tweakable stat.
|
||||
/// </summary>
|
||||
public float MaxExtension
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<RobocraftX.Blocks.DampedSpringReadOnlyStruct>(this).maxExtent;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<RobocraftX.Blocks.DampedSpringReadOnlyStruct>(this).maxExtent = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
382
TechbloxModdingAPI/Blocks/Engine.cs
Normal file
382
TechbloxModdingAPI/Blocks/Engine.cs
Normal file
|
@ -0,0 +1,382 @@
|
|||
namespace TechbloxModdingAPI.Blocks
|
||||
{
|
||||
using RobocraftX.Common;
|
||||
using Svelto.ECS;
|
||||
|
||||
|
||||
public class Engine : SignalingBlock
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a(n) Engine object representing an existing block.
|
||||
/// </summary>
|
||||
public Engine(EGID egid) :
|
||||
base(egid)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a(n) Engine object representing an existing block.
|
||||
/// </summary>
|
||||
public Engine(uint id) :
|
||||
base(new EGID(id, CommonExclusiveGroups.ENGINE_BLOCK_BUILD_GROUP))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's On property. May not be saved.
|
||||
/// </summary>
|
||||
public bool On
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).engineOn;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).engineOn = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's CurrentGear property. May not be saved.
|
||||
/// </summary>
|
||||
public int CurrentGear
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).currentGear;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).currentGear = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's GearChangeCountdown property. May not be saved.
|
||||
/// </summary>
|
||||
public float GearChangeCountdown
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).gearChangeCountdown;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).gearChangeCountdown = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's CurrentRpmAV property. May not be saved.
|
||||
/// </summary>
|
||||
public float CurrentRpmAV
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).currentRpmAV;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).currentRpmAV = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's CurrentRpmLV property. May not be saved.
|
||||
/// </summary>
|
||||
public float CurrentRpmLV
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).currentRpmLV;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).currentRpmLV = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's TargetRpmAV property. May not be saved.
|
||||
/// </summary>
|
||||
public float TargetRpmAV
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).targetRpmAV;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).targetRpmAV = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's TargetRpmLV property. May not be saved.
|
||||
/// </summary>
|
||||
public float TargetRpmLV
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).targetRpmLV;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).targetRpmLV = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's CurrentTorque property. May not be saved.
|
||||
/// </summary>
|
||||
public float CurrentTorque
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).currentTorque;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).currentTorque = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's TotalWheelVelocityAV property. May not be saved.
|
||||
/// </summary>
|
||||
public float TotalWheelVelocityAV
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).totalWheelVelocityAV;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).totalWheelVelocityAV = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's TotalWheelVelocityLV property. May not be saved.
|
||||
/// </summary>
|
||||
public float TotalWheelVelocityLV
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).totalWheelVelocityLV;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).totalWheelVelocityLV = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's TotalWheelCount property. May not be saved.
|
||||
/// </summary>
|
||||
public int TotalWheelCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).totalWheelCount;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).totalWheelCount = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's LastGearUpInput property. May not be saved.
|
||||
/// </summary>
|
||||
public bool LastGearUpInput
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).lastGearUpInput;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).lastGearUpInput = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's LastGearDownInput property. May not be saved.
|
||||
/// </summary>
|
||||
public bool LastGearDownInput
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).lastGearDownInput;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).lastGearDownInput = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's ManualToAutoGearCoolOffCounter property. May not be saved.
|
||||
/// </summary>
|
||||
public float ManualToAutoGearCoolOffCounter
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).manualToAutoGearCoolOffCounter;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).manualToAutoGearCoolOffCounter = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's Load property. May not be saved.
|
||||
/// </summary>
|
||||
public float Load
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).load;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockComponent>(this).load = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's Power property. Tweakable stat.
|
||||
/// </summary>
|
||||
public float Power
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockTweakableComponent>(this).power;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockTweakableComponent>(this).power = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's AutomaticGears property. Tweakable stat.
|
||||
/// </summary>
|
||||
public bool AutomaticGears
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockTweakableComponent>(this).automaticGears;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockTweakableComponent>(this).automaticGears = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's GearChangeTime property. May not be saved.
|
||||
/// </summary>
|
||||
public float GearChangeTime
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockReadonlyComponent>(this).gearChangeTime;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockReadonlyComponent>(this).gearChangeTime = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's MinRpm property. May not be saved.
|
||||
/// </summary>
|
||||
public float MinRpm
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockReadonlyComponent>(this).minRpm;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockReadonlyComponent>(this).minRpm = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's MaxRpm property. May not be saved.
|
||||
/// </summary>
|
||||
public float MaxRpm
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockReadonlyComponent>(this).maxRpm;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockReadonlyComponent>(this).maxRpm = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Engine's GearDownRpms property. May not be saved.
|
||||
/// </summary>
|
||||
public float[] GearDownRpms
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockReadonlyComponent>(this).gearDownRpms.ToManagedArray<float>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's GearUpRpm property. May not be saved.
|
||||
/// </summary>
|
||||
public float GearUpRpm
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockReadonlyComponent>(this).gearUpRpm;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockReadonlyComponent>(this).gearUpRpm = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's MaxRpmChange property. May not be saved.
|
||||
/// </summary>
|
||||
public float MaxRpmChange
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockReadonlyComponent>(this).maxRpmChange;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockReadonlyComponent>(this).maxRpmChange = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Engine's ManualToAutoGearCoolOffTime property. May not be saved.
|
||||
/// </summary>
|
||||
public float ManualToAutoGearCoolOffTime
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockReadonlyComponent>(this).manualToAutoGearCoolOffTime;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.EngineBlock.EngineBlockReadonlyComponent>(this).manualToAutoGearCoolOffTime = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
105
TechbloxModdingAPI/Blocks/Engines/BlockCloneEngine.cs
Normal file
105
TechbloxModdingAPI/Blocks/Engines/BlockCloneEngine.cs
Normal file
|
@ -0,0 +1,105 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Gamecraft.Wires;
|
||||
using HarmonyLib;
|
||||
using RobocraftX.Blocks;
|
||||
using RobocraftX.Character;
|
||||
using RobocraftX.Common;
|
||||
using Svelto.DataStructures;
|
||||
using Svelto.ECS;
|
||||
using TechbloxModdingAPI.Engines;
|
||||
|
||||
namespace TechbloxModdingAPI.Blocks.Engines
|
||||
{
|
||||
public class BlockCloneEngine : IApiEngine
|
||||
{
|
||||
private static Type copyEngineType =
|
||||
AccessTools.TypeByName("Gamecraft.GUI.Tweaks.Engines.CopyTweaksOnPickEngine");
|
||||
private static Type copyWireEngineType =
|
||||
AccessTools.TypeByName("Gamecraft.Wires.WireConnectionCopyOnPickEngine");
|
||||
private static Type createWireEngineType =
|
||||
AccessTools.TypeByName("RobocraftX.GUI.Wires.WireConnectionCreateOnPlaceEngine");
|
||||
|
||||
private MethodBase copyFromBlock = AccessTools.Method(copyEngineType, "CopyTweaksFromBlock");
|
||||
private MethodBase copyToBlock = AccessTools.Method(copyEngineType, "ApplyTweaksToPlacedBlock");
|
||||
private MethodBase copyWireFromBlock = AccessTools.Method(copyWireEngineType, "CopyWireInputsAndOutputs");
|
||||
private MethodBase copyWireToBlock = AccessTools.Method(createWireEngineType, "PlaceWiresOnPlaceNewCube");
|
||||
|
||||
public void Ready()
|
||||
{
|
||||
}
|
||||
|
||||
public EntitiesDB entitiesDB { get; set; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public void CopyBlockStats(EGID sourceID, EGID targetID)
|
||||
{
|
||||
var allCharacters = (LocalFasterReadOnlyList<ExclusiveGroupStruct>) CharacterExclusiveGroups.AllCharacters;
|
||||
foreach (var ((pickedBlockColl, count), _) in entitiesDB.QueryEntities<PickedBlockExtraDataStruct>(allCharacters))
|
||||
{
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
ref PickedBlockExtraDataStruct pickedBlock = ref pickedBlockColl[i];
|
||||
var oldStruct = pickedBlock;
|
||||
pickedBlock.pickedBlockEntityID = sourceID;
|
||||
pickedBlock.placedBlockEntityID = targetID;
|
||||
pickedBlock.placedBlockTweaksMustCopy = true;
|
||||
if (entitiesDB.Exists<DBEntityStruct>(pickedBlock.pickedBlockEntityID)
|
||||
&& entitiesDB.Exists<DBEntityStruct>(pickedBlock.placedBlockEntityID))
|
||||
{
|
||||
copyFromBlock.Invoke(Patch.copyEngine, new object[] {pickedBlock.ID, pickedBlock});
|
||||
|
||||
uint playerID = Player.LocalPlayer.Id;
|
||||
var parameters = new object[] {playerID, pickedBlock};
|
||||
copyWireFromBlock.Invoke(Patch.copyWireEngine, parameters);
|
||||
pickedBlock = (PickedBlockExtraDataStruct) parameters[1]; //ref arg
|
||||
|
||||
copyToBlock.Invoke(Patch.copyEngine, new object[] {pickedBlock.ID, pickedBlock});
|
||||
|
||||
ExclusiveGroupStruct group = WiresExclusiveGroups.WIRES_COPY_GROUP + playerID;
|
||||
copyWireToBlock.Invoke(Patch.createWireEngine, new object[] {group, pickedBlock.ID});
|
||||
|
||||
pickedBlock.placedBlockTweaksMustCopy = false;
|
||||
}
|
||||
|
||||
pickedBlock = oldStruct; //Make sure to not interfere with the game - Although that might not be the case with the wire copying
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch]
|
||||
private static class Patch
|
||||
{
|
||||
public static object copyEngine;
|
||||
public static object copyWireEngine;
|
||||
public static object createWireEngine;
|
||||
|
||||
public static void Postfix(object __instance)
|
||||
{
|
||||
if (__instance.GetType() == copyEngineType)
|
||||
copyEngine = __instance;
|
||||
else if (__instance.GetType() == copyWireEngineType)
|
||||
copyWireEngine = __instance;
|
||||
else if (__instance.GetType() == createWireEngineType)
|
||||
createWireEngine = __instance;
|
||||
}
|
||||
|
||||
public static IEnumerable<MethodBase> TargetMethods()
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
AccessTools.GetDeclaredConstructors(copyEngineType)[0],
|
||||
AccessTools.GetDeclaredConstructors(copyWireEngineType)[0],
|
||||
AccessTools.GetDeclaredConstructors(createWireEngineType)[0]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public string Name { get; } = "TechbloxModdingAPIBlockCloneGameEngine";
|
||||
public bool isRemovable { get; } = false;
|
||||
}
|
||||
}
|
274
TechbloxModdingAPI/Blocks/Engines/BlockEngine.cs
Normal file
274
TechbloxModdingAPI/Blocks/Engines/BlockEngine.cs
Normal file
|
@ -0,0 +1,274 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using HarmonyLib;
|
||||
|
||||
using Gamecraft.ColourPalette;
|
||||
using Gamecraft.TimeRunning;
|
||||
using Gamecraft.Wires;
|
||||
using RobocraftX.Blocks;
|
||||
using RobocraftX.Common;
|
||||
using RobocraftX.Physics;
|
||||
using RobocraftX.Rendering;
|
||||
using RobocraftX.Rendering.GPUI;
|
||||
using Svelto.DataStructures;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.Hybrid;
|
||||
using Techblox.BuildingDrone;
|
||||
using Unity.Mathematics;
|
||||
|
||||
using TechbloxModdingAPI.Engines;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
namespace TechbloxModdingAPI.Blocks.Engines
|
||||
{
|
||||
/// <summary>
|
||||
/// Engine for executing general block actions
|
||||
/// </summary>
|
||||
public partial class BlockEngine : IApiEngine
|
||||
{
|
||||
public string Name { get; } = "TechbloxModdingAPIBlockGameEngine";
|
||||
|
||||
public EntitiesDB entitiesDB { set; private get; }
|
||||
|
||||
public bool isRemovable => false;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public void Ready()
|
||||
{
|
||||
}
|
||||
|
||||
public Block[] GetConnectedBlocks(EGID blockID)
|
||||
{
|
||||
if (!BlockExists(blockID)) return new Block[0];
|
||||
Stack<EGID> cubeStack = new Stack<EGID>();
|
||||
FasterList<EGID> cubes = new FasterList<EGID>(10);
|
||||
var coll = entitiesDB.QueryEntities<GridConnectionsEntityStruct>();
|
||||
foreach (var (ecoll, _) in coll)
|
||||
{
|
||||
var ecollB = ecoll.ToBuffer();
|
||||
for(int i = 0; i < ecoll.count; i++)
|
||||
{
|
||||
ref var conn = ref ecollB.buffer[i];
|
||||
conn.isProcessed = false;
|
||||
}
|
||||
}
|
||||
|
||||
ConnectedCubesUtility.TreeTraversal.GetConnectedCubes(entitiesDB, blockID, cubeStack, cubes,
|
||||
(in GridConnectionsEntityStruct g) => { return false; });
|
||||
|
||||
var ret = new Block[cubes.count];
|
||||
for (int i = 0; i < cubes.count; i++)
|
||||
ret[i] = Block.New(cubes[i]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public float4 ConvertBlockColor(byte index) => index == byte.MaxValue
|
||||
? new float4(-1f, -1f, -1f, -1f)
|
||||
: entitiesDB.QueryEntity<PaletteEntryEntityStruct>(index,
|
||||
CommonExclusiveGroups.COLOUR_PALETTE_GROUP).Colour;
|
||||
|
||||
public OptionalRef<T> GetBlockInfoOptional<T>(Block block) where T : unmanaged, IEntityComponent
|
||||
{
|
||||
return entitiesDB.QueryEntityOptional<T>(block);
|
||||
}
|
||||
|
||||
public ref T GetBlockInfo<T>(Block block) where T : unmanaged, IEntityComponent
|
||||
{
|
||||
return ref entitiesDB.QueryEntityOrDefault<T>(block);
|
||||
}
|
||||
|
||||
internal ref T GetBlockInfo<T>(EcsObjectBase obj) where T : unmanaged, IEntityComponent
|
||||
{
|
||||
return ref entitiesDB.QueryEntityOrDefault<T>(obj);
|
||||
}
|
||||
|
||||
public ref T GetBlockInfoViewComponent<T>(Block block) where T : struct, IEntityViewComponent
|
||||
{
|
||||
return ref entitiesDB.QueryEntityOrDefault<T>(block);
|
||||
}
|
||||
|
||||
public void UpdateDisplayedBlock(EGID id)
|
||||
{
|
||||
if (!BlockExists(id)) return;
|
||||
RenderingPatch.UpdateBlocks();
|
||||
}
|
||||
|
||||
internal void UpdatePrefab(Block block, byte material, bool flipped)
|
||||
{
|
||||
var prefabAssetIDOpt = entitiesDB.QueryEntityOptional<PrefabAssetIDComponent>(block);
|
||||
uint prefabAssetID = prefabAssetIDOpt
|
||||
? prefabAssetIDOpt.Get().prefabAssetID
|
||||
: uint.MaxValue;
|
||||
if (prefabAssetID == uint.MaxValue)
|
||||
{
|
||||
if (entitiesDB.QueryEntityOptional<DBEntityStruct>(block)) //The block exists
|
||||
throw new BlockException("Prefab asset ID not found for block " + block); //Set by the game
|
||||
return;
|
||||
}
|
||||
|
||||
uint prefabId =
|
||||
PrefabsID.GetOrCreatePrefabID((ushort) prefabAssetID, material, 1, flipped);
|
||||
entitiesDB.QueryEntityOrDefault<GFXPrefabEntityStructGPUI>(block).prefabID = prefabId;
|
||||
if (block.Exists)
|
||||
{
|
||||
entitiesDB.PublishEntityChange<CubeMaterialStruct>(block.Id);
|
||||
entitiesDB.PublishEntityChange<GFXPrefabEntityStructGPUI>(block.Id);
|
||||
|
||||
ref BuildingActionComponent local =
|
||||
ref entitiesDB.QueryEntity<BuildingActionComponent>(BuildingDroneUtility
|
||||
.GetLocalBuildingDrone(entitiesDB).ToEGID(entitiesDB));
|
||||
local.buildAction = BuildAction.ChangeMaterial;
|
||||
local.targetPosition = block.Position;
|
||||
this.entitiesDB.PublishEntityChange<BuildingActionComponent>(local.ID);
|
||||
}
|
||||
//Phyiscs prefab: prefabAssetID, set on block creation from the CubeListData
|
||||
}
|
||||
|
||||
public bool BlockExists(EGID blockID)
|
||||
{
|
||||
return entitiesDB.Exists<DBEntityStruct>(blockID);
|
||||
}
|
||||
|
||||
public SimBody[] GetSimBodiesFromID(byte id)
|
||||
{
|
||||
var ret = new FasterList<SimBody>(4);
|
||||
var oide = entitiesDB.QueryEntities<ObjectIdEntityStruct>();
|
||||
EGIDMapper<GridConnectionsEntityStruct>? connections = null;
|
||||
foreach (var ((oids, count), _) in oide)
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
ref ObjectIdEntityStruct oid = ref oids[i];
|
||||
if (oid.objectId != id) continue;
|
||||
if (!connections.HasValue) //Would need reflection to get the group from the build group otherwise
|
||||
connections = entitiesDB.QueryMappedEntities<GridConnectionsEntityStruct>(oid.ID.groupID);
|
||||
var rid = connections.Value.Entity(oid.ID.entityID).machineRigidBodyId;
|
||||
foreach (var rb in ret)
|
||||
{
|
||||
if (rb.Id.entityID == rid)
|
||||
goto DUPLICATE; //Multiple Object Identifiers on one rigid body
|
||||
}
|
||||
|
||||
ret.Add(new SimBody(rid));
|
||||
DUPLICATE: ;
|
||||
}
|
||||
}
|
||||
|
||||
return ret.ToArray();
|
||||
}
|
||||
|
||||
public SimBody[] GetConnectedSimBodies(uint id)
|
||||
{
|
||||
var joints = entitiesDB.QueryEntities<JointEntityStruct>(MachineSimulationGroups.JOINTS_GROUP).ToBuffer();
|
||||
var list = new FasterList<SimBody>(4);
|
||||
for (int i = 0; i < joints.count; i++)
|
||||
{
|
||||
ref var joint = ref joints.buffer[i];
|
||||
if (joint.isBroken) continue;
|
||||
if (joint.connectedEntityA == id) list.Add(new SimBody(joint.connectedEntityB));
|
||||
else if (joint.connectedEntityB == id) list.Add(new SimBody(joint.connectedEntityA));
|
||||
}
|
||||
|
||||
return list.ToArray();
|
||||
}
|
||||
|
||||
public SimBody[] GetClusterBodies(uint cid)
|
||||
{
|
||||
var groups = entitiesDB.QueryEntities<GridConnectionsEntityStruct>();
|
||||
var bodies = new HashSet<uint>();
|
||||
foreach (var (coll, _) in groups)
|
||||
{
|
||||
var array = coll.ToBuffer().buffer;
|
||||
for (var index = 0; index < array.capacity; index++)
|
||||
{
|
||||
var conn = array[index];
|
||||
if (conn.clusterId == cid)
|
||||
bodies.Add(conn.machineRigidBodyId);
|
||||
}
|
||||
}
|
||||
|
||||
return bodies.Select(id => new SimBody(id, cid)).ToArray();
|
||||
}
|
||||
|
||||
public EGID? FindBlockEGID(uint id)
|
||||
{
|
||||
var groups = entitiesDB.FindGroups<DBEntityStruct>();
|
||||
foreach (ExclusiveGroupStruct group in groups)
|
||||
{
|
||||
if (entitiesDB.Exists<DBEntityStruct>(id, group))
|
||||
return new EGID(id, group);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Cluster GetCluster(uint sbid)
|
||||
{
|
||||
var groups = entitiesDB.QueryEntities<GridConnectionsEntityStruct>();
|
||||
foreach (var (coll, _) in groups)
|
||||
{
|
||||
var array = coll.ToBuffer().buffer;
|
||||
for (var index = 0; index < array.capacity; index++)
|
||||
{
|
||||
var conn = array[index];
|
||||
//Static blocks don't have a cluster ID but the cluster destruction manager should have one
|
||||
if (conn.machineRigidBodyId == sbid && conn.clusterId != uint.MaxValue)
|
||||
return new Cluster(conn.clusterId);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Block[] GetBodyBlocks(uint sbid)
|
||||
{
|
||||
var groups = entitiesDB.QueryEntities<GridConnectionsEntityStruct>();
|
||||
var set = new HashSet<Block>();
|
||||
foreach (var (coll, _) in groups)
|
||||
{
|
||||
var array = coll.ToBuffer().buffer;
|
||||
for (var index = 0; index < array.capacity; index++)
|
||||
{
|
||||
var conn = array[index];
|
||||
if (conn.machineRigidBodyId == sbid)
|
||||
set.Add(Block.New(conn.ID));
|
||||
}
|
||||
}
|
||||
|
||||
return set.ToArray();
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
public EntitiesDB GetEntitiesDB()
|
||||
{
|
||||
return entitiesDB;
|
||||
}
|
||||
#endif
|
||||
|
||||
[HarmonyPatch]
|
||||
public static class RenderingPatch
|
||||
{
|
||||
private static ComputeRenderingEntitiesMatricesEngine Engine;
|
||||
|
||||
public static void Postfix(ComputeRenderingEntitiesMatricesEngine __instance)
|
||||
{
|
||||
Engine = __instance;
|
||||
}
|
||||
|
||||
public static MethodBase TargetMethod()
|
||||
{
|
||||
return typeof(ComputeRenderingEntitiesMatricesEngine).GetConstructors()[0];
|
||||
}
|
||||
|
||||
public static void UpdateBlocks()
|
||||
{
|
||||
var data = new RenderingDataStruct();
|
||||
Engine.Add(ref data, new EGID(0, CommonExclusiveGroups.BUTTON_BLOCK_GROUP));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,18 +1,17 @@
|
|||
using System;
|
||||
|
||||
using RobocraftX.Common;
|
||||
using RobocraftX.Blocks;
|
||||
using Svelto.ECS;
|
||||
|
||||
using GamecraftModdingAPI.Engines;
|
||||
using GamecraftModdingAPI.Utility;
|
||||
using RobocraftX.Blocks;
|
||||
using TechbloxModdingAPI.Engines;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftModdingAPI.Blocks
|
||||
namespace TechbloxModdingAPI.Blocks.Engines
|
||||
{
|
||||
public class BlockEventsEngine : IReactionaryEngine<BlockTagEntityStruct>
|
||||
{
|
||||
public event EventHandler<BlockPlacedRemovedEventArgs> Placed;
|
||||
public event EventHandler<BlockPlacedRemovedEventArgs> Removed;
|
||||
public WrappedHandler<BlockPlacedRemovedEventArgs> Placed;
|
||||
public WrappedHandler<BlockPlacedRemovedEventArgs> Removed;
|
||||
|
||||
public void Ready()
|
||||
{
|
||||
|
@ -24,7 +23,7 @@ namespace GamecraftModdingAPI.Blocks
|
|||
{
|
||||
}
|
||||
|
||||
public string Name { get; } = "GamecraftModdingAPIBlockEventsEngine";
|
||||
public string Name { get; } = "TechbloxModdingAPIBlockEventsEngine";
|
||||
public bool isRemovable { get; } = false;
|
||||
|
||||
private bool shouldAddRemove;
|
||||
|
@ -32,25 +31,22 @@ namespace GamecraftModdingAPI.Blocks
|
|||
{
|
||||
if (!(shouldAddRemove = !shouldAddRemove))
|
||||
return;
|
||||
ExceptionUtil.InvokeEvent(Placed, this,
|
||||
new BlockPlacedRemovedEventArgs {ID = egid});
|
||||
Placed.Invoke(this, new BlockPlacedRemovedEventArgs {ID = egid});
|
||||
}
|
||||
|
||||
public void Remove(ref BlockTagEntityStruct entityComponent, EGID egid)
|
||||
{
|
||||
if (!(shouldAddRemove = !shouldAddRemove))
|
||||
return;
|
||||
ExceptionUtil.InvokeEvent(Removed, this,
|
||||
new BlockPlacedRemovedEventArgs {ID = egid});
|
||||
Removed.Invoke(this, new BlockPlacedRemovedEventArgs {ID = egid});
|
||||
}
|
||||
}
|
||||
|
||||
public struct BlockPlacedRemovedEventArgs
|
||||
{
|
||||
public EGID ID;
|
||||
public BlockIDs Type;
|
||||
private Block block;
|
||||
|
||||
public Block Block => block ?? (block = new Block(ID));
|
||||
public Block Block => block ??= Block.New(ID);
|
||||
}
|
||||
}
|
|
@ -1,26 +1,32 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Gamecraft.Blocks.BlockGroups;
|
||||
using Gamecraft.GUI.Blueprints;
|
||||
using GamecraftModdingAPI.Engines;
|
||||
using GamecraftModdingAPI.Utility;
|
||||
using HarmonyLib;
|
||||
using RobocraftX.Blocks;
|
||||
using RobocraftX.Blocks.Ghost;
|
||||
using RobocraftX.Common;
|
||||
using RobocraftX.CR.MachineEditing.BoxSelect;
|
||||
using RobocraftX.CR.MachineEditing.BoxSelect.ClipboardOperations;
|
||||
using RobocraftX.Physics;
|
||||
using RobocraftX.Rendering;
|
||||
using RobocraftX.Rendering.GPUI;
|
||||
using Svelto.DataStructures;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.DataStructures;
|
||||
using Svelto.ECS.EntityStructs;
|
||||
using Svelto.ECS.Native;
|
||||
using Svelto.ECS.Serialization;
|
||||
using Techblox.Blocks;
|
||||
using TechbloxModdingAPI.Engines;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
using Unity.Collections;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
using Allocator = Svelto.Common.Allocator;
|
||||
|
||||
namespace GamecraftModdingAPI.Blocks
|
||||
namespace TechbloxModdingAPI.Blocks.Engines
|
||||
{
|
||||
public class BlueprintEngine : IFactoryEngine
|
||||
{
|
||||
|
@ -29,13 +35,18 @@ namespace GamecraftModdingAPI.Blocks
|
|||
|
||||
private NativeDynamicArray selectedBlocksInGroup;
|
||||
private NativeHashSet<ulong> removedConnections = new NativeHashSet<ulong>();
|
||||
|
||||
private int addingToBlockGroup = -1;
|
||||
|
||||
private static readonly Type PlaceBlueprintUtilityType =
|
||||
AccessTools.TypeByName("RobocraftX.CR.MachineEditing.PlaceBlueprintUtility");
|
||||
private static readonly FieldInfo LocalBlockMap =
|
||||
AccessTools.DeclaredField(PlaceBlueprintUtilityType, "_localBlockMap");
|
||||
private static readonly MethodInfo BuildBlock = AccessTools.Method(PlaceBlueprintUtilityType, "BuildBlock");
|
||||
private static readonly MethodInfo BuildWires = AccessTools.Method(PlaceBlueprintUtilityType, "BuildWires");
|
||||
private static readonly Type SerializeGhostBlueprintType =
|
||||
AccessTools.TypeByName("RobocraftX.CR.MachineEditing.BoxSelect.SerializeGhostChildrenOnAddEngine");
|
||||
private static readonly MethodInfo SerializeGhostBlueprint =
|
||||
AccessTools.Method(SerializeGhostBlueprintType, "SerializeClipboardGhostEntities");
|
||||
|
||||
private static NativeEntityRemove nativeRemove;
|
||||
private static MachineGraphConnectionEntityFactory connectionFactory;
|
||||
|
@ -44,13 +55,16 @@ namespace GamecraftModdingAPI.Blocks
|
|||
private static IEntitySerialization entitySerialization;
|
||||
private static IEntityFactory entityFactory;
|
||||
private static FasterList<EGID> globalBlockMap;
|
||||
|
||||
private static object SerializeGhostBlueprintInstance;
|
||||
private static GhostChildEntityFactory BuildGhostBlueprintFactory;
|
||||
|
||||
public void Ready()
|
||||
{
|
||||
selectedBlocksInGroup = NativeDynamicArray.Alloc<EGID>(Allocator.Persistent);
|
||||
}
|
||||
|
||||
public EntitiesDB entitiesDB { get; set; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
selectedBlocksInGroup.Dispose();
|
||||
|
@ -67,7 +81,7 @@ namespace GamecraftModdingAPI.Blocks
|
|||
int count = selectedBlocksInGroup.Count<EGID>();
|
||||
var ret = new Block[count];
|
||||
for (uint i = 0; i < count; i++)
|
||||
ret[i] = new Block(selectedBlocksInGroup.Get<EGID>(i));
|
||||
ret[i] = Block.New(selectedBlocksInGroup.Get<EGID>(i));
|
||||
selectedBlocksInGroup.FastClear();
|
||||
return ret;
|
||||
}
|
||||
|
@ -90,17 +104,26 @@ namespace GamecraftModdingAPI.Blocks
|
|||
return nextFilterId;
|
||||
}
|
||||
|
||||
public void AddBlockToGroup(EGID blockID, int groupID)
|
||||
{
|
||||
if (globalBlockMap == null)
|
||||
globalBlockMap = FullGameFields._deserialisedBlockMap;
|
||||
if (groupID != addingToBlockGroup)
|
||||
{
|
||||
Logging.MetaDebugLog("Changing current block group from " + addingToBlockGroup + " to " + groupID);
|
||||
addingToBlockGroup = groupID;
|
||||
globalBlockMap.Clear();
|
||||
}
|
||||
|
||||
globalBlockMap.Add(blockID);
|
||||
}
|
||||
|
||||
public void SelectBlueprint(uint resourceID)
|
||||
{
|
||||
if (resourceID == uint.MaxValue)
|
||||
BlueprintUtil.UnselectBlueprint(entitiesDB);
|
||||
else
|
||||
{
|
||||
BlueprintUtil.SelectBlueprint(entitiesDB, new BlueprintInventoryItemEntityStruct
|
||||
{
|
||||
blueprintResourceId = resourceID,
|
||||
});
|
||||
}
|
||||
BlueprintUtil.SelectBlueprint(entitiesDB, resourceID, false, -1);
|
||||
}
|
||||
|
||||
public uint CreateBlueprint()
|
||||
|
@ -108,7 +131,7 @@ namespace GamecraftModdingAPI.Blocks
|
|||
uint index = clipboardManager.AllocateSerializationData();
|
||||
return index;
|
||||
}
|
||||
|
||||
|
||||
public void ReplaceBlueprint(uint playerID, uint blueprintID, ICollection<Block> selected, float3 pos, quaternion rot)
|
||||
{
|
||||
var blockIDs = new EGID[selected.Count];
|
||||
|
@ -122,7 +145,7 @@ namespace GamecraftModdingAPI.Blocks
|
|||
}
|
||||
|
||||
var serializationData = clipboardManager.GetSerializationData(blueprintID);
|
||||
SelectionSerializationUtility.ClearClipboard(playerID, entitiesDB, entityFunctions, serializationData.blueprintData);
|
||||
SelectionSerializationUtility.ClearClipboard(playerID, entitiesDB, entityFunctions, serializationData.blueprintData, -1);
|
||||
if (selected.Count == 0)
|
||||
return;
|
||||
//ref BlockGroupTransformEntityComponent groupTransform = ref EntityNativeDBExtensions.QueryEntity<BlockGroupTransformEntityComponent>(entitiesDb, (uint) local1.currentBlockGroup, BlockGroupExclusiveGroups.BlockGroupEntityGroup);
|
||||
|
@ -130,11 +153,28 @@ namespace GamecraftModdingAPI.Blocks
|
|||
//float3 bottomOffset = PlaceBlockUtility.GetBottomOffset(collider);
|
||||
//var rootPosition = math.mul(groupTransform.blockGroupGridRotation, bottomOffset) + groupTransform.blockGroupGridPosition;
|
||||
//var rootRotation = groupTransform.blockGroupGridRotation;
|
||||
|
||||
|
||||
clipboardManager.SetGhostSerialized(blueprintID, false);
|
||||
SelectionSerializationUtility.CopySelectionToClipboard(playerID, entitiesDB,
|
||||
serializationData.blueprintData, entitySerialization, entityFactory, blockIDs,
|
||||
(uint) blockIDs.Length, pos, rot);
|
||||
(uint) blockIDs.Length, pos, rot, -1);
|
||||
BuildGhostBlueprint(selected, pos, rot, playerID);
|
||||
SerializeGhostBlueprint.Invoke(SerializeGhostBlueprintInstance, new object[] {playerID, blueprintID});
|
||||
|
||||
}
|
||||
|
||||
private void BuildGhostBlueprint(ICollection<Block> blocks, float3 pos, quaternion rot, uint playerID)
|
||||
{
|
||||
GhostChildUtility.ClearGhostChildren(playerID, entitiesDB, entityFunctions);
|
||||
var bssesopt = entitiesDB.QueryEntityOptional<BoxSelectStateEntityStruct>(new EGID(playerID,
|
||||
BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup));
|
||||
if (!bssesopt)
|
||||
return;
|
||||
foreach (var block in blocks)
|
||||
{
|
||||
GhostChildUtility.BuildGhostChild(in playerID, block.Id, in pos, in rot, entitiesDB,
|
||||
BuildGhostBlueprintFactory, false, bssesopt.Get().buildingDroneReference);
|
||||
}
|
||||
}
|
||||
|
||||
public Block[] PlaceBlueprintBlocks(uint blueprintID, uint playerID, float3 pos, float3 rot)
|
||||
|
@ -159,11 +199,12 @@ namespace GamecraftModdingAPI.Blocks
|
|||
}
|
||||
}
|
||||
int nextFilterId1 = BlockGroupUtility.NextFilterId;
|
||||
entityFactory.BuildEntity<BlockGroupEntityDescriptor>(new EGID((uint) nextFilterId1, BlockGroupExclusiveGroups.BlockGroupEntityGroup)).Init(new BlockGroupTransformEntityComponent
|
||||
{
|
||||
blockGroupGridPosition = selectionPosition.position,
|
||||
blockGroupGridRotation = selectionRotation.rotation
|
||||
});
|
||||
entityFactory.BuildEntity<BlockGroupEntityDescriptor>(new EGID((uint) nextFilterId1,
|
||||
BlockGroupExclusiveGroups.BlockGroupEntityGroup)).Init(new BlockGroupTransformEntityComponent
|
||||
{
|
||||
blockGroupGridPosition = selectionPosition.position,
|
||||
blockGroupGridRotation = selectionRotation.rotation
|
||||
});
|
||||
var frot = Quaternion.Euler(rot);
|
||||
var grid = new GridRotationStruct {position = pos, rotation = frot};
|
||||
var poss = new PositionEntityStruct {position = pos};
|
||||
|
@ -173,7 +214,7 @@ namespace GamecraftModdingAPI.Blocks
|
|||
new object[]
|
||||
{
|
||||
playerID, grid, poss, rots, selectionPosition, selectionRotation, blueprintData,
|
||||
entitiesDB, entitySerialization, nextFilterId1
|
||||
entitySerialization, nextFilterId1
|
||||
});
|
||||
/*
|
||||
uint playerId, in GridRotationStruct ghostParentGrid,
|
||||
|
@ -191,10 +232,22 @@ namespace GamecraftModdingAPI.Blocks
|
|||
new object[] {playerID, blueprintData, entitySerialization, entitiesDB, entityFactory});
|
||||
var blocks = new Block[placedBlocks.count];
|
||||
for (int i = 0; i < blocks.Length; i++)
|
||||
blocks[i] = new Block(placedBlocks[i]);
|
||||
blocks[i] = Block.New(placedBlocks[i]);
|
||||
return blocks;
|
||||
}
|
||||
|
||||
public void GetBlueprintInfo(uint blueprintID, out float3 pos, out quaternion rot, out uint selectionSize)
|
||||
{
|
||||
var serializationData = clipboardManager.GetSerializationData(blueprintID);
|
||||
var blueprintData = serializationData.blueprintData;
|
||||
blueprintData.dataPos = 0U;
|
||||
BoxSelectSerializationUtilities.ReadClipboardHeader(blueprintData, out selectionSize, out var posst,
|
||||
out var rotst, out _);
|
||||
blueprintData.dataPos = 0U; //Just to be sure, it gets reset when it's read anyway
|
||||
pos = posst.position;
|
||||
rot = rotst.rotation;
|
||||
}
|
||||
|
||||
public void InitBlueprint(uint blueprintID)
|
||||
{
|
||||
clipboardManager.IncrementRefCount(blueprintID);
|
||||
|
@ -204,8 +257,78 @@ namespace GamecraftModdingAPI.Blocks
|
|||
{
|
||||
clipboardManager.DecrementRefCount(blueprintID);
|
||||
}
|
||||
|
||||
|
||||
public string Name { get; } = "GamecraftModdingAPIBlueprintGameEngine";
|
||||
//GhostChildUtility.BuildGhostChild
|
||||
public Block BuildGhostChild()
|
||||
{
|
||||
var sourceId = new EGID(Player.LocalPlayer.Id, GHOST_BLOCKS_ENABLED.Group);
|
||||
var positionEntityStruct = entitiesDB.QueryEntity<PositionEntityStruct>(sourceId);
|
||||
var rotationEntityStruct = entitiesDB.QueryEntity<RotationEntityStruct>(sourceId);
|
||||
var scalingEntityStruct = entitiesDB.QueryEntity<ScalingEntityStruct>(sourceId);
|
||||
var dbStruct = entitiesDB.QueryEntity<DBEntityStruct>(sourceId);
|
||||
var colliderStruct = entitiesDB.QueryEntity<ColliderAabb>(sourceId);
|
||||
var colorStruct = entitiesDB.QueryEntity<ColourParameterEntityStruct>(sourceId);
|
||||
uint ghostChildBlockId = CommonExclusiveGroups.GetNewGhostChildBlockID();
|
||||
var ghostEntityReference = GhostBlockUtils.GetGhostEntityReference(sourceId.entityID, entitiesDB);
|
||||
var entityInitializer = BuildGhostBlueprintFactory.Build(
|
||||
new EGID(ghostChildBlockId, BoxSelectExclusiveGroups.GhostChildEntitiesExclusiveGroup), /*dbStruct.DBID*/ (uint)BlockIDs.Cube);
|
||||
entityInitializer.Init(dbStruct);
|
||||
entityInitializer.Init(new GFXPrefabEntityStructGPUI(
|
||||
PrefabsID.GetOrCreatePrefabID((ushort)entityInitializer.Get<PrefabAssetIDComponent>().prefabAssetID,
|
||||
entitiesDB.QueryEntity<CubeMaterialStruct>(sourceId).materialId, 7,
|
||||
FlippedBlockUtils.IsFlipped(in scalingEntityStruct.scale)), true));
|
||||
entityInitializer.Init(entitiesDB.QueryEntity<CubeMaterialStruct>(sourceId));
|
||||
entityInitializer.Init(new GhostParentEntityStruct
|
||||
{
|
||||
ghostBlockParentEntityReference = ghostEntityReference,
|
||||
ownerMustSerializeOnAdd = false
|
||||
});
|
||||
entityInitializer.Init(colorStruct);
|
||||
entityInitializer.Init(colliderStruct);
|
||||
entityInitializer.Init(new RigidBodyEntityStruct
|
||||
{
|
||||
position = positionEntityStruct.position,
|
||||
rotation = rotationEntityStruct.rotation
|
||||
});
|
||||
entityInitializer.Init(new ScalingEntityStruct
|
||||
{
|
||||
scale = scalingEntityStruct.scale
|
||||
});
|
||||
entityInitializer.Init(new LocalTransformEntityStruct
|
||||
{
|
||||
position = positionEntityStruct.position,
|
||||
rotation = rotationEntityStruct.rotation
|
||||
});
|
||||
entityInitializer.Init(new RotationEntityStruct
|
||||
{
|
||||
rotation = rotationEntityStruct.rotation
|
||||
});
|
||||
entityInitializer.Init(new PositionEntityStruct
|
||||
{
|
||||
position = positionEntityStruct.position
|
||||
});
|
||||
entityInitializer.Init(new SkewComponent
|
||||
{
|
||||
skewMatrix = entitiesDB.QueryEntity<SkewComponent>(sourceId).skewMatrix
|
||||
});
|
||||
entityInitializer.Init(new BlockPlacementInfoStruct
|
||||
{
|
||||
placedByBuildingDrone = entitiesDB
|
||||
.QueryEntityOptional<BoxSelectStateEntityStruct>(new EGID(Player.LocalPlayer.Id,
|
||||
BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup)).Get().buildingDroneReference
|
||||
});
|
||||
entityInitializer.Init(new GridRotationStruct
|
||||
{
|
||||
position = float3.zero,
|
||||
rotation = quaternion.identity
|
||||
});
|
||||
var block = Block.New(entityInitializer.EGID);
|
||||
block.InitData = entityInitializer;
|
||||
return block;
|
||||
}
|
||||
|
||||
public string Name { get; } = "TechbloxModdingAPIBlueprintGameEngine";
|
||||
public bool isRemovable { get; } = false;
|
||||
|
||||
[HarmonyPatch]
|
||||
|
@ -243,6 +366,34 @@ namespace GamecraftModdingAPI.Blocks
|
|||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch]
|
||||
private static class SerializeGhostBlueprintPatch
|
||||
{
|
||||
public static void Postfix(object __instance)
|
||||
{
|
||||
SerializeGhostBlueprintInstance = __instance;
|
||||
}
|
||||
|
||||
public static MethodBase TargetMethod()
|
||||
{
|
||||
return AccessTools.GetDeclaredConstructors(SerializeGhostBlueprintType)[0];
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch]
|
||||
private static class BuildGhostBlueprintPatch
|
||||
{
|
||||
public static void Postfix(GhostChildEntityFactory ghostChildEntityFactory)
|
||||
{
|
||||
BuildGhostBlueprintFactory = ghostChildEntityFactory;
|
||||
}
|
||||
|
||||
public static MethodBase TargetMethod()
|
||||
{
|
||||
return AccessTools.GetDeclaredConstructors(AccessTools.TypeByName("RobocraftX.CR.MachineEditing.BuildGhostChildForMultiblockPickEngine"))[0];
|
||||
}
|
||||
}
|
||||
|
||||
public IEntityFactory Factory { get; set; }
|
||||
}
|
||||
}
|
68
TechbloxModdingAPI/Blocks/Engines/MovementEngine.cs
Normal file
68
TechbloxModdingAPI/Blocks/Engines/MovementEngine.cs
Normal file
|
@ -0,0 +1,68 @@
|
|||
using RobocraftX.Common;
|
||||
using RobocraftX.UECS;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.EntityStructs;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Transforms;
|
||||
|
||||
using TechbloxModdingAPI.Engines;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
namespace TechbloxModdingAPI.Blocks.Engines
|
||||
{
|
||||
/// <summary>
|
||||
/// Engine which executes block movement actions
|
||||
/// </summary>
|
||||
public class MovementEngine : IApiEngine
|
||||
{
|
||||
public string Name { get; } = "TechbloxModdingAPIMovementGameEngine";
|
||||
|
||||
public EntitiesDB entitiesDB { set; private get; }
|
||||
|
||||
public bool isRemovable => false;
|
||||
|
||||
public bool IsInGame = false;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
IsInGame = false;
|
||||
}
|
||||
|
||||
public void Ready()
|
||||
{
|
||||
IsInGame = true;
|
||||
}
|
||||
|
||||
// implementations for Movement static class
|
||||
|
||||
internal float3 MoveBlock(Block block, float3 vector)
|
||||
{
|
||||
ref PositionEntityStruct posStruct = ref this.entitiesDB.QueryEntityOrDefault<PositionEntityStruct>(block);
|
||||
ref GridRotationStruct gridStruct = ref this.entitiesDB.QueryEntityOrDefault<GridRotationStruct>(block);
|
||||
ref LocalTransformEntityStruct transStruct = ref this.entitiesDB.QueryEntityOrDefault<LocalTransformEntityStruct>(block);
|
||||
ref UECSPhysicsEntityStruct phyStruct = ref this.entitiesDB.QueryEntityOrDefault<UECSPhysicsEntityStruct>(block);
|
||||
// main (persistent) position
|
||||
posStruct.position = vector;
|
||||
// placement grid position
|
||||
gridStruct.position = vector;
|
||||
// rendered position
|
||||
transStruct.position = vector;
|
||||
// collision position
|
||||
if (phyStruct.ID != default)
|
||||
{ //It exists
|
||||
FullGameFields._physicsWorld.EntityManager.SetComponentData(phyStruct.uecsEntity, new Translation
|
||||
{
|
||||
Value = posStruct.position
|
||||
});
|
||||
}
|
||||
|
||||
entitiesDB.QueryEntityOrDefault<GridConnectionsEntityStruct>(block).isProcessed = false;
|
||||
return posStruct.position;
|
||||
}
|
||||
|
||||
internal float3 GetPosition(Block block)
|
||||
{
|
||||
return entitiesDB.QueryEntityOrDefault<PositionEntityStruct>(block).position;
|
||||
}
|
||||
}
|
||||
}
|
130
TechbloxModdingAPI/Blocks/Engines/PlacementEngine.cs
Normal file
130
TechbloxModdingAPI/Blocks/Engines/PlacementEngine.cs
Normal file
|
@ -0,0 +1,130 @@
|
|||
using System.Reflection;
|
||||
|
||||
using DataLoader;
|
||||
using Gamecraft.Blocks.BlockGroups;
|
||||
using Gamecraft.Wires;
|
||||
using HarmonyLib;
|
||||
using RobocraftX.Blocks;
|
||||
using RobocraftX.Character;
|
||||
using RobocraftX.Common;
|
||||
using RobocraftX.CR.MachineEditing.BoxSelect;
|
||||
using RobocraftX.Rendering;
|
||||
using RobocraftX.Rendering.GPUI;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.EntityStructs;
|
||||
using Unity.Mathematics;
|
||||
|
||||
using TechbloxModdingAPI.Engines;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
namespace TechbloxModdingAPI.Blocks.Engines
|
||||
{
|
||||
/// <summary>
|
||||
/// Engine which executes block placement actions
|
||||
/// </summary>
|
||||
public class PlacementEngine : IApiEngine
|
||||
{
|
||||
public bool IsInGame;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
IsInGame = false;
|
||||
}
|
||||
|
||||
public void Ready()
|
||||
{
|
||||
IsInGame = true;
|
||||
}
|
||||
|
||||
public EntitiesDB entitiesDB { get; set; }
|
||||
private static BlockEntityFactory _blockEntityFactory; //Injected from PlaceSingleBlockEngine
|
||||
private static IEntityFactory _entityFactory;
|
||||
|
||||
public EntityInitializer PlaceBlock(BlockIDs block, float3 position, Player player, bool autoWire)
|
||||
{ //It appears that only the non-uniform scale has any visible effect, but if that's not given here it will be set to the uniform one
|
||||
return BuildBlock((ushort) block, position, autoWire, (player ?? Player.LocalPlayer).Id);
|
||||
}
|
||||
|
||||
private EntityInitializer BuildBlock(ushort block, float3 position, bool autoWire, uint playerId)
|
||||
{
|
||||
if (_blockEntityFactory == null)
|
||||
throw new BlockException("The factory is null.");
|
||||
if(!FullGameFields._dataDb.ContainsKey<CubeListData>(block))
|
||||
throw new BlockException("Block with ID " + block + " not found!");
|
||||
//RobocraftX.CR.MachineEditing.PlaceSingleBlockEngine
|
||||
DBEntityStruct dbEntity = new DBEntityStruct {DBID = block};
|
||||
|
||||
EntityInitializer structInitializer = _blockEntityFactory.Build(CommonExclusiveGroups.nextBlockEntityID, block); //The ghost block index is only used for triggers
|
||||
uint prefabAssetID = structInitializer.Has<PrefabAssetIDComponent>()
|
||||
? structInitializer.Get<PrefabAssetIDComponent>().prefabAssetID
|
||||
: throw new BlockException("Prefab asset ID not found!"); //Set by the game
|
||||
uint prefabId = PrefabsID.GetOrCreatePrefabID((ushort) prefabAssetID, (byte) BlockMaterial.SteelBodywork, 1, false);
|
||||
structInitializer.Init(new GFXPrefabEntityStructGPUI(prefabId));
|
||||
structInitializer.Init(dbEntity);
|
||||
structInitializer.Init(new PositionEntityStruct {position = position});
|
||||
structInitializer.Init(new RotationEntityStruct {rotation = quaternion.identity});
|
||||
structInitializer.Init(new ScalingEntityStruct {scale = new float3(1, 1, 1)});
|
||||
structInitializer.Init(new GridRotationStruct
|
||||
{
|
||||
position = position,
|
||||
rotation = quaternion.identity
|
||||
});
|
||||
structInitializer.Init(new UniformBlockScaleEntityStruct {scaleFactor = 1});
|
||||
structInitializer.Get<CubeMaterialStruct>().materialId = (byte) BlockMaterial.SteelBodywork;
|
||||
var bssesopt = entitiesDB.QueryEntityOptional<BoxSelectStateEntityStruct>(new EGID(playerId,
|
||||
BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup));
|
||||
if (!bssesopt)
|
||||
throw new BlockException("Invalid player ID specified for block placement");
|
||||
structInitializer.Init(new BlockPlacementInfoStruct
|
||||
{
|
||||
loadedFromDisk = false,
|
||||
placedByBuildingDrone = bssesopt.Get().buildingDroneReference,
|
||||
triggerAutoWiring = autoWire && structInitializer.Has<BlockPortsStruct>()
|
||||
});
|
||||
|
||||
int nextFilterId = BlockGroupUtility.NextFilterId;
|
||||
structInitializer.Init(new BlockGroupEntityComponent
|
||||
{
|
||||
currentBlockGroup = nextFilterId
|
||||
});
|
||||
_entityFactory.BuildEntity<BlockGroupEntityDescriptor>((uint) nextFilterId,
|
||||
BlockGroupExclusiveGroups.BlockGroupEntityGroup)
|
||||
.Init(new BlockGroupTransformEntityComponent
|
||||
{
|
||||
blockGroupGridRotation = quaternion.identity,
|
||||
blockGroupGridPosition = position
|
||||
});
|
||||
|
||||
foreach (var group in CharacterExclusiveGroups.AllCharacters)
|
||||
{
|
||||
EGID playerEGID = new EGID(playerId, group);
|
||||
if (!entitiesDB.TryQueryEntitiesAndIndex<PickedBlockExtraDataStruct>(playerEGID, out uint index,
|
||||
out var array)) continue;
|
||||
ref PickedBlockExtraDataStruct pickedBlock = ref array[index];
|
||||
pickedBlock.placedBlockEntityID = structInitializer.EGID;
|
||||
pickedBlock.placedBlockWasAPickedBlock = false;
|
||||
}
|
||||
return structInitializer;
|
||||
}
|
||||
|
||||
public string Name => "TechbloxModdingAPIPlacementGameEngine";
|
||||
|
||||
public bool isRemovable => false;
|
||||
|
||||
[HarmonyPatch]
|
||||
class FactoryObtainerPatch
|
||||
{
|
||||
static void Postfix(BlockEntityFactory blockEntityFactory, IEntityFactory entityFactory)
|
||||
{
|
||||
_blockEntityFactory = blockEntityFactory;
|
||||
_entityFactory = entityFactory;
|
||||
Logging.MetaDebugLog("Block entity factory injected.");
|
||||
}
|
||||
|
||||
static MethodBase TargetMethod(Harmony instance)
|
||||
{
|
||||
return AccessTools.TypeByName("RobocraftX.CR.MachineEditing.PlaceSingleBlockEngine").GetConstructors()[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,12 +3,14 @@ using System.Reflection;
|
|||
using HarmonyLib;
|
||||
using RobocraftX.Blocks;
|
||||
using RobocraftX.Common;
|
||||
using Svelto.Common;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.Native;
|
||||
|
||||
using GamecraftModdingAPI.Utility;
|
||||
using GamecraftModdingAPI.Engines;
|
||||
using TechbloxModdingAPI.Engines;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftModdingAPI.Blocks
|
||||
namespace TechbloxModdingAPI.Blocks.Engines
|
||||
{
|
||||
public class RemovalEngine : IApiEngine
|
||||
{
|
||||
|
@ -21,8 +23,8 @@ namespace GamecraftModdingAPI.Blocks
|
|||
return false;
|
||||
var connections = entitiesDB.QueryEntity<MachineGraphConnectionsEntityStruct>(target);
|
||||
var groups = entitiesDB.FindGroups<MachineGraphConnectionsEntityStruct>();
|
||||
var connStructMapper =
|
||||
entitiesDB.QueryNativeMappedEntities<MachineGraphConnectionsEntityStruct>(groups);
|
||||
using var connStructMapper = //The allocator needs to be persistent because that's what is used in the Dispose() method
|
||||
entitiesDB.QueryNativeMappedEntities<MachineGraphConnectionsEntityStruct>(groups, Allocator.Persistent);
|
||||
for (int i = connections.connections.Count<MachineConnectionStruct>() - 1; i >= 0; i--)
|
||||
_connectionFactory.RemoveConnection(connections, i, connStructMapper);
|
||||
_entityFunctions.RemoveEntity<BlockEntityDescriptor>(target);
|
||||
|
@ -39,12 +41,12 @@ namespace GamecraftModdingAPI.Blocks
|
|||
{
|
||||
}
|
||||
|
||||
public string Name { get; } = "GamecraftModdingAPIRemovalGameEngine";
|
||||
public string Name { get; } = "TechbloxModdingAPIRemovalGameEngine";
|
||||
|
||||
public bool isRemovable => false;
|
||||
|
||||
[HarmonyPatch]
|
||||
public class FactoryObtainerPatch
|
||||
class FactoryObtainerPatch
|
||||
{
|
||||
static void Postfix(IEntityFunctions entityFunctions,
|
||||
MachineGraphConnectionEntityFactory machineGraphConnectionEntityFactory)
|
73
TechbloxModdingAPI/Blocks/Engines/RotationEngine.cs
Normal file
73
TechbloxModdingAPI/Blocks/Engines/RotationEngine.cs
Normal file
|
@ -0,0 +1,73 @@
|
|||
using RobocraftX.Common;
|
||||
using RobocraftX.UECS;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.EntityStructs;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
|
||||
using TechbloxModdingAPI.Engines;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
namespace TechbloxModdingAPI.Blocks.Engines
|
||||
{
|
||||
/// <summary>
|
||||
/// Engine which executes block movement actions
|
||||
/// </summary>
|
||||
public class RotationEngine : IApiEngine
|
||||
{
|
||||
public string Name { get; } = "TechbloxModdingAPIRotationGameEngine";
|
||||
|
||||
public EntitiesDB entitiesDB { set; private get; }
|
||||
|
||||
public bool isRemovable => false;
|
||||
|
||||
public bool IsInGame = false;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
IsInGame = false;
|
||||
}
|
||||
|
||||
public void Ready()
|
||||
{
|
||||
IsInGame = true;
|
||||
}
|
||||
|
||||
// implementations for Rotation static class
|
||||
|
||||
internal float3 RotateBlock(Block block, Vector3 vector)
|
||||
{
|
||||
ref RotationEntityStruct rotStruct = ref this.entitiesDB.QueryEntityOrDefault<RotationEntityStruct>(block);
|
||||
ref GridRotationStruct gridStruct = ref this.entitiesDB.QueryEntityOrDefault<GridRotationStruct>(block);
|
||||
ref LocalTransformEntityStruct transStruct = ref this.entitiesDB.QueryEntityOrDefault<LocalTransformEntityStruct>(block);
|
||||
ref UECSPhysicsEntityStruct phyStruct = ref this.entitiesDB.QueryEntityOrDefault<UECSPhysicsEntityStruct>(block);
|
||||
// main (persistent) rotation
|
||||
Quaternion newRotation = rotStruct.rotation;
|
||||
newRotation.eulerAngles = vector;
|
||||
rotStruct.rotation = newRotation;
|
||||
// placement grid rotation
|
||||
gridStruct.rotation = newRotation;
|
||||
// rendered rotation
|
||||
transStruct.rotation = newRotation;
|
||||
// collision rotation
|
||||
if (phyStruct.ID != default)
|
||||
{ //It exists
|
||||
FullGameFields._physicsWorld.EntityManager.SetComponentData(phyStruct.uecsEntity,
|
||||
new Unity.Transforms.Rotation
|
||||
{
|
||||
Value = rotStruct.rotation
|
||||
});
|
||||
}
|
||||
|
||||
entitiesDB.QueryEntityOrDefault<GridConnectionsEntityStruct>(block).isProcessed = false;
|
||||
return ((Quaternion)rotStruct.rotation).eulerAngles;
|
||||
|
||||
}
|
||||
|
||||
internal float3 GetRotation(Block block)
|
||||
{
|
||||
ref RotationEntityStruct rotStruct = ref entitiesDB.QueryEntityOrDefault<RotationEntityStruct>(block);
|
||||
return ((Quaternion) rotStruct.rotation).eulerAngles;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,10 +6,10 @@ using RobocraftX.UECS;
|
|||
using Svelto.ECS;
|
||||
using Unity.Entities;
|
||||
|
||||
using GamecraftModdingAPI.Engines;
|
||||
using GamecraftModdingAPI.Utility;
|
||||
using TechbloxModdingAPI.Engines;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftModdingAPI.Blocks
|
||||
namespace TechbloxModdingAPI.Blocks.Engines
|
||||
{
|
||||
public class ScalingEngine : IApiEngine
|
||||
{
|
||||
|
@ -24,7 +24,7 @@ namespace GamecraftModdingAPI.Blocks
|
|||
{
|
||||
}
|
||||
|
||||
public string Name { get; } = "GamecraftModdingAPIScalingEngine";
|
||||
public string Name { get; } = "TechbloxModdingAPIScalingEngine";
|
||||
public bool isRemovable { get; } = false;
|
||||
|
||||
private EntityManager _entityManager; //Unity entity manager
|
||||
|
@ -41,7 +41,7 @@ namespace GamecraftModdingAPI.Blocks
|
|||
}
|
||||
|
||||
[HarmonyPatch]
|
||||
public class PhysicsEnginePatch
|
||||
class PhysicsEnginePatch
|
||||
{
|
||||
static void Postfix(IReactOnAddAndRemove<UECSPhysicsEntityCreationStruct> __instance)
|
||||
{
|
|
@ -1,11 +1,13 @@
|
|||
using System;
|
||||
using Svelto.ECS;
|
||||
using Svelto.DataStructures;
|
||||
|
||||
using Gamecraft.Wires;
|
||||
using Svelto.DataStructures;
|
||||
using Svelto.ECS;
|
||||
|
||||
using GamecraftModdingAPI.Engines;
|
||||
using TechbloxModdingAPI.Engines;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftModdingAPI.Blocks
|
||||
namespace TechbloxModdingAPI.Blocks.Engines
|
||||
{
|
||||
/// <summary>
|
||||
/// Engine which executes signal actions
|
||||
|
@ -17,7 +19,7 @@ namespace GamecraftModdingAPI.Blocks
|
|||
public const float HIGH = 1.0f;
|
||||
public const float ZERO = 0.0f;
|
||||
|
||||
public string Name { get; } = "GamecraftModdingAPISignalGameEngine";
|
||||
public string Name { get; } = "TechbloxModdingAPISignalGameEngine";
|
||||
|
||||
public EntitiesDB entitiesDB { set; private get; }
|
||||
|
||||
|
@ -42,7 +44,7 @@ namespace GamecraftModdingAPI.Blocks
|
|||
public WireEntityStruct CreateNewWire(EGID startBlock, byte startPort, EGID endBlock, byte endPort)
|
||||
{
|
||||
EGID wireEGID = new EGID(WiresExclusiveGroups.NewWireEntityId, NamedExclusiveGroup<WiresGroup>.Group);
|
||||
EntityComponentInitializer wireInitializer = Factory.BuildEntity<WireEntityDescriptor>(wireEGID);
|
||||
EntityInitializer wireInitializer = Factory.BuildEntity<WireEntityDescriptor>(wireEGID);
|
||||
wireInitializer.Init(new WireEntityStruct
|
||||
{
|
||||
sourceBlockEGID = startBlock,
|
||||
|
@ -88,8 +90,8 @@ namespace GamecraftModdingAPI.Blocks
|
|||
|
||||
public ref PortEntityStruct GetPortByOffset(Block block, byte portNumber, bool input)
|
||||
{
|
||||
BlockPortsStruct bps = GetFromDbOrInitData<BlockPortsStruct>(block, block.Id, out bool exists);
|
||||
if (!exists)
|
||||
var bps = entitiesDB.QueryEntityOptional<BlockPortsStruct>(block);
|
||||
if (!bps)
|
||||
{
|
||||
throw new BlockException("Block does not exist");
|
||||
}
|
||||
|
@ -206,46 +208,35 @@ namespace GamecraftModdingAPI.Blocks
|
|||
}
|
||||
return outputs;
|
||||
}
|
||||
|
||||
public EGID MatchBlockInputToPort(Block block, byte portUsage, out bool exists)
|
||||
|
||||
public OptionalRef<PortEntityStruct> MatchBlockIOToPort(Block block, byte portUsage, bool output)
|
||||
{
|
||||
BlockPortsStruct ports = GetFromDbOrInitData<BlockPortsStruct>(block, block.Id, out exists);
|
||||
return new EGID(ports.firstInputID + portUsage, NamedExclusiveGroup<InputPortsGroup>.Group);
|
||||
return MatchBlockIOToPort(block.Id, portUsage, output);
|
||||
}
|
||||
|
||||
public EGID MatchBlockInputToPort(EGID block, byte portUsage, out bool exists)
|
||||
public OptionalRef<PortEntityStruct> MatchBlockIOToPort(EGID block, byte portUsage, bool output)
|
||||
{
|
||||
if (!entitiesDB.Exists<BlockPortsStruct>(block))
|
||||
{
|
||||
exists = false;
|
||||
return default;
|
||||
}
|
||||
exists = true;
|
||||
var group = output
|
||||
? NamedExclusiveGroup<OutputPortsGroup>.Group
|
||||
: NamedExclusiveGroup<InputPortsGroup>.Group;
|
||||
BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(block);
|
||||
return new EGID(ports.firstInputID + portUsage, NamedExclusiveGroup<InputPortsGroup>.Group);
|
||||
}
|
||||
|
||||
public EGID MatchBlockOutputToPort(Block block, byte portUsage, out bool exists)
|
||||
{
|
||||
BlockPortsStruct ports = GetFromDbOrInitData<BlockPortsStruct>(block, block.Id, out exists);
|
||||
return new EGID(ports.firstOutputID + portUsage, NamedExclusiveGroup<OutputPortsGroup>.Group);
|
||||
}
|
||||
|
||||
public EGID MatchBlockOutputToPort(EGID block, byte portUsage, out bool exists)
|
||||
{
|
||||
if (!entitiesDB.Exists<BlockPortsStruct>(block))
|
||||
{
|
||||
exists = false;
|
||||
if (!entitiesDB.TryQueryMappedEntities<PortEntityStruct>(group, out var mapper))
|
||||
return default;
|
||||
for (uint i = 0; i < (output ? ports.outputCount : ports.inputCount); ++i)
|
||||
{
|
||||
uint entityID = (output ? ports.firstOutputID : ports.firstInputID) + i;
|
||||
if (!mapper.TryGetArrayAndEntityIndex(entityID, out var index, out var array) ||
|
||||
array[index].usage != portUsage) continue;
|
||||
return new OptionalRef<PortEntityStruct>(array, index);
|
||||
}
|
||||
exists = true;
|
||||
BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(block);
|
||||
return new EGID(ports.firstOutputID + portUsage, NamedExclusiveGroup<OutputPortsGroup>.Group);
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
public ref WireEntityStruct MatchPortToWire(EGID portID, EGID blockID, out bool exists)
|
||||
public ref WireEntityStruct MatchPortToWire(PortEntityStruct port, EGID blockID, out bool exists)
|
||||
{
|
||||
ref PortEntityStruct port = ref entitiesDB.QueryEntity<PortEntityStruct>(portID);
|
||||
var wires = entitiesDB.QueryEntities<WireEntityStruct>(NamedExclusiveGroup<WiresGroup>.Group);
|
||||
var wiresB = wires.ToBuffer().buffer;
|
||||
for (uint i = 0; i < wires.count; i++)
|
||||
|
@ -262,8 +253,7 @@ namespace GamecraftModdingAPI.Blocks
|
|||
return ref defRef[0];
|
||||
}
|
||||
|
||||
public ref WireEntityStruct MatchBlocksToWire(EGID startBlock, EGID endBlock, out bool exists, byte startPort = byte.MaxValue,
|
||||
byte endPort = byte.MaxValue)
|
||||
public EGID MatchBlocksToWire(EGID startBlock, EGID endBlock, byte startPort = byte.MaxValue, byte endPort = byte.MaxValue)
|
||||
{
|
||||
EGID[] startPorts;
|
||||
if (startPort == byte.MaxValue)
|
||||
|
@ -302,31 +292,23 @@ namespace GamecraftModdingAPI.Blocks
|
|||
if ((wiresB[w].destinationPortUsage == endPES.usage && wiresB[w].destinationBlockEGID == endBlock)
|
||||
&& (wiresB[w].sourcePortUsage == startPES.usage && wiresB[w].sourceBlockEGID == startBlock))
|
||||
{
|
||||
exists = true;
|
||||
return ref wiresB[w];
|
||||
return wiresB[w].ID;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exists = false;
|
||||
WireEntityStruct[] defRef = new WireEntityStruct[1];
|
||||
return ref defRef[0];
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
public ref ChannelDataStruct GetChannelDataStruct(EGID portID, out bool exists)
|
||||
{
|
||||
ref PortEntityStruct port = ref entitiesDB.QueryEntity<PortEntityStruct>(portID);
|
||||
public OptionalRef<ChannelDataStruct> GetChannelDataStruct(EGID portID)
|
||||
{
|
||||
var port = GetPort(portID);
|
||||
var channels = entitiesDB.QueryEntities<ChannelDataStruct>(NamedExclusiveGroup<ChannelDataGroup>.Group);
|
||||
var channelsB = channels.ToBuffer();
|
||||
if (port.firstChannelIndexCachedInSim < channels.count)
|
||||
{
|
||||
exists = true;
|
||||
return ref channelsB.buffer[port.firstChannelIndexCachedInSim];
|
||||
}
|
||||
exists = false;
|
||||
ChannelDataStruct[] defRef = new ChannelDataStruct[1];
|
||||
return ref defRef[0];
|
||||
return port.firstChannelIndexCachedInSim < channels.count
|
||||
? new OptionalRef<ChannelDataStruct>(channelsB.buffer, port.firstChannelIndexCachedInSim)
|
||||
: default;
|
||||
}
|
||||
|
||||
public EGID[] GetElectricBlocks()
|
||||
|
@ -386,29 +368,6 @@ namespace GamecraftModdingAPI.Blocks
|
|||
return results.ToArray();
|
||||
}
|
||||
|
||||
private ref T GetFromDbOrInitData<T>(Block block, EGID id, out bool exists) where T : unmanaged, IEntityComponent
|
||||
{
|
||||
T[] defRef = new T[1];
|
||||
if (entitiesDB.Exists<T>(id))
|
||||
{
|
||||
exists = true;
|
||||
return ref entitiesDB.QueryEntity<T>(id);
|
||||
}
|
||||
if (block == null || block.InitData.Group == null)
|
||||
{
|
||||
exists = false;
|
||||
return ref defRef[0];
|
||||
}
|
||||
EntityComponentInitializer initializer = new EntityComponentInitializer(block.Id, block.InitData.Group);
|
||||
if (initializer.Has<T>())
|
||||
{
|
||||
exists = true;
|
||||
return ref initializer.Get<T>();
|
||||
}
|
||||
exists = false;
|
||||
return ref defRef[0];
|
||||
}
|
||||
|
||||
private EntityCollection<ChannelDataStruct> GetSignalStruct(uint signalID, out uint index, bool input = true)
|
||||
{
|
||||
ExclusiveGroup group = input
|
26
TechbloxModdingAPI/Blocks/LogicGate.cs
Normal file
26
TechbloxModdingAPI/Blocks/LogicGate.cs
Normal file
|
@ -0,0 +1,26 @@
|
|||
namespace TechbloxModdingAPI.Blocks
|
||||
{
|
||||
using RobocraftX.Common;
|
||||
using Svelto.ECS;
|
||||
|
||||
|
||||
public class LogicGate : SignalingBlock
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a(n) LogicGate object representing an existing block.
|
||||
/// </summary>
|
||||
public LogicGate(EGID egid) :
|
||||
base(egid)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a(n) LogicGate object representing an existing block.
|
||||
/// </summary>
|
||||
public LogicGate(uint id) :
|
||||
base(new EGID(id, CommonExclusiveGroups.LOGIC_BLOCK_GROUP))
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue