Compare commits
284 commits
Author | SHA1 | Date | |
---|---|---|---|
5dff88d703 | |||
a8a451f8e4 | |||
67f32b8810 | |||
b3b1e9b9e7 | |||
e0cd7f6aec | |||
23439abde3 | |||
5e90c5ee26 | |||
5117b69500 | |||
f70b65e796 | |||
55344d1352 | |||
dfe1bfb504 | |||
a610623644 | |||
f9aa6ce2bb | |||
23abe47c72 | |||
c0ef8f1fae | |||
c4a9125ed3 | |||
3eecdf2cf5 | |||
2db7b607f0 | |||
7f63944a6e | |||
c6dae688fe | |||
7b2ac973d8 | |||
0ec47cd38b | |||
ddaa933e7d | |||
5fea7dc3b3 | |||
4684b33c69 | |||
d27bcee8d5 | |||
09d3c5e81c | |||
966fdd4c3a | |||
5602ef9268 | |||
93a0b2287a | |||
4ac8d53a2d | |||
f817becc6e | |||
2a1782cd82 | |||
5c1fe34f46 | |||
ef1b3de1a1 | |||
fef66c349d | |||
e3a7961be4 | |||
f53d0b63e7 | |||
619a5003cf | |||
6204b226d1 | |||
4bd636b8ed | |||
8a03277d84 | |||
aa947eaba1 | |||
63295f82c9 | |||
033ebdb86d | |||
2513040343 | |||
77d5e59ef6 | |||
9693341d7a | |||
c0eae77421 | |||
3351993936 | |||
49c3b60963 | |||
ece71c45a6 | |||
2a1676ce0f | |||
74d5a5c6b1 | |||
76faa69c74 | |||
52ccbe4dad | |||
0b2ffef0d3 | |||
99f077a917 | |||
c1c226ef2a | |||
|
06cb911ea3 | ||
b31eaa20c0 | |||
94c0c1370b | |||
b8fd14d934 | |||
5bfd0b7f10 | |||
220eb02a19 | |||
e8515ef42b | |||
f5e3010e48 | |||
1cbe252727 | |||
b3f7dcd36d | |||
e9df67f462 | |||
70b322583a | |||
4f0645492c | |||
58d703f502 | |||
|
db08bf1ac0 | ||
dd2680abd5 | |||
4807c12387 | |||
3432a1ae33 | |||
7a53e1d32f | |||
aa12b848d0 | |||
b6b9a29a3c | |||
6fedf90380 | |||
3eef859095 | |||
858a5c9b5c | |||
d238c97906 | |||
61184145a9 | |||
2d99d1d478 | |||
78ee3b3bcd | |||
aea3ef3623 | |||
62afd3b780 | |||
5172b13b7c | |||
c914b5b393 | |||
a6f52070ee | |||
807470e289 | |||
df6a2e84e1 | |||
6e03847ab0 | |||
55b38f1678 | |||
eb7a09ed22 | |||
6a2459b3e7 | |||
cc4850a073 | |||
677c8b0907 | |||
1f688195af | |||
9a4ff858f3 | |||
124ef410c7 | |||
98e00de642 | |||
2d41026a05 | |||
a6b69d94c9 | |||
|
37e3c6f718 | ||
|
0ef875b6b2 | ||
d954060a5a | |||
fdc47832f4 | |||
|
95574a50f8 | ||
|
1c014e36ac | ||
879901f4b9 | |||
6a90739197 | |||
9c5c980c0b | |||
712ece86db | |||
a7f6a16231 | |||
4e16f251ee | |||
78f0ea0162 | |||
5dfb01ef0b | |||
432d6bcf96 | |||
|
be7d8ba33a | ||
56a64daa18 | |||
ab1ae51ece | |||
|
404c47c7c0 | ||
fad3b5cbf4 | |||
1f911b1d32 | |||
f30dcd251f | |||
680721256c | |||
64b42830a3 | |||
d744aaab79 | |||
3dd61b5e4f | |||
1c4e2a0db2 | |||
4f8feaa24b | |||
|
8eec1358e9 | ||
|
08138e3589 | ||
987fbe673a | |||
4580ae3b66 | |||
f1376f5df6 | |||
2179ba6386 | |||
1a986056a1 | |||
|
d891f12701 | ||
|
1cb663b4d1 | ||
0bd348bd47 | |||
3929144171 | |||
|
c6a1ea35cc | ||
b0b496f22f | |||
4701b3577d | |||
abbb83da26 | |||
64aace3bde | |||
9e9f56881f | |||
c9e71d84b4 | |||
|
4dfa7b0f4e | ||
1a0c98dd67 | |||
f2ce037564 | |||
92965404ce | |||
58cfba443e | |||
ee6a0e3af6 | |||
|
9e6edc19bd | ||
d581ec598a | |||
1e9d1c8f81 | |||
53bdd27166 | |||
c06ed340a2 | |||
f295f712b6 | |||
|
aae2057972 | ||
|
daf4a24bc9 | ||
|
11b94e384e | ||
cfdc5e8c26 | |||
|
fd97194903 | ||
2172364d26 | |||
|
89f354b647 | ||
|
50ebf4f0a6 | ||
|
167ea5388b | ||
|
8354123169 | ||
|
7f5a36cb62 | ||
|
e56871f5ef | ||
|
708dbdd81d | ||
|
ca0e6e089d | ||
|
b81562ea58 | ||
057a030c20 | |||
15485481a2 | |||
b53dff5d12 | |||
5264d98ce7 | |||
5e335e78ff | |||
7336fe8353 | |||
89d32956d9 | |||
|
ea8a9184bc | ||
|
926d968eed | ||
|
421faf7167 | ||
47126d2d79 | |||
c5e9599c46 | |||
3f2139d592 | |||
|
9e47bbcd9a | ||
|
cda57afade | ||
|
4a9ceecc29 | ||
16521ab7eb | |||
cc4ed3e174 | |||
|
e0aa052305 | ||
d842df7681 | |||
3592c6f464 | |||
5bbb54c0c5 | |||
aa0aefd41b | |||
f403feb298 | |||
|
6f589f1744 | ||
|
b376133d28 | ||
|
60f231f939 | ||
|
b6a5074fd2 | ||
|
189c3ca2a5 | ||
|
0019b7c073 | ||
|
78122ee445 | ||
|
c912f3ba64 | ||
9b1e2548d1 | |||
|
0d17a1b509 | ||
|
960ab16f0b | ||
|
d2c9cde1d2 | ||
ae1f53e119 | |||
62318b0843 | |||
3dcce18ceb | |||
448cf3af48 | |||
cae626197f | |||
|
cd78fd6718 | ||
f62211309e | |||
|
197fc5f2f9 | ||
|
6a137472c1 | ||
5660bfc28d | |||
dba7c0a46f | |||
|
98c7e624f8 | ||
|
2d89b82759 | ||
0b8491cecf | |||
269d30b0db | |||
ab7c5805fe | |||
dca6fe4c1b | |||
084cbb40c4 | |||
|
4e08acf44c | ||
|
e8f59e8641 | ||
|
8326d70cbf | ||
|
fb841b6542 | ||
3ff98f29bb | |||
|
97de5e606b | ||
6dce87fb66 | |||
1c5ce37fce | |||
|
ebea9da232 | ||
|
500cb9b716 | ||
|
782e00e3dd | ||
|
06b8c3664e | ||
|
205b3fc51a | ||
|
9cb6917d28 | ||
6f8241554d | |||
ff57a16565 | |||
|
5168bfbad7 | ||
|
211c9c9c31 | ||
|
e3b3fd5ef4 | ||
|
42b21bc16d | ||
|
a17e5a6449 | ||
|
bc77c35bdb | ||
|
ac2d3b38c7 | ||
|
1f35b2a434 | ||
|
f244eb9683 | ||
|
4e5cc40be4 | ||
|
5cb27b05c0 | ||
|
934b542ea1 | ||
|
1faa9898f7 | ||
|
36516ec185 | ||
|
07ba6f2dc4 | ||
83427b806e | |||
|
2963517764 | ||
9609187cff | |||
81f2f613f7 | |||
f231ea9f6d | |||
|
d1c0556b9c | ||
|
a9322a08dd | ||
|
5ca18d272a | ||
|
d00bdc80ed | ||
|
2149458d96 | ||
|
5654c041c5 | ||
|
206ec19d1e | ||
|
e29c5fb107 | ||
b4e70fcebe | |||
|
c6f8afa812 | ||
|
c1bb863578 | ||
|
fd98879ccd | ||
|
7ac5120ef5 | ||
|
7519bc37ae | ||
|
feab9e38c9 |
170 changed files with 16960 additions and 3027 deletions
|
@ -1,66 +0,0 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Automation
|
||||
{
|
||||
class Automation
|
||||
{
|
||||
private static readonly string Name = "automation";
|
||||
|
||||
private static readonly string XmlPrefix = "<!--Start Dependencies-->\n <ItemGroup>\n";
|
||||
private static readonly string XmlSuffix = " </ItemGroup>\n<!--End Dependencies-->";
|
||||
|
||||
private static readonly string GamecraftModdingAPI_csproj = @"\GamecraftModdingAPI.csproj";
|
||||
static void Main(string[] args)
|
||||
{
|
||||
if (args.Length != 2 || args[0] == "-h" || args[0] == "--help")
|
||||
{
|
||||
Console.WriteLine($"Usage : {Name}.exe <path to Gamecraft DLLs> <path to GamecraftModdingAPI.csproj>");
|
||||
Console.WriteLine("Other arguments:");
|
||||
Console.WriteLine(" --help : display this message");
|
||||
Console.WriteLine($" --version : display {Name}'s version");
|
||||
return;
|
||||
}
|
||||
Console.WriteLine("Building Assembly references");
|
||||
string asmXml = BuildAssemblyReferencesXML(args[0]);
|
||||
//Console.WriteLine(asmXml);
|
||||
string csprojPath = args[1].EndsWith(GamecraftModdingAPI_csproj)? args[1] : args[1]+GamecraftModdingAPI_csproj;
|
||||
Console.WriteLine($"Parsing {csprojPath}");
|
||||
string csprojContents = File.ReadAllText(csprojPath);
|
||||
Match dependenciesStart = (new Regex(@"<\s*!--\s*Start\s+Dependencies\s*--\s*>", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Multiline)).Match(csprojContents);
|
||||
Match dependenciesEnd = (new Regex(@"<\s*!--\s*End\s+Dependencies\s*--\s*>", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Multiline)).Match(csprojContents);
|
||||
if (!dependenciesEnd.Success || !dependenciesStart.Success)
|
||||
{
|
||||
Console.WriteLine("Unable to find dependency XML comments, aborting!");
|
||||
return;
|
||||
}
|
||||
csprojContents = csprojContents.Substring(0, dependenciesStart.Index) + asmXml + csprojContents.Substring(dependenciesEnd.Index+dependenciesEnd.Length);
|
||||
//Console.WriteLine(csprojContents);
|
||||
Console.WriteLine($"Writing Assembly references into {csprojPath}");
|
||||
File.WriteAllText(csprojPath, csprojContents);
|
||||
Console.WriteLine("Successfully generated Gamecraft assembly DLL references");
|
||||
}
|
||||
|
||||
static string BuildAssemblyReferencesXML(string path)
|
||||
{
|
||||
StringBuilder result = new StringBuilder();
|
||||
result.Append(XmlPrefix);
|
||||
string[] files = Directory.GetFiles(path, "*.dll");
|
||||
for(int i = 0; i<files.Length; i++)
|
||||
{
|
||||
if (files[i].Contains(@"\System.") || files[i].EndsWith(@"mscorlib.dll") || files[i].Contains(@"\Mono."))
|
||||
{
|
||||
// no
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Append($" <Reference Include=\"{files[i].Substring(files[i].LastIndexOf(@"\") + 1).Replace(".dll", "")}\">\n <HintPath>{files[i]}</HintPath>\n </Reference>\n");
|
||||
}
|
||||
}
|
||||
result.Append(XmlSuffix);
|
||||
return result.ToString();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
67
Automation/bump_version.py
Executable file
67
Automation/bump_version.py
Executable file
|
@ -0,0 +1,67 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
import argparse
|
||||
import re
|
||||
# this assumes a mostly semver-complient version number
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Increment TechbloxModdingAPI version")
|
||||
parser.add_argument('version', metavar="VN", type=str, help="The version number to increment, or the index of the number (zero-indexed).")
|
||||
args = parser.parse_args()
|
||||
|
||||
version_index = -1
|
||||
try:
|
||||
version_index = int(args.version)
|
||||
except Exception:
|
||||
if args.version.lower() == "major":
|
||||
version_index = 0
|
||||
elif args.version.lower() == "minor":
|
||||
version_index = 1
|
||||
elif args.version.lower() == "patch":
|
||||
version_index = 2
|
||||
|
||||
if version_index < 0:
|
||||
print("Could not parse version argument.")
|
||||
exit(version_index)
|
||||
|
||||
print(version_index)
|
||||
old_version = ""
|
||||
new_version = ""
|
||||
|
||||
with open("../TechbloxModdingAPI/TechbloxModdingAPI.csproj", "r") as xmlFile:
|
||||
print("Parsing TechbloxModdingAPI.csproj")
|
||||
fileStr = xmlFile.read()
|
||||
versionMatch = re.search(r"<Version>(.+)</Version>", fileStr)
|
||||
if versionMatch is None:
|
||||
print("Unable to find version number in TechbloxModdingAPI.csproj")
|
||||
exit(1)
|
||||
old_version = versionMatch.group(1)
|
||||
versionList = old_version.split(".")
|
||||
if len(versionList) <= version_index:
|
||||
print("Invalid version string")
|
||||
exit(1)
|
||||
versionList[version_index] = str(int(versionList[version_index]) + 1)
|
||||
for i in range(version_index + 1, len(versionList)):
|
||||
try:
|
||||
int(versionList[i])
|
||||
versionList[i] = "0"
|
||||
except Exception:
|
||||
tmp = versionList[i].split("-")
|
||||
tmp[0] = "0"
|
||||
versionList[i] = "-".join(tmp)
|
||||
new_version = ".".join(versionList)
|
||||
print(new_version)
|
||||
newFileContents = fileStr.replace("<Version>"+old_version+"</Version>", "<Version>"+new_version+"</Version>")
|
||||
|
||||
with open("../TechbloxModdingAPI/TechbloxModdingAPI.csproj", "w") as xmlFile:
|
||||
print("Writing new version to project file")
|
||||
xmlFile.write(newFileContents)
|
||||
|
||||
with open("../doxygen.conf", "r") as doxFile:
|
||||
print("Parsing doxygen.conf")
|
||||
doxStr = doxFile.read()
|
||||
newFileContents = doxStr.replace("= \"v" + old_version + "\"", "= \"v" + new_version + "\"")
|
||||
|
||||
with open("../doxygen.conf", "w") as doxFile:
|
||||
print("Writing new version to doxygen config")
|
||||
doxFile.write(newFileContents)
|
62
Automation/gen_csproj.py
Executable file
62
Automation/gen_csproj.py
Executable file
|
@ -0,0 +1,62 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
import argparse
|
||||
from pathlib import Path, PurePath
|
||||
import re
|
||||
import os
|
||||
|
||||
DLL_EXCLUSIONS_REGEX = r"(System|Microsoft|Mono|IronPython|DiscordRPC|IllusionInjector|IllusionPlugin|netstandard)\."
|
||||
|
||||
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)) is None and str(child).lower().endswith(".dll"):
|
||||
childstr = str(child)
|
||||
childstr = os.path.relpath(childstr, addedPath).replace("\\", "/")
|
||||
result.append(childstr)
|
||||
result.sort(key=str.lower)
|
||||
result = [path + "/IllusionInjector.dll", path + "/IllusionPlugin.dll"] + result # Always put it on top
|
||||
return result
|
||||
|
||||
def buildReferencesXml(path):
|
||||
assemblyPathes = getAssemblyReferences(path)
|
||||
result = list()
|
||||
for asm in assemblyPathes:
|
||||
asmPath = str(asm)
|
||||
xml = " <Reference Include=\"" + asmPath[asmPath.rfind("/") + 1:].replace(".dll", "") + "\">\n" \
|
||||
+ " <HintPath>" + asmPath.replace("/", "\\") + "</HintPath>\n" \
|
||||
+ " <HintPath>..\\" + asmPath.replace("/", "\\") + "</HintPath>\n" \
|
||||
+ " </Reference>\n"
|
||||
result.append(xml)
|
||||
return "<!--Start Dependencies-->\n <ItemGroup>\n" + "".join(result) + " </ItemGroup>\n<!--End Dependencies-->"
|
||||
|
||||
if __name__ == "__main__":
|
||||
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_TB/Techblox_Data/Managed")
|
||||
# print(asmXml)
|
||||
|
||||
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)
|
||||
depsEnd = re.search(r"\<!--\s*End\s+Dependencies\s*--\>", fileStr)
|
||||
if depsStart is None or depsEnd is None:
|
||||
print("Unable to find dependency XML comments, aborting!")
|
||||
exit(1)
|
||||
newFileStr = fileStr[:depsStart.start() - 1] + "\n" + asmXml + "\n" + fileStr[depsEnd.end() + 1:]
|
||||
with open("../TechbloxModdingAPI/TechbloxModdingAPI.csproj", "w") as xmlFile:
|
||||
print("Writing Assembly references")
|
||||
xmlFile.write(newFileStr)
|
||||
# print(newFileStr)
|
||||
|
||||
|
10
CodeGenerator/App.config
Normal file
10
CodeGenerator/App.config
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><configuration>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="mscorlib" publicKeyToken="b77a5c561934e089" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
158
CodeGenerator/BlockClassGenerator.cs
Normal file
158
CodeGenerator/BlockClassGenerator.cs
Normal file
|
@ -0,0 +1,158 @@
|
|||
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;
|
||||
|
||||
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");
|
||||
bool reflection = type.IsNotPublic;
|
||||
var reflectedType = new CodeSnippetExpression($"HarmonyLib.AccessTools.TypeByName(\"{type.FullName}\")");
|
||||
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 getStruct = new CodeMethodInvokeExpression(
|
||||
new CodeMethodReferenceExpression(new CodeSnippetExpression("BlockEngine"),
|
||||
"GetBlockInfo", new CodeTypeReference(type)),
|
||||
new CodeThisReferenceExpression());
|
||||
CodeExpression structFieldReference = new CodeFieldReferenceExpression(getStruct, field.Name);
|
||||
CodeExpression reflectedGet = new CodeCastExpression(field.FieldType, new CodeMethodInvokeExpression(
|
||||
new CodeMethodReferenceExpression(new CodeSnippetExpression("BlockEngine"),
|
||||
"GetBlockInfo"),
|
||||
new CodeThisReferenceExpression(), reflectedType, new CodePrimitiveExpression(field.Name)));
|
||||
CodeExpression reflectedSet = new CodeMethodInvokeExpression(
|
||||
new CodeMethodReferenceExpression(new CodeSnippetExpression("BlockEngine"),
|
||||
"SetBlockInfo"),
|
||||
new CodeThisReferenceExpression(), reflectedType, new CodePrimitiveExpression(field.Name),
|
||||
new CodePropertySetValueReferenceExpression());
|
||||
cl.Members.Add(new CodeMemberProperty
|
||||
{
|
||||
Name = propName,
|
||||
HasGet = true,
|
||||
HasSet = true,
|
||||
GetStatements =
|
||||
{
|
||||
new CodeMethodReturnStatement(reflection ? reflectedGet : structFieldReference)
|
||||
},
|
||||
SetStatements =
|
||||
{
|
||||
reflection
|
||||
? (CodeStatement)new CodeExpressionStatement(reflectedSet)
|
||||
: 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);
|
||||
}
|
||||
}
|
1592
CodeGenerator/CodeGenerator.csproj
Normal file
1592
CodeGenerator/CodeGenerator.csproj
Normal file
File diff suppressed because it is too large
Load diff
53
CodeGenerator/Program.cs
Normal file
53
CodeGenerator/Program.cs
Normal file
|
@ -0,0 +1,53 @@
|
|||
using System.Collections.Generic;
|
||||
using HarmonyLib;
|
||||
using RobocraftX.Blocks;
|
||||
using RobocraftX.Common;
|
||||
using RobocraftX.GroupTags;
|
||||
using RobocraftX.PilotSeat;
|
||||
using Svelto.ECS;
|
||||
using Techblox.EngineBlock;
|
||||
using Techblox.ServoBlocksServer;
|
||||
using Techblox.WheelRigBlock;
|
||||
|
||||
namespace CodeGenerator
|
||||
{
|
||||
internal class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
GenerateBlockClasses();
|
||||
}
|
||||
|
||||
private static void GenerateBlockClasses()
|
||||
{
|
||||
var bcg = new BlockClassGenerator();
|
||||
bcg.Generate("Engine", null, new Dictionary<string, string>
|
||||
{
|
||||
{ "engineOn", "On" }
|
||||
}, AccessTools.TypeByName("Techblox.EngineBlock.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(ServoReadOnlyTweakableComponent), 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));
|
||||
//bcg.Generate("ObjectID", "ObjectIDBlockExclusiveGroups.OBJECT_ID_BLOCK_GROUP", null, typeof(ObjectIDTweakableComponent));
|
||||
}
|
||||
}
|
||||
}
|
4
CodeGenerator/packages.config
Normal file
4
CodeGenerator/packages.config
Normal file
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Lib.Harmony" version="2.2.0" targetFramework="net472" />
|
||||
</packages>
|
|
@ -1,31 +0,0 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.29411.108
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GamecraftModdingAPI", "GamecraftModdingAPI\GamecraftModdingAPI.csproj", "{7FD5A7D8-4F3E-426A-B07D-7DC70442A4DF}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Automation", "Automation\Automation.csproj", "{1DF6E538-4DA4-4708-A567-781AF788921A}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{7FD5A7D8-4F3E-426A-B07D-7DC70442A4DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7FD5A7D8-4F3E-426A-B07D-7DC70442A4DF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7FD5A7D8-4F3E-426A-B07D-7DC70442A4DF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7FD5A7D8-4F3E-426A-B07D-7DC70442A4DF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1DF6E538-4DA4-4708-A567-781AF788921A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1DF6E538-4DA4-4708-A567-781AF788921A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1DF6E538-4DA4-4708-A567-781AF788921A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1DF6E538-4DA4-4708-A567-781AF788921A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {72FB94D0-6C50-475B-81E0-C94C7D7A2A17}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -1,20 +0,0 @@
|
|||
namespace GamecraftModdingAPI.Blocks
|
||||
{
|
||||
/// <summary>
|
||||
/// Preset block colours
|
||||
/// </summary>
|
||||
public enum BlockColors
|
||||
{
|
||||
Default = byte.MaxValue,
|
||||
White = 0,
|
||||
Pink,
|
||||
Purple,
|
||||
Blue,
|
||||
Aqua,
|
||||
Green,
|
||||
Lime,
|
||||
Yellow,
|
||||
Orange,
|
||||
Red
|
||||
}
|
||||
}
|
|
@ -1,186 +0,0 @@
|
|||
namespace GamecraftModdingAPI.Blocks
|
||||
{
|
||||
/// <summary>
|
||||
/// Possible block types
|
||||
/// </summary>
|
||||
public enum BlockIDs
|
||||
{
|
||||
AluminiumCube,
|
||||
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,
|
||||
BeachTree1 = 200,
|
||||
BeachTree2,
|
||||
BeachTree3,
|
||||
Rock1,
|
||||
Rock2,
|
||||
Rock3,
|
||||
Rock4,
|
||||
BirchTree1,
|
||||
BirchTree2,
|
||||
BirchTree3,
|
||||
PineTree1,
|
||||
PineTree2,
|
||||
PineTree3,
|
||||
Flower1,
|
||||
Flower2,
|
||||
Flower3,
|
||||
Shrub1,
|
||||
Shrub2,
|
||||
Shrub3
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Svelto.ECS;
|
||||
using RobocraftX.Common;
|
||||
|
||||
namespace GamecraftModdingAPI.Blocks
|
||||
{
|
||||
/// <summary>
|
||||
/// ExclusiveGroups and IDs used with blocks
|
||||
/// </summary>
|
||||
public static class BlockIdentifiers
|
||||
{
|
||||
/// <summary>
|
||||
/// Blocks placed by the player
|
||||
/// </summary>
|
||||
public static ExclusiveGroup OWNED_BLOCKS { get { return CommonExclusiveGroups.OWNED_BLOCKS_GROUP; } }
|
||||
|
||||
/// <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.BLOCKS_DISABLED_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; } }
|
||||
|
||||
public static ExclusiveGroup OUTPUT_SIGNAL_CHANNELS { get { return CommonExclusiveGroups.CHANNEL_OUTPUT_SIGNAL_GROUPS; } }
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the most recently placed block
|
||||
/// </summary>
|
||||
public static uint LatestBlockID { get { return CommonExclusiveGroups.CurrentBlockEntityID - 1; } }
|
||||
}
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace GamecraftModdingAPI.Blocks
|
||||
{
|
||||
/// <summary>
|
||||
/// Common block movement operations
|
||||
/// </summary>
|
||||
public static class Movement
|
||||
{
|
||||
private static MovementEngine movementEngine = new MovementEngine();
|
||||
|
||||
/// <summary>
|
||||
/// Move a single block by a specific (x,y,z) amount
|
||||
/// </summary>
|
||||
/// <param name="id">The block's id</param>
|
||||
/// <param name="vector">The movement amount (x,y,z)</param>
|
||||
/// <returns>Whether the operation was successful</returns>
|
||||
public static bool MoveBlock(uint id, float3 vector)
|
||||
{
|
||||
if (movementEngine.IsInGame && movementEngine.IsBuildMode())
|
||||
{
|
||||
movementEngine.MoveBlock(id, vector);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Move all connected blocks by a specific (x,y,z) amount
|
||||
/// </summary>
|
||||
/// <param name="id">The starting block's id</param>
|
||||
/// <param name="vector">The movement amount (x,y,z)</param>
|
||||
/// <returns>Whether the operation was successful</returns>
|
||||
public static bool MoveConnectedBlocks(uint id, float3 vector)
|
||||
{
|
||||
if (movementEngine.IsInGame && movementEngine.IsBuildMode())
|
||||
{
|
||||
movementEngine.MoveConnectedBlocks(id, vector);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
GamecraftModdingAPI.Utility.GameEngineManager.AddGameEngine(movementEngine);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using RobocraftX;
|
||||
using RobocraftX.Blocks;
|
||||
using RobocraftX.Blocks.Ghost;
|
||||
using RobocraftX.Common;
|
||||
using RobocraftX.Multiplayer;
|
||||
using RobocraftX.SimulationModeState;
|
||||
using RobocraftX.UECS;
|
||||
using Unity.Entities;
|
||||
using Svelto.Context;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.EntityStructs;
|
||||
using Unity.Transforms;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
|
||||
using GamecraftModdingAPI.Utility;
|
||||
using Svelto.DataStructures;
|
||||
|
||||
namespace GamecraftModdingAPI.Blocks
|
||||
{
|
||||
/// <summary>
|
||||
/// Engine which executes block movement actions
|
||||
/// </summary>
|
||||
public class MovementEngine : IApiEngine
|
||||
{
|
||||
public string Name { get; } = "GamecraftModdingAPIMovementGameEngine";
|
||||
|
||||
public IEntitiesDB entitiesDB { set; private get; }
|
||||
|
||||
public bool IsInGame = false;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
IsInGame = false;
|
||||
}
|
||||
|
||||
public void Ready()
|
||||
{
|
||||
IsInGame = true;
|
||||
}
|
||||
|
||||
// implementations for Movement static class
|
||||
|
||||
public float3 MoveBlock(uint blockID, float3 vector)
|
||||
{
|
||||
ref PositionEntityStruct posStruct = ref this.entitiesDB.QueryEntity<PositionEntityStruct>(blockID, CommonExclusiveGroups.OWNED_BLOCKS_GROUP);
|
||||
ref GridRotationStruct gridStruct = ref this.entitiesDB.QueryEntity<GridRotationStruct>(blockID, CommonExclusiveGroups.OWNED_BLOCKS_GROUP);
|
||||
ref LocalTransformEntityStruct transStruct = ref this.entitiesDB.QueryEntity<LocalTransformEntityStruct>(blockID, CommonExclusiveGroups.OWNED_BLOCKS_GROUP);
|
||||
ref UECSPhysicsEntityStruct phyStruct = ref this.entitiesDB.QueryEntity<UECSPhysicsEntityStruct>(blockID, CommonExclusiveGroups.OWNED_BLOCKS_GROUP);
|
||||
// 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
|
||||
});
|
||||
return posStruct.position;
|
||||
}
|
||||
|
||||
public float3 MoveConnectedBlocks(uint blockID, float3 vector)
|
||||
{
|
||||
Stack<uint> cubeStack = new Stack<uint>();
|
||||
FasterList<uint> cubesToMove = new FasterList<uint>();
|
||||
ConnectedCubesUtility.TreeTraversal.GetConnectedCubes(entitiesDB, blockID, cubeStack, cubesToMove, (in GridConnectionsEntityStruct g) => { return false; });
|
||||
for (int i = 0; i < cubesToMove.count; i++)
|
||||
{
|
||||
MoveBlock(cubesToMove[i], vector);
|
||||
entitiesDB.QueryEntity<GridConnectionsEntityStruct>(cubesToMove[i], CommonExclusiveGroups.OWNED_BLOCKS_GROUP).isProcessed = false;
|
||||
}
|
||||
return this.entitiesDB.QueryEntity<PositionEntityStruct>(blockID, CommonExclusiveGroups.OWNED_BLOCKS_GROUP).position;
|
||||
}
|
||||
|
||||
public bool IsBuildMode()
|
||||
{
|
||||
return GamecraftModdingAPI.Utility.GameState.IsBuildMode();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
using System;
|
||||
|
||||
using Unity.Mathematics;
|
||||
|
||||
using GamecraftModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftModdingAPI.Blocks
|
||||
{
|
||||
/// <summary>
|
||||
/// Common block placement operations
|
||||
/// </summary>
|
||||
public static class Placement
|
||||
{
|
||||
private static PlacementEngine placementEngine = new PlacementEngine();
|
||||
|
||||
/// <summary>
|
||||
/// Place a 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.
|
||||
/// </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 in the grid - 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="playerId">The player who placed the block</param>
|
||||
/// <returns>Whether the operation was successful</returns>
|
||||
public static bool PlaceBlock(BlockIDs block, float3 position,
|
||||
float3 rotation = default, BlockColors color = BlockColors.Default, byte darkness = 0,
|
||||
int uscale = 1, float3 scale = default, uint playerId = 0)
|
||||
{
|
||||
if (placementEngine.IsInGame && GameState.IsBuildMode())
|
||||
{
|
||||
try
|
||||
{
|
||||
placementEngine.PlaceBlock(block, color, darkness, position, uscale, scale, playerId, rotation);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
uREPL.Log.Output(e.Message);
|
||||
#if DEBUG
|
||||
//Logging.LogException(e);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
GameEngineManager.AddGameEngine(placementEngine);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,142 +0,0 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
using DataLoader;
|
||||
using Harmony;
|
||||
using RobocraftX.Blocks;
|
||||
using RobocraftX.Blocks.Ghost;
|
||||
using RobocraftX.Blocks.Scaling;
|
||||
using RobocraftX.Character;
|
||||
using RobocraftX.CommandLine.Custom;
|
||||
using RobocraftX.Common;
|
||||
using RobocraftX.Common.Input;
|
||||
using RobocraftX.Common.Utilities;
|
||||
using RobocraftX.CR.MachineEditing;
|
||||
using RobocraftX.StateSync;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.EntityStructs;
|
||||
using Unity.Jobs;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
using uREPL;
|
||||
|
||||
using GamecraftModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftModdingAPI.Blocks
|
||||
{
|
||||
/// <summary>
|
||||
/// Engine which executes block placement actions
|
||||
/// </summary>
|
||||
public class PlacementEngine : IApiEngine
|
||||
{
|
||||
public bool IsInGame = false;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
IsInGame = false;
|
||||
}
|
||||
|
||||
public void Ready()
|
||||
{
|
||||
IsInGame = true;
|
||||
}
|
||||
|
||||
public IEntitiesDB entitiesDB { get; set; }
|
||||
internal static BlockEntityFactory _blockEntityFactory; //Injected from PlaceBlockEngine
|
||||
|
||||
public void PlaceBlock(BlockIDs block, BlockColors color, byte darkness, float3 position, int uscale,
|
||||
float3 scale, uint playerId, float3 rotation)
|
||||
{ //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.)");
|
||||
BuildBlock((ushort) block, (byte) (color + darkness * 10), position, uscale, scale, rotation).Init(
|
||||
new BlockPlacementInfoStruct()
|
||||
{
|
||||
loadedFromDisk = false,
|
||||
placedBy = playerId
|
||||
});
|
||||
}
|
||||
|
||||
private EntityStructInitializer BuildBlock(ushort block, byte color, float3 position, int uscale, float3 scale, float3 rot)
|
||||
{
|
||||
if (_blockEntityFactory == null)
|
||||
throw new Exception("The factory is null.");
|
||||
if (uscale < 1)
|
||||
throw new Exception("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.DBIDMAP.ContainsKey(dbid))
|
||||
throw new Exception("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};
|
||||
CubeCategoryStruct category = new CubeCategoryStruct
|
||||
{category = CubeCategory.General, type = CubeType.Block};
|
||||
DBEntityStruct dbEntity = new DBEntityStruct {DBID = dbid};
|
||||
uint num = PrefabsID.DBIDMAP[dbid];
|
||||
GFXPrefabEntityStructGO gfx = new GFXPrefabEntityStructGO {prefabID = num};
|
||||
BlockPlacementScaleEntityStruct placementScale = new BlockPlacementScaleEntityStruct
|
||||
{
|
||||
blockPlacementHeight = uscale, blockPlacementWidth = uscale, desiredScaleFactor = uscale,
|
||||
snapGridScale = uscale,
|
||||
unitSnapOffset = 0, isUsingUnitSize = true
|
||||
};
|
||||
EquippedColourStruct colour = new EquippedColourStruct {indexInPalette = color};
|
||||
EGID egid2;
|
||||
switch (category.category)
|
||||
{
|
||||
case CubeCategory.SpawnPoint:
|
||||
case CubeCategory.BuildingSpawnPoint:
|
||||
egid2 = MachineEditingGroups.NewSpawnPointBlockID;
|
||||
break;
|
||||
default:
|
||||
egid2 = MachineEditingGroups.NewBlockID;
|
||||
break;
|
||||
}
|
||||
|
||||
int cubeId = PrefabsID.GenerateDBID((ushort) category.category, block);
|
||||
EntityStructInitializer
|
||||
structInitializer =
|
||||
_blockEntityFactory.Build(egid2, (uint) cubeId); //The ghost block index is only used for triggers
|
||||
if (colour.indexInPalette != byte.MaxValue)
|
||||
structInitializer.Init(new ColourParameterEntityStruct
|
||||
{
|
||||
indexInPalette = colour.indexInPalette
|
||||
});
|
||||
structInitializer.Init(new GFXPrefabEntityStructGPUI(gfx.prefabID));
|
||||
structInitializer.Init(new PhysicsPrefabEntityStruct(gfx.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
|
||||
});
|
||||
return structInitializer;
|
||||
}
|
||||
|
||||
public string Name { get; } = "GamecraftModdingAPIPlacementGameEngine";
|
||||
|
||||
[HarmonyPatch]
|
||||
public class FactoryObtainerPatch
|
||||
{
|
||||
static void Postfix(BlockEntityFactory blockEntityFactory)
|
||||
{
|
||||
_blockEntityFactory = blockEntityFactory;
|
||||
Logging.MetaDebugLog("Block entity factory injected.");
|
||||
}
|
||||
|
||||
static MethodBase TargetMethod(HarmonyInstance instance)
|
||||
{
|
||||
return typeof(PlaceBlockEngine).GetConstructors()[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace GamecraftModdingAPI.Blocks
|
||||
{
|
||||
/// <summary>
|
||||
/// Common block movement operations
|
||||
/// </summary>
|
||||
public static class Rotation
|
||||
{
|
||||
private static RotationEngine rotationEngine = new RotationEngine();
|
||||
|
||||
/// <summary>
|
||||
/// Rotate a single block by a specific amount in degrees
|
||||
/// </summary>
|
||||
/// <param name="id">The block's id</param>
|
||||
/// <param name="vector">The rotation amount around the x,y,z-axis</param>
|
||||
/// <returns>Whether the operation was successful</returns>
|
||||
public static bool RotateBlock(uint id, float3 vector)
|
||||
{
|
||||
if (rotationEngine.IsInGame && rotationEngine.IsBuildMode())
|
||||
{
|
||||
rotationEngine.RotateBlock(id, vector);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rotate all connected blocks by a specific amount in degrees
|
||||
/// </summary>
|
||||
/// <param name="id">The starting block's id</param>
|
||||
/// <param name="vector">The rotation around the x,y,z-axis</param>
|
||||
/// <returns>Whether the operation was successful</returns>
|
||||
public static bool RotateConnectedBlocks(uint id, float3 vector)
|
||||
{
|
||||
if (rotationEngine.IsInGame && rotationEngine.IsBuildMode())
|
||||
{
|
||||
rotationEngine.RotateConnectedBlocks(id, vector);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
GamecraftModdingAPI.Utility.GameEngineManager.AddGameEngine(rotationEngine);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using RobocraftX;
|
||||
using RobocraftX.Blocks;
|
||||
using RobocraftX.Blocks.Ghost;
|
||||
using RobocraftX.Common;
|
||||
using RobocraftX.Multiplayer;
|
||||
using RobocraftX.SimulationModeState;
|
||||
using RobocraftX.UECS;
|
||||
using Unity.Entities;
|
||||
using Svelto.Context;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.EntityStructs;
|
||||
using Unity.Transforms;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
|
||||
using GamecraftModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftModdingAPI.Blocks
|
||||
{
|
||||
/// <summary>
|
||||
/// Engine which executes block movement actions
|
||||
/// </summary>
|
||||
public class RotationEngine : IApiEngine
|
||||
{
|
||||
public string Name { get; } = "GamecraftModdingAPIRotationGameEngine";
|
||||
|
||||
public IEntitiesDB entitiesDB { set; private get; }
|
||||
|
||||
public bool IsInGame = false;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
IsInGame = false;
|
||||
}
|
||||
|
||||
public void Ready()
|
||||
{
|
||||
IsInGame = true;
|
||||
}
|
||||
|
||||
// implementations for Movement static class
|
||||
|
||||
public float3 RotateBlock(uint blockID, Vector3 vector)
|
||||
{
|
||||
ref RotationEntityStruct rotStruct = ref this.entitiesDB.QueryEntity<RotationEntityStruct>(blockID, CommonExclusiveGroups.OWNED_BLOCKS_GROUP);
|
||||
ref GridRotationStruct gridStruct = ref this.entitiesDB.QueryEntity<GridRotationStruct>(blockID, CommonExclusiveGroups.OWNED_BLOCKS_GROUP);
|
||||
ref LocalTransformEntityStruct transStruct = ref this.entitiesDB.QueryEntity<LocalTransformEntityStruct>(blockID, CommonExclusiveGroups.OWNED_BLOCKS_GROUP);
|
||||
ref UECSPhysicsEntityStruct phyStruct = ref this.entitiesDB.QueryEntity<UECSPhysicsEntityStruct>(blockID, CommonExclusiveGroups.OWNED_BLOCKS_GROUP);
|
||||
// main (persistent) position
|
||||
Quaternion newRotation = (Quaternion)rotStruct.rotation;
|
||||
newRotation.eulerAngles += vector;
|
||||
rotStruct.rotation = (quaternion)newRotation;
|
||||
// placement grid rotation
|
||||
Quaternion newGridRotation = (Quaternion)gridStruct.rotation;
|
||||
newGridRotation.eulerAngles += vector;
|
||||
gridStruct.rotation = (quaternion)newGridRotation;
|
||||
// rendered position
|
||||
Quaternion newTransRotation = (Quaternion)rotStruct.rotation;
|
||||
newTransRotation.eulerAngles += vector;
|
||||
transStruct.rotation = newTransRotation;
|
||||
// collision position
|
||||
FullGameFields._physicsWorld.EntityManager.SetComponentData(phyStruct.uecsEntity, new Unity.Transforms.Rotation
|
||||
{
|
||||
Value = rotStruct.rotation
|
||||
});
|
||||
return ((Quaternion)rotStruct.rotation).eulerAngles;
|
||||
|
||||
}
|
||||
|
||||
public float3 RotateConnectedBlocks(uint blockID, Vector3 vector)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool IsBuildMode()
|
||||
{
|
||||
return GamecraftModdingAPI.Utility.GameState.IsBuildMode();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,182 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using RobocraftX;
|
||||
using RobocraftX.Blocks;
|
||||
using RobocraftX.Blocks.Ghost;
|
||||
using RobocraftX.Common;
|
||||
using RobocraftX.Multiplayer;
|
||||
using RobocraftX.SimulationModeState;
|
||||
using RobocraftX.UECS;
|
||||
using Unity.Entities;
|
||||
using Svelto.Context;
|
||||
using Svelto.DataStructures;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.EntityStructs;
|
||||
using Unity.Transforms;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
using Gamecraft.Wires;
|
||||
|
||||
using GamecraftModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftModdingAPI.Blocks
|
||||
{
|
||||
/// <summary>
|
||||
/// Engine which executes signal actions
|
||||
/// </summary>
|
||||
public class SignalEngine : IApiEngine
|
||||
{
|
||||
public string Name { get; } = "GamecraftModdingAPISignalGameEngine";
|
||||
|
||||
public IEntitiesDB entitiesDB { set; private get; }
|
||||
|
||||
public bool IsInGame = false;
|
||||
|
||||
private System.Random random = new System.Random();
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
IsInGame = false;
|
||||
}
|
||||
|
||||
public void Ready()
|
||||
{
|
||||
IsInGame = true;
|
||||
}
|
||||
|
||||
public void DoTheThing()
|
||||
{
|
||||
GamecraftModdingAPI.Tasks.Repeatable thing = new GamecraftModdingAPI.Tasks.Repeatable(
|
||||
() => { Thing(); },
|
||||
() => { return IsSimulationMode(); } );
|
||||
GamecraftModdingAPI.Tasks.Scheduler.Schedule(thing);
|
||||
}
|
||||
|
||||
public void Thing()
|
||||
{
|
||||
uint count = 0;
|
||||
EGID[] eBlocks = GetElectricBlocks();
|
||||
for (uint i = 0u; i < eBlocks.Length; i++)
|
||||
{
|
||||
uint[] ids = GetSignalIDs(eBlocks[i]);
|
||||
for (uint j = 0u; j < ids.Length; j++)
|
||||
{
|
||||
SetSignal(ids[j], (float)random.NextDouble());
|
||||
count++;
|
||||
}
|
||||
}
|
||||
Logging.Log($"Did the thing on {count} inputs");
|
||||
}
|
||||
|
||||
// implementations for Signal static class
|
||||
|
||||
public bool SetSignal(EGID blockID, float signal, out uint signalID, bool input = true)
|
||||
{
|
||||
signalID = GetSignalIDs(blockID, input)[0];
|
||||
return SetSignal(signalID, signal);
|
||||
}
|
||||
|
||||
public bool SetSignal(uint signalID, float signal, bool input = true)
|
||||
{
|
||||
ExclusiveGroup group = input ? NamedExclusiveGroup<InputPortsGroup>.Group : NamedExclusiveGroup<OutputPortsGroup>.Group;
|
||||
if (entitiesDB.Exists<PortEntityStruct>(signalID, group))
|
||||
{
|
||||
entitiesDB.QueryEntity<PortEntityStruct>(signalID, group).value = signal;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public float AddSignal(EGID blockID, float signal, out uint signalID, bool clamp = true, bool input = true)
|
||||
{
|
||||
signalID = GetSignalIDs(blockID, input)[0];
|
||||
return AddSignal(signalID, signal, clamp, input);
|
||||
}
|
||||
|
||||
public float AddSignal(uint signalID, float signal, bool clamp = true, bool input = true)
|
||||
{
|
||||
ExclusiveGroup group = input ? NamedExclusiveGroup<InputPortsGroup>.Group : NamedExclusiveGroup<OutputPortsGroup>.Group;
|
||||
if (entitiesDB.Exists<PortEntityStruct>(signalID, group))
|
||||
{
|
||||
ref PortEntityStruct pes = ref entitiesDB.QueryEntity<PortEntityStruct>(signalID, group);
|
||||
pes.value += signal;
|
||||
if (clamp)
|
||||
{
|
||||
if (pes.value > Signals.POSITIVE_HIGH)
|
||||
{
|
||||
pes.value = Signals.POSITIVE_HIGH;
|
||||
}
|
||||
else if (pes.value < Signals.NEGATIVE_HIGH)
|
||||
{
|
||||
pes.value = Signals.NEGATIVE_HIGH;
|
||||
}
|
||||
return pes.value;
|
||||
}
|
||||
}
|
||||
return signal;
|
||||
}
|
||||
|
||||
public float GetSignal(EGID blockID, out uint signalID, bool input = true)
|
||||
{
|
||||
signalID = GetSignalIDs(blockID, input)[0];
|
||||
return GetSignal(signalID, input);
|
||||
}
|
||||
|
||||
public float GetSignal(uint signalID, bool input = true)
|
||||
{
|
||||
ExclusiveGroup group = input ? NamedExclusiveGroup<InputPortsGroup>.Group : NamedExclusiveGroup<OutputPortsGroup>.Group;
|
||||
if (entitiesDB.Exists<PortEntityStruct>(signalID, group))
|
||||
{
|
||||
return entitiesDB.QueryEntity<PortEntityStruct>(signalID, group).value;
|
||||
}
|
||||
return 0f;
|
||||
}
|
||||
|
||||
public uint[] GetSignalIDs(EGID blockID, bool input = true)
|
||||
{
|
||||
ref BlockPortsStruct bps = ref entitiesDB.QueryEntity<BlockPortsStruct>(blockID);
|
||||
uint[] signals;
|
||||
if (input) {
|
||||
signals = new uint[bps.inputCount];
|
||||
for (uint i = 0u; i < bps.inputCount; i++)
|
||||
{
|
||||
signals[i] = bps.firstInputID + i;
|
||||
}
|
||||
} else {
|
||||
signals = new uint[bps.outputCount];
|
||||
for (uint i = 0u; i < bps.outputCount; i++)
|
||||
{
|
||||
signals[i] = bps.firstOutputID + i;
|
||||
}
|
||||
}
|
||||
return signals;
|
||||
}
|
||||
|
||||
public EGID[] GetElectricBlocks()
|
||||
{
|
||||
uint count = entitiesDB.Count<BlockPortsStruct>(BlockIdentifiers.OWNED_BLOCKS) + entitiesDB.Count<BlockPortsStruct>(BlockIdentifiers.FUNCTIONAL_BLOCK_PARTS);
|
||||
uint i = 0;
|
||||
EGID[] res = new EGID[count];
|
||||
foreach (ref BlockPortsStruct s in entitiesDB.QueryEntities<BlockPortsStruct>(BlockIdentifiers.OWNED_BLOCKS))
|
||||
{
|
||||
res[i] = s.ID;
|
||||
i++;
|
||||
}
|
||||
foreach (ref BlockPortsStruct s in entitiesDB.QueryEntities<BlockPortsStruct>(BlockIdentifiers.FUNCTIONAL_BLOCK_PARTS))
|
||||
{
|
||||
res[i] = s.ID;
|
||||
i++;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public bool IsSimulationMode()
|
||||
{
|
||||
return GamecraftModdingAPI.Utility.GameState.IsSimulationMode();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,182 +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.Blocks
|
||||
{
|
||||
/// <summary>
|
||||
/// [EXPERIMENTAL] Common block signal operations
|
||||
/// </summary>
|
||||
public static class Signals
|
||||
{
|
||||
// Signal constants
|
||||
public static readonly float HIGH = 1.0f;
|
||||
public static readonly float POSITIVE_HIGH = HIGH;
|
||||
public static readonly float NEGATIVE_HIGH = -1.0f;
|
||||
public static readonly float LOW = 0.0f;
|
||||
|
||||
private static SignalEngine signalEngine = new SignalEngine();
|
||||
|
||||
/// <summary>
|
||||
/// Set the electric block's (first) signal value.
|
||||
/// </summary>
|
||||
/// <param name="blockID">The block's id.</param>
|
||||
/// <param name="signal">The signal value (-1 to 1; not enforced).</param>
|
||||
/// <param name="input">Whether to retrieve input IDs (true) or output IDs (false).</param>
|
||||
/// <param name="owned">Whether the block is in the owned group (true) or functional group (false)</param>
|
||||
public static void SetSignalByBlock(uint blockID, float signal, bool input = true, bool owned = true)
|
||||
{
|
||||
EGID egid = new EGID(blockID, owned ? BlockIdentifiers.OWNED_BLOCKS : BlockIdentifiers.FUNCTIONAL_BLOCK_PARTS);
|
||||
if (signalEngine.IsInGame && signalEngine.IsSimulationMode())
|
||||
{
|
||||
signalEngine.SetSignal(egid, signal, out uint _, input);
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetSignalByBlock(EGID blockID, float signal, bool input = true)
|
||||
{
|
||||
if (signalEngine.IsInGame && signalEngine.IsSimulationMode())
|
||||
{
|
||||
signalEngine.SetSignal(blockID, signal, out uint _, input);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set a signal's value.
|
||||
/// </summary>
|
||||
/// <param name="signalID">The channel cluster's id.</param>
|
||||
/// <param name="signal">The signal value (-1 to 1; not enforced).</param>
|
||||
/// <param name="input">Whether to retrieve input IDs (true) or output IDs (false).</param>
|
||||
public static void SetSignalByID(uint signalID, float signal, bool input = true)
|
||||
{
|
||||
if (signalEngine.IsInGame && signalEngine.IsSimulationMode())
|
||||
{
|
||||
signalEngine.SetSignal(signalID, signal, input);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a value to an electric block's signal.
|
||||
/// </summary>
|
||||
/// <param name="blockID">The block's id.</param>
|
||||
/// <param name="signal">The signal value to add.</param>
|
||||
/// <param name="clamp">Whether to clamp the resulting signal value between -1 and 1.</param>
|
||||
/// <param name="input">Whether to retrieve input IDs (true) or output IDs (false).</param>
|
||||
/// <param name="owned">Whether the block is in the owned group (true) or functional group (false)</param>
|
||||
/// <returns>The signal's new value.</returns>
|
||||
public static float AddSignalByBlock(uint blockID, float signal, bool clamp = true, bool input = true, bool owned = true)
|
||||
{
|
||||
EGID egid = new EGID(blockID, owned ? BlockIdentifiers.OWNED_BLOCKS : BlockIdentifiers.FUNCTIONAL_BLOCK_PARTS);
|
||||
if (signalEngine.IsInGame && signalEngine.IsSimulationMode())
|
||||
{
|
||||
return signalEngine.AddSignal(egid, signal, out uint _, clamp, input);
|
||||
}
|
||||
return 0f;
|
||||
}
|
||||
|
||||
public static float AddSignalByBlock(EGID blockID, float signal, bool clamp = true, bool input = true)
|
||||
{
|
||||
if (signalEngine.IsInGame && signalEngine.IsSimulationMode())
|
||||
{
|
||||
return signalEngine.AddSignal(blockID, signal, out uint _, clamp, input);
|
||||
}
|
||||
return 0f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a value to a conductive cluster channel.
|
||||
/// </summary>
|
||||
/// <param name="signalID">The channel cluster's id.</param>
|
||||
/// <param name="signal">The signal value to add.</param>
|
||||
/// <param name="clamp">Whether to clamp the resulting signal value between -1 and 1.</param>
|
||||
/// <param name="input">Whether to retrieve input IDs (true) or output IDs (false).</param>
|
||||
/// <returns>The signal's new value.</returns>
|
||||
public static float AddSignalByID(uint signalID, float signal, bool clamp = true, bool input = true)
|
||||
{
|
||||
if (signalEngine.IsInGame && signalEngine.IsSimulationMode())
|
||||
{
|
||||
return signalEngine.AddSignal(signalID, signal, clamp, input);
|
||||
}
|
||||
return 0f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a electric block's signal's (first) value.
|
||||
/// </summary>
|
||||
/// <param name="blockID">The block's id.</param>
|
||||
/// <param name="input">Whether to retrieve input IDs (true) or output IDs (false).</param>
|
||||
/// <param name="owned">Whether the block is in the owned group (true) or functional group (false)</param>
|
||||
/// <returns>The signal's value.</returns>
|
||||
public static float GetSignalByBlock(uint blockID, bool input = true, bool owned = true)
|
||||
{
|
||||
EGID egid = new EGID(blockID, owned? BlockIdentifiers.OWNED_BLOCKS : BlockIdentifiers.FUNCTIONAL_BLOCK_PARTS);
|
||||
if (signalEngine.IsInGame && signalEngine.IsSimulationMode())
|
||||
{
|
||||
return signalEngine.GetSignal(egid, out uint _, input);
|
||||
}
|
||||
return 0f;
|
||||
}
|
||||
|
||||
public static float GetSignalByBlock(EGID blockID, bool input = true)
|
||||
{
|
||||
if (signalEngine.IsInGame && signalEngine.IsSimulationMode())
|
||||
{
|
||||
return signalEngine.GetSignal(blockID, out uint _, input);
|
||||
}
|
||||
return 0f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a signal's value.
|
||||
/// </summary>
|
||||
/// <param name="signalID">The signal's id.</param>
|
||||
/// <param name="input">Whether to retrieve input IDs (true) or output IDs (false).</param>
|
||||
/// <returns>The signal's value.</returns>
|
||||
public static float GetSignalByID(uint signalID, bool input = true)
|
||||
{
|
||||
if (signalEngine.IsInGame && signalEngine.IsSimulationMode())
|
||||
{
|
||||
return signalEngine.GetSignal(signalID, input);
|
||||
}
|
||||
return 0f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the ID of every electric block in the game world.
|
||||
/// </summary>
|
||||
/// <returns>The block IDs.</returns>
|
||||
public static EGID[] GetElectricBlocks()
|
||||
{
|
||||
return signalEngine.GetElectricBlocks();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the unique identifiers for the input wires connected to an electric block.
|
||||
/// </summary>
|
||||
/// <param name="blockID">The block's id.</param>
|
||||
/// <param name="input">Whether to retrieve input IDs (true) or output IDs (false).</param>
|
||||
/// <param name="owned">Whether the block is in the owned group (true) or functional group (false)</param>
|
||||
/// <returns>The unique IDs.</returns>
|
||||
public static uint[] GetSignalIDs(uint blockID, bool input = true, bool owned = true)
|
||||
{
|
||||
EGID egid = new EGID(blockID, owned ? BlockIdentifiers.OWNED_BLOCKS : BlockIdentifiers.FUNCTIONAL_BLOCK_PARTS);
|
||||
return signalEngine.GetSignalIDs(egid, input);
|
||||
}
|
||||
|
||||
public static uint[] GetSignalIDs(EGID blockID, bool input = true, bool owned = true)
|
||||
{
|
||||
return signalEngine.GetSignalIDs(blockID, input);
|
||||
}
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
GameEngineManager.AddGameEngine(signalEngine);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Reflection;
|
||||
|
||||
using Harmony;
|
||||
using Svelto.Context;
|
||||
using Svelto.ECS;
|
||||
using RobocraftX;
|
||||
using RobocraftX.Multiplayer;
|
||||
using RobocraftX.StateSync;
|
||||
using Unity.Entities;
|
||||
|
||||
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(object contextHolder, EnginesRoot enginesRoot, World physicsWorld, Action reloadGame, MultiplayerInitParameters multiplayerParameters, ref StateSyncRegistrationHelper stateSyncReg)
|
||||
{
|
||||
// When a game is loaded, register the command engines
|
||||
CommandManager.RegisterEngines(enginesRoot);
|
||||
}
|
||||
|
||||
public static MethodBase TargetMethod(HarmonyInstance instance)
|
||||
{
|
||||
return typeof(RobocraftX.GUI.CommandLine.CommandLineCompositionRoot).GetMethod("Compose").MakeGenericMethod(typeof(object));
|
||||
//return func.Method;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,60 +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>
|
||||
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, object 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, object type, Action<IEntitiesDB> onActivated, Action<IEntitiesDB> 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, object type, bool isRemovable = true)
|
||||
{
|
||||
var engine = new SimpleEventEmitterEngine(type, name, isRemovable);
|
||||
EventManager.AddEventEmitter(engine);
|
||||
return engine;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,120 +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>
|
||||
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)
|
||||
{
|
||||
_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)
|
||||
{
|
||||
_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,22 +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
|
||||
}
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Harmony;
|
||||
using RobocraftX;
|
||||
using Svelto.ECS;
|
||||
|
||||
using GamecraftModdingAPI.Utility;
|
||||
using RobocraftX.CR.MainGame;
|
||||
|
||||
namespace GamecraftModdingAPI.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Patch of RobocraftX.FullGameCompositionRoot.ActivateGame()
|
||||
/// </summary>
|
||||
[HarmonyPatch]
|
||||
class GameActivatedComposePatch
|
||||
{
|
||||
public static bool IsGameSwitching = false;
|
||||
|
||||
public static bool IsGameReloading = false;
|
||||
|
||||
public static void Postfix(ref object contextHolder, ref EnginesRoot enginesRoot)
|
||||
{
|
||||
// register custom game engines
|
||||
GameEngineManager.RegisterEngines(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,25 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Harmony;
|
||||
using RobocraftX;
|
||||
|
||||
using GamecraftModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftModdingAPI.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Patch of RobocraftX.FullGameCompositionRoot.ReloadGame()
|
||||
/// </summary>
|
||||
[HarmonyPatch(typeof(FullGameCompositionRoot), "ReloadGame")]
|
||||
class GameReloadedPatch
|
||||
{
|
||||
public static void Postfix()
|
||||
{
|
||||
GameActivatedComposePatch.IsGameReloading = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Reflection;
|
||||
|
||||
using Harmony;
|
||||
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>
|
||||
[HarmonyPatch(typeof(FullGameCompositionRoot), "SwitchToGame")]
|
||||
class GameSwitchedToPatch
|
||||
{
|
||||
public static void Prefix()
|
||||
{
|
||||
GameActivatedComposePatch.IsGameSwitching = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,40 +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>
|
||||
/// Engine interface to create a ModEventEntityStruct in entitiesDB when Emit() is called.
|
||||
/// </summary>
|
||||
public interface IEventEmitterEngine : IApiEngine
|
||||
{
|
||||
/// <summary>
|
||||
/// The type of event emitted
|
||||
/// </summary>
|
||||
object type { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the emitter can be removed with Manager.RemoveEventEmitter(name)
|
||||
/// </summary>
|
||||
bool isRemovable { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The EntityFactory for the entitiesDB.
|
||||
/// Use this to create a ModEventEntityStruct when Emit() is called.
|
||||
/// </summary>
|
||||
IEntityFactory Factory { set; }
|
||||
|
||||
/// <summary>
|
||||
/// Emit the event so IEventHandlerEngines can handle it.
|
||||
/// Call Emit() to trigger the IEventEmitterEngine's event.
|
||||
/// </summary>
|
||||
void Emit();
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Harmony;
|
||||
using RobocraftX;
|
||||
using Svelto.ECS;
|
||||
|
||||
using GamecraftModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftModdingAPI.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Patch of RobocraftX.FullGameCompositionRoot.ActivateMenu()
|
||||
/// </summary>
|
||||
[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);
|
||||
Logging.Log("Dispatching App Init event");
|
||||
EventManager.GetEventEmitter("GamecraftModdingAPIApplicationInitializedEventEmitter").Emit();
|
||||
}
|
||||
Logging.Log("Dispatching Menu Activated event");
|
||||
EventManager.GetEventEmitter("GamecraftModdingAPIMenuActivatedEventEmitter").Emit();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Harmony;
|
||||
using RobocraftX;
|
||||
using Svelto.ECS;
|
||||
|
||||
using GamecraftModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftModdingAPI.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Patch of RobocraftX.FullGameCompositionRoot.SwitchToMenu()
|
||||
/// </summary>
|
||||
[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,21 +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 : IEntityStruct
|
||||
{
|
||||
/// <summary>
|
||||
/// The type of event that has been emitted
|
||||
/// </summary>
|
||||
public object type;
|
||||
}
|
||||
}
|
|
@ -1,65 +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>
|
||||
public class SimpleEventEmitterEngine : IEventEmitterEngine
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public object type { get; set; }
|
||||
|
||||
public bool isRemovable { get; }
|
||||
|
||||
public IEntityFactory Factory { private get; set; }
|
||||
|
||||
public IEntitiesDB 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 = 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(object type, string name, bool isRemovable = true)
|
||||
{
|
||||
this.type = type;
|
||||
this.Name = name;
|
||||
this.isRemovable = isRemovable;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,92 +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>
|
||||
/// A simple implementation of IEventHandlerEngine sufficient for most uses
|
||||
/// </summary>
|
||||
public class SimpleEventHandlerEngine : IEventHandlerEngine
|
||||
{
|
||||
public object type { get; set; }
|
||||
public string Name { get; set; }
|
||||
|
||||
private bool isActivated = false;
|
||||
|
||||
private readonly Action<IEntitiesDB> onActivated;
|
||||
|
||||
private readonly Action<IEntitiesDB> onDestroyed;
|
||||
|
||||
public IEntitiesDB entitiesDB { set; private get; }
|
||||
|
||||
public void Add(ref ModEventEntityStruct entityView, EGID egid)
|
||||
{
|
||||
if (entityView.type.Equals(this.type))
|
||||
{
|
||||
isActivated = true;
|
||||
onActivated.Invoke(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;
|
||||
}
|
||||
|
||||
public void Ready() { }
|
||||
|
||||
public void Remove(ref ModEventEntityStruct entityView, EGID egid)
|
||||
{
|
||||
if (entityView.type.Equals(this.type) && isActivated)
|
||||
{
|
||||
isActivated = false;
|
||||
onDestroyed.Invoke(entitiesDB);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (isActivated)
|
||||
{
|
||||
isActivated = false;
|
||||
onDestroyed.Invoke(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, object type, string name, bool simple = true)
|
||||
: this((IEntitiesDB _) => { activated.Invoke(); }, (IEntitiesDB _) => { 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<IEntitiesDB> activated, Action<IEntitiesDB> removed, object type, string name)
|
||||
{
|
||||
this.type = type;
|
||||
this.Name = name;
|
||||
this.onActivated = activated;
|
||||
this.onDestroyed = removed;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,549 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net472</TargetFramework>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<Version>0.1.3.0</Version>
|
||||
<Authors>Exmods</Authors>
|
||||
<PackageLicenseExpression>GNU General Public Licence 3+</PackageLicenseExpression>
|
||||
<PackageProjectUrl>https://git.exmods.org/modtainers/GamecraftModdingAPI</PackageProjectUrl>
|
||||
<NeutralLanguage>en-CA</NeutralLanguage>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Lib.Harmony" Version="1.2.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<!--Start Dependencies-->
|
||||
<ItemGroup>
|
||||
<Reference Include="Analytics">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Analytics.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Assembly-CSharp-firstpass">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Assembly-CSharp-firstpass.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Assembly-CSharp">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Assembly-CSharp.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Authentication">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Authentication.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="BlockEntityFactory">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\BlockEntityFactory.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="CommandLine">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\CommandLine.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="DataLoader">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\DataLoader.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="DDNA">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\DDNA.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Facepunch.Steamworks.Win64">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Facepunch.Steamworks.Win64.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="FMOD">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\FMOD.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="FullGame">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\FullGame.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Gamecraft.Blocks.ConsoleBlock">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.Blocks.ConsoleBlock.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Gamecraft.Effects">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.Effects.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Gamecraft.GUI.GraphicsScreen">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.GraphicsScreen.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Gamecraft.GUI.Tweaks">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.Tweaks.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Gamecraft.GUI.Wires">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.Wires.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Gamecraft.Tweaks">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.Tweaks.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Gamecraft.Wires">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.Wires.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Gamecraft.Wires.Input">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Gamecraft.Wires.Input.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="GameState">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\GameState.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="GPUInstancer">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\GPUInstancer.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Havok.Physics">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Havok.Physics.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Havok.Physics.Hybrid">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Havok.Physics.Hybrid.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="HdgRemoteDebugRuntime">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\HdgRemoteDebugRuntime.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="IllusionInjector">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\IllusionInjector.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="IllusionPlugin">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\IllusionPlugin.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="JWT">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\JWT.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="LZ4">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\LZ4.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="MultiplayerNetworking">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\MultiplayerNetworking.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="MultiplayerTest">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\MultiplayerTest.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RCX.ScreenshotTaker">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\RCX.ScreenshotTaker.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Rewired_Core">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Rewired_Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Rewired_Windows">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Rewired_Windows.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Robocraft.MainGame.AutoEnterSimulation">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Robocraft.MainGame.AutoEnterSimulation.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RobocraftX.AccountPreferences">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.AccountPreferences.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RobocraftX.Blocks">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.Blocks.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RobocraftX.Blocks.Ghost">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.Blocks.Ghost.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RobocraftX.Blocks.Triggers">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.Blocks.Triggers.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RobocraftX.Character">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.Character.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RobocraftX.Common">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.Common.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RobocraftX.ControlsScreen">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.ControlsScreen.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RobocraftX.Crosshair">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.Crosshair.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RobocraftX.EntityStreamUtility">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.EntityStreamUtility.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RobocraftX.FrontEnd">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.FrontEnd.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RobocraftX.GameSignalHandling">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.GameSignalHandling.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RobocraftX.GUI.DebugDisplay">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.GUI.DebugDisplay.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RobocraftX.GUI">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.GUI.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RobocraftX.GUI.RemoveBlock">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.GUI.RemoveBlock.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RobocraftX.GUI.ScaleGhost">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.GUI.ScaleGhost.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RobocraftX.GUI.SignalLabel">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.GUI.SignalLabel.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RobocraftX.GUIs.WorkshopPrefabs">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.GUIs.WorkshopPrefabs.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RobocraftX.Input">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.Input.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RobocraftX.Inventory">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.Inventory.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RobocraftX.MachineEditor">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.MachineEditor.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RobocraftX.MainGame">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.MainGame.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RobocraftX.MainSimulation">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.MainSimulation.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RobocraftX.Multiplayer">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.Multiplayer.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RobocraftX.Multiplayer.NetworkEntityStream">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.Multiplayer.NetworkEntityStream.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RobocraftX.MultiplayerInput">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.MultiplayerInput.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RobocraftX.Party">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.Party.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RobocraftX.PartyGui">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.PartyGui.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RobocraftX.Physics">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.Physics.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RobocraftX.PilotSeat">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.PilotSeat.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RobocraftX.Player">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.Player.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RobocraftX.Priority">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.Priority.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RobocraftX.Rendering">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.Rendering.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RobocraftX.Rendering.Mock">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.Rendering.Mock.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RobocraftX.SaveAndLoad">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.SaveAndLoad.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RobocraftX.SaveGameDialog">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.SaveGameDialog.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RobocraftX.Serializers">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.Serializers.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RobocraftX.Services">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.Services.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RobocraftX.SignalHandling">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.SignalHandling.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RobocraftX.StateSync">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX.StateSync.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RobocraftX_SpawnPoints">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX_SpawnPoints.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RobocraftX_TextBlock">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\RobocraftX_TextBlock.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RobocratX.SimulationCompositionRoot">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\RobocratX.SimulationCompositionRoot.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="StringFormatter">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\StringFormatter.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Svelto.Common">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Svelto.Common.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Svelto.ECS.Debugger">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Svelto.ECS.Debugger.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Svelto.ECS">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Svelto.ECS.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Svelto.Services">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Svelto.Services.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Svelto.Tasks">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Svelto.Tasks.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Addressables">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Addressables.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Burst">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Burst.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Burst.Unsafe">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Burst.Unsafe.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Cloud.UserReporting.Client">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Cloud.UserReporting.Client.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Cloud.UserReporting.Plugin">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Cloud.UserReporting.Plugin.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Collections">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Collections.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Entities">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Entities.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Entities.Hybrid">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Entities.Hybrid.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Entities.Properties">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Entities.Properties.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Entities.StaticTypeRegistry">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Entities.StaticTypeRegistry.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Jobs">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Jobs.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Mathematics">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Mathematics.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Mathematics.Extensions">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Mathematics.Extensions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Mathematics.Extensions.Hybrid">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Mathematics.Extensions.Hybrid.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Physics">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Physics.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Physics.Hybrid">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Physics.Hybrid.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Postprocessing.Runtime">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Postprocessing.Runtime.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Properties">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Properties.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Unity.RenderPipelines.Core.Runtime">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.RenderPipelines.Core.Runtime.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Unity.RenderPipelines.Core.ShaderLibrary">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.RenderPipelines.Core.ShaderLibrary.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Unity.RenderPipelines.Lightweight.Runtime">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.RenderPipelines.Lightweight.Runtime.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Unity.ResourceManager">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.ResourceManager.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Scenes.Hybrid">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Scenes.Hybrid.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Unity.ScriptableBuildPipeline">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.ScriptableBuildPipeline.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Unity.TextMeshPro">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.TextMeshPro.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Timeline">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Timeline.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Transforms">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Transforms.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Transforms.Hybrid">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\Unity.Transforms.Hybrid.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.AccessibilityModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.AccessibilityModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.AIModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.AIModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.AndroidJNIModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.AndroidJNIModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.AnimationModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.AnimationModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.ARModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.ARModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.AssetBundleModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.AssetBundleModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.AudioModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.AudioModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.ClothModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.ClothModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.ClusterInputModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.ClusterInputModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.ClusterRendererModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.ClusterRendererModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.CoreModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.CoreModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.CrashReportingModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.CrashReportingModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.DirectorModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.DirectorModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.DSPGraphModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.DSPGraphModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.FileSystemHttpModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.FileSystemHttpModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.GameCenterModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.GameCenterModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.GridModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.GridModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.HotReloadModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.HotReloadModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.ImageConversionModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.ImageConversionModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.IMGUIModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.IMGUIModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.InputLegacyModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.InputLegacyModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.InputModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.InputModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.JSONSerializeModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.JSONSerializeModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.LocalizationModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.LocalizationModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.ParticleSystemModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.ParticleSystemModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.PerformanceReportingModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.PerformanceReportingModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.Physics2DModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.Physics2DModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.PhysicsModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.PhysicsModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.ProfilerModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.ProfilerModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.ScreenCaptureModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.ScreenCaptureModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.SharedInternalsModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.SharedInternalsModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.SpriteMaskModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.SpriteMaskModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.SpriteShapeModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.SpriteShapeModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.StreamingModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.StreamingModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.SubstanceModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.SubstanceModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.TerrainModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.TerrainModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.TerrainPhysicsModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.TerrainPhysicsModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.TextCoreModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.TextCoreModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.TextRenderingModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.TextRenderingModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.TilemapModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.TilemapModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.TLSModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.TLSModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.UI">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.UI.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.UIElementsModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.UIElementsModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.UIModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.UIModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.UmbraModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.UmbraModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.UNETModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.UNETModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.UnityAnalyticsModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.UnityAnalyticsModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.UnityConnectModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.UnityConnectModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.UnityTestProtocolModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.UnityTestProtocolModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.UnityWebRequestAssetBundleModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.UnityWebRequestAssetBundleModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.UnityWebRequestAudioModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.UnityWebRequestAudioModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.UnityWebRequestModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.UnityWebRequestModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.UnityWebRequestTextureModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.UnityWebRequestTextureModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.UnityWebRequestWWWModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.UnityWebRequestWWWModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.VehiclesModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.VehiclesModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.VFXModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.VFXModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.VideoModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.VideoModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.VRModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.VRModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.WindModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.WindModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.XRModule">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UnityEngine.XRModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="uREPL">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\uREPL.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="UserReporting">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\UserReporting.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="VisualProfiler">
|
||||
<HintPath>..\ref\Gamecraft_Data\Managed\VisualProfiler.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<!--End Dependencies-->
|
||||
|
||||
</Project>
|
|
@ -1,91 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Reflection;
|
||||
|
||||
using Harmony;
|
||||
|
||||
using GamecraftModdingAPI.Utility;
|
||||
using GamecraftModdingAPI.Events;
|
||||
using GamecraftModdingAPI.Tasks;
|
||||
|
||||
namespace GamecraftModdingAPI
|
||||
{
|
||||
/// <summary>
|
||||
/// The main class of the GamecraftModdingAPI.
|
||||
/// Use this to initialize the API before calling it.
|
||||
/// </summary>
|
||||
public static class Main
|
||||
{
|
||||
private static HarmonyInstance harmony;
|
||||
|
||||
public static bool IsInitialized {
|
||||
get { return harmony != null; }
|
||||
}
|
||||
|
||||
private static int referenceCount = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the GamecraftModdingAPI.
|
||||
/// Call this as soon as possible after Gamecraft starts up.
|
||||
/// Ideally, this should be called from your main Plugin class's OnApplicationStart() method.
|
||||
/// </summary>
|
||||
public static void Init()
|
||||
{
|
||||
referenceCount++;
|
||||
if (referenceCount > 1) { return; }
|
||||
if (IsInitialized)
|
||||
{
|
||||
Logging.LogWarning("GamecraftModdingAPI.Main.Init() called but API is already initialized!");
|
||||
return;
|
||||
}
|
||||
Logging.MetaDebugLog($"Patching Gamecraft");
|
||||
var currentAssembly = Assembly.GetExecutingAssembly();
|
||||
harmony = HarmonyInstance.Create(currentAssembly.GetName().Name);
|
||||
harmony.PatchAll(currentAssembly);
|
||||
// init utility
|
||||
Logging.MetaDebugLog($"Initializing Utility");
|
||||
Utility.GameState.Init();
|
||||
// create default event emitters
|
||||
Logging.MetaDebugLog($"Initializing Events");
|
||||
EventManager.AddEventEmitter(new SimpleEventEmitterEngine(EventType.ApplicationInitialized, "GamecraftModdingAPIApplicationInitializedEventEmitter", false));
|
||||
EventManager.AddEventEmitter(new SimpleEventEmitterEngine(EventType.Menu, "GamecraftModdingAPIMenuActivatedEventEmitter", false));
|
||||
EventManager.AddEventEmitter(new SimpleEventEmitterEngine(EventType.MenuSwitchedTo, "GamecraftModdingAPIMenuSwitchedToEventEmitter", false));
|
||||
EventManager.AddEventEmitter(new SimpleEventEmitterEngine(EventType.Game, "GamecraftModdingAPIGameActivatedEventEmitter", false));
|
||||
EventManager.AddEventEmitter(new SimpleEventEmitterEngine(EventType.GameReloaded, "GamecraftModdingAPIGameReloadedEventEmitter", false));
|
||||
EventManager.AddEventEmitter(new SimpleEventEmitterEngine(EventType.GameSwitchedTo, "GamecraftModdingAPIGameSwitchedToEventEmitter", false));
|
||||
// init block implementors
|
||||
Logging.MetaDebugLog($"Initializing Blocks");
|
||||
Blocks.Movement.Init();
|
||||
Blocks.Rotation.Init();
|
||||
Blocks.Signals.Init();
|
||||
Blocks.Placement.Init();
|
||||
Logging.MetaLog($"{currentAssembly.GetName().Name} v{currentAssembly.GetName().Version} initialized");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shuts down & cleans up the GamecraftModdingAPI.
|
||||
/// Call this as late as possible before Gamecraft quits.
|
||||
/// Ideally, this should be called from your main Plugin class's OnApplicationQuit() method.
|
||||
/// </summary>
|
||||
public static void Shutdown()
|
||||
{
|
||||
if (referenceCount > 0) { referenceCount--; }
|
||||
if (referenceCount == 0)
|
||||
{
|
||||
if (!IsInitialized)
|
||||
{
|
||||
Logging.LogWarning("GamecraftModdingAPI.Main.Shutdown() called but API is not initialized!");
|
||||
return;
|
||||
}
|
||||
Scheduler.Dispose();
|
||||
var currentAssembly = Assembly.GetExecutingAssembly();
|
||||
harmony.UnpatchAll(currentAssembly.GetName().Name);
|
||||
harmony = null;
|
||||
Logging.MetaLog($"{currentAssembly.GetName().Name} v{currentAssembly.GetName().Version} shutdown");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,144 +0,0 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
using Harmony;
|
||||
// test
|
||||
using Svelto.ECS;
|
||||
using RobocraftX.Blocks;
|
||||
using RobocraftX.Common;
|
||||
using RobocraftX.SimulationModeState;
|
||||
|
||||
using GamecraftModdingAPI.Commands;
|
||||
using GamecraftModdingAPI.Events;
|
||||
using GamecraftModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftModdingAPI.Tests
|
||||
{
|
||||
// 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
|
||||
#if DEBUG
|
||||
: IllusionPlugin.IEnhancedPlugin
|
||||
#endif
|
||||
{
|
||||
private static HarmonyInstance harmony { get; set; }
|
||||
|
||||
public string[] Filter { get; } = new string[] { "Gamecraft", "GamecraftPreview" };
|
||||
|
||||
public string Name { get; } = Assembly.GetExecutingAssembly().GetName().Name;
|
||||
|
||||
public string Version { get; } = Assembly.GetExecutingAssembly().GetName().Version.ToString();
|
||||
|
||||
public string HarmonyID { get; } = "org.git.exmods.modtainers.gamecraftmoddingapi";
|
||||
|
||||
public void OnApplicationQuit()
|
||||
{
|
||||
GamecraftModdingAPI.Main.Shutdown();
|
||||
}
|
||||
|
||||
public void OnApplicationStart()
|
||||
{
|
||||
GamecraftModdingAPI.Main.Init();
|
||||
// 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 :(
|
||||
|
||||
// debug/test handlers
|
||||
EventManager.AddEventHandler(new SimpleEventHandlerEngine(() => { Logging.Log("App Inited event!"); }, () => { },
|
||||
EventType.ApplicationInitialized, "appinit API debug"));
|
||||
EventManager.AddEventHandler(new SimpleEventHandlerEngine(() => { Logging.Log("Menu Activated event!"); },
|
||||
() => { Logging.Log("Menu Destroyed event!"); },
|
||||
EventType.Menu, "menuact API debug"));
|
||||
EventManager.AddEventHandler(new SimpleEventHandlerEngine(() => { Logging.Log("Menu Switched To event!"); }, () => { },
|
||||
EventType.MenuSwitchedTo, "menuswitch API debug"));
|
||||
EventManager.AddEventHandler(new SimpleEventHandlerEngine(() => { Logging.Log("Game Activated event!"); },
|
||||
() => { Logging.Log("Game Destroyed event!"); },
|
||||
EventType.Game, "gameact API debug"));
|
||||
EventManager.AddEventHandler(new SimpleEventHandlerEngine(() => { Logging.Log("Game Reloaded event!"); }, () => { },
|
||||
EventType.GameReloaded, "gamerel API debug"));
|
||||
EventManager.AddEventHandler(new SimpleEventHandlerEngine(() => { Logging.Log("Game Switched To event!"); }, () => { },
|
||||
EventType.GameSwitchedTo, "gameswitch API debug"));
|
||||
|
||||
// debug/test commands
|
||||
if (Dependency.Hell("ExtraCommands"))
|
||||
{
|
||||
CommandManager.AddCommand(new SimpleCustomCommandEngine(() => { UnityEngine.Application.Quit(); },
|
||||
"Exit", "Close Gamecraft without any prompts"));
|
||||
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"));
|
||||
Analytics.DeltaDNAHelper.PlayerLifetimeParameters plp = new Analytics.DeltaDNAHelper.PlayerLifetimeParameters();
|
||||
CommandManager.AddCommand(new SimpleCustomCommandEngine<string>(
|
||||
(s) => { Analytics.DeltaDNAHelper.SendActionCompletedEvent(in plp, s.Replace(", ", " ")); },
|
||||
"SendAnalyticsAction", "Send an analytics action"));
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
public void OnFixedUpdate() { }
|
||||
|
||||
public void OnLateUpdate() { }
|
||||
|
||||
public void OnLevelWasInitialized(int level) { }
|
||||
|
||||
public void OnLevelWasLoaded(int level) { }
|
||||
|
||||
public void OnUpdate() { }
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Reflection;
|
||||
|
||||
using Analytics;
|
||||
using Harmony;
|
||||
using RobocraftX.Common;
|
||||
using Svelto.ECS;
|
||||
|
||||
namespace GamecraftModdingAPI.Utility
|
||||
{
|
||||
/// <summary>
|
||||
/// Patch of Analytics.AnalyticsCompositionRoot.Compose<T>(...)
|
||||
/// This stops some analytics collection built into Gamecraft.
|
||||
/// DO NOT USE! (This will likely crash your game on shutdown)
|
||||
/// </summary>
|
||||
[HarmonyPatch]
|
||||
class AnalyticsDisablerPatch
|
||||
{
|
||||
/// <summary>
|
||||
/// Don't activate gameplay analytics?
|
||||
/// </summary>
|
||||
public static bool DisableAnalytics = false;
|
||||
|
||||
public static bool Prefix(object contextHolder, EnginesRoot enginesRoot, RCXMode rcxMode)
|
||||
{
|
||||
return !DisableAnalytics;
|
||||
}
|
||||
|
||||
public static MethodBase TargetMethod(HarmonyInstance instance)
|
||||
{
|
||||
return typeof(Analytics.AnalyticsCompositionRoot).GetMethod("Compose").MakeGenericMethod(typeof(object));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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.Utility
|
||||
{
|
||||
public static class ApiExclusiveGroups
|
||||
{
|
||||
public static readonly ExclusiveGroup eventsExclusiveGroup = new ExclusiveGroup();
|
||||
|
||||
public static uint eventID;
|
||||
}
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
using System;
|
||||
|
||||
using IllusionInjector;
|
||||
using IllusionPlugin;
|
||||
|
||||
namespace GamecraftModdingAPI.Utility
|
||||
{
|
||||
/// <summary>
|
||||
/// Simple plugin interaction operations
|
||||
/// </summary>
|
||||
public static class Dependency
|
||||
{
|
||||
/// <summary>
|
||||
/// Find a plugin by name
|
||||
/// </summary>
|
||||
/// <returns>The plugin.</returns>
|
||||
/// <param name="name">The plugin's name.</param>
|
||||
public static IPlugin GetPlugin(string name)
|
||||
{
|
||||
foreach(IPlugin plugin in PluginManager.Plugins)
|
||||
{
|
||||
if (plugin.Name == name)
|
||||
{
|
||||
return plugin;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin version.
|
||||
/// This gives priority to the plugin's Version string but falls back to the Assembly's version
|
||||
/// </summary>
|
||||
/// <returns>The plugin's version.</returns>
|
||||
/// <param name="name">The plugin's name.</param>
|
||||
public static Version GetPluginVersion(string name)
|
||||
{
|
||||
IPlugin plugin = GetPlugin(name);
|
||||
if (plugin != null) {
|
||||
try
|
||||
{
|
||||
return new Version(plugin.Version);
|
||||
} catch (Exception e) when (
|
||||
e is ArgumentException
|
||||
|| e is ArgumentNullException
|
||||
|| e is ArgumentOutOfRangeException
|
||||
|| e is FormatException
|
||||
|| e is OverflowException) {}
|
||||
return plugin.GetType().Assembly.GetName().Version;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// (I'm leaving the auto-generated version)
|
||||
// <summary>
|
||||
// Hell the specified name and version.
|
||||
// </summary>
|
||||
// <returns>The hell.</returns>
|
||||
// <param name="name">Name.</param>
|
||||
// <param name="version">Version.</param>
|
||||
/// <summary>
|
||||
/// Detect if you're in dependency hell with respect to the plugin.
|
||||
/// ie Check if the plugin doesn't exist or is out of date.
|
||||
/// When version is null, this only checks if the plugin exists.
|
||||
/// The version is retrieved using GetPluginVersion(string name).
|
||||
/// </summary>
|
||||
/// <returns>Are you in dependency hell?</returns>
|
||||
/// <param name="name">The plugin's name'</param>
|
||||
/// <param name="version">The target version.</param>
|
||||
public static bool Hell(string name, Version version = null)
|
||||
{
|
||||
Version pluginVersion = GetPluginVersion(name);
|
||||
if (version == null) {
|
||||
return pluginVersion == null;
|
||||
}
|
||||
return (pluginVersion == null || pluginVersion < version);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Harmony;
|
||||
using RobocraftX.FrontEnd;
|
||||
|
||||
namespace GamecraftModdingAPI.Utility
|
||||
{
|
||||
/// <summary>
|
||||
/// Patch of bool RobocraftX.FrontEnd.MinimumSpecsCheck.CheckRequirementsMet()
|
||||
/// </summary>
|
||||
[HarmonyPatch(typeof(MinimumSpecsCheck), "CheckRequirementsMet")]
|
||||
class MinimumSpecsCheckPatch
|
||||
{
|
||||
/// <summary>
|
||||
/// Ignore result of the requirement check?
|
||||
/// </summary>
|
||||
public static bool ForcePassMinimumSpecCheck = false;
|
||||
|
||||
public static void Postfix(ref bool __result)
|
||||
{
|
||||
__result = __result || ForcePassMinimumSpecCheck;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Harmony;
|
||||
using RobocraftX.Common;
|
||||
|
||||
namespace GamecraftModdingAPI.Utility
|
||||
{
|
||||
/// <summary>
|
||||
/// Patch of bool RobocraftX.Common.SteamManager.VerifyOrInit()
|
||||
/// This does not let you run Gamecraft without Steam.
|
||||
/// DO NOT USE!
|
||||
/// </summary>
|
||||
[HarmonyPatch(typeof(SteamManager), "VerifyOrInit")]
|
||||
class SteamInitPatch
|
||||
{
|
||||
/// <summary>
|
||||
/// Ignore the result of steam initialization?
|
||||
/// </summary>
|
||||
public static bool ForcePassSteamCheck = false;
|
||||
|
||||
public static void Postfix(ref bool __result)
|
||||
{
|
||||
__result = __result || ForcePassSteamCheck;
|
||||
}
|
||||
}
|
||||
}
|
1574
MakeEverythingPublicInGame/MakeEverythingPublicInGame.csproj
Normal file
1574
MakeEverythingPublicInGame/MakeEverythingPublicInGame.csproj
Normal file
File diff suppressed because it is too large
Load diff
25
MakeEverythingPublicInGame/Program.cs
Normal file
25
MakeEverythingPublicInGame/Program.cs
Normal file
|
@ -0,0 +1,25 @@
|
|||
using System.Text.RegularExpressions;
|
||||
using Mono.Cecil;
|
||||
|
||||
Console.WriteLine("Starting assembly editing...");
|
||||
var fileRegex =
|
||||
new Regex(".*(Techblox|Gamecraft|RobocraftX|FullGame|RobocraftECS|DataLoader|RCX|GameState|Svelto\\.ECS)[^/]*(\\.dll)");
|
||||
foreach (var file in Directory.EnumerateFiles(@"../../../../../ref/Techblox_Data/Managed"))
|
||||
{
|
||||
if (!fileRegex.IsMatch(file)) continue;
|
||||
Console.WriteLine(file);
|
||||
ProcessAssembly(file);
|
||||
}
|
||||
|
||||
void ProcessAssembly(string path)
|
||||
{
|
||||
using var mod = ModuleDefinition.ReadModule(path, new(ReadingMode.Immediate) { ReadWrite = true });
|
||||
foreach (var typeDefinition in mod.Types)
|
||||
{
|
||||
typeDefinition.IsPublic = true;
|
||||
foreach (var method in typeDefinition.Methods) method.IsPublic = true;
|
||||
foreach (var field in typeDefinition.Fields) field.IsPublic = true;
|
||||
}
|
||||
|
||||
mod.Write();
|
||||
}
|
31
README.md
31
README.md
|
@ -1,22 +1,31 @@
|
|||
# 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:
|
||||
- *Ease-of-Use* The API provides many easy methods to call to do common tasks such as moving blocks and adding commands.
|
||||
All of the Harmony patchings are done for you, so you can focus on writing your mod instead of reading swathes of undocumented code.
|
||||
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 & support, please join the ExMods Discord: https://discord.gg/xjnFxQV
|
||||
For more info, please check out the [official documentation](https://mod.exmods.org).
|
||||
|
||||
For more support, join the ExMods [Discord](https://discord.exmods.org).
|
||||
|
||||
## Installation
|
||||
[Please follow the official mod installation guide](https://www.exmods.org/guides/install.html)
|
||||
[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 Techblox install folder.
|
||||
This will allow your IDE to resolve references to Techblox files for building and IDE tools.
|
||||
|
||||
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.
|
||||
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).
|
||||
|
||||
|
|
44
TechbloxModdingAPI.sln
Normal file
44
TechbloxModdingAPI.sln
Normal file
|
@ -0,0 +1,44 @@
|
|||
|
||||
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}") = "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
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MakeEverythingPublicInGame", "MakeEverythingPublicInGame\MakeEverythingPublicInGame.csproj", "{391A3107-E5C6-4A04-9467-6D868AA9A8B4}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Test|Any CPU = Test|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{7FD5A7D8-4F3E-426A-B07D-7DC70442A4DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7FD5A7D8-4F3E-426A-B07D-7DC70442A4DF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7FD5A7D8-4F3E-426A-B07D-7DC70442A4DF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{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
|
||||
{391A3107-E5C6-4A04-9467-6D868AA9A8B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{391A3107-E5C6-4A04-9467-6D868AA9A8B4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{391A3107-E5C6-4A04-9467-6D868AA9A8B4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{391A3107-E5C6-4A04-9467-6D868AA9A8B4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{391A3107-E5C6-4A04-9467-6D868AA9A8B4}.Test|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{391A3107-E5C6-4A04-9467-6D868AA9A8B4}.Test|Any CPU.Build.0 = Debug|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {72FB94D0-6C50-475B-81E0-C94C7D7A2A17}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
58
TechbloxModdingAPI/App/AntiAntiCheatPatch.cs
Normal file
58
TechbloxModdingAPI/App/AntiAntiCheatPatch.cs
Normal file
|
@ -0,0 +1,58 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using HarmonyLib;
|
||||
using Svelto.Tasks;
|
||||
using Techblox.Anticheat.Client;
|
||||
|
||||
namespace TechbloxModdingAPI.App
|
||||
{
|
||||
public static class AntiAntiCheatPatch
|
||||
{
|
||||
private delegate bool AntiAnticheatDelegate(ref object __result);
|
||||
|
||||
private delegate bool AntiAnticheatDelegateBool(ref bool __result);
|
||||
|
||||
private delegate bool AntiAnticheatDelegateTask(ref IEnumerator<TaskContract> __result);
|
||||
|
||||
public static void Init(Harmony harmony)
|
||||
{
|
||||
var type = AccessTools.TypeByName("Techblox.Services.Eos.Anticheat.Client.Services.AnticheatClientService");
|
||||
harmony.Patch(type.GetConstructors()[0], new HarmonyMethod(((Func<bool>) AntiAntiCheat).Method));
|
||||
harmony.Patch(AccessTools.Method(type, "Shutdown"), new HarmonyMethod(((Func<bool>) AntiAntiCheat).Method));
|
||||
harmony.Patch(AccessTools.Method(type, "StartProtectedSession"), new HarmonyMethod(((AntiAnticheatDelegate) AntiAntiCheat).Method));
|
||||
harmony.Patch(AccessTools.Method(type, "StopProtectedSession"), new HarmonyMethod(((AntiAnticheatDelegateBool) AntiAntiCheat).Method));
|
||||
harmony.Patch(AccessTools.Method("Techblox.Services.Eos.Anticheat.Client.EosGetPendingMessagesToSendServiceRequest:Execute"), new HarmonyMethod(((AntiAnticheatDelegateTask)AntiAntiCheatTask).Method));
|
||||
harmony.Patch(AccessTools.Method("Techblox.Anticheat.Client.Engines.ProcessEACViolationEngine:PollAnticheatStatus"), new HarmonyMethod(((AntiAnticheatDelegateTask)AntiAntiCheatTask).Method));
|
||||
harmony.Patch(AccessTools.Method(typeof(AnticheatClientCompositionRoot), "ClientComposeTimeRunning"), new HarmonyMethod(((Func<bool>)AntiAntiCheat).Method));
|
||||
}
|
||||
|
||||
private static bool AntiAntiCheat() => false;
|
||||
|
||||
private static bool AntiAntiCheat(ref object __result)
|
||||
{
|
||||
var targetType =
|
||||
AccessTools.TypeByName("Techblox.Services.Eos.Anticheat.Client.Services.StartProtectedSessionResult");
|
||||
var target = Activator.CreateInstance(targetType);
|
||||
targetType.GetField("Success").SetValue(target, true);
|
||||
__result = target;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool AntiAntiCheat(ref bool __result)
|
||||
{
|
||||
__result = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool AntiAntiCheatTask(ref IEnumerator<TaskContract> __result)
|
||||
{
|
||||
IEnumerator<TaskContract> Func()
|
||||
{
|
||||
yield return Yield.It;
|
||||
}
|
||||
|
||||
__result = Func();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
35
TechbloxModdingAPI/App/AppCallbacksTest.cs
Normal file
35
TechbloxModdingAPI/App/AppCallbacksTest.cs
Normal file
|
@ -0,0 +1,35 @@
|
|||
using System;
|
||||
|
||||
using TechbloxModdingAPI.Tests;
|
||||
|
||||
namespace TechbloxModdingAPI.App
|
||||
{
|
||||
#if TEST
|
||||
/// <summary>
|
||||
/// App callbacks tests.
|
||||
/// Only available in TEST builds.
|
||||
/// </summary>
|
||||
[APITestClass]
|
||||
public static class AppCallbacksTest
|
||||
{
|
||||
[APITestStartUp]
|
||||
public static void StartUp()
|
||||
{
|
||||
// this could be split into 6 separate test cases
|
||||
Game.Enter += Assert.CallsBack<GameEventArgs>("GameEnter");
|
||||
Game.Exit += Assert.CallsBack<GameEventArgs>("GameExit");
|
||||
Game.Simulate += Assert.CallsBack<GameEventArgs>("GameSimulate");
|
||||
Game.Edit += Assert.CallsBack<GameEventArgs>("GameEdit");
|
||||
Client.EnterMenu += Assert.CallsBack<MenuEventArgs>("MenuEnter");
|
||||
Client.ExitMenu += Assert.CallsBack<MenuEventArgs>("MenuExit");
|
||||
}
|
||||
|
||||
[APITestCase(TestType.Game)]
|
||||
public static void Test()
|
||||
{
|
||||
// the test is actually completely implemented in StartUp()
|
||||
// this is here just so it looks less weird (not required)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
42
TechbloxModdingAPI/App/AppExceptions.cs
Normal file
42
TechbloxModdingAPI/App/AppExceptions.cs
Normal file
|
@ -0,0 +1,42 @@
|
|||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace TechbloxModdingAPI.App
|
||||
{
|
||||
public class AppException : TechbloxModdingAPIException
|
||||
{
|
||||
public AppException()
|
||||
{
|
||||
}
|
||||
|
||||
public AppException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public AppException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class AppStateException : AppException
|
||||
{
|
||||
public AppStateException()
|
||||
{
|
||||
}
|
||||
|
||||
public AppStateException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class GameNotFoundException : AppException
|
||||
{
|
||||
public GameNotFoundException()
|
||||
{
|
||||
}
|
||||
|
||||
public GameNotFoundException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
171
TechbloxModdingAPI/App/Client.cs
Normal file
171
TechbloxModdingAPI/App/Client.cs
Normal file
|
@ -0,0 +1,171 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using HarmonyLib;
|
||||
|
||||
using RobocraftX.Services;
|
||||
using UnityEngine;
|
||||
using RobocraftX.Common;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
namespace TechbloxModdingAPI.App
|
||||
{
|
||||
/// <summary>
|
||||
/// The Techblox application that is running this code right now.
|
||||
/// </summary>
|
||||
public class Client
|
||||
{
|
||||
public static Client Instance { get; } = new Client();
|
||||
|
||||
protected static Func<object> ErrorHandlerInstanceGetter;
|
||||
|
||||
protected static Action<object, Error> EnqueueError;
|
||||
|
||||
/// <summary>
|
||||
/// An event that fires whenever the main menu is loaded.
|
||||
/// </summary>
|
||||
public static event EventHandler<MenuEventArgs> EnterMenu
|
||||
{
|
||||
add => Game.menuEngine.EnterMenu += value;
|
||||
remove => Game.menuEngine.EnterMenu -= value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An event that fire whenever the main menu is exited.
|
||||
/// </summary>
|
||||
public static event EventHandler<MenuEventArgs> ExitMenu
|
||||
{
|
||||
add => Game.menuEngine.ExitMenu += value;
|
||||
remove => Game.menuEngine.ExitMenu -= value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Techblox build version string.
|
||||
/// Usually this is in the form YYYY.mm.DD.HH.MM.SS
|
||||
/// </summary>
|
||||
/// <value>The version.</value>
|
||||
public string Version
|
||||
{
|
||||
get => Application.version;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unity version string.
|
||||
/// </summary>
|
||||
/// <value>The unity version.</value>
|
||||
public string UnityVersion
|
||||
{
|
||||
get => Application.unityVersion;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Game saves currently visible in the menu.
|
||||
/// These take a second to completely populate after the EnterMenu event fires.
|
||||
/// </summary>
|
||||
/// <value>My games.</value>
|
||||
public Game[] MyGames
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!Game.menuEngine.IsInMenu) return Array.Empty<Game>();
|
||||
return Game.menuEngine.GetMyGames();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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 => Game.menuEngine.IsInMenu;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open a popup which prompts the user to click a button.
|
||||
/// 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)
|
||||
{
|
||||
// if the stuff wasn't mostly set to internal, this would be written as:
|
||||
// RobocraftX.Services.ErrorHandler.Instance.EqueueError(error);
|
||||
object errorHandlerInstance = ErrorHandlerInstanceGetter();
|
||||
EnqueueError(errorHandlerInstance, popup);
|
||||
}
|
||||
|
||||
public void CloseCurrentPrompt()
|
||||
{
|
||||
object errorHandlerInstance = ErrorHandlerInstanceGetter();
|
||||
var popup = GetPopupCloseMethods(errorHandlerInstance);
|
||||
popup.Close();
|
||||
}
|
||||
|
||||
public void SelectFirstPromptButton()
|
||||
{
|
||||
object errorHandlerInstance = ErrorHandlerInstanceGetter();
|
||||
var popup = GetPopupCloseMethods(errorHandlerInstance);
|
||||
popup.FirstButton();
|
||||
}
|
||||
|
||||
public void SelectSecondPromptButton()
|
||||
{
|
||||
object errorHandlerInstance = ErrorHandlerInstanceGetter();
|
||||
var popup = GetPopupCloseMethods(errorHandlerInstance);
|
||||
popup.SecondButton();
|
||||
}
|
||||
|
||||
internal static void Init()
|
||||
{
|
||||
// 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("TechbloxModdingAPI.App.Client:GenInstanceGetter")
|
||||
.MakeGenericMethod(errorHandler)
|
||||
.Invoke(null, new object[0]);
|
||||
EnqueueError = (Action<object, Error>) AccessTools.Method("TechbloxModdingAPI.App.Client:GenEnqueueError")
|
||||
.MakeGenericMethod(errorHandler, errorHandle)
|
||||
.Invoke(null, new object[0]);
|
||||
}
|
||||
|
||||
// Creating delegates once is faster than reflection every time
|
||||
// Admittedly, this way is more difficult to code and less readable
|
||||
private static Func<object> GenInstanceGetter<T>()
|
||||
{
|
||||
Type errorHandler = AccessTools.TypeByName("RobocraftX.Services.ErrorHandler");
|
||||
MethodInfo instance = AccessTools.PropertyGetter(errorHandler, "Instance");
|
||||
Func<T> getterSimple = (Func<T>) Delegate.CreateDelegate(typeof(Func<T>), null, instance);
|
||||
Func<object> getterCasted = () => (object) getterSimple();
|
||||
return getterCasted;
|
||||
}
|
||||
|
||||
private static Action<object, Error> GenEnqueueError<T, TRes>()
|
||||
{
|
||||
Type errorHandler = AccessTools.TypeByName("RobocraftX.Services.ErrorHandler");
|
||||
MethodInfo enqueueError = AccessTools.Method(errorHandler, "EnqueueError");
|
||||
Func<T, Error, TRes> enqueueSimple =
|
||||
(Func<T, Error, TRes>) Delegate.CreateDelegate(typeof(Func<T, Error, TRes>), enqueueError);
|
||||
Action<object, Error> enqueueCasted =
|
||||
(object instance, Error error) => { enqueueSimple((T) instance, error); };
|
||||
return enqueueCasted;
|
||||
}
|
||||
|
||||
private static (Action Close, Action FirstButton, Action SecondButton) _errorPopup;
|
||||
|
||||
private static (Action Close, Action FirstButton, Action SecondButton) GetPopupCloseMethods(object handler)
|
||||
{
|
||||
if (_errorPopup.Close != null)
|
||||
return _errorPopup;
|
||||
Type errorHandler = handler.GetType();
|
||||
FieldInfo field = AccessTools.Field(errorHandler, "errorPopup");
|
||||
var errorPopup = (ErrorPopup)field.GetValue(handler);
|
||||
MethodInfo info = AccessTools.Method(errorPopup.GetType(), "ClosePopup");
|
||||
var close = (Action)Delegate.CreateDelegate(typeof(Action), errorPopup, info);
|
||||
info = AccessTools.Method(errorPopup.GetType(), "HandleFirstOption");
|
||||
var first = (Action)Delegate.CreateDelegate(typeof(Action), errorPopup, info);
|
||||
info = AccessTools.Method(errorPopup.GetType(), "HandleSecondOption");
|
||||
var second = (Action)Delegate.CreateDelegate(typeof(Action), errorPopup, info);
|
||||
_errorPopup = (close, first, second);
|
||||
return _errorPopup;
|
||||
}
|
||||
}
|
||||
}
|
67
TechbloxModdingAPI/App/ClientAlertTest.cs
Normal file
67
TechbloxModdingAPI/App/ClientAlertTest.cs
Normal file
|
@ -0,0 +1,67 @@
|
|||
using System;
|
||||
using HarmonyLib;
|
||||
|
||||
using RobocraftX.Services;
|
||||
|
||||
using TechbloxModdingAPI.Tests;
|
||||
|
||||
namespace TechbloxModdingAPI.App
|
||||
{
|
||||
#if TEST
|
||||
/// <summary>
|
||||
/// Client popups tests.
|
||||
/// Only available in TEST builds.
|
||||
/// </summary>
|
||||
[APITestClass]
|
||||
public static class ClientAlertTest
|
||||
{
|
||||
private static DualChoicePrompt popup2 = null;
|
||||
|
||||
private static SingleChoicePrompt popup1 = null;
|
||||
|
||||
[APITestStartUp]
|
||||
public static void StartUp2()
|
||||
{
|
||||
popup2 = new DualChoicePrompt("This is a test double-button popup",
|
||||
"The cake is a lie",
|
||||
"lmao",
|
||||
() => { },
|
||||
"kek",
|
||||
() => { });
|
||||
}
|
||||
|
||||
[APITestStartUp]
|
||||
public static void StartUp1()
|
||||
{
|
||||
popup1 = new SingleChoicePrompt("The cake is a lie",
|
||||
"This is a test single-button popup",
|
||||
"qwertyuiop",
|
||||
() => { });
|
||||
}
|
||||
|
||||
[APITestCase(TestType.Menu)]
|
||||
public static void TestPopUp2()
|
||||
{
|
||||
Client.Instance.PromptUser(popup2);
|
||||
}
|
||||
|
||||
[APITestCase(TestType.Menu)]
|
||||
public static void TestPopUp1()
|
||||
{
|
||||
Client.Instance.PromptUser(popup1);
|
||||
}
|
||||
|
||||
[APITestCase(TestType.Menu)]
|
||||
public static void TestPopUpClose1()
|
||||
{
|
||||
Client.Instance.CloseCurrentPrompt();
|
||||
}
|
||||
|
||||
[APITestCase(TestType.Menu)]
|
||||
public static void TestPopUpClose2()
|
||||
{
|
||||
Client.Instance.CloseCurrentPrompt();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
27
TechbloxModdingAPI/App/CurrentGameMode.cs
Normal file
27
TechbloxModdingAPI/App/CurrentGameMode.cs
Normal file
|
@ -0,0 +1,27 @@
|
|||
using System;
|
||||
|
||||
namespace TechbloxModdingAPI.App
|
||||
{
|
||||
public enum CurrentGameMode
|
||||
{
|
||||
None,
|
||||
/// <summary>
|
||||
/// Building a world
|
||||
/// </summary>
|
||||
Build,
|
||||
/// <summary>
|
||||
/// Playing on a map
|
||||
/// </summary>
|
||||
Play,
|
||||
/// <summary>
|
||||
/// Viewing a prefab (doesn't exist anymore)
|
||||
/// </summary>
|
||||
[Obsolete]
|
||||
View,
|
||||
/// <summary>
|
||||
/// Viewing a tutorial (doesn't exist anymore)
|
||||
/// </summary>
|
||||
[Obsolete]
|
||||
Tutorial
|
||||
}
|
||||
}
|
490
TechbloxModdingAPI/App/Game.cs
Normal file
490
TechbloxModdingAPI/App/Game.cs
Normal file
|
@ -0,0 +1,490 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
using RobocraftX.GUI.MyGamesScreen;
|
||||
using Svelto.ECS;
|
||||
using Techblox.GameSelection;
|
||||
|
||||
using TechbloxModdingAPI.Blocks;
|
||||
using TechbloxModdingAPI.Tasks;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
namespace TechbloxModdingAPI.App
|
||||
{
|
||||
/// <summary>
|
||||
/// An in-game save.
|
||||
/// This can be a menu item for a local save or the currently loaded save.
|
||||
/// Support for Steam Workshop coming soon (hopefully).
|
||||
/// </summary>
|
||||
public class Game
|
||||
{
|
||||
// extensible engines
|
||||
protected static GameGameEngine gameEngine = new GameGameEngine();
|
||||
protected internal static GameMenuEngine menuEngine = new GameMenuEngine();
|
||||
protected static DebugInterfaceEngine debugOverlayEngine = new DebugInterfaceEngine();
|
||||
protected static GameBuildSimEventEngine buildSimEventEngine = new GameBuildSimEventEngine();
|
||||
|
||||
private List<string> debugIds = new List<string>();
|
||||
|
||||
private bool menuMode = true;
|
||||
private bool hasId = false;
|
||||
|
||||
/// <summary>
|
||||
/// 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))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:TechbloxModdingAPI.App.Game"/> class.
|
||||
/// </summary>
|
||||
/// <param name="id">Menu identifier.</param>
|
||||
public Game(EGID id)
|
||||
{
|
||||
this.Id = id.entityID;
|
||||
this.EGID = id;
|
||||
this.hasId = true;
|
||||
menuMode = true;
|
||||
if (!VerifyMode()) throw new AppStateException("Game cannot be created while not in a game nor in a menu (is the game in a loading screen?)");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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()
|
||||
{
|
||||
menuMode = false;
|
||||
if (!VerifyMode()) throw new AppStateException("Game cannot be created while not in a game nor in a menu (is the game in a loading screen?)");
|
||||
if (menuEngine.IsInMenu) throw new GameNotFoundException("Game not found.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the currently loaded game.
|
||||
/// If in a menu, manipulating the returned object may not work as intended.
|
||||
/// </summary>
|
||||
/// <returns>The current game.</returns>
|
||||
public static Game CurrentGame()
|
||||
{
|
||||
return new Game();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new game and adds it to the menu.
|
||||
/// If not in a menu, this will throw AppStateException.
|
||||
/// </summary>
|
||||
/// <returns>The new game.</returns>
|
||||
public static Game NewGame()
|
||||
{
|
||||
if (!menuEngine.IsInMenu) throw new AppStateException("New Game cannot be created while not in a menu.");
|
||||
uint nextId = menuEngine.HighestID() + 1;
|
||||
EGID egid = new EGID(nextId, MyGamesScreenExclusiveGroups.MyGames);
|
||||
menuEngine.CreateMyGame(egid);
|
||||
return new Game(egid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An event that fires whenever a game is switched to simulation mode (time running mode).
|
||||
/// </summary>
|
||||
public static event EventHandler<GameEventArgs> Simulate
|
||||
{
|
||||
add => buildSimEventEngine.SimulationMode += value;
|
||||
remove => buildSimEventEngine.SimulationMode -= value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An event that fires whenever a game is switched to edit mode (time stopped mode).
|
||||
/// This does not fire when a game is loaded.
|
||||
/// </summary>
|
||||
public static event EventHandler<GameEventArgs> Edit
|
||||
{
|
||||
add => buildSimEventEngine.BuildMode += value;
|
||||
remove => buildSimEventEngine.BuildMode -= value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An event that fires right after a game is completely loaded.
|
||||
/// </summary>
|
||||
public static event EventHandler<GameEventArgs> Enter
|
||||
{
|
||||
add => gameEngine.EnterGame += value;
|
||||
remove => gameEngine.EnterGame -= value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An event that fires right before a game returns to the main menu.
|
||||
/// At this point, Techblox is transitioning state so many things are invalid/unstable here.
|
||||
/// </summary>
|
||||
public static event EventHandler<GameEventArgs> Exit
|
||||
{
|
||||
add => gameEngine.ExitGame += value;
|
||||
remove => gameEngine.ExitGame -= value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The game's unique menu identifier.
|
||||
/// </summary>
|
||||
/// <value>The identifier.</value>
|
||||
public uint Id
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The game's unique menu EGID.
|
||||
/// </summary>
|
||||
/// <value>The egid.</value>
|
||||
public EGID EGID
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the game is a (valid) menu item.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if menu item; otherwise, <c>false</c>.</value>
|
||||
public bool MenuItem
|
||||
{
|
||||
get => menuMode && hasId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The game's name.
|
||||
/// </summary>
|
||||
/// <value>The name.</value>
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!VerifyMode()) return null;
|
||||
if (menuMode) return menuEngine.GetGameInfo(EGID).GameName;
|
||||
return gameEngine.GetGameData().saveName;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (!VerifyMode()) return;
|
||||
if (menuMode)
|
||||
{
|
||||
menuEngine.SetGameName(EGID, value);
|
||||
} // Save details are directly saved from user input or not changed at all when in game
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The game's description.
|
||||
/// </summary>
|
||||
/// <value>The description.</value>
|
||||
public string Description
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!VerifyMode()) return null;
|
||||
if (menuMode) return menuEngine.GetGameInfo(EGID).GameDescription;
|
||||
return "";
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (!VerifyMode()) return;
|
||||
if (menuMode)
|
||||
{
|
||||
menuEngine.SetGameDescription(EGID, value);
|
||||
} // No description exists in-game
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The path to the game's save folder.
|
||||
/// </summary>
|
||||
/// <value>The path.</value>
|
||||
public string Path
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!VerifyMode()) return null;
|
||||
if (menuMode) return menuEngine.GetGameInfo(EGID).SavedGamePath;
|
||||
return gameEngine.GetGameData().gameID;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (!VerifyMode()) return;
|
||||
if (menuMode)
|
||||
{
|
||||
menuEngine.GetGameInfo(EGID).SavedGamePath.Set(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Steam Workshop Id of the game save.
|
||||
/// 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
|
||||
{
|
||||
return 0uL; // Not supported anymore
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the game is in simulation mode.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if is simulating; otherwise, <c>false</c>.</value>
|
||||
public bool IsSimulating
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!VerifyMode()) return false;
|
||||
return !menuMode && gameEngine.IsTimeRunningMode();
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (!VerifyMode()) return;
|
||||
if (!menuMode && gameEngine.IsTimeRunningMode() != value)
|
||||
gameEngine.ToggleTimeMode();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the game is in time-running mode.
|
||||
/// Alias of IsSimulating.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if is time running; otherwise, <c>false</c>.</value>
|
||||
public bool IsTimeRunning
|
||||
{
|
||||
get => IsSimulating;
|
||||
|
||||
set
|
||||
{
|
||||
IsSimulating = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the game is in time-stopped mode.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if is time stopped; otherwise, <c>false</c>.</value>
|
||||
public bool IsTimeStopped
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!VerifyMode()) return false;
|
||||
return !menuMode && gameEngine.IsTimeStoppedMode();
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (!VerifyMode()) return;
|
||||
if (!menuMode && gameEngine.IsTimeStoppedMode() != value)
|
||||
gameEngine.ToggleTimeMode();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Toggles the time mode.
|
||||
/// </summary>
|
||||
public void ToggleTimeMode()
|
||||
{
|
||||
if (!VerifyMode()) return;
|
||||
if (menuMode || !gameEngine.IsInGame)
|
||||
{
|
||||
throw new AppStateException("Game menu item cannot toggle it's time mode");
|
||||
}
|
||||
gameEngine.ToggleTimeMode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The mode of the game.
|
||||
/// </summary>
|
||||
public CurrentGameMode Mode
|
||||
{
|
||||
get
|
||||
{
|
||||
if (menuMode || !VerifyMode()) return CurrentGameMode.None;
|
||||
return gameEngine.GetGameData().gameMode == GameMode.CreateWorld ? CurrentGameMode.Build : CurrentGameMode.Play;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load the game save.
|
||||
/// This happens asynchronously, so when this method returns the game not loaded yet.
|
||||
/// Use the Game.Enter event to perform operations after the game has completely loaded.
|
||||
/// </summary>
|
||||
public void EnterGame()
|
||||
{
|
||||
if (!VerifyMode()) return;
|
||||
if (!hasId)
|
||||
{
|
||||
throw new GameNotFoundException("Game has an invalid ID");
|
||||
}
|
||||
ISchedulable task = new Once(() => { menuEngine.EnterGame(EGID); this.menuMode = false; });
|
||||
Scheduler.Schedule(task);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return to the menu.
|
||||
/// Part of this always happens asynchronously, so when this method returns the game has not exited yet.
|
||||
/// Use the Client.EnterMenu event to perform operations after the game has completely exited.
|
||||
/// </summary>
|
||||
/// <param name="async">If set to <c>true</c>, do this async.</param>
|
||||
public void ExitGame(bool async = false)
|
||||
{
|
||||
if (!VerifyMode()) return;
|
||||
if (menuMode)
|
||||
{
|
||||
throw new GameNotFoundException("Cannot exit game using menu ID");
|
||||
}
|
||||
gameEngine.ExitCurrentGame(async);
|
||||
this.menuMode = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the game.
|
||||
/// Part of this happens asynchronously, so when this method returns the game has not been saved yet.
|
||||
/// </summary>
|
||||
public void SaveGame()
|
||||
{
|
||||
if (!VerifyMode()) return;
|
||||
if (menuMode)
|
||||
{
|
||||
throw new GameNotFoundException("Cannot save game using menu ID");
|
||||
}
|
||||
gameEngine.SaveCurrentGame();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add information to the in-game debug display.
|
||||
/// 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">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("game_" + id, contentGetter);
|
||||
debugIds.Add(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove 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 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("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>
|
||||
/// Gets the blocks in the game.
|
||||
/// This returns null when in a loading state, and throws AppStateException when in menu.
|
||||
/// </summary>
|
||||
/// <returns>The blocks in game.</returns>
|
||||
/// <param name="filter">The block to search for. BlockIDs.Invalid will return all blocks.</param>
|
||||
public Block[] GetBlocksInGame(BlockIDs filter = BlockIDs.Invalid)
|
||||
{
|
||||
if (!VerifyMode()) return null;
|
||||
if (menuMode)
|
||||
{
|
||||
throw new AppStateException("Game object references a menu item but GetBlocksInGame only works on the currently-loaded game");
|
||||
}
|
||||
EGID[] blockEGIDs = gameEngine.GetAllBlocksInGame(filter);
|
||||
Block[] blocks = new Block[blockEGIDs.Length];
|
||||
for (int b = 0; b < blockEGIDs.Length; 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)
|
||||
{
|
||||
debugOverlayEngine.RemoveInfo(id);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private bool VerifyMode()
|
||||
{
|
||||
if (menuMode && (!menuEngine.IsInMenu || gameEngine.IsInGame))
|
||||
{
|
||||
// either game loading or API is broken
|
||||
return false;
|
||||
}
|
||||
if (!menuMode && (menuEngine.IsInMenu || !gameEngine.IsInGame))
|
||||
{
|
||||
// either game loading or API is broken
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
internal static void Init()
|
||||
{
|
||||
GameEngineManager.AddGameEngine(gameEngine);
|
||||
GameEngineManager.AddGameEngine(debugOverlayEngine);
|
||||
GameEngineManager.AddGameEngine(buildSimEventEngine);
|
||||
MenuEngineManager.AddMenuEngine(menuEngine);
|
||||
}
|
||||
}
|
||||
}
|
47
TechbloxModdingAPI/App/GameBuildSimEventEngine.cs
Normal file
47
TechbloxModdingAPI/App/GameBuildSimEventEngine.cs
Normal file
|
@ -0,0 +1,47 @@
|
|||
using System;
|
||||
|
||||
using RobocraftX.Common;
|
||||
using RobocraftX.StateSync;
|
||||
using Svelto.ECS;
|
||||
using Unity.Jobs;
|
||||
using TechbloxModdingAPI.Engines;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
namespace TechbloxModdingAPI.App
|
||||
{
|
||||
public class GameBuildSimEventEngine : IApiEngine, IUnorderedInitializeOnTimeRunningModeEntered, IUnorderedInitializeOnTimeStoppedModeEntered
|
||||
{
|
||||
public WrappedHandler<GameEventArgs> SimulationMode;
|
||||
|
||||
public WrappedHandler<GameEventArgs> BuildMode;
|
||||
|
||||
public string Name => "TechbloxModdingAPIBuildSimEventGameEngine";
|
||||
|
||||
public bool isRemovable => false;
|
||||
|
||||
public EntitiesDB entitiesDB { set; private get; }
|
||||
|
||||
public void Dispose() { }
|
||||
|
||||
public void Ready() { }
|
||||
|
||||
public JobHandle OnInitializeTimeRunningMode(JobHandle inputDeps)
|
||||
{
|
||||
SimulationMode.Invoke(this, new GameEventArgs { GameName = "", GamePath = "" }); // TODO
|
||||
return inputDeps;
|
||||
}
|
||||
|
||||
public JobHandle OnInitializeTimeStoppedMode(JobHandle inputDeps)
|
||||
{
|
||||
BuildMode.Invoke(this, new GameEventArgs { GameName = "", GamePath = "" });
|
||||
return inputDeps;
|
||||
}
|
||||
}
|
||||
|
||||
public struct GameEventArgs
|
||||
{
|
||||
public string GameName;
|
||||
|
||||
public string GamePath;
|
||||
}
|
||||
}
|
195
TechbloxModdingAPI/App/GameGameEngine.cs
Normal file
195
TechbloxModdingAPI/App/GameGameEngine.cs
Normal file
|
@ -0,0 +1,195 @@
|
|||
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 RobocraftX.Blocks;
|
||||
using RobocraftX.Common.Loading;
|
||||
using RobocraftX.Multiplayer;
|
||||
using RobocraftX.ScreenshotTaker;
|
||||
using Techblox.Environment.Transition;
|
||||
using Techblox.GameSelection;
|
||||
|
||||
using TechbloxModdingAPI.Blocks;
|
||||
using TechbloxModdingAPI.Engines;
|
||||
using TechbloxModdingAPI.Input;
|
||||
using TechbloxModdingAPI.Players;
|
||||
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;
|
||||
private bool loadingFinished;
|
||||
private bool playerJoined;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (GameReloadedPatch.IsReload)
|
||||
return; // Toggling time mode
|
||||
ExitGame.Invoke(this, new GameEventArgs { GameName = GetGameData().saveName, GamePath = GetGameData().gameID });
|
||||
IsInGame = false;
|
||||
loadingFinished = false;
|
||||
playerJoined = false;
|
||||
enteredGame = false;
|
||||
}
|
||||
|
||||
public void Ready()
|
||||
{
|
||||
if (GameReloadedPatch.IsReload)
|
||||
return; // Toggling time mode
|
||||
enteredGame = true;
|
||||
Player.Joined += OnPlayerJoined;
|
||||
}
|
||||
|
||||
private void OnPlayerJoined(object sender, PlayerEventArgs args)
|
||||
{
|
||||
if (args.Player.Type != PlayerType.Local) return;
|
||||
playerJoined = true;
|
||||
Player.Joined -= OnPlayerJoined;
|
||||
CheckJoinEvent();
|
||||
}
|
||||
|
||||
// game functionality
|
||||
|
||||
public bool IsInGame
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
} = false;
|
||||
|
||||
public void ExitCurrentGame(bool async = false)
|
||||
{
|
||||
if (async)
|
||||
{
|
||||
ExitCurrentGameAsync().RunOn(ClientLean.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 (TimeRunningModeUtil.IsTimeStoppedMode(entitiesDB))
|
||||
FakeInput.ActionInput(toggleMode: true);
|
||||
else
|
||||
{
|
||||
IEnumerator<TaskContract> ReloadBuildModeTask()
|
||||
{
|
||||
SwitchAnimationUtil.Start(entitiesDB);
|
||||
while (SwitchAnimationUtil.IsFadeOutActive(entitiesDB))
|
||||
yield return (TaskContract)Yield.It;
|
||||
FullGameFields._multiplayerParams.MultiplayerMode = MultiplayerMode.SinglePlayer;
|
||||
AccessTools.Method(typeof(FullGameCompositionRoot), "ReloadGame")
|
||||
.Invoke(FullGameFields.Instance, new object[] { });
|
||||
}
|
||||
|
||||
ReloadBuildModeTask().RunOn(ClientLean.UIScheduler);
|
||||
}
|
||||
}
|
||||
|
||||
public EGID[] GetAllBlocksInGame(BlockIDs filter = BlockIDs.Invalid)
|
||||
{
|
||||
var allBlocks = entitiesDB.QueryEntities<BlockTagEntityStruct>();
|
||||
List<EGID> blockEGIDs = new List<EGID>();
|
||||
foreach (var ((_, ids, count), group) in allBlocks)
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var id = new EGID(ids[i], group);
|
||||
uint dbid;
|
||||
if (filter == BlockIDs.Invalid)
|
||||
dbid = (uint)filter;
|
||||
else
|
||||
dbid = entitiesDB.QueryEntity<DBEntityStruct>(id).DBID;
|
||||
var ownership = entitiesDB.QueryEntity<BlockOwnershipComponent>(id).BlockOwnership;
|
||||
if ((ownership & BlockOwnership.User) != 0 && dbid == (ulong)filter)
|
||||
blockEGIDs.Add(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;
|
||||
enteredGame = false;
|
||||
loadingFinished = true;
|
||||
CheckJoinEvent();
|
||||
}
|
||||
|
||||
private void CheckJoinEvent()
|
||||
{
|
||||
if (!loadingFinished || !playerJoined) return;
|
||||
EnterGame.Invoke(this, new GameEventArgs { GameName = GetGameData().saveName, GamePath = GetGameData().gameID });
|
||||
IsInGame = true;
|
||||
}
|
||||
}
|
||||
}
|
197
TechbloxModdingAPI/App/GameMenuEngine.cs
Normal file
197
TechbloxModdingAPI/App/GameMenuEngine.cs
Normal file
|
@ -0,0 +1,197 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using HarmonyLib;
|
||||
|
||||
using RobocraftX;
|
||||
using RobocraftX.GUI;
|
||||
using RobocraftX.GUI.MyGamesScreen;
|
||||
using RobocraftX.Multiplayer;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.Experimental;
|
||||
using Techblox.GameSelection;
|
||||
|
||||
using TechbloxModdingAPI.Engines;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
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()
|
||||
{
|
||||
var (mgsevs, count) = entitiesDB.QueryEntities<MyGameDataEntityStruct>(MyGamesScreenExclusiveGroups.MyGames);
|
||||
Game[] games = new Game[count];
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
Utility.Logging.MetaDebugLog($"Found game named {mgsevs[i].GameName}");
|
||||
games[i] = new Game(mgsevs[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()
|
||||
{
|
||||
var (games, count) = entitiesDB.QueryEntities<MyGameDataEntityStruct>(MyGamesScreenExclusiveGroups.MyGames);
|
||||
uint max = 0;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (games[i].ID.entityID > max)
|
||||
{
|
||||
max = games[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(ECSString gameName, string fileId, bool autoEnterSim = false)
|
||||
{
|
||||
FullGameFields._multiplayerParams.MultiplayerMode = MultiplayerMode.SinglePlayer;
|
||||
ref var selection = ref entitiesDB.QueryEntity<GameSelectionComponent>(GameSelectionConstants.GameSelectionEGID);
|
||||
selection.userContentID.Set(fileId);
|
||||
selection.triggerStart = true;
|
||||
selection.saveType = SaveType.ExistingSave;
|
||||
selection.saveName = gameName;
|
||||
selection.gameMode = GameMode.PlayGame;
|
||||
selection.gameID.Set("GAMEID_Road_Track"); //TODO: Expose to the API
|
||||
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), "SwitchToMenu");
|
||||
}
|
||||
}
|
||||
|
||||
[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
|
||||
{
|
||||
|
||||
}
|
||||
}
|
27
TechbloxModdingAPI/App/UserPrompts.cs
Normal file
27
TechbloxModdingAPI/App/UserPrompts.cs
Normal file
|
@ -0,0 +1,27 @@
|
|||
using System;
|
||||
using HarmonyLib;
|
||||
|
||||
using RobocraftX.Services;
|
||||
|
||||
namespace TechbloxModdingAPI.App
|
||||
{
|
||||
public class DualChoicePrompt : MultiChoiceError
|
||||
{
|
||||
public DualChoicePrompt(string errorMessage, string title, string firstButtonText, Action firstButtonAction, string secondButtonText, Action secondButtonAction) : base(errorMessage, firstButtonText, firstButtonAction, secondButtonText, secondButtonAction)
|
||||
{
|
||||
// internal readonly field smh
|
||||
new Traverse(this).Field<string>("Title").Value = title;
|
||||
}
|
||||
}
|
||||
|
||||
public class SingleChoicePrompt : SingleChoiceError
|
||||
{
|
||||
public SingleChoicePrompt(string errorMessage, string buttonText, Action buttonClickAction) : base(errorMessage, buttonText, buttonClickAction)
|
||||
{
|
||||
}
|
||||
|
||||
public SingleChoicePrompt(string titleText, string errorMessage, string buttonText, Action buttonClickAction) : base(titleText, errorMessage, buttonText, buttonClickAction)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
519
TechbloxModdingAPI/Block.cs
Normal file
519
TechbloxModdingAPI/Block.cs
Normal file
|
@ -0,0 +1,519 @@
|
|||
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 HarmonyLib;
|
||||
using RobocraftX.PilotSeat;
|
||||
using RobocraftX.Rendering;
|
||||
using Techblox.BlockLabelsServer;
|
||||
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>
|
||||
/// <returns>The placed block or null if failed</returns>
|
||||
public static Block PlaceNew(BlockIDs block, float3 position, bool autoWire = false, Player player = null)
|
||||
{
|
||||
if (PlacementEngine.IsInGame && GameState.IsBuildMode())
|
||||
{
|
||||
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 = CommonExclusiveGroups.blockIDGeneratorClient.Peek() - 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>
|
||||
public Block(BlockIDs type, float3 position, bool autoWire = false, Player player = null)
|
||||
: base(block =>
|
||||
{
|
||||
if (!PlacementEngine.IsInGame || !GameState.IsBuildMode())
|
||||
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
|
||||
BlockEngine.UpdateBlockColor(Id);
|
||||
}
|
||||
}
|
||||
|
||||
/// <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
|
||||
{
|
||||
var opt = BlockEngine.GetBlockInfoOptional<LabelResourceIDComponent>(this);
|
||||
return opt ? FullGameFields._managers.blockLabelResourceManager.GetText(opt.Get().instanceID) : null;
|
||||
}
|
||||
set
|
||||
{
|
||||
var opt = BlockEngine.GetBlockInfoOptional<LabelResourceIDComponent>(this);
|
||||
if (opt) FullGameFields._managers.blockLabelResourceManager.SetText(opt.Get().instanceID, 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;
|
||||
if (!GameState.IsBuildMode()) return null; // Breaks in simulation
|
||||
var bgec = BlockEngine.GetBlockInfo<BlockGroupEntityComponent>(this);
|
||||
return blockGroup = bgec.currentBlockGroup == -1
|
||||
? null
|
||||
: GetInstance(new EGID((uint)bgec.currentBlockGroup, BlockGroupExclusiveGroups.BlockGroupEntityGroup),
|
||||
egid => new BlockGroup((int)egid.entityID, 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>
|
||||
/// The mass of the block.
|
||||
/// </summary>
|
||||
public float Mass
|
||||
{
|
||||
get => BlockEngine.GetBlockInfo<MassStruct>(this).mass;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Block complexity used for build rules. Determines the 'cost' of the block.
|
||||
/// </summary>
|
||||
public BlockComplexity Complexity
|
||||
{
|
||||
get => new(BlockEngine.GetBlockInfo<BlockComplexityComponent>(this));
|
||||
set => BlockEngine.GetBlockInfo<BlockComplexityComponent>(this) = 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) - TODO:
|
||||
: null;*/
|
||||
return 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
|
||||
}
|
||||
}
|
||||
}
|
217
TechbloxModdingAPI/BlockGroup.cs
Normal file
217
TechbloxModdingAPI/BlockGroup.cs
Normal file
|
@ -0,0 +1,217 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Gamecraft.Blocks.BlockGroups;
|
||||
using Svelto.ECS;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
|
||||
using TechbloxModdingAPI.Blocks;
|
||||
using TechbloxModdingAPI.Blocks.Engines;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
namespace TechbloxModdingAPI
|
||||
{
|
||||
/// <summary>
|
||||
/// A group of blocks that can be selected together. The placed version of blueprints. Dispose after usage.
|
||||
/// </summary>
|
||||
public class BlockGroup : EcsObjectBase, ICollection<Block>, IDisposable
|
||||
{
|
||||
internal static BlueprintEngine _engine = new BlueprintEngine();
|
||||
private readonly Block sourceBlock;
|
||||
private readonly List<Block> blocks;
|
||||
private float3 position, rotation;
|
||||
internal bool PosAndRotCalculated;
|
||||
|
||||
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!");
|
||||
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). Can only be used after initialization is complete.
|
||||
/// </summary>
|
||||
public float3 Position
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!PosAndRotCalculated)
|
||||
Refresh();
|
||||
return position;
|
||||
}
|
||||
set
|
||||
{
|
||||
var diff = value - position;
|
||||
foreach (var block in blocks)
|
||||
block.Position += diff;
|
||||
if (!PosAndRotCalculated) //The condition can only be true if a block has been added/removed manually
|
||||
Refresh(); //So the blocks array is up to date
|
||||
else
|
||||
position += diff;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The rotation of the block group. Can only be used after initialization is complete.
|
||||
/// </summary>
|
||||
public float3 Rotation
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!PosAndRotCalculated)
|
||||
Refresh();
|
||||
return rotation;
|
||||
}
|
||||
set
|
||||
{
|
||||
var diff = value - rotation;
|
||||
var qdiff = Quaternion.Euler(diff);
|
||||
foreach (var block in blocks)
|
||||
{
|
||||
block.Rotation += diff;
|
||||
block.Position = qdiff * block.Position;
|
||||
}
|
||||
if (!PosAndRotCalculated)
|
||||
Refresh();
|
||||
else
|
||||
rotation += diff;
|
||||
}
|
||||
}
|
||||
|
||||
/*/// <summary>
|
||||
/// Removes all of the blocks in this group from the world.
|
||||
/// </summary>
|
||||
public void RemoveBlocks()
|
||||
{
|
||||
_engine.RemoveBlockGroup(Id); - TODO: Causes a hard crash
|
||||
}*/
|
||||
|
||||
/// <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 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)
|
||||
{
|
||||
var bg = new BlockGroup(_engine.CreateBlockGroup(block.Position, Quaternion.Euler(block.Rotation)), block);
|
||||
block.BlockGroup = bg;
|
||||
return bg;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Collects each block that is a part of this group. Also sets the position and rotation.
|
||||
/// </summary>
|
||||
/// <returns>An array of blocks</returns>
|
||||
private Block[] GetBlocks()
|
||||
{
|
||||
if (!sourceBlock.Exists) return new[] {sourceBlock}; //The block must exist to get the others
|
||||
var ret = _engine.GetBlocksFromGroup(sourceBlock.Id, out var pos, out var rot);
|
||||
position = pos;
|
||||
rotation = ((Quaternion) rot).eulerAngles;
|
||||
PosAndRotCalculated = true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
private void Refresh()
|
||||
{
|
||||
blocks.Clear();
|
||||
blocks.AddRange(GetBlocks());
|
||||
}
|
||||
|
||||
internal static void Init()
|
||||
{
|
||||
GameEngineManager.AddGameEngine(_engine);
|
||||
}
|
||||
|
||||
public IEnumerator<Block> GetEnumerator() => blocks.GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => blocks.GetEnumerator();
|
||||
|
||||
/// <summary>
|
||||
/// 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>
|
||||
/// <exception cref="NullReferenceException"></exception>
|
||||
public void Add(Block item)
|
||||
{
|
||||
if (item == null) throw new NullReferenceException("Cannot add null to a block group");
|
||||
item.BlockGroup = this; //Calls AddInternal
|
||||
}
|
||||
|
||||
internal void AddInternal(Block item)
|
||||
{
|
||||
blocks.Add(item);
|
||||
_engine.AddBlockToGroup(item.Id, (int) Id.entityID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all blocks from this group.
|
||||
/// You cannot remove blocks that have been initialized, only those that you placed recently.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
while (blocks.Count > 0)
|
||||
Remove(blocks[blocks.Count - 1]);
|
||||
}
|
||||
|
||||
public bool Contains(Block item) => blocks.Contains(item);
|
||||
public void CopyTo(Block[] array, int arrayIndex) => blocks.CopyTo(array, arrayIndex);
|
||||
|
||||
/// <summary>
|
||||
/// Removes a block from this group.
|
||||
/// You cannot remove blocks that have been initialized, only those that you placed recently.
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="NullReferenceException"></exception>
|
||||
public bool Remove(Block item)
|
||||
{
|
||||
if (item == null) throw new NullReferenceException("Cannot remove null from a block group");
|
||||
bool ret = item.BlockGroup == this;
|
||||
if (ret)
|
||||
item.BlockGroup = null; //Calls RemoveInternal
|
||||
return ret;
|
||||
}
|
||||
|
||||
internal void RemoveInternal(Block item) => blocks.Remove(item);
|
||||
|
||||
public int Count => blocks.Count;
|
||||
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
|
||||
}
|
||||
}
|
17
TechbloxModdingAPI/Blocks/BlockComplexity.cs
Normal file
17
TechbloxModdingAPI/Blocks/BlockComplexity.cs
Normal file
|
@ -0,0 +1,17 @@
|
|||
using RobocraftX.Blocks;
|
||||
|
||||
namespace TechbloxModdingAPI.Blocks
|
||||
{
|
||||
public record BlockComplexity(int Cpu, int Power)
|
||||
{
|
||||
public BlockComplexity(BlockComplexityComponent component) : this(component.cpu, component.power)
|
||||
{
|
||||
}
|
||||
|
||||
public int Cpu { get; } = Cpu;
|
||||
public int Power { get; } = Power;
|
||||
|
||||
public static implicit operator BlockComplexityComponent(BlockComplexity complexity) =>
|
||||
new() { cpu = complexity.Cpu, power = complexity.Power };
|
||||
}
|
||||
}
|
65
TechbloxModdingAPI/Blocks/BlockExceptions.cs
Normal file
65
TechbloxModdingAPI/Blocks/BlockExceptions.cs
Normal file
|
@ -0,0 +1,65 @@
|
|||
using System;
|
||||
|
||||
using TechbloxModdingAPI;
|
||||
|
||||
namespace TechbloxModdingAPI.Blocks
|
||||
{
|
||||
public class BlockException : TechbloxModdingAPIException
|
||||
{
|
||||
public BlockException()
|
||||
{
|
||||
}
|
||||
|
||||
public BlockException(System.String message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public BlockException(System.String message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class BlockTypeException : BlockException
|
||||
{
|
||||
public BlockTypeException()
|
||||
{
|
||||
}
|
||||
|
||||
public BlockTypeException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class BlockSpecializationException : BlockException
|
||||
{
|
||||
public BlockSpecializationException()
|
||||
{
|
||||
}
|
||||
|
||||
public BlockSpecializationException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class WiringException : BlockException
|
||||
{
|
||||
public WiringException()
|
||||
{
|
||||
}
|
||||
|
||||
public WiringException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class WireInvalidException : WiringException
|
||||
{
|
||||
public WireInvalidException()
|
||||
{
|
||||
}
|
||||
|
||||
public WireInvalidException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
383
TechbloxModdingAPI/Blocks/BlockIDs.cs
Normal file
383
TechbloxModdingAPI/Blocks/BlockIDs.cs
Normal file
|
@ -0,0 +1,383 @@
|
|||
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,
|
||||
CityDoubleCrossing,
|
||||
CityDoubleCrossroads,
|
||||
CitySmallDoubleJunction,
|
||||
CityDoubleJunction,
|
||||
CityDoubleToSingleJunction,
|
||||
CitySmallDoubleRoad,
|
||||
CityDoubleRoad,
|
||||
CitySmallDoubleTurn,
|
||||
CityLargeDoubleTurn,
|
||||
CitySmallSingleTurn,
|
||||
CityLargeSingleTurn,
|
||||
CitySingleJunction,
|
||||
CitySingleRoad,
|
||||
SegoeUITextblock,
|
||||
GravtracTextblock,
|
||||
HauserTextblock,
|
||||
TechnopollasTextblock,
|
||||
CityDoubleHillRoad,
|
||||
DiagonalTrackTile,
|
||||
DiagonalTrackTile2,
|
||||
DiagonalTransitionTile,
|
||||
SplitLane,
|
||||
BitBlock,
|
||||
Timer,
|
||||
CityNightAtmosphere,
|
||||
FloodLight,
|
||||
SoccerBall,
|
||||
CircularWallLight,
|
||||
BlueSkyAtmos,
|
||||
DirtToGrassTransitionTile = 393,
|
||||
DirtToGrassTransitionInnerTile,
|
||||
DirtToGrassTransitionOuterTile,
|
||||
DirtToGrassTransitionHillTile,
|
||||
DirtToGrassTransitionRoadTile,
|
||||
DirtHill2 = 399,
|
||||
DirtHill3,
|
||||
DirtInnerCorner2 = 402,
|
||||
DirtInnerCorner3,
|
||||
DirtOuterCorner2 = 405,
|
||||
DirtOuterCorner3,
|
||||
CityTarmacEdgeInner,
|
||||
CityTarmacEdgeOuter,
|
||||
CityTarmacEdgeRoad,
|
||||
CityTarmac,
|
||||
SmallGrassQuarterTile,
|
||||
CityToRacetrackTransition,
|
||||
HUDTimer,
|
||||
CentreHUD,
|
||||
Checkpoint,
|
||||
ScoreboardHUD,
|
||||
GameplaySFX,
|
||||
SpawnPoint,
|
||||
AreaSensor,
|
||||
WorldResetter,
|
||||
SmallJet,
|
||||
MediumJet,
|
||||
LargeJet,
|
||||
DistanceSensor,
|
||||
Stabilizer,
|
||||
ObjectID,
|
||||
ScoreToTechpointConversion,
|
||||
TeamScore,
|
||||
ScorePickupBlock,
|
||||
SportyHatchbackDriverSeat,
|
||||
SportyHatchbackPassengerSeat,
|
||||
FlamingExhaust = 433,
|
||||
SmokingExhaust,
|
||||
StreetLamp,
|
||||
Vector7HatchbackWheel,
|
||||
Vector7HatchbackWheelWideProfile,
|
||||
Vector7SedanWheel,
|
||||
Vector7SedanWideProfile,
|
||||
Vector7FormulaWheel,
|
||||
Vector7FormulaWheelRear,
|
||||
Vector7MonsterTruckWheel,
|
||||
Vector7TruckWheel,
|
||||
Vector7TruckWheelDouble,
|
||||
BusSeat,
|
||||
XLJet,
|
||||
XXLJet,
|
||||
ElectricSedanEngine,
|
||||
HeadlampIndicator,
|
||||
HeadlampSrip,
|
||||
HeadlampStripEdge,
|
||||
ConstantBlock,
|
||||
CounterBlock,
|
||||
SmallGridHill,
|
||||
SmallGridHillInnerCorner,
|
||||
SmallGridHillOuterCorner,
|
||||
AimingAxleServo,
|
||||
AimingHingeServo,
|
||||
WeaponDisabler,
|
||||
Vector7SmallJet,
|
||||
Vector7MediumJet,
|
||||
Vector7LargeJet,
|
||||
Vector7XLJet,
|
||||
Vector7XXLJet,
|
||||
APCWheelRigNoSteering,
|
||||
APCWheelRigWithSteering,
|
||||
APCWheel,
|
||||
APCSeat,
|
||||
APCEngine,
|
||||
DamageScoreBlock,
|
||||
KillScoreBlock,
|
||||
Autocannon = 480
|
||||
}
|
||||
}
|
46
TechbloxModdingAPI/Blocks/BlockMaterial.cs
Normal file
46
TechbloxModdingAPI/Blocks/BlockMaterial.cs
Normal file
|
@ -0,0 +1,46 @@
|
|||
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,
|
||||
Boundary,
|
||||
Emissive,
|
||||
AircraftPanelingRivetedPainted,
|
||||
AircraftPanelingRivetedMetallic,
|
||||
SteelBodyworkPearlescent,
|
||||
SteelBodyworkRadWrap,
|
||||
SteelBodyworkGlitter,
|
||||
BouncyRubber,
|
||||
BouncyRubberTieDye,
|
||||
BrickPainted,
|
||||
FuturisticPanelingRivetedPainted,
|
||||
}
|
||||
}
|
256
TechbloxModdingAPI/Blocks/BlockTests.cs
Normal file
256
TechbloxModdingAPI/Blocks/BlockTests.cs
Normal file
|
@ -0,0 +1,256 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
using DataLoader;
|
||||
using Svelto.Tasks;
|
||||
using Unity.Mathematics;
|
||||
|
||||
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++)
|
||||
{
|
||||
var block = blocks[index];
|
||||
if (!block.Exists) continue;
|
||||
foreach (var property in block.GetType().GetProperties())
|
||||
{
|
||||
//Includes specialised block properties
|
||||
if (property.SetMethod == null) continue;
|
||||
|
||||
bool3 Float3Compare(float3 a, float3 b)
|
||||
{ // From Unity reference code
|
||||
return math.abs(b - a) < math.max(
|
||||
0.000001f * math.max(math.abs(a), math.abs(b)),
|
||||
float.Epsilon * 8
|
||||
);
|
||||
}
|
||||
bool4 Float4Compare(float4 a, float4 b)
|
||||
{ // From Unity reference code
|
||||
return math.abs(b - a) < math.max(
|
||||
0.000001f * math.max(math.abs(a), math.abs(b)),
|
||||
float.Epsilon * 8
|
||||
);
|
||||
}
|
||||
|
||||
var testValues = new (Type, object, Predicate<(object Value, object Default)>)[]
|
||||
{
|
||||
//(type, default value, predicate or null for equality)
|
||||
(typeof(long), 3, null),
|
||||
(typeof(int), 4, null),
|
||||
(typeof(double), 5.2f, t => Math.Abs((double) t.Value - (double) t.Default) < float.Epsilon),
|
||||
(typeof(float), 5.2f, t => Math.Abs((float) t.Value - (float) t.Default) < float.Epsilon),
|
||||
(typeof(bool), true, t => (bool) t.Value),
|
||||
(typeof(string), "Test", t => (string) t.Value == "Test"), //String equality check
|
||||
(typeof(float3), (float3) 20, t => math.all(Float3Compare((float3)t.Value, (float3)t.Default))),
|
||||
(typeof(BlockColor), new BlockColor(BlockColors.Aqua, 2), null),
|
||||
(typeof(float4), (float4) 5, t => math.all(Float4Compare((float4)t.Value, (float4)t.Default)))
|
||||
};
|
||||
var propType = property.PropertyType;
|
||||
if (!propType.IsValueType) continue;
|
||||
(object valueToUse, Predicate<(object Value, object Default)> predicateToUse) = (null, null);
|
||||
foreach (var (type, value, predicate) in testValues)
|
||||
{
|
||||
if (type.IsAssignableFrom(propType))
|
||||
{
|
||||
valueToUse = value;
|
||||
predicateToUse = predicate ?? (t => Equals(t.Value, t.Default));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (propType.IsEnum)
|
||||
{
|
||||
var values = propType.GetEnumValues();
|
||||
valueToUse = values.GetValue(values.Length / 2);
|
||||
predicateToUse = t => Equals(t.Value, t.Default);
|
||||
}
|
||||
|
||||
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, valueToUse)) && (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> - TODO: Internal struct access
|
||||
/// 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<BlockTagEntityStruct>(pickedBlock.pickedBlockEntityID)
|
||||
&& entitiesDB.Exists<BlockTagEntityStruct>(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 = BuildModeWiresGroups.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;
|
||||
}
|
||||
}
|
292
TechbloxModdingAPI/Blocks/Engines/BlockEngine.cs
Normal file
292
TechbloxModdingAPI/Blocks/Engines/BlockEngine.cs
Normal file
|
@ -0,0 +1,292 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using HarmonyLib;
|
||||
|
||||
using Gamecraft.ColourPalette;
|
||||
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.EntityStructs;
|
||||
using Svelto.ECS.Experimental;
|
||||
using Svelto.ECS.Hybrid;
|
||||
using Techblox.BuildingDrone;
|
||||
using Techblox.ObjectIDBlockServer;
|
||||
using Unity.Mathematics;
|
||||
|
||||
using TechbloxModdingAPI.Engines;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
using TechbloxModdingAPI.Utility.ECS;
|
||||
using PrefabsID = RobocraftX.Common.PrefabsID;
|
||||
|
||||
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 Array.Empty<Block>();
|
||||
Stack<EGID> cubeStack = new Stack<EGID>();
|
||||
FasterList<EGID> cubes = new FasterList<EGID>(10);
|
||||
var coll = entitiesDB.QueryEntities<GridConnectionsEntityStruct>();
|
||||
foreach (var ((ecoll, count), _) in coll)
|
||||
{
|
||||
for(int i = 0; i < count; i++)
|
||||
{
|
||||
ecoll[i].areConnectionsAssigned = false;
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: GetConnectedCubesUtility
|
||||
/*ConnectedCubesUtility.TreeTraversal.GetConnectedCubes(entitiesDB, blockID, cubeStack, cubes,
|
||||
(in GridConnectionsEntityStruct _) => 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,
|
||||
ColourPaletteExclusiveGroups.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
|
||||
{
|
||||
#if DEBUG
|
||||
if (!typeof(BlockTagEntityStruct).IsAssignableFrom(typeof(T)) && block.Exists && block.InitData.Valid)
|
||||
throw new ArgumentException("The block exists but the init data has not been removed!");
|
||||
#endif
|
||||
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);
|
||||
}
|
||||
|
||||
internal object GetBlockInfo(Block block, Type type, string name)
|
||||
{
|
||||
var opt = AccessTools.Method(typeof(NativeApiExtensions), "QueryEntityOptional",
|
||||
new[] { typeof(EntitiesDB), typeof(EcsObjectBase), typeof(ExclusiveGroupStruct) }, new[] { type })
|
||||
.Invoke(null, new object[] { entitiesDB, block, null });
|
||||
var str = AccessTools.Property(opt.GetType(), "Value").GetValue(opt);
|
||||
return AccessTools.Field(str.GetType(), name).GetValue(str);
|
||||
}
|
||||
|
||||
internal void SetBlockInfo(Block block, Type type, string name, object value)
|
||||
{
|
||||
var opt = AccessTools.Method(typeof(BlockEngine), "GetBlockInfoOptional", generics: new[] { type })
|
||||
.Invoke(this, new object[] { block });
|
||||
var prop = AccessTools.Property(opt.GetType(), "Value");
|
||||
var str = prop.GetValue(opt);
|
||||
AccessTools.Field(str.GetType(), name).SetValue(str, value);
|
||||
prop.SetValue(opt, str);
|
||||
}
|
||||
|
||||
public void UpdateDisplayedBlock(EGID id)
|
||||
{
|
||||
if (!BlockExists(id)) return;
|
||||
var pos = entitiesDB.QueryEntity<PositionEntityStruct>(id);
|
||||
var rot = entitiesDB.QueryEntity<RotationEntityStruct>(id);
|
||||
var scale = entitiesDB.QueryEntity<ScalingEntityStruct>(id);
|
||||
var skew = entitiesDB.QueryEntity<SkewComponent>(id);
|
||||
entitiesDB.QueryEntity<RenderingDataStruct>(id).matrix =
|
||||
math.mul(float4x4.TRS(pos.position, rot.rotation, scale.scale), skew.skewMatrix);
|
||||
entitiesDB.PublishEntityChangeDelayed<GFXPrefabEntityStructGPUI>(id); // Signal a prefab change so it updates the render buffers
|
||||
}
|
||||
|
||||
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<BlockTagEntityStruct>(block)) //The block exists
|
||||
throw new BlockException("Prefab asset ID not found for block " + block); //Set by the game
|
||||
return;
|
||||
}
|
||||
|
||||
uint prefabId =
|
||||
PrefabsID.GetOrAddPrefabID((ushort) prefabAssetID, material, 1, flipped);
|
||||
entitiesDB.QueryEntityOrDefault<GFXPrefabEntityStructGPUI>(block).prefabID = prefabId;
|
||||
if (block.Exists)
|
||||
{
|
||||
entitiesDB.PublishEntityChangeDelayed<CubeMaterialStruct>(block.Id);
|
||||
entitiesDB.PublishEntityChangeDelayed<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.PublishEntityChangeDelayed<BuildingActionComponent>(local.ID);
|
||||
}
|
||||
//Phyiscs prefab: prefabAssetID, set on block creation from the CubeListData
|
||||
}
|
||||
|
||||
public void UpdateBlockColor(EGID id)
|
||||
{
|
||||
entitiesDB.PublishEntityChangeDelayed<ColourParameterEntityStruct>(id);
|
||||
}
|
||||
|
||||
public bool BlockExists(EGID blockID)
|
||||
{
|
||||
return entitiesDB.Exists<BlockTagEntityStruct>(blockID);
|
||||
}
|
||||
|
||||
public SimBody[] GetSimBodiesFromID(byte id)
|
||||
{
|
||||
var ret = new FasterList<SimBody>(4);
|
||||
var oids = entitiesDB.QueryEntitiesOptional<ObjectIdEntityStruct>(ObjectIDBlockExclusiveGroups.OBJECT_ID_BLOCK_GROUP);
|
||||
EGIDMapper<GridConnectionsEntityStruct>? connections = null;
|
||||
foreach (var oid in oids)
|
||||
{
|
||||
if (oid.Get().objectId != id) continue;
|
||||
if (!connections.HasValue) //Would need reflection to get the group from the build group otherwise
|
||||
connections = entitiesDB.QueryMappedEntities<GridConnectionsEntityStruct>(oid.EGID.groupID);
|
||||
//var rid = connections.Value.Entity(tag.ID.entityID).machineRigidBodyId;
|
||||
/*foreach (var rb in ret) - TODO
|
||||
{
|
||||
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, count) = entitiesDB.QueryEntities<JointEntityStruct>(MachineSimulationGroups.JOINTS_GROUP);
|
||||
var list = new FasterList<SimBody>(4);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
ref var joint = ref joints[i];
|
||||
if (joint.isBroken) continue;
|
||||
/*if (joint.connectedEntityA == id) list.Add(new SimBody(joint.connectedEntityB)); - TODO:
|
||||
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, count), _) in groups)
|
||||
{
|
||||
for (var index = 0; index < count; index++)
|
||||
{
|
||||
var conn = coll[index];
|
||||
/*if (conn.clusterId == cid) - TODO
|
||||
bodies.Add(conn.machineRigidBodyId);*/
|
||||
}
|
||||
}
|
||||
|
||||
return bodies.Select(id => new SimBody(id, cid)).ToArray();
|
||||
}
|
||||
|
||||
public EGID? FindBlockEGID(uint id)
|
||||
{
|
||||
var groups = entitiesDB.FindGroups<BlockTagEntityStruct>();
|
||||
foreach (ExclusiveGroupStruct group in groups)
|
||||
{
|
||||
if (entitiesDB.Exists<BlockTagEntityStruct>(id, group))
|
||||
return new EGID(id, group);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Cluster GetCluster(uint sbid)
|
||||
{
|
||||
var groups = entitiesDB.QueryEntities<GridConnectionsEntityStruct>();
|
||||
foreach (var ((coll, count), _) in groups)
|
||||
{
|
||||
for (var index = 0; index < count; index++)
|
||||
{
|
||||
var conn = coll[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) - TODO:
|
||||
return new Cluster(conn.clusterId);*/
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Block[] GetBodyBlocks(uint sbid)
|
||||
{
|
||||
var groups = entitiesDB.FindGroups<GridConnectionsEntityStruct>();
|
||||
groups = new QueryGroups(groups).Except(CommonExclusiveGroups.DISABLED_JOINTS_IN_SIM_GROUP).Evaluate().result;
|
||||
var set = new HashSet<Block>();
|
||||
foreach (var ((coll, tags, count), _) in entitiesDB.QueryEntities<GridConnectionsEntityStruct, BlockTagEntityStruct>(groups))
|
||||
{
|
||||
for (var index = 0; index < count; index++)
|
||||
{
|
||||
var conn = coll[index];
|
||||
/*if (conn.machineRigidBodyId == sbid) - TODO
|
||||
set.Add(Block.New(tags[index].ID));*/
|
||||
}
|
||||
}
|
||||
|
||||
return set.ToArray();
|
||||
}
|
||||
|
||||
public ObjectID[] GetObjectIDsFromID(byte id)
|
||||
{
|
||||
if (!entitiesDB.HasAny<ObjectIDTweakableComponent>(ObjectIDBlockExclusiveGroups.OBJECT_ID_BLOCK_GROUP))
|
||||
return Array.Empty<ObjectID>();
|
||||
|
||||
var ret = new FasterList<ObjectID>(4);
|
||||
var oids = entitiesDB.QueryEntitiesOptional<ObjectIDTweakableComponent>(ObjectIDBlockExclusiveGroups.OBJECT_ID_BLOCK_GROUP);
|
||||
foreach (var oid in oids)
|
||||
{
|
||||
if (oid.Get().objectIDToTrigger == id)
|
||||
ret.Add(new ObjectID(oid.EGID));
|
||||
}
|
||||
|
||||
return ret.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
47
TechbloxModdingAPI/Blocks/Engines/BlockEventsEngine.cs
Normal file
47
TechbloxModdingAPI/Blocks/Engines/BlockEventsEngine.cs
Normal file
|
@ -0,0 +1,47 @@
|
|||
using System;
|
||||
|
||||
using RobocraftX.Blocks;
|
||||
using Svelto.ECS;
|
||||
|
||||
using TechbloxModdingAPI.Engines;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
namespace TechbloxModdingAPI.Blocks.Engines
|
||||
{
|
||||
public class BlockEventsEngine : IReactionaryEngine<BlockTagEntityStruct>
|
||||
{
|
||||
public WrappedHandler<BlockPlacedRemovedEventArgs> Placed;
|
||||
public WrappedHandler<BlockPlacedRemovedEventArgs> Removed;
|
||||
|
||||
public void Ready()
|
||||
{
|
||||
}
|
||||
|
||||
public EntitiesDB entitiesDB { get; set; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public string Name { get; } = "TechbloxModdingAPIBlockEventsEngine";
|
||||
public bool isRemovable { get; } = false;
|
||||
|
||||
public void Add(ref BlockTagEntityStruct entityComponent, EGID egid)
|
||||
{
|
||||
Placed.Invoke(this, new BlockPlacedRemovedEventArgs {ID = egid});
|
||||
}
|
||||
|
||||
public void Remove(ref BlockTagEntityStruct entityComponent, EGID egid)
|
||||
{
|
||||
Removed.Invoke(this, new BlockPlacedRemovedEventArgs {ID = egid});
|
||||
}
|
||||
}
|
||||
|
||||
public struct BlockPlacedRemovedEventArgs
|
||||
{
|
||||
public EGID ID;
|
||||
private Block block;
|
||||
|
||||
public Block Block => block ??= Block.New(ID);
|
||||
}
|
||||
}
|
404
TechbloxModdingAPI/Blocks/Engines/BlueprintEngine.cs
Normal file
404
TechbloxModdingAPI/Blocks/Engines/BlueprintEngine.cs
Normal file
|
@ -0,0 +1,404 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Gamecraft.Blocks.BlockGroups;
|
||||
using Gamecraft.GUI.Blueprints;
|
||||
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.Connections;
|
||||
using TechbloxModdingAPI.Engines;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
using TechbloxModdingAPI.Utility.ECS;
|
||||
using Unity.Collections;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
using Allocator = Svelto.Common.Allocator;
|
||||
|
||||
namespace TechbloxModdingAPI.Blocks.Engines
|
||||
{
|
||||
public class BlueprintEngine : IFactoryEngine
|
||||
{
|
||||
private readonly MethodInfo getBlocksFromGroup =
|
||||
AccessTools.Method("RobocraftX.CR.MachineEditing.PlaceBlockUtility:GetBlocksSharingBlockgroup");
|
||||
|
||||
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 nativeBlockRemove;
|
||||
private static NativeEntityRemove nativeConnectionRemove;
|
||||
private static MachineGraphConnectionEntityFactory connectionFactory;
|
||||
private static IEntityFunctions entityFunctions;
|
||||
private static ClipboardSerializationDataResourceManager clipboardManager;
|
||||
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();
|
||||
}
|
||||
|
||||
public Block[] GetBlocksFromGroup(EGID blockID, out float3 pos, out quaternion rot)
|
||||
{
|
||||
var blockPos = default(float3);
|
||||
var blockRot = default(quaternion);
|
||||
var parameters = new object[] {blockID, selectedBlocksInGroup, entitiesDB, blockPos, blockRot};
|
||||
getBlocksFromGroup.Invoke(null, parameters);
|
||||
pos = (float3) parameters[3];
|
||||
rot = (quaternion) parameters[4];
|
||||
int count = selectedBlocksInGroup.Count<EGID>();
|
||||
var ret = new Block[count];
|
||||
for (uint i = 0; i < count; i++)
|
||||
ret[i] = Block.New(selectedBlocksInGroup.Get<EGID>(i));
|
||||
selectedBlocksInGroup.FastClear();
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void RemoveBlockGroup(int id)
|
||||
{
|
||||
BlockGroupUtility.RemoveAllBlocksInBlockGroup(id, entitiesDB, removedConnections, nativeBlockRemove,
|
||||
nativeConnectionRemove, connectionFactory, default).Complete();
|
||||
}
|
||||
|
||||
public int CreateBlockGroup(float3 position, quaternion rotation)
|
||||
{
|
||||
int nextFilterId = BlockGroupUtility.NextFilterId;
|
||||
Factory.BuildEntity<BlockGroupEntityDescriptor>((uint) nextFilterId,
|
||||
BlockGroupExclusiveGroups.BlockGroupEntityGroup).Init(new BlockGroupTransformEntityComponent
|
||||
{
|
||||
blockGroupGridRotation = rotation,
|
||||
blockGroupGridPosition = position
|
||||
});
|
||||
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, resourceID, false, -1);
|
||||
}
|
||||
|
||||
public uint CreateBlueprint()
|
||||
{
|
||||
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];
|
||||
using (var enumerator = selected.GetEnumerator())
|
||||
{
|
||||
for (var i = 0; enumerator.MoveNext(); i++)
|
||||
{
|
||||
var block = enumerator.Current;
|
||||
blockIDs[i] = block.Id;
|
||||
}
|
||||
}
|
||||
|
||||
var serializationData = clipboardManager.GetSerializationData(blueprintID);
|
||||
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);
|
||||
//ref ColliderAabb collider = ref EntityNativeDBExtensions.QueryEntity<ColliderAabb>(entitiesDB, (uint) groupID, BlockGroupExclusiveGroups.BlockGroupEntityGroup);
|
||||
//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, -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,
|
||||
FullGameFields._managers.blockLabelResourceManager);
|
||||
}
|
||||
}
|
||||
|
||||
public Block[] PlaceBlueprintBlocks(uint blueprintID, uint playerID, float3 pos, float3 rot)
|
||||
{ //RobocraftX.CR.MachineEditing.PlaceBlueprintUtility.PlaceBlocksFromSerialisedData
|
||||
var serializationData = clipboardManager.GetSerializationData(blueprintID);
|
||||
var blueprintData = serializationData.blueprintData;
|
||||
blueprintData.dataPos = 0U;
|
||||
uint selectionSize;
|
||||
PositionEntityStruct selectionPosition;
|
||||
RotationEntityStruct selectionRotation;
|
||||
uint version;
|
||||
BoxSelectSerializationUtilities.ReadClipboardHeader(blueprintData, out selectionSize, out selectionPosition, out selectionRotation, out version);
|
||||
((FasterList<EGID>) LocalBlockMap.GetValue(null)).Clear();
|
||||
if (version <= 1U)
|
||||
{
|
||||
uint groupsCount;
|
||||
BoxSelectSerializationUtilities.ReadBlockGroupData(blueprintData, out groupsCount);
|
||||
for (int index = 0; (long) index < (long) groupsCount; ++index)
|
||||
{
|
||||
int nextFilterId = BlockGroupUtility.NextFilterId;
|
||||
entitySerialization.DeserializeNewEntity(new EGID((uint) nextFilterId, BlockGroupExclusiveGroups.BlockGroupEntityGroup), blueprintData, 1);
|
||||
}
|
||||
}
|
||||
int nextFilterId1 = BlockGroupUtility.NextFilterId;
|
||||
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};
|
||||
var rots = new RotationEntityStruct {rotation = frot};
|
||||
for (int index = 0; (long) index < (long) selectionSize; ++index)
|
||||
BuildBlock.Invoke(null,
|
||||
new object[]
|
||||
{
|
||||
playerID, grid, poss, rots, selectionPosition, selectionRotation, blueprintData,
|
||||
entitySerialization, nextFilterId1
|
||||
});
|
||||
/*
|
||||
uint playerId, in GridRotationStruct ghostParentGrid,
|
||||
in PositionEntityStruct ghostParentPosition, in RotationEntityStruct ghostParentRotation,
|
||||
in PositionEntityStruct selectionPosition, in RotationEntityStruct selectionRotation,
|
||||
ISerializationData serializationData, EntitiesDB entitiesDb,
|
||||
IEntitySerialization entitySerialization, int blockGroupId
|
||||
*/
|
||||
if (globalBlockMap == null)
|
||||
globalBlockMap = FullGameFields._deserialisedBlockMap;
|
||||
var placedBlocks = (FasterList<EGID>) LocalBlockMap.GetValue(null);
|
||||
globalBlockMap.Clear();
|
||||
globalBlockMap.AddRange(placedBlocks);
|
||||
BuildWires.Invoke(null,
|
||||
new object[] {playerID, blueprintData, entitySerialization, entitiesDB, entityFactory});
|
||||
var blocks = new Block[placedBlocks.count];
|
||||
for (int i = 0; i < blocks.Length; 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);
|
||||
}
|
||||
|
||||
public void DisposeBlueprint(uint blueprintID)
|
||||
{
|
||||
clipboardManager.DecrementRefCount(blueprintID);
|
||||
}
|
||||
|
||||
|
||||
//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,
|
||||
FullGameFields._managers.blockLabelResourceManager);
|
||||
entityInitializer.Init(dbStruct);
|
||||
entityInitializer.Init(new GFXPrefabEntityStructGPUI(
|
||||
PrefabsID.GetOrAddPrefabID((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]
|
||||
private static class RemoveEnginePatch
|
||||
{
|
||||
public static void Prefix(IEntityFunctions entityFunctions,
|
||||
MachineGraphConnectionEntityFactory machineGraphConnectionEntityFactory)
|
||||
{
|
||||
nativeBlockRemove = entityFunctions.ToNativeRemove<BlockEntityDescriptor>("TBAPI" + nameof(BlueprintEngine));
|
||||
nativeConnectionRemove = entityFunctions.ToNativeRemove<MachineConnectionEntityDescriptor>("TBAPI" + nameof(BlueprintEngine));
|
||||
connectionFactory = machineGraphConnectionEntityFactory;
|
||||
BlueprintEngine.entityFunctions = entityFunctions;
|
||||
}
|
||||
|
||||
public static MethodBase TargetMethod()
|
||||
{
|
||||
return AccessTools.GetDeclaredConstructors(AccessTools.TypeByName("RobocraftX.CR.MachineEditing.RemoveBlockEngine"))[0];
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch]
|
||||
private static class SelectEnginePatch
|
||||
{
|
||||
public static void Prefix(ClipboardSerializationDataResourceManager clipboardSerializationDataResourceManager,
|
||||
IEntitySerialization entitySerialization,
|
||||
IEntityFactory entityFactory)
|
||||
{
|
||||
clipboardManager = clipboardSerializationDataResourceManager;
|
||||
BlueprintEngine.entitySerialization = entitySerialization;
|
||||
BlueprintEngine.entityFactory = entityFactory;
|
||||
}
|
||||
|
||||
public static MethodBase TargetMethod()
|
||||
{
|
||||
return AccessTools.GetDeclaredConstructors(AccessTools.TypeByName("RobocraftX.CR.MachineEditing.SelectBlockEngine"))[0];
|
||||
}
|
||||
}
|
||||
|
||||
[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; }
|
||||
}
|
||||
}
|
69
TechbloxModdingAPI/Blocks/Engines/MovementEngine.cs
Normal file
69
TechbloxModdingAPI/Blocks/Engines/MovementEngine.cs
Normal file
|
@ -0,0 +1,69 @@
|
|||
using RobocraftX.Common;
|
||||
using RobocraftX.DOTS;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.EntityStructs;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Transforms;
|
||||
|
||||
using TechbloxModdingAPI.Engines;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
using TechbloxModdingAPI.Utility.ECS;
|
||||
|
||||
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);
|
||||
var phyStruct = this.entitiesDB.QueryEntityOptional<DOTSPhysicsEntityStruct>(block);
|
||||
// main (persistent) position
|
||||
posStruct.position = vector;
|
||||
// placement grid position
|
||||
gridStruct.position = vector;
|
||||
// rendered position
|
||||
transStruct.position = vector;
|
||||
// collision position
|
||||
if (phyStruct)
|
||||
{ //It exists
|
||||
FullGameFields._physicsWorld.EntityManager.SetComponentData(phyStruct.Get().dotsEntity, new Translation
|
||||
{
|
||||
Value = posStruct.position
|
||||
});
|
||||
}
|
||||
|
||||
entitiesDB.QueryEntityOrDefault<GridConnectionsEntityStruct>(block).areConnectionsAssigned = false;
|
||||
return posStruct.position;
|
||||
}
|
||||
|
||||
internal float3 GetPosition(Block block)
|
||||
{
|
||||
return entitiesDB.QueryEntityOrDefault<PositionEntityStruct>(block).position;
|
||||
}
|
||||
}
|
||||
}
|
131
TechbloxModdingAPI/Blocks/Engines/PlacementEngine.cs
Normal file
131
TechbloxModdingAPI/Blocks/Engines/PlacementEngine.cs
Normal file
|
@ -0,0 +1,131 @@
|
|||
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;
|
||||
using TechbloxModdingAPI.Utility.ECS;
|
||||
|
||||
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.blockIDGeneratorClient.Next(), 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.GetOrAddPrefabID((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];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
86
TechbloxModdingAPI/Blocks/Engines/RemovalEngine.cs
Normal file
86
TechbloxModdingAPI/Blocks/Engines/RemovalEngine.cs
Normal file
|
@ -0,0 +1,86 @@
|
|||
using System.Reflection;
|
||||
|
||||
using Gamecraft.Blocks.BlockGroups;
|
||||
using HarmonyLib;
|
||||
using RobocraftX.Blocks;
|
||||
using RobocraftX.Common;
|
||||
using RobocraftX.GroupTags;
|
||||
using RobocraftX.StateSync;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.Native;
|
||||
using Techblox.Blocks.Connections;
|
||||
using Unity.Collections;
|
||||
using Unity.Jobs;
|
||||
using Allocator = Unity.Collections.Allocator;
|
||||
|
||||
using TechbloxModdingAPI.Engines;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
namespace TechbloxModdingAPI.Blocks.Engines
|
||||
{
|
||||
public class RemovalEngine : IApiEngine, IDeterministicTimeStopped
|
||||
{
|
||||
private static IEntityFunctions _entityFunctions;
|
||||
private static MachineGraphConnectionEntityFactory _connectionFactory;
|
||||
private NativeHashSet<ulong> removedConnections;
|
||||
|
||||
public bool RemoveBlock(EGID target)
|
||||
{
|
||||
if (!entitiesDB.Exists<MachineGraphConnectionsEntityStruct>(target))
|
||||
return false;
|
||||
using var connStructMapper =
|
||||
entitiesDB.QueryNativeMappedEntities<MachineGraphConnectionsEntityStruct>(GroupTag<BLOCK_TAG>.Groups,
|
||||
Svelto.Common.Allocator.Temp);
|
||||
if (entitiesDB.TryQueryNativeMappedEntities<MachineConnectionComponent>(
|
||||
ConnectionsExclusiveGroups.MACHINE_CONNECTION_GROUP, out var mapper))
|
||||
{
|
||||
BlockGroupUtility.RemoveBlockConnections(target, removedConnections, _connectionFactory,
|
||||
connStructMapper, mapper, entitiesDB.GetEntityReferenceMap(), _entityFunctions);
|
||||
}
|
||||
|
||||
_entityFunctions.RemoveEntity<BlockEntityDescriptor>(target);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Ready()
|
||||
{
|
||||
removedConnections = new(2000, Allocator.Persistent);
|
||||
}
|
||||
|
||||
public EntitiesDB entitiesDB { get; set; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
removedConnections.Dispose();
|
||||
}
|
||||
|
||||
public string Name => "TechbloxModdingAPIRemovalGameEngine";
|
||||
public string name => Name;
|
||||
|
||||
public bool isRemovable => false;
|
||||
|
||||
[HarmonyPatch]
|
||||
class FactoryObtainerPatch
|
||||
{
|
||||
static void Postfix(IEntityFunctions entityFunctions,
|
||||
MachineGraphConnectionEntityFactory machineGraphConnectionEntityFactory)
|
||||
{
|
||||
_entityFunctions = entityFunctions;
|
||||
_connectionFactory = machineGraphConnectionEntityFactory;
|
||||
Logging.MetaDebugLog("Requirements injected.");
|
||||
}
|
||||
|
||||
static MethodBase TargetMethod(Harmony instance)
|
||||
{
|
||||
return AccessTools.TypeByName("RobocraftX.CR.MachineEditing.RemoveBlockEngine").GetConstructors()[0];
|
||||
}
|
||||
}
|
||||
|
||||
public JobHandle DeterministicStep(in float deltaTime, JobHandle inputDeps)
|
||||
{
|
||||
if (removedConnections.IsCreated)
|
||||
removedConnections.Clear();
|
||||
return default;
|
||||
}
|
||||
}
|
||||
}
|
76
TechbloxModdingAPI/Blocks/Engines/RotationEngine.cs
Normal file
76
TechbloxModdingAPI/Blocks/Engines/RotationEngine.cs
Normal file
|
@ -0,0 +1,76 @@
|
|||
using RobocraftX.Common;
|
||||
using RobocraftX.DOTS;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.EntityStructs;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
|
||||
using TechbloxModdingAPI.Engines;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
using TechbloxModdingAPI.Utility.ECS;
|
||||
|
||||
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);
|
||||
var phyStruct = this.entitiesDB.QueryEntityOptional<DOTSPhysicsEntityStruct>(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)
|
||||
{ //It exists
|
||||
FullGameFields._physicsWorld.EntityManager.SetComponentData(phyStruct.Get().dotsEntity,
|
||||
new Unity.Transforms.Rotation
|
||||
{
|
||||
Value = rotStruct.rotation
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: Connections probably need to be assigned (maybe)
|
||||
// They are assigned during machine processing anyway
|
||||
entitiesDB.QueryEntityOrDefault<GridConnectionsEntityStruct>(block).areConnectionsAssigned = false;
|
||||
return ((Quaternion)rotStruct.rotation).eulerAngles;
|
||||
|
||||
}
|
||||
|
||||
internal float3 GetRotation(Block block)
|
||||
{
|
||||
ref RotationEntityStruct rotStruct = ref entitiesDB.QueryEntityOrDefault<RotationEntityStruct>(block);
|
||||
return ((Quaternion) rotStruct.rotation).eulerAngles;
|
||||
}
|
||||
}
|
||||
}
|
59
TechbloxModdingAPI/Blocks/Engines/ScalingEngine.cs
Normal file
59
TechbloxModdingAPI/Blocks/Engines/ScalingEngine.cs
Normal file
|
@ -0,0 +1,59 @@
|
|||
using System.Reflection;
|
||||
|
||||
using HarmonyLib;
|
||||
using RobocraftX.Common;
|
||||
using RobocraftX.DOTS;
|
||||
using Svelto.ECS;
|
||||
using Unity.Entities;
|
||||
|
||||
using TechbloxModdingAPI.Engines;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
namespace TechbloxModdingAPI.Blocks.Engines
|
||||
{
|
||||
public class ScalingEngine : IApiEngine
|
||||
{
|
||||
private static IReactOnAddAndRemove<DOTSPhysicsEntityCreationStruct> physicsEngine;
|
||||
|
||||
public void Ready()
|
||||
{
|
||||
}
|
||||
|
||||
public EntitiesDB entitiesDB { get; set; }
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public string Name { get; } = "TechbloxModdingAPIScalingEngine";
|
||||
public bool isRemovable { get; } = false;
|
||||
|
||||
private EntityManager _entityManager; //Unity entity manager
|
||||
|
||||
public void UpdateCollision(EGID egid)
|
||||
{
|
||||
if (_entityManager == default)
|
||||
_entityManager = FullGameFields._physicsWorld.EntityManager;
|
||||
//Assuming the block exists
|
||||
var entity = entitiesDB.QueryEntity<DOTSPhysicsEntityStruct>(egid).dotsEntity;
|
||||
var pes = new DOTSPhysicsEntityCreationStruct();
|
||||
physicsEngine.Add(ref pes, egid); //Create new DOTS entity
|
||||
_entityManager.DestroyEntity(entity);
|
||||
}
|
||||
|
||||
[HarmonyPatch]
|
||||
class PhysicsEnginePatch
|
||||
{
|
||||
static void Postfix(IReactOnAddAndRemove<DOTSPhysicsEntityCreationStruct> __instance)
|
||||
{
|
||||
physicsEngine = __instance;
|
||||
Logging.MetaDebugLog("Physics engine injected.");
|
||||
}
|
||||
|
||||
static MethodBase TargetMethod(Harmony instance)
|
||||
{
|
||||
return AccessTools.Method("RobocraftX.StateSync.HandleDOTSPhysicEntitiesWithPrefabCreationEngine" +
|
||||
":Ready");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
357
TechbloxModdingAPI/Blocks/Engines/SignalEngine.cs
Normal file
357
TechbloxModdingAPI/Blocks/Engines/SignalEngine.cs
Normal file
|
@ -0,0 +1,357 @@
|
|||
using System;
|
||||
|
||||
using Gamecraft.Wires;
|
||||
using Svelto.DataStructures;
|
||||
using Svelto.ECS;
|
||||
|
||||
using TechbloxModdingAPI.Engines;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
using TechbloxModdingAPI.Utility.ECS;
|
||||
|
||||
namespace TechbloxModdingAPI.Blocks.Engines
|
||||
{
|
||||
/// <summary>
|
||||
/// Engine which executes signal actions
|
||||
/// </summary>
|
||||
public class SignalEngine : IApiEngine, IFactoryEngine
|
||||
{
|
||||
public const float POSITIVE_HIGH = 1.0f;
|
||||
public const float NEGATIVE_HIGH = -1.0f;
|
||||
public const float HIGH = 1.0f;
|
||||
public const float ZERO = 0.0f;
|
||||
|
||||
public string Name { get; } = "TechbloxModdingAPISignalGameEngine";
|
||||
|
||||
public EntitiesDB entitiesDB { set; private get; }
|
||||
|
||||
public IEntityFactory Factory { get; set; }
|
||||
|
||||
public bool isRemovable => false;
|
||||
|
||||
public bool IsInGame = false;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
IsInGame = false;
|
||||
}
|
||||
|
||||
public void Ready()
|
||||
{
|
||||
IsInGame = true;
|
||||
}
|
||||
|
||||
// implementations for block wiring
|
||||
|
||||
public (WireEntityStruct Wire, EGID ID) CreateNewWire(EGID startBlock, byte startPort, EGID endBlock, byte endPort)
|
||||
{
|
||||
EGID wireEGID = new EGID(BuildModeWiresGroups.NewWireEntityId, BuildModeWiresGroups.WiresGroup.Group);
|
||||
EntityInitializer wireInitializer = Factory.BuildEntity<WireEntityDescriptor>(wireEGID);
|
||||
wireInitializer.Init(new WireEntityStruct
|
||||
{
|
||||
sourceBlockEGID = startBlock,
|
||||
sourcePortUsage = startPort,
|
||||
destinationBlockEGID = endBlock,
|
||||
destinationPortUsage = endPort
|
||||
});
|
||||
return (wireInitializer.Get<WireEntityStruct>(), wireEGID);
|
||||
}
|
||||
|
||||
public ref WireEntityStruct GetWire(EGID wire)
|
||||
{
|
||||
if (!entitiesDB.Exists<WireEntityStruct>(wire))
|
||||
{
|
||||
throw new WiringException($"Wire {wire} does not exist");
|
||||
}
|
||||
return ref entitiesDB.QueryEntity<WireEntityStruct>(wire);
|
||||
}
|
||||
|
||||
public ref PortEntityStruct GetPort(EGID port)
|
||||
{
|
||||
if (!entitiesDB.Exists<PortEntityStruct>(port))
|
||||
{
|
||||
throw new WiringException($"Port {port} does not exist (yet?)");
|
||||
}
|
||||
return ref entitiesDB.QueryEntity<PortEntityStruct>(port);
|
||||
}
|
||||
|
||||
public ref PortEntityStruct GetPortByOffset(BlockPortsStruct bps, byte portNumber, bool input)
|
||||
{
|
||||
ExclusiveGroup group = input
|
||||
? NamedExclusiveGroup<BuildModeWiresGroups.InputPortsGroup>.Group
|
||||
: NamedExclusiveGroup<BuildModeWiresGroups.OutputPortsGroup>.Group;
|
||||
uint id = (input ? bps.firstInputID : bps.firstOutputID) + portNumber;
|
||||
EGID egid = new EGID(id, group);
|
||||
if (!entitiesDB.Exists<PortEntityStruct>(egid))
|
||||
{
|
||||
throw new WiringException("Port does not exist");
|
||||
}
|
||||
return ref entitiesDB.QueryEntity<PortEntityStruct>(egid);
|
||||
}
|
||||
|
||||
public ref PortEntityStruct GetPortByOffset(Block block, byte portNumber, bool input)
|
||||
{
|
||||
var bps = entitiesDB.QueryEntityOptional<BlockPortsStruct>(block);
|
||||
if (!bps)
|
||||
{
|
||||
throw new BlockException("Block does not exist");
|
||||
}
|
||||
return ref GetPortByOffset(bps, portNumber, input);
|
||||
}
|
||||
|
||||
public ref T GetComponent<T>(EGID egid) where T : unmanaged, IEntityComponent
|
||||
{
|
||||
return ref entitiesDB.QueryEntity<T>(egid);
|
||||
}
|
||||
|
||||
public bool Exists<T>(EGID egid) where T : struct, IEntityComponent
|
||||
{
|
||||
return entitiesDB.Exists<T>(egid);
|
||||
}
|
||||
|
||||
public bool SetSignal(EGID blockID, float signal, out uint signalID, bool input = true)
|
||||
{
|
||||
signalID = GetSignalIDs(blockID, input)[0];
|
||||
return SetSignal(signalID, signal);
|
||||
}
|
||||
|
||||
public bool SetSignal(uint signalID, float signal, bool input = true)
|
||||
{
|
||||
var (array, count) = GetSignalStruct(signalID, out uint index, input);
|
||||
if (count > 0) array[index].valueAsFloat = signal;
|
||||
return false;
|
||||
}
|
||||
|
||||
public float AddSignal(EGID blockID, float signal, out uint signalID, bool clamp = true, bool input = true)
|
||||
{
|
||||
signalID = GetSignalIDs(blockID, input)[0];
|
||||
return AddSignal(signalID, signal, clamp, input);
|
||||
}
|
||||
|
||||
public float AddSignal(uint signalID, float signal, bool clamp = true, bool input = true)
|
||||
{
|
||||
var (array, count) = GetSignalStruct(signalID, out uint index, input);
|
||||
if (count > 0)
|
||||
{
|
||||
ref var channelData = ref array[index];
|
||||
channelData.valueAsFloat += signal;
|
||||
if (clamp)
|
||||
{
|
||||
if (channelData.valueAsFloat > POSITIVE_HIGH)
|
||||
{
|
||||
channelData.valueAsFloat = POSITIVE_HIGH;
|
||||
}
|
||||
else if (channelData.valueAsFloat < NEGATIVE_HIGH)
|
||||
{
|
||||
channelData.valueAsFloat = NEGATIVE_HIGH;
|
||||
}
|
||||
|
||||
return channelData.valueAsFloat;
|
||||
}
|
||||
}
|
||||
|
||||
return signal;
|
||||
}
|
||||
|
||||
public float GetSignal(EGID blockID, out uint signalID, bool input = true)
|
||||
{
|
||||
signalID = GetSignalIDs(blockID, input)[0];
|
||||
return GetSignal(signalID, input);
|
||||
}
|
||||
|
||||
public float GetSignal(uint signalID, bool input = true)
|
||||
{
|
||||
var (array, count) = GetSignalStruct(signalID, out uint index, input);
|
||||
return count > 0 ? array[index].valueAsFloat : 0f;
|
||||
}
|
||||
|
||||
public uint[] GetSignalIDs(EGID blockID, bool input = true)
|
||||
{
|
||||
ref BlockPortsStruct bps = ref entitiesDB.QueryEntity<BlockPortsStruct>(blockID);
|
||||
uint[] signals;
|
||||
if (input) {
|
||||
signals = new uint[bps.inputCount];
|
||||
for (uint i = 0u; i < bps.inputCount; i++)
|
||||
{
|
||||
signals[i] = bps.firstInputID + i;
|
||||
}
|
||||
} else {
|
||||
signals = new uint[bps.outputCount];
|
||||
for (uint i = 0u; i < bps.outputCount; i++)
|
||||
{
|
||||
signals[i] = bps.firstOutputID + i;
|
||||
}
|
||||
}
|
||||
return signals;
|
||||
}
|
||||
|
||||
public EGID[] GetSignalInputs(EGID blockID)
|
||||
{
|
||||
BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(blockID);
|
||||
EGID[] inputs = new EGID[ports.inputCount];
|
||||
for (uint i = 0; i < ports.inputCount; i++)
|
||||
{
|
||||
inputs[i] = new EGID(i + ports.firstInputID, NamedExclusiveGroup<BuildModeWiresGroups.InputPortsGroup>.Group);
|
||||
}
|
||||
return inputs;
|
||||
}
|
||||
|
||||
public EGID[] GetSignalOutputs(EGID blockID)
|
||||
{
|
||||
BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(blockID);
|
||||
EGID[] outputs = new EGID[ports.outputCount];
|
||||
for (uint i = 0; i < ports.outputCount; i++)
|
||||
{
|
||||
outputs[i] = new EGID(i + ports.firstOutputID, NamedExclusiveGroup<BuildModeWiresGroups.OutputPortsGroup>.Group);
|
||||
}
|
||||
return outputs;
|
||||
}
|
||||
|
||||
public OptionalRef<PortEntityStruct> MatchBlockIOToPort(Block block, byte portUsage, bool output)
|
||||
{
|
||||
return MatchBlockIOToPort(block.Id, portUsage, output);
|
||||
}
|
||||
|
||||
public OptionalRef<PortEntityStruct> MatchBlockIOToPort(EGID block, byte portUsage, bool output)
|
||||
{
|
||||
if (!entitiesDB.Exists<BlockPortsStruct>(block))
|
||||
return default;
|
||||
var group = output
|
||||
? NamedExclusiveGroup<BuildModeWiresGroups.OutputPortsGroup>.Group
|
||||
: NamedExclusiveGroup<BuildModeWiresGroups.InputPortsGroup>.Group;
|
||||
BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(block);
|
||||
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, new EGID(entityID, group));
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
public OptionalRef<WireEntityStruct> MatchPortToWire(PortEntityStruct port, EGID blockID, out EGID wireID)
|
||||
{
|
||||
var (wires, ids, count) = entitiesDB.QueryEntities<WireEntityStruct>(NamedExclusiveGroup<BuildModeWiresGroups.WiresGroup>.Group);
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
if ((wires[i].destinationPortUsage == port.usage && wires[i].destinationBlockEGID == blockID)
|
||||
|| (wires[i].sourcePortUsage == port.usage && wires[i].sourceBlockEGID == blockID))
|
||||
{
|
||||
wireID = new EGID(ids[i], BuildModeWiresGroups.WiresGroup.Group);
|
||||
return new OptionalRef<WireEntityStruct>(wires, i);
|
||||
}
|
||||
}
|
||||
|
||||
wireID = default;
|
||||
return default;
|
||||
}
|
||||
|
||||
public EGID MatchBlocksToWire(EGID startBlock, EGID endBlock, byte startPort = byte.MaxValue, byte endPort = byte.MaxValue)
|
||||
{
|
||||
EGID[] startPorts;
|
||||
if (startPort == byte.MaxValue)
|
||||
{
|
||||
// search all output ports on source block
|
||||
startPorts = GetSignalOutputs(startBlock);
|
||||
}
|
||||
else
|
||||
{
|
||||
BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(startBlock);
|
||||
startPorts = new EGID[] {new EGID(ports.firstOutputID + startPort, NamedExclusiveGroup<BuildModeWiresGroups.OutputPortsGroup>.Group) };
|
||||
}
|
||||
|
||||
EGID[] endPorts;
|
||||
if (startPort == byte.MaxValue)
|
||||
{
|
||||
// search all input ports on destination block
|
||||
endPorts = GetSignalInputs(endBlock);
|
||||
}
|
||||
else
|
||||
{
|
||||
BlockPortsStruct ports = entitiesDB.QueryEntity<BlockPortsStruct>(endBlock);
|
||||
endPorts = new EGID[] {new EGID(ports.firstInputID + endPort, NamedExclusiveGroup<BuildModeWiresGroups.InputPortsGroup>.Group) };
|
||||
}
|
||||
|
||||
for (int endIndex = 0; endIndex < endPorts.Length; endIndex++)
|
||||
{
|
||||
PortEntityStruct endPES = entitiesDB.QueryEntity<PortEntityStruct>(endPorts[endIndex]);
|
||||
for (int startIndex = 0; startIndex < startPorts.Length; startIndex++)
|
||||
{
|
||||
PortEntityStruct startPES = entitiesDB.QueryEntity<PortEntityStruct>(startPorts[startIndex]);
|
||||
foreach (var wireOpt in entitiesDB.QueryEntitiesOptional<WireEntityStruct>(
|
||||
NamedExclusiveGroup<BuildModeWiresGroups.WiresGroup>.Group))
|
||||
{
|
||||
var wire = wireOpt.Get();
|
||||
if ((wire.destinationPortUsage == endPES.usage && wire.destinationBlockEGID == endBlock)
|
||||
&& (wire.sourcePortUsage == startPES.usage && wire.sourceBlockEGID == startBlock))
|
||||
{
|
||||
return wireOpt.EGID;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
public OptionalRef<ChannelDataStruct> GetChannelDataStruct(EGID portID)
|
||||
{
|
||||
var port = GetPort(portID);
|
||||
var (channels, count) = entitiesDB.QueryEntities<ChannelDataStruct>(NamedExclusiveGroup<BuildModeWiresGroups.ChannelDataGroup>.Group);
|
||||
return port.firstChannelIndexCachedInSim < count
|
||||
? new OptionalRef<ChannelDataStruct>(channels, port.firstChannelIndexCachedInSim)
|
||||
: default;
|
||||
}
|
||||
|
||||
public EGID[] GetElectricBlocks()
|
||||
{
|
||||
var res = new FasterList<EGID>();
|
||||
foreach (var ((coll, ids, count), _) in entitiesDB.QueryEntities<BlockPortsStruct>())
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
ref BlockPortsStruct s = ref coll[i];
|
||||
//res.Add(s.ID); - TODO: Would need to search for the groups for each block
|
||||
}
|
||||
}
|
||||
|
||||
return res.ToArray();
|
||||
}
|
||||
|
||||
public EGID[] WiredToInput(EGID block, byte port)
|
||||
{
|
||||
return entitiesDB
|
||||
.QueryEntitiesOptional<WireEntityStruct>(NamedExclusiveGroup<BuildModeWiresGroups.WiresGroup>.Group)
|
||||
.ToArray(wire => wire.ID,
|
||||
wire => wire.Component.destinationPortUsage == port && wire.Component.destinationBlockEGID == block);
|
||||
}
|
||||
|
||||
public EGID[] WiredToOutput(EGID block, byte port)
|
||||
{
|
||||
return entitiesDB
|
||||
.QueryEntitiesOptional<WireEntityStruct>(NamedExclusiveGroup<BuildModeWiresGroups.WiresGroup>.Group)
|
||||
.ToArray(wire => wire.ID,
|
||||
wire => wire.Component.sourcePortUsage == port && wire.Component.sourceBlockEGID == block);
|
||||
}
|
||||
|
||||
private EntityCollection<ChannelDataStruct> GetSignalStruct(uint signalID, out uint index, bool input = true)
|
||||
{
|
||||
ExclusiveGroup group = input
|
||||
? NamedExclusiveGroup<BuildModeWiresGroups.InputPortsGroup>.Group
|
||||
: NamedExclusiveGroup<BuildModeWiresGroups.OutputPortsGroup>.Group;
|
||||
if (entitiesDB.Exists<PortEntityStruct>(signalID, group))
|
||||
{
|
||||
index = entitiesDB.QueryEntity<PortEntityStruct>(signalID, group).firstChannelIndexCachedInSim;
|
||||
var channelData =
|
||||
entitiesDB.QueryEntities<ChannelDataStruct>(NamedExclusiveGroup<BuildModeWiresGroups.ChannelDataGroup>.Group);
|
||||
return channelData;
|
||||
}
|
||||
|
||||
index = 0;
|
||||
return default; //count: 0
|
||||
}
|
||||
}
|
||||
}
|
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))
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
71
TechbloxModdingAPI/Blocks/Motor.cs
Normal file
71
TechbloxModdingAPI/Blocks/Motor.cs
Normal file
|
@ -0,0 +1,71 @@
|
|||
namespace TechbloxModdingAPI.Blocks
|
||||
{
|
||||
using RobocraftX.Common;
|
||||
using Svelto.ECS;
|
||||
|
||||
|
||||
public class Motor : SignalingBlock
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a(n) Motor object representing an existing block.
|
||||
/// </summary>
|
||||
public Motor(EGID egid) :
|
||||
base(egid)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a(n) Motor object representing an existing block.
|
||||
/// </summary>
|
||||
public Motor(uint id) :
|
||||
base(new EGID(id, CommonExclusiveGroups.MOTOR_BLOCK_GROUP))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Motor's TopSpeed property. Tweakable stat.
|
||||
/// </summary>
|
||||
public float TopSpeed
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<RobocraftX.Blocks.MotorReadOnlyStruct>(this).maxVelocity;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<RobocraftX.Blocks.MotorReadOnlyStruct>(this).maxVelocity = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Motor's Torque property. Tweakable stat.
|
||||
/// </summary>
|
||||
public float Torque
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<RobocraftX.Blocks.MotorReadOnlyStruct>(this).maxForce;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<RobocraftX.Blocks.MotorReadOnlyStruct>(this).maxForce = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Motor's Reverse property. Tweakable stat.
|
||||
/// </summary>
|
||||
public bool Reverse
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<RobocraftX.Blocks.MotorReadOnlyStruct>(this).reverse;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<RobocraftX.Blocks.MotorReadOnlyStruct>(this).reverse = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
50
TechbloxModdingAPI/Blocks/ObjectID.cs
Normal file
50
TechbloxModdingAPI/Blocks/ObjectID.cs
Normal file
|
@ -0,0 +1,50 @@
|
|||
using System;
|
||||
using Techblox.ObjectIDBlockServer;
|
||||
|
||||
namespace TechbloxModdingAPI.Blocks
|
||||
{
|
||||
using Svelto.ECS;
|
||||
|
||||
|
||||
public class ObjectID : SignalingBlock
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a(n) ObjectID object representing an existing block.
|
||||
/// </summary>
|
||||
public ObjectID(EGID egid) :
|
||||
base(egid)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a(n) ObjectID object representing an existing block.
|
||||
/// </summary>
|
||||
public ObjectID(uint id) :
|
||||
base(new EGID(id, ObjectIDBlockExclusiveGroups.OBJECT_ID_BLOCK_GROUP))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the ObjectID's Identifier property. Tweakable stat.
|
||||
/// </summary>
|
||||
public char Identifier
|
||||
{
|
||||
get => (char) (BlockEngine.GetBlockInfo<ObjectIDTweakableComponent>(this).objectIDToTrigger + 'A');
|
||||
set
|
||||
{
|
||||
if(value is < 'A' or > 'Z')
|
||||
throw new ArgumentOutOfRangeException(nameof(value), "ObjectIdentifier must be set to a letter between A and Z.");
|
||||
BlockEngine.GetBlockInfo<ObjectIDTweakableComponent>(this).objectIDToTrigger = (byte) (value - 'A');
|
||||
Label = value + ""; //The label isn't updated automatically
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 ObjectID[] GetByID(char id) => BlockEngine.GetObjectIDsFromID((byte) (id - 'A'));
|
||||
}
|
||||
}
|
71
TechbloxModdingAPI/Blocks/Piston.cs
Normal file
71
TechbloxModdingAPI/Blocks/Piston.cs
Normal file
|
@ -0,0 +1,71 @@
|
|||
namespace TechbloxModdingAPI.Blocks
|
||||
{
|
||||
using RobocraftX.Common;
|
||||
using Svelto.ECS;
|
||||
|
||||
|
||||
public class Piston : SignalingBlock
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a(n) Piston object representing an existing block.
|
||||
/// </summary>
|
||||
public Piston(EGID egid) :
|
||||
base(egid)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a(n) Piston object representing an existing block.
|
||||
/// </summary>
|
||||
public Piston(uint id) :
|
||||
base(new EGID(id, CommonExclusiveGroups.PISTON_BLOCK_GROUP))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Piston's MaximumForce property. Tweakable stat.
|
||||
/// </summary>
|
||||
public float MaximumForce
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<RobocraftX.Blocks.PistonReadOnlyStruct>(this).pistonVelocity;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<RobocraftX.Blocks.PistonReadOnlyStruct>(this).pistonVelocity = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Piston's MaxExtension property. Tweakable stat.
|
||||
/// </summary>
|
||||
public float MaxExtension
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<RobocraftX.Blocks.PistonReadOnlyStruct>(this).maxDeviation;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<RobocraftX.Blocks.PistonReadOnlyStruct>(this).maxDeviation = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Piston's InputIsExtension property. Tweakable stat.
|
||||
/// </summary>
|
||||
public bool InputIsExtension
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<RobocraftX.Blocks.PistonReadOnlyStruct>(this).hasProportionalInput;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<RobocraftX.Blocks.PistonReadOnlyStruct>(this).hasProportionalInput = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
56
TechbloxModdingAPI/Blocks/Seat.cs
Normal file
56
TechbloxModdingAPI/Blocks/Seat.cs
Normal file
|
@ -0,0 +1,56 @@
|
|||
namespace TechbloxModdingAPI.Blocks
|
||||
{
|
||||
using RobocraftX.Common;
|
||||
using Svelto.ECS;
|
||||
|
||||
|
||||
public class Seat : SignalingBlock
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a(n) Seat object representing an existing block.
|
||||
/// </summary>
|
||||
public Seat(EGID egid) :
|
||||
base(egid)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a(n) Seat object representing an existing block.
|
||||
/// </summary>
|
||||
public Seat(uint id) :
|
||||
base(new EGID(id, RobocraftX.PilotSeat.SeatGroups.PILOTSEAT_BLOCK_BUILD_GROUP))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Seat's FollowCam property. Tweakable stat.
|
||||
/// </summary>
|
||||
public bool FollowCam
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<RobocraftX.PilotSeat.SeatFollowCamComponent>(this).followCam;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<RobocraftX.PilotSeat.SeatFollowCamComponent>(this).followCam = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Seat's CharacterColliderHeight property. May not be saved.
|
||||
/// </summary>
|
||||
public float CharacterColliderHeight
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<RobocraftX.PilotSeat.SeatReadOnlySettingsComponent>(this).characterColliderHeight;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<RobocraftX.PilotSeat.SeatReadOnlySettingsComponent>(this).characterColliderHeight = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
146
TechbloxModdingAPI/Blocks/Servo.cs
Normal file
146
TechbloxModdingAPI/Blocks/Servo.cs
Normal file
|
@ -0,0 +1,146 @@
|
|||
namespace TechbloxModdingAPI.Blocks
|
||||
{
|
||||
using RobocraftX.Common;
|
||||
using Svelto.ECS;
|
||||
|
||||
|
||||
public class Servo : SignalingBlock
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a(n) Servo object representing an existing block.
|
||||
/// </summary>
|
||||
public Servo(EGID egid) :
|
||||
base(egid)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a(n) Servo object representing an existing block.
|
||||
/// </summary>
|
||||
public Servo(uint id) :
|
||||
base(new EGID(id, CommonExclusiveGroups.SERVO_BLOCK_GROUP))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Servo's MaximumForce property. Tweakable stat.
|
||||
/// </summary>
|
||||
public float MaximumForce
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).servoVelocity;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).servoVelocity = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Servo's MinimumAngle property. Tweakable stat.
|
||||
/// </summary>
|
||||
public float MinimumAngle
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).minDeviation;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).minDeviation = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Servo's MaximumAngle property. Tweakable stat.
|
||||
/// </summary>
|
||||
public float MaximumAngle
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).maxDeviation;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).maxDeviation = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Servo's Reverse property. Tweakable stat.
|
||||
/// </summary>
|
||||
public bool Reverse
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).reverse;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).reverse = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Servo's InputIsAngle property. Tweakable stat.
|
||||
/// </summary>
|
||||
public bool InputIsAngle
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).hasProportionalInput;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).hasProportionalInput = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Servo's DirectionVector property. May not be saved.
|
||||
/// </summary>
|
||||
public Unity.Mathematics.float3 DirectionVector
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).directionVector;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).directionVector = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Servo's RotationAxis property. May not be saved.
|
||||
/// </summary>
|
||||
public Unity.Mathematics.float3 RotationAxis
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).rotationAxis;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).rotationAxis = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Servo's ForceAxis property. May not be saved.
|
||||
/// </summary>
|
||||
public Unity.Mathematics.float3 ForceAxis
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).forceAxis;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.ServoBlocksServer.ServoReadOnlyTweakableComponent>(this).forceAxis = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
165
TechbloxModdingAPI/Blocks/SignalingBlock.cs
Normal file
165
TechbloxModdingAPI/Blocks/SignalingBlock.cs
Normal file
|
@ -0,0 +1,165 @@
|
|||
using System;
|
||||
|
||||
using Gamecraft.Wires;
|
||||
using Svelto.ECS;
|
||||
using Unity.Mathematics;
|
||||
|
||||
using TechbloxModdingAPI;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
namespace TechbloxModdingAPI.Blocks
|
||||
{
|
||||
/// <summary>
|
||||
/// Common implementation for blocks that support wiring.
|
||||
/// </summary>
|
||||
public class SignalingBlock : Block
|
||||
{
|
||||
public SignalingBlock(EGID id) : base(id)
|
||||
{
|
||||
}
|
||||
|
||||
public SignalingBlock(uint id) : base(id)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates the input port identifiers.
|
||||
/// </summary>
|
||||
/// <returns>The input identifiers.</returns>
|
||||
protected EGID[] GetInputIds()
|
||||
{
|
||||
return SignalEngine.GetSignalInputs(Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates the output port identifiers.
|
||||
/// </summary>
|
||||
/// <returns>The output identifiers.</returns>
|
||||
protected EGID[] GetOutputIds()
|
||||
{
|
||||
return SignalEngine.GetSignalOutputs(Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the connected wire.
|
||||
/// </summary>
|
||||
/// <returns>The connected wire.</returns>
|
||||
/// <param name="portId">Port identifier.</param>
|
||||
/// <param name="connected">Whether the port has a wire connected to it.</param>
|
||||
protected OptionalRef<WireEntityStruct> GetConnectedWire(PortEntityStruct port, out EGID egid)
|
||||
{
|
||||
return SignalEngine.MatchPortToWire(port, Id, out egid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// [EXPERIMENTAL] Gets the channel data.
|
||||
/// </summary>
|
||||
/// <returns>The channel data.</returns>
|
||||
/// <param name="portId">Port identifier.</param>
|
||||
protected OptionalRef<ChannelDataStruct> GetChannelData(EGID portId)
|
||||
{
|
||||
return SignalEngine.GetChannelDataStruct(portId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The input port count.
|
||||
/// </summary>
|
||||
public uint InputCount
|
||||
{
|
||||
get => BlockEngine.GetBlockInfo<BlockPortsStruct>(this).inputCount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The output port count.
|
||||
/// </summary>
|
||||
public uint OutputCount
|
||||
{
|
||||
get => BlockEngine.GetBlockInfo<BlockPortsStruct>(this).outputCount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Connect an output on this block to an input on another block.
|
||||
/// </summary>
|
||||
/// <param name="sourcePort">Output port number.</param>
|
||||
/// <param name="destination">Input block.</param>
|
||||
/// <param name="destinationPort">Input port number.</param>
|
||||
/// <returns>The wire connection</returns>
|
||||
/// <exception cref="WiringException">The wire could not be created.</exception>
|
||||
public Wire Connect(byte sourcePort, SignalingBlock destination, byte destinationPort)
|
||||
{
|
||||
if (sourcePort >= OutputCount)
|
||||
{
|
||||
throw new WiringException("Source port does not exist");
|
||||
}
|
||||
|
||||
if (destinationPort >= destination.InputCount)
|
||||
{
|
||||
throw new WiringException("Destination port does not exist");
|
||||
}
|
||||
return Wire.Connect(this, sourcePort, destination, destinationPort);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The port's name.
|
||||
/// This is localized to the user's language, so this is not reliable for port identification.
|
||||
/// </summary>
|
||||
/// <param name="port">Port number.</param>
|
||||
/// <param name="input">Whether the port is an input (true) or an output (false).</param>
|
||||
/// <returns>The localized port name.</returns>
|
||||
public string PortName(byte port, bool input)
|
||||
{
|
||||
PortEntityStruct pes = SignalEngine.GetPortByOffset(this, port, input);
|
||||
return pes.portNameLocalised;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The input port's name.
|
||||
/// </summary>
|
||||
/// <param name="port">Input port number.</param>
|
||||
/// <returns>The port name, localized to the user's language.</returns>
|
||||
public string InputPortName(byte port) => PortName(port, true);
|
||||
|
||||
/// <summary>
|
||||
/// The output port's name.
|
||||
/// </summary>
|
||||
/// <param name="port">Output port number.</param>
|
||||
/// <returns>The port name, localized to the user's language.</returns>
|
||||
public string OutputPortName(byte port) => PortName(port, false);
|
||||
|
||||
/// <summary>
|
||||
/// All wires connected to the input port.
|
||||
/// These wires will always be wired output -> input.
|
||||
/// </summary>
|
||||
/// <param name="port">Port number.</param>
|
||||
/// <returns>Wires connected to the input port.</returns>
|
||||
public Wire[] ConnectedToInput(byte port)
|
||||
{
|
||||
if (port >= InputCount) throw new WiringException($"Port input {port} does not exist");
|
||||
EGID[] wireEgids = SignalEngine.WiredToInput(Id, port);
|
||||
Wire[] wires = new Wire[wireEgids.Length];
|
||||
for (uint i = 0; i < wireEgids.Length; i++)
|
||||
{
|
||||
wires[i] = new Wire(wireEgids[i]);
|
||||
}
|
||||
return wires;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// All wires connected to the output port.
|
||||
/// These wires will always be wired output -> input.
|
||||
/// </summary>
|
||||
/// <param name="port">Port number.</param>
|
||||
/// <returns>Wires connected to the output port.</returns>
|
||||
public Wire[] ConnectedToOutput(byte port)
|
||||
{
|
||||
if (port >= OutputCount) throw new WiringException($"Port output {port} does not exist");
|
||||
EGID[] wireEgids = SignalEngine.WiredToOutput(Id, port);
|
||||
Wire[] wires = new Wire[wireEgids.Length];
|
||||
for (uint i = 0; i < wireEgids.Length; i++)
|
||||
{
|
||||
wires[i] = new Wire(wireEgids[i]);
|
||||
}
|
||||
return wires;
|
||||
}
|
||||
}
|
||||
}
|
137
TechbloxModdingAPI/Blocks/WheelRig.cs
Normal file
137
TechbloxModdingAPI/Blocks/WheelRig.cs
Normal file
|
@ -0,0 +1,137 @@
|
|||
using TechbloxModdingAPI.Tests;
|
||||
|
||||
namespace TechbloxModdingAPI.Blocks
|
||||
{
|
||||
using RobocraftX.Common;
|
||||
using Svelto.ECS;
|
||||
|
||||
|
||||
public class WheelRig : SignalingBlock
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a(n) WheelRig object representing an existing block.
|
||||
/// </summary>
|
||||
public WheelRig(EGID egid) :
|
||||
base(egid)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a(n) WheelRig object representing an existing block.
|
||||
/// </summary>
|
||||
public WheelRig(uint id) :
|
||||
base(new EGID(id, CommonExclusiveGroups.WHEELRIG_BLOCK_BUILD_GROUP))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the WheelRig's BrakingStrength property. Tweakable stat.
|
||||
/// </summary>
|
||||
public float BrakingStrength
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.WheelRigBlock.WheelRigTweakableStruct>(this).brakingStrength;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.WheelRigBlock.WheelRigTweakableStruct>(this).brakingStrength = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the WheelRig's FlipDirection property. Tweakable stat.
|
||||
/// </summary>
|
||||
public bool FlipDirection
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.WheelRigBlock.WheelRigTweakableStruct>(this).flipDirection;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.WheelRigBlock.WheelRigTweakableStruct>(this).flipDirection = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the WheelRig's MaxVelocity property. May not be saved.
|
||||
/// </summary>
|
||||
public float MaxVelocity
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.WheelRigBlock.WheelRigReadOnlyStruct>(this).maxVelocity;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.WheelRigBlock.WheelRigReadOnlyStruct>(this).maxVelocity = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the WheelRig's SteerAngle property. Tweakable stat.
|
||||
/// </summary>
|
||||
[TestValue(0f)] // Can be 0 for no steer variant
|
||||
public float SteerAngle
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.WheelRigBlock.WheelRigSteerableTweakableStruct>(this).steerAngle;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.WheelRigBlock.WheelRigSteerableTweakableStruct>(this).steerAngle = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the WheelRig's FlipSteering property. Tweakable stat.
|
||||
/// </summary>
|
||||
[TestValue(false)]
|
||||
public bool FlipSteering
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.WheelRigBlock.WheelRigSteerableTweakableStruct>(this).flipSteering;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.WheelRigBlock.WheelRigSteerableTweakableStruct>(this).flipSteering = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the WheelRig's VelocityForMinAngle property. May not be saved.
|
||||
/// </summary>
|
||||
[TestValue(0f)]
|
||||
public float VelocityForMinAngle
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.WheelRigBlock.WheelRigSteerableReadOnlyStruct>(this).velocityForMinAngle;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.WheelRigBlock.WheelRigSteerableReadOnlyStruct>(this).velocityForMinAngle = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the WheelRig's MinSteerAngleFactor property. May not be saved.
|
||||
/// </summary>
|
||||
[TestValue(0f)]
|
||||
public float MinSteerAngleFactor
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockEngine.GetBlockInfo<Techblox.WheelRigBlock.WheelRigSteerableReadOnlyStruct>(this).minSteerAngleFactor;
|
||||
}
|
||||
set
|
||||
{
|
||||
BlockEngine.GetBlockInfo<Techblox.WheelRigBlock.WheelRigSteerableReadOnlyStruct>(this).minSteerAngleFactor = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
313
TechbloxModdingAPI/Blocks/Wire.cs
Normal file
313
TechbloxModdingAPI/Blocks/Wire.cs
Normal file
|
@ -0,0 +1,313 @@
|
|||
using System;
|
||||
|
||||
using Gamecraft.Wires;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.Experimental;
|
||||
|
||||
using TechbloxModdingAPI.Blocks.Engines;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
namespace TechbloxModdingAPI.Blocks
|
||||
{
|
||||
public class Wire : EcsObjectBase
|
||||
{
|
||||
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)
|
||||
{
|
||||
var (wire, id) = signalEngine.CreateNewWire(start.Id, startPort, end.Id, endPort);
|
||||
return new Wire(wire, start, end, id);
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
var port = signalEngine.MatchBlockIOToPort(end, endPort, false);
|
||||
if (!port) return null;
|
||||
var wire = signalEngine.MatchPortToWire(port, end.Id, out var egid);
|
||||
return wire
|
||||
? new Wire(wire.Get().sourceBlockEGID, end.Id, wire.Get().sourcePortUsage, endPort, egid, false)
|
||||
: 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)
|
||||
{
|
||||
var port = signalEngine.MatchBlockIOToPort(start, startPort, true);
|
||||
if (!port) return null;
|
||||
var wire = signalEngine.MatchPortToWire(port, start.Id, out var egid);
|
||||
return wire
|
||||
? new Wire(start.Id, wire.Get().destinationBlockEGID, startPort, wire.Get().destinationPortUsage, egid, false)
|
||||
: null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a wire object froam n 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) : base(ecs =>
|
||||
{
|
||||
var th = (Wire)ecs;
|
||||
th.startBlockEGID = start.Id;
|
||||
th.endBlockEGID = end.Id;
|
||||
bool flipped = false;
|
||||
// find block ports
|
||||
EGID wire = signalEngine.MatchBlocksToWire(start.Id, end.Id, startPort, endPort);
|
||||
if (wire == default)
|
||||
{
|
||||
// flip I/O around and try again
|
||||
wire = signalEngine.MatchBlocksToWire(end.Id, start.Id, endPort, startPort);
|
||||
flipped = true;
|
||||
// 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
|
||||
}
|
||||
|
||||
if (wire != default)
|
||||
{
|
||||
th.Construct(start.Id, end.Id, startPort, endPort, wire, flipped);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new WireInvalidException("Wire not found");
|
||||
}
|
||||
|
||||
return th.wireEGID;
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
/// <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(start.Id, end.Id, startPort, endPort, wire, inputToOutput)
|
||||
{
|
||||
}
|
||||
|
||||
private Wire(EGID startBlock, EGID endBlock, byte startPort, byte endPort, EGID wire, bool inputToOutput) : base(wire)
|
||||
{
|
||||
Construct(startBlock, endBlock, startPort, endPort, wire, inputToOutput);
|
||||
}
|
||||
|
||||
private void Construct(EGID startBlock, EGID endBlock, byte startPort, byte endPort, EGID wire, bool inputToOutput)
|
||||
{
|
||||
this.startBlockEGID = startBlock;
|
||||
this.endBlockEGID = endBlock;
|
||||
this.inputToOutput = inputToOutput;
|
||||
this.wireEGID = wire;
|
||||
endPortEGID = signalEngine.MatchBlockIOToPort(startBlock, startPort, inputToOutput).EGID;
|
||||
if (endPortEGID == default) throw new WireInvalidException("Wire end port not found");
|
||||
startPortEGID = signalEngine.MatchBlockIOToPort(endBlock, endPort, !inputToOutput).EGID;
|
||||
if (startPortEGID == default) 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) : base(wireEgid)
|
||||
{
|
||||
WireEntityStruct wire = signalEngine.GetWire(wireEGID);
|
||||
Construct(wire.sourceBlockEGID, wire.destinationBlockEGID, wire.sourcePortUsage, wire.destinationPortUsage,
|
||||
wireEgid, false);
|
||||
}
|
||||
|
||||
private Wire(WireEntityStruct wire, SignalingBlock src, SignalingBlock dest, EGID wireEgid)
|
||||
: this(src, dest, wire.sourcePortUsage, wire.destinationPortUsage, wireEgid, false)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The wire's signal value, as a float.
|
||||
/// </summary>
|
||||
public float Float
|
||||
{
|
||||
get
|
||||
{
|
||||
return signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsFloat;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsFloat = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The wire's string signal.
|
||||
/// </summary>
|
||||
public string String
|
||||
{
|
||||
get
|
||||
{
|
||||
return signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsEcsString;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsEcsString.Set(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The wire's raw string signal.
|
||||
/// </summary>
|
||||
public ECSString ECSString
|
||||
{
|
||||
get
|
||||
{
|
||||
return signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsEcsString;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsEcsString = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The wire's signal id.
|
||||
/// I'm 50% sure this is useless.
|
||||
/// </summary>
|
||||
public uint SignalId
|
||||
{
|
||||
get
|
||||
{
|
||||
return signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsID;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsID = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The block at the beginning of the wire.
|
||||
/// </summary>
|
||||
public SignalingBlock Start
|
||||
{
|
||||
get => (SignalingBlock)Block.New(startBlockEGID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The port number that the beginning of the wire connects to.
|
||||
/// </summary>
|
||||
public byte StartPort
|
||||
{
|
||||
get => startPort;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The display name of the start port.
|
||||
/// </summary>
|
||||
public string StartPortName
|
||||
{
|
||||
get => signalEngine.GetPort(startPortEGID).portNameLocalised;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The block at the end of the wire.
|
||||
/// </summary>
|
||||
public SignalingBlock End
|
||||
{
|
||||
get => (SignalingBlock)Block.New(endBlockEGID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The port number that the end of the wire connects to.
|
||||
/// </summary>
|
||||
public byte EndPort
|
||||
{
|
||||
get => endPort;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The display name of the end port.
|
||||
/// </summary>
|
||||
public string EndPortName
|
||||
{
|
||||
get => signalEngine.GetPort(endPortEGID).portNameLocalised;
|
||||
}
|
||||
|
||||
/// <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 GetInstance(wireEGID, egid => new Wire(egid));
|
||||
}
|
||||
|
||||
/// <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
|
||||
(endBlockEGID, startBlockEGID) = (startBlockEGID, endBlockEGID);
|
||||
var tempPort = endPortEGID;
|
||||
endPortEGID = startPortEGID;
|
||||
startPortEGID = tempPort;
|
||||
(endPort, startPort) = (startPort, endPort);
|
||||
}
|
||||
}
|
||||
|
||||
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 {(StartPort != byte.MaxValue ? Start.PortName(StartPort, inputToOutput) : "")}) -> ({End.Type}::{EndPort} aka {(EndPort != byte.MaxValue ? 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() { }
|
||||
}
|
||||
}
|
102
TechbloxModdingAPI/Blueprint.cs
Normal file
102
TechbloxModdingAPI/Blueprint.cs
Normal file
|
@ -0,0 +1,102 @@
|
|||
using System;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
|
||||
namespace TechbloxModdingAPI
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a blueprint in the inventory. When placed it becomes a block group.
|
||||
/// </summary>
|
||||
public class Blueprint : IDisposable
|
||||
{
|
||||
public uint Id { get; }
|
||||
|
||||
internal Blueprint(uint id)
|
||||
{
|
||||
Id = id;
|
||||
BlockGroup._engine.InitBlueprint(id);
|
||||
Refresh();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The center of the blueprint. Can only be set using the StoreBlocks() method.
|
||||
/// </summary>
|
||||
public float3 Position { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The rotation of the blueprint. Can only be set using the StoreBlocks() method.
|
||||
/// </summary>
|
||||
public float3 Rotation { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The amount of blocks in the blueprint. Gan only be set using the StoreBlocks() method.
|
||||
/// </summary>
|
||||
public uint BlockCount { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new, empty blueprint. It will be deleted on disposal unless the game holds a reference to it.
|
||||
/// </summary>
|
||||
/// <returns>A blueprint that doesn't have any blocks</returns>
|
||||
public static Blueprint Create()
|
||||
{
|
||||
return new Blueprint(BlockGroup._engine.CreateBlueprint());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the blocks that the blueprint contains.
|
||||
/// Use the BlockGroup overload for automatically calculated position and rotation.
|
||||
/// </summary>
|
||||
/// <param name="blocks">The array of blocks to use</param>
|
||||
/// <param name="position">The anchor (center) position of the blueprint</param>
|
||||
/// <param name="rotation">The base rotation of the blueprint</param>
|
||||
public void StoreBlocks(Block[] blocks, float3 position, float3 rotation)
|
||||
{
|
||||
BlockGroup._engine.ReplaceBlueprint(Player.LocalPlayer.Id, Id, blocks, position,
|
||||
quaternion.Euler(rotation));
|
||||
Refresh();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Store the blocks from the given group in the blueprint with correct position and rotation for the blueprint.
|
||||
/// </summary>
|
||||
/// <param name="group">The block group to store</param>
|
||||
public void StoreBlocks(BlockGroup group)
|
||||
{
|
||||
BlockGroup._engine.ReplaceBlueprint(Player.LocalPlayer.Id, Id, group, group.Position,
|
||||
Quaternion.Euler(group.Rotation));
|
||||
Refresh();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Places the blocks the blueprint contains at the specified position and rotation.
|
||||
/// </summary>
|
||||
/// <param name="position">The position of the blueprint</param>
|
||||
/// <param name="rotation">The rotation of the blueprint</param>
|
||||
/// <returns>An array of the placed blocks</returns>
|
||||
public Block[] PlaceBlocks(float3 position, float3 rotation)
|
||||
{
|
||||
return BlockGroup._engine.PlaceBlueprintBlocks(Id, Player.LocalPlayer.Id, position, rotation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the properties based on the blueprint data. Only necessary if the blueprint is changed from the game.
|
||||
/// </summary>
|
||||
public void Refresh()
|
||||
{
|
||||
BlockGroup._engine.GetBlueprintInfo(Id, out var pos, out var rot, out uint count);
|
||||
Position = pos;
|
||||
Rotation = ((Quaternion) rot).eulerAngles;
|
||||
BlockCount = count;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
BlockGroup._engine.DisposeBlueprint(Id);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{nameof(Id)}: {Id}, {nameof(Position)}: {Position}, {nameof(Rotation)}: {Rotation}, {nameof(BlockCount)}: {BlockCount}";
|
||||
}
|
||||
}
|
||||
}
|
75
TechbloxModdingAPI/Cluster.cs
Normal file
75
TechbloxModdingAPI/Cluster.cs
Normal file
|
@ -0,0 +1,75 @@
|
|||
using Svelto.ECS;
|
||||
using Techblox.TimeRunning.Clusters;
|
||||
|
||||
namespace TechbloxModdingAPI
|
||||
{
|
||||
/// <summary>
|
||||
/// Represnts a cluster of blocks in time running mode, meaning blocks that are connected either directly or via joints.
|
||||
/// Only exists if a cluster destruction manager is present. Static blocks like grass and dirt aren't part of a cluster.
|
||||
/// </summary>
|
||||
public class Cluster : EcsObjectBase
|
||||
{
|
||||
public Cluster(EGID id) : base(id)
|
||||
{
|
||||
}
|
||||
|
||||
public Cluster(uint id) : this(new EGID(id, ClustersExclusiveGroups.SIMULATION_CLUSTERS_GROUP))
|
||||
{
|
||||
}
|
||||
|
||||
public float InitialHealth //TODO
|
||||
{
|
||||
get => 0f;
|
||||
set { }
|
||||
}
|
||||
|
||||
public float CurrentHealth
|
||||
{
|
||||
get => 0f;
|
||||
set { }
|
||||
}
|
||||
|
||||
public float HealthMultiplier
|
||||
{
|
||||
get => 0f;
|
||||
set { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The mass of the cluster.
|
||||
/// </summary>
|
||||
public float Mass => Block.BlockEngine.GetBlockInfo<ClusterMassComponent>(this).mass;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the simulation-time rigid bodies for the chunks in this cluster.
|
||||
/// </summary>
|
||||
/// <returns>An array of sim-bodies</returns>
|
||||
public SimBody[] GetSimBodies()
|
||||
{
|
||||
return Block.BlockEngine.GetClusterBodies(Id.entityID);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{nameof(Id)}: {Id}";
|
||||
}
|
||||
|
||||
protected bool Equals(Cluster other)
|
||||
{
|
||||
return Id.Equals(other.Id);
|
||||
}
|
||||
|
||||
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((Cluster) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Id.GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
276
TechbloxModdingAPI/Commands/CommandBuilder.cs
Normal file
276
TechbloxModdingAPI/Commands/CommandBuilder.cs
Normal file
|
@ -0,0 +1,276 @@
|
|||
using System;
|
||||
|
||||
using Svelto.ECS;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
namespace TechbloxModdingAPI.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom Command builder.
|
||||
/// </summary>
|
||||
public class CommandBuilder
|
||||
{
|
||||
private string name;
|
||||
|
||||
private string description;
|
||||
|
||||
private short parameterCount;
|
||||
|
||||
private ICustomCommandEngine commandEngine;
|
||||
|
||||
private bool fromExisting = false;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new command builder.
|
||||
/// </summary>
|
||||
public CommandBuilder()
|
||||
{
|
||||
name = "";
|
||||
description = null;
|
||||
parameterCount = -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create and return a command builder.
|
||||
/// </summary>
|
||||
/// <returns>The builder.</returns>
|
||||
public static CommandBuilder Builder()
|
||||
{
|
||||
return new CommandBuilder();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new command builder.
|
||||
/// </summary>
|
||||
/// <param name="name">The command name.</param>
|
||||
/// <param name="description">The command description (shown in help).</param>
|
||||
public CommandBuilder(string name, string description = null)
|
||||
{
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
parameterCount = -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create and return a command builder.
|
||||
/// If name and description are provided, this is equivalent to <code>Builder().Name(name).Description(description)</code>
|
||||
/// </summary>
|
||||
/// <param name="name">The command name.</param>
|
||||
/// <param name="description">The command description (shown in help).</param>
|
||||
/// <returns>The builder.</returns>
|
||||
public static CommandBuilder Builder(string name, string description = null)
|
||||
{
|
||||
return new CommandBuilder(name, description);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Name the command.
|
||||
/// </summary>
|
||||
/// <returns>The builder.</returns>
|
||||
/// <param name="name">The command name.</param>
|
||||
public CommandBuilder Name(string name)
|
||||
{
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Describe the command.
|
||||
/// </summary>
|
||||
/// <returns>The builder.</returns>
|
||||
/// <param name="description">The command description (shown in help).</param>
|
||||
public CommandBuilder Description(string description)
|
||||
{
|
||||
this.description = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the action the command performs.
|
||||
/// </summary>
|
||||
/// <returns>The builder.</returns>
|
||||
/// <param name="action">The action to perform when the command is called.</param>
|
||||
public CommandBuilder Action(Action action)
|
||||
{
|
||||
parameterCount = 0;
|
||||
commandEngine = new SimpleCustomCommandEngine(action, name, description);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the action the command performs.
|
||||
/// </summary>
|
||||
/// <returns>The builder.</returns>
|
||||
/// <param name="action">The action to perform when the command is called.</param>
|
||||
/// <typeparam name="A">The 1st parameter's type.</typeparam>
|
||||
public CommandBuilder Action<A>(Action<A> action)
|
||||
{
|
||||
parameterCount = 1;
|
||||
commandEngine = new SimpleCustomCommandEngine<A>(action, name, description);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the action the command performs.
|
||||
/// </summary>
|
||||
/// <returns>The builder.</returns>
|
||||
/// <param name="action">The action to perform when the command is called.</param>
|
||||
/// <typeparam name="A">The 1st parameter's type.</typeparam>
|
||||
/// <typeparam name="B">The 2nd parameter's type.</typeparam>
|
||||
public CommandBuilder Action<A,B>(Action<A,B> action)
|
||||
{
|
||||
parameterCount = 2;
|
||||
commandEngine = new SimpleCustomCommandEngine<A,B>(action, name, description);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the action the command performs.
|
||||
/// </summary>
|
||||
/// <returns>The builder.</returns>
|
||||
/// <param name="action">The action to perform when the command is called.</param>
|
||||
/// <typeparam name="A">The 1st parameter's type.</typeparam>
|
||||
/// <typeparam name="B">The 2nd parameter's type.</typeparam>
|
||||
/// <typeparam name="C">The 3rd parameter's type.</typeparam>
|
||||
public CommandBuilder Action<A,B,C>(Action<A,B,C> action)
|
||||
{
|
||||
parameterCount = 3;
|
||||
commandEngine = new SimpleCustomCommandEngine<A,B,C>(action, name, description);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Build the command from an existing command.
|
||||
/// </summary>
|
||||
/// <returns>The command. Use Invoke() to execute it.</returns>
|
||||
public SimpleCustomCommandEngine FromExisting()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
throw new CommandParameterMissingException("Command name must be defined before FromExisting() is called");
|
||||
}
|
||||
if (!ExistingCommands.Exists(name))
|
||||
{
|
||||
throw new CommandNotFoundException("Command cannot be built from existing because it does not exist.");
|
||||
}
|
||||
fromExisting = true;
|
||||
return new SimpleCustomCommandEngine(
|
||||
() => { ExistingCommands.Call(name); },
|
||||
name,
|
||||
description);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Build the command from an existing command.
|
||||
/// </summary>
|
||||
/// <returns>The command. Use Invoke() to execute it.</returns>
|
||||
/// <typeparam name="A">The 1st parameter's type.</typeparam>
|
||||
public SimpleCustomCommandEngine<A> FromExisting<A>()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
throw new CommandParameterMissingException("Command name must be defined before FromExisting() is called");
|
||||
}
|
||||
if (!ExistingCommands.Exists(name))
|
||||
{
|
||||
throw new CommandNotFoundException("Command cannot be built from existing because it does not exist.");
|
||||
}
|
||||
fromExisting = true;
|
||||
return new SimpleCustomCommandEngine<A>(
|
||||
(A a) => { ExistingCommands.Call<A>(name, a); },
|
||||
name,
|
||||
description);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Build the command from an existing command.
|
||||
/// </summary>
|
||||
/// <returns>The command. Use Invoke() to execute it.</returns>
|
||||
/// <typeparam name="A">The 1st parameter's type.</typeparam>
|
||||
/// <typeparam name="B">The 2nd parameter's type.</typeparam>
|
||||
public SimpleCustomCommandEngine<A,B> FromExisting<A,B>()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
throw new CommandParameterMissingException("Command name must be defined before FromExisting() is called");
|
||||
}
|
||||
if (!ExistingCommands.Exists(name))
|
||||
{
|
||||
throw new CommandNotFoundException("Command cannot be built from existing because it does not exist.");
|
||||
}
|
||||
fromExisting = true;
|
||||
return new SimpleCustomCommandEngine<A,B>(
|
||||
(A a, B b) => { ExistingCommands.Call<A,B>(name, a, b); },
|
||||
name,
|
||||
description);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Build the command from an existing command.
|
||||
/// </summary>
|
||||
/// <returns>The command. Use Invoke() to execute it.</returns>
|
||||
/// <typeparam name="A">The 1st parameter's type.</typeparam>
|
||||
/// <typeparam name="B">The 2nd parameter's type.</typeparam>
|
||||
/// <typeparam name="C">The 3rd parameter's type.</typeparam>
|
||||
public SimpleCustomCommandEngine<A,B,C> FromExisting<A,B,C>()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
throw new CommandParameterMissingException("Command name must be defined before FromExisting() is called");
|
||||
}
|
||||
if (!ExistingCommands.Exists(name))
|
||||
{
|
||||
throw new CommandNotFoundException("Command cannot be built from existing because it does not exist.");
|
||||
}
|
||||
fromExisting = true;
|
||||
return new SimpleCustomCommandEngine<A,B,C>(
|
||||
(A a, B b, C c) => { ExistingCommands.Call<A,B,C>(name, a, b, c); },
|
||||
name,
|
||||
description);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Build the command.
|
||||
/// </summary>
|
||||
/// <returns>The built command.</returns>
|
||||
/// <param name="register">Automatically register the command with CommandManager.AddCommand()?</param>
|
||||
public ICustomCommandEngine Build(bool register = true)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
throw new CommandParameterMissingException("Command name must be defined before Build() is called");
|
||||
}
|
||||
if (fromExisting)
|
||||
{
|
||||
throw new CommandAlreadyBuiltException("Command was already built by FromExisting()");
|
||||
}
|
||||
if (commandEngine == null)
|
||||
{
|
||||
throw new CommandParameterMissingException("Command action must be defined before Build() is called");
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(description))
|
||||
{
|
||||
Logging.LogWarning($"Command {FullName()} was built without a description");
|
||||
}
|
||||
if (register)
|
||||
{
|
||||
CommandManager.AddCommand(commandEngine);
|
||||
Logging.MetaDebugLog($"Command {FullName()} was automatically registered");
|
||||
}
|
||||
return commandEngine;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the full command name, in the form [name]::[description]::[# of parameters]
|
||||
/// </summary>
|
||||
/// <returns>The name.</returns>
|
||||
public string FullName()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(description))
|
||||
{
|
||||
return name + "::" + parameterCount;
|
||||
}
|
||||
return name + "::" + description + "::" + parameterCount;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GamecraftModdingAPI.Commands
|
||||
namespace TechbloxModdingAPI.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// UNIMPLEMENTED!
|
71
TechbloxModdingAPI/Commands/CommandExceptions.cs
Normal file
71
TechbloxModdingAPI/Commands/CommandExceptions.cs
Normal file
|
@ -0,0 +1,71 @@
|
|||
using System;
|
||||
namespace TechbloxModdingAPI.Commands
|
||||
{
|
||||
public class CommandException : TechbloxModdingAPIException
|
||||
{
|
||||
public CommandException() : base() {}
|
||||
|
||||
public CommandException(string msg) : base(msg) {}
|
||||
|
||||
public CommandException(string msg, Exception innerException) : base(msg, innerException) {}
|
||||
}
|
||||
|
||||
public class CommandNotFoundException : CommandException
|
||||
{
|
||||
public CommandNotFoundException()
|
||||
{
|
||||
}
|
||||
|
||||
public CommandNotFoundException(string msg) : base(msg)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class CommandAlreadyExistsException : CommandException
|
||||
{
|
||||
public CommandAlreadyExistsException()
|
||||
{
|
||||
}
|
||||
|
||||
public CommandAlreadyExistsException(string msg) : base(msg)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class CommandRuntimeException : CommandException
|
||||
{
|
||||
public CommandRuntimeException()
|
||||
{
|
||||
}
|
||||
|
||||
public CommandRuntimeException(string msg) : base(msg)
|
||||
{
|
||||
}
|
||||
|
||||
public CommandRuntimeException(string msg, Exception innerException) : base(msg, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class CommandAlreadyBuiltException : CommandException
|
||||
{
|
||||
public CommandAlreadyBuiltException()
|
||||
{
|
||||
}
|
||||
|
||||
public CommandAlreadyBuiltException(string msg) : base(msg)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class CommandParameterMissingException : CommandException
|
||||
{
|
||||
public CommandParameterMissingException()
|
||||
{
|
||||
}
|
||||
|
||||
public CommandParameterMissingException(string msg) : base(msg)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,10 +5,9 @@ using System.Text;
|
|||
using System.Threading.Tasks;
|
||||
|
||||
using Svelto.ECS;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
using GamecraftModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftModdingAPI.Commands
|
||||
namespace TechbloxModdingAPI.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Keeps track of custom commands
|
||||
|
@ -22,6 +21,10 @@ namespace GamecraftModdingAPI.Commands
|
|||
|
||||
public static void AddCommand(ICustomCommandEngine engine)
|
||||
{
|
||||
if (ExistsCommand(engine))
|
||||
{
|
||||
throw new CommandAlreadyExistsException($"Command {engine.Name} already exists");
|
||||
}
|
||||
_customCommands[engine.Name] = engine;
|
||||
if (_lastEngineRoot != null)
|
||||
{
|
|
@ -4,22 +4,17 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using uREPL;
|
||||
using RobocraftX.CommandLine.Custom;
|
||||
|
||||
namespace GamecraftModdingAPI.Commands
|
||||
namespace TechbloxModdingAPI.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Convenient methods for registering commands to Gamecraft.
|
||||
/// Convenient methods for registering commands to Techblox.
|
||||
/// All methods register to the command line and console block by default.
|
||||
/// </summary>
|
||||
public static class CommandRegistrationHelper
|
||||
{
|
||||
public static void Register(string name, Action action, string desc, bool noConsole = false)
|
||||
{
|
||||
RuntimeCommands.Register(name, action, desc);
|
||||
if (noConsole) { return; }
|
||||
ConsoleCommands.Register(name, action, desc);
|
||||
CustomCommands.Register(name, action, desc);
|
||||
}
|
||||
|
||||
public static void Register(string name, Action<object> action, string desc, bool noConsole = false)
|
||||
|
@ -39,50 +34,42 @@ namespace GamecraftModdingAPI.Commands
|
|||
|
||||
public static void Register<Param0>(string name, Action<Param0> action, string desc, bool noConsole = false)
|
||||
{
|
||||
RuntimeCommands.Register<Param0>(name, action, desc);
|
||||
if (noConsole) { return; }
|
||||
ConsoleCommands.Register<Param0>(name, action, desc);
|
||||
CustomCommands.Register(name, action, desc);
|
||||
}
|
||||
|
||||
public static void Register<Param0, Param1>(string name, Action<Param0, Param1> action, string desc, bool noConsole = false)
|
||||
{
|
||||
RuntimeCommands.Register<Param0, Param1>(name, action, desc);
|
||||
if (noConsole) { return; }
|
||||
ConsoleCommands.Register<Param0, Param1>(name, action, desc);
|
||||
CustomCommands.Register(name, action, desc);
|
||||
}
|
||||
|
||||
public static void Register<Param0, Param1, Param2>(string name, Action<Param0, Param1, Param2> action, string desc, bool noConsole = false)
|
||||
{
|
||||
RuntimeCommands.Register<Param0, Param1, Param2>(name, action, desc);
|
||||
if (noConsole) { return; }
|
||||
ConsoleCommands.Register<Param0, Param1, Param2>(name, action, desc);
|
||||
CustomCommands.Register(name, action, desc);
|
||||
}
|
||||
|
||||
public static void Unregister(string name, bool noConsole = false)
|
||||
{
|
||||
RuntimeCommands.Unregister(name);
|
||||
if (noConsole) { return; }
|
||||
ConsoleCommands.Unregister(name);
|
||||
CustomCommands.Unregister(name);
|
||||
}
|
||||
|
||||
public static void Call(string name)
|
||||
{
|
||||
RuntimeCommands.Call(name);
|
||||
CustomCommands.Call(name);
|
||||
}
|
||||
|
||||
public static void Call<Param0>(string name, Param0 param0)
|
||||
{
|
||||
RuntimeCommands.Call<Param0>(name, param0);
|
||||
CustomCommands.Call(name, param0);
|
||||
}
|
||||
|
||||
public static void Call<Param0, Param1>(string name, Param0 param0, Param1 param1)
|
||||
{
|
||||
RuntimeCommands.Call<Param0, Param1>(name, param0, param1);
|
||||
CustomCommands.Call(name, param0, param1);
|
||||
}
|
||||
|
||||
public static void Call<Param0, Param1, Param2>(string name, Param0 param0, Param1 param1, Param2 param2)
|
||||
{
|
||||
RuntimeCommands.Call<Param0, Param1, Param2>(name, param0, param1, param2);
|
||||
CustomCommands.Call(name, param0, param1, param2);
|
||||
}
|
||||
}
|
||||
}
|
57
TechbloxModdingAPI/Commands/CustomCommands.cs
Normal file
57
TechbloxModdingAPI/Commands/CustomCommands.cs
Normal file
|
@ -0,0 +1,57 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace TechbloxModdingAPI.Commands
|
||||
{
|
||||
internal static class CustomCommands
|
||||
{
|
||||
public struct CommandData
|
||||
{
|
||||
public string Name;
|
||||
public string Description;
|
||||
public Delegate Action;
|
||||
}
|
||||
|
||||
private static Dictionary<string, CommandData> _commands = new Dictionary<string, CommandData>();
|
||||
public static void Register(string name, Delegate action, string desc)
|
||||
{
|
||||
_commands.Add(name, new CommandData
|
||||
{
|
||||
Name = name,
|
||||
Description = desc,
|
||||
Action = action
|
||||
});
|
||||
}
|
||||
|
||||
public static void Call(string name, params object[] args)
|
||||
{
|
||||
if (_commands.TryGetValue(name, out var command))
|
||||
{
|
||||
var paramz = command.Action.Method.GetParameters();
|
||||
if (paramz.Length > args.Length)
|
||||
throw new CommandParameterMissingException(
|
||||
$"This command requires {paramz.Length} arguments, {args.Length} given");
|
||||
for (var index = 0; index < paramz.Length; index++)
|
||||
{
|
||||
args[index] = Convert.ChangeType(args[index], paramz[index].ParameterType);
|
||||
}
|
||||
|
||||
command.Action.DynamicInvoke(args);
|
||||
}
|
||||
else
|
||||
throw new CommandNotFoundException($"Command {name} does not exist!");
|
||||
}
|
||||
|
||||
public static void Unregister(string name)
|
||||
{
|
||||
_commands.Remove(name);
|
||||
}
|
||||
|
||||
public static bool Exists(string name) => _commands.ContainsKey(name);
|
||||
|
||||
public static ReadOnlyDictionary<string, CommandData> GetAllCommandData() =>
|
||||
new ReadOnlyDictionary<string, CommandData>(_commands);
|
||||
}
|
||||
}
|
37
TechbloxModdingAPI/Commands/ExistingCommands.cs
Normal file
37
TechbloxModdingAPI/Commands/ExistingCommands.cs
Normal file
|
@ -0,0 +1,37 @@
|
|||
using System.Linq;
|
||||
|
||||
namespace TechbloxModdingAPI.Commands
|
||||
{
|
||||
public static class ExistingCommands
|
||||
{
|
||||
public static void Call(string commandName)
|
||||
{
|
||||
CustomCommands.Call(commandName);
|
||||
}
|
||||
|
||||
public static void Call<Arg0>(string commandName, Arg0 arg0)
|
||||
{
|
||||
CustomCommands.Call(commandName, arg0);
|
||||
}
|
||||
|
||||
public static void Call<Arg0, Arg1>(string commandName, Arg0 arg0, Arg1 arg1)
|
||||
{
|
||||
CustomCommands.Call(commandName, arg0, arg1);
|
||||
}
|
||||
|
||||
public static void Call<Arg0, Arg1, Arg2>(string commandName, Arg0 arg0, Arg1 arg1, Arg2 arg2)
|
||||
{
|
||||
CustomCommands.Call(commandName, arg0, arg1, arg2);
|
||||
}
|
||||
|
||||
public static bool Exists(string commandName)
|
||||
{
|
||||
return CustomCommands.Exists(commandName);
|
||||
}
|
||||
|
||||
public static (string Name, string Description)[] GetCommandNamesAndDescriptions()
|
||||
{
|
||||
return CustomCommands.GetAllCommandData().Values.Select(command => (command.Name, command.Description)).ToArray();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,12 +6,15 @@ using System.Threading.Tasks;
|
|||
|
||||
using Svelto.ECS;
|
||||
|
||||
using GamecraftModdingAPI.Utility;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
using TechbloxModdingAPI.Engines;
|
||||
|
||||
namespace GamecraftModdingAPI.Commands
|
||||
namespace TechbloxModdingAPI.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Engine interface to handle command operations
|
||||
/// Engine interface to handle command operations.
|
||||
/// If you are using implementing this yourself, you must manually register the command.
|
||||
/// See SimpleCustomCommandEngine's Ready() and Dispose() methods for an example of command registration.
|
||||
/// </summary>
|
||||
public interface ICustomCommandEngine : IApiEngine
|
||||
{
|
|
@ -5,8 +5,9 @@ using System.Text;
|
|||
using System.Threading.Tasks;
|
||||
|
||||
using Svelto.ECS;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftModdingAPI.Commands
|
||||
namespace TechbloxModdingAPI.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// A simple implementation of ICustomCommandEngine sufficient for most commands.
|
||||
|
@ -29,18 +30,20 @@ namespace GamecraftModdingAPI.Commands
|
|||
/// </summary>
|
||||
private Action runCommand;
|
||||
|
||||
public IEntitiesDB entitiesDB { set; private get; }
|
||||
public EntitiesDB entitiesDB { set; private get; }
|
||||
|
||||
public void Dispose()
|
||||
public bool isRemovable => true;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GamecraftModdingAPI.Utility.Logging.MetaDebugLog($"Unregistering SimpleCustomCommandEngine {this.Name}");
|
||||
Logging.MetaDebugLog($"Unregistering SimpleCustomCommandEngine {this.Name}");
|
||||
CommandRegistrationHelper.Unregister(this.Name);
|
||||
}
|
||||
|
||||
public void Ready()
|
||||
{
|
||||
GamecraftModdingAPI.Utility.Logging.MetaDebugLog($"Registering SimpleCustomCommandEngine {this.Name}");
|
||||
CommandRegistrationHelper.Register(this.Name, this.runCommand, this.Description);
|
||||
Logging.MetaDebugLog($"Registering SimpleCustomCommandEngine {this.Name}");
|
||||
CommandRegistrationHelper.Register(this.Name, this.InvokeCatchError, this.Description);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -55,5 +58,24 @@ namespace GamecraftModdingAPI.Commands
|
|||
this.Name = name;
|
||||
this.Description = description;
|
||||
}
|
||||
|
||||
public void Invoke()
|
||||
{
|
||||
runCommand();
|
||||
}
|
||||
|
||||
private void InvokeCatchError()
|
||||
{
|
||||
try
|
||||
{
|
||||
runCommand();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
CommandRuntimeException wrappedException = new CommandRuntimeException($"Command {Name} threw an exception when executed", e);
|
||||
Logging.LogWarning(wrappedException.ToString());
|
||||
Logging.CommandLogError(wrappedException.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,8 +5,9 @@ using System.Text;
|
|||
using System.Threading.Tasks;
|
||||
|
||||
using Svelto.ECS;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftModdingAPI.Commands
|
||||
namespace TechbloxModdingAPI.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// A simple implementation of ICustomCommandEngine sufficient for most commands.
|
||||
|
@ -20,18 +21,20 @@ namespace GamecraftModdingAPI.Commands
|
|||
|
||||
private Action<A> runCommand;
|
||||
|
||||
public IEntitiesDB entitiesDB { set; private get; }
|
||||
public EntitiesDB entitiesDB { set; private get; }
|
||||
|
||||
public void Dispose()
|
||||
public bool isRemovable => true;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GamecraftModdingAPI.Utility.Logging.MetaDebugLog($"Unregistering SimpleCustomCommandEngine {this.Name}");
|
||||
Logging.MetaDebugLog($"Unregistering SimpleCustomCommandEngine {this.Name}");
|
||||
CommandRegistrationHelper.Unregister(this.Name);
|
||||
}
|
||||
|
||||
public void Ready()
|
||||
{
|
||||
GamecraftModdingAPI.Utility.Logging.MetaDebugLog($"Registering SimpleCustomCommandEngine {this.Name}");
|
||||
CommandRegistrationHelper.Register<A>(this.Name, this.runCommand, this.Description);
|
||||
Logging.MetaDebugLog($"Registering SimpleCustomCommandEngine {this.Name}");
|
||||
CommandRegistrationHelper.Register<A>(this.Name, this.InvokeCatchError, this.Description);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -45,6 +48,25 @@ namespace GamecraftModdingAPI.Commands
|
|||
this.runCommand = command;
|
||||
this.Name = name;
|
||||
this.Description = description;
|
||||
}
|
||||
|
||||
public void Invoke(A a)
|
||||
{
|
||||
runCommand(a);
|
||||
}
|
||||
|
||||
private void InvokeCatchError(A a)
|
||||
{
|
||||
try
|
||||
{
|
||||
runCommand(a);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
CommandRuntimeException wrappedException = new CommandRuntimeException($"Command {Name} threw an exception when executed", e);
|
||||
Logging.LogWarning(wrappedException.ToString());
|
||||
Logging.CommandLogError(wrappedException.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,8 +5,9 @@ using System.Text;
|
|||
using System.Threading.Tasks;
|
||||
|
||||
using Svelto.ECS;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
namespace GamecraftModdingAPI.Commands
|
||||
namespace TechbloxModdingAPI.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// A simple implementation of ICustomCommandEngine sufficient for most commands.
|
||||
|
@ -20,18 +21,20 @@ namespace GamecraftModdingAPI.Commands
|
|||
|
||||
private Action<A,B> runCommand;
|
||||
|
||||
public IEntitiesDB entitiesDB { set; private get; }
|
||||
public EntitiesDB entitiesDB { set; private get; }
|
||||
|
||||
public void Dispose()
|
||||
public bool isRemovable => true;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GamecraftModdingAPI.Utility.Logging.MetaDebugLog($"Unregistering SimpleCustomCommandEngine {this.Name}");
|
||||
Logging.MetaDebugLog($"Unregistering SimpleCustomCommandEngine {this.Name}");
|
||||
CommandRegistrationHelper.Unregister(this.Name);
|
||||
}
|
||||
|
||||
public void Ready()
|
||||
{
|
||||
GamecraftModdingAPI.Utility.Logging.MetaDebugLog($"Registering SimpleCustomCommandEngine {this.Name}");
|
||||
CommandRegistrationHelper.Register<A,B>(this.Name, this.runCommand, this.Description);
|
||||
Logging.MetaDebugLog($"Registering SimpleCustomCommandEngine {this.Name}");
|
||||
CommandRegistrationHelper.Register<A,B>(this.Name, this.InvokeCatchError, this.Description);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -45,6 +48,25 @@ namespace GamecraftModdingAPI.Commands
|
|||
this.runCommand = command;
|
||||
this.Name = name;
|
||||
this.Description = description;
|
||||
}
|
||||
|
||||
public void Invoke(A a, B b)
|
||||
{
|
||||
runCommand(a, b);
|
||||
}
|
||||
|
||||
private void InvokeCatchError(A a, B b)
|
||||
{
|
||||
try
|
||||
{
|
||||
runCommand(a, b);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
CommandRuntimeException wrappedException = new CommandRuntimeException($"Command {Name} threw an exception when executed", e);
|
||||
Logging.LogWarning(wrappedException.ToString());
|
||||
Logging.CommandLogError(wrappedException.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue