Implement Robocraft factory importing
This commit is contained in:
parent
2e314595ac
commit
bfe0c1972c
12 changed files with 719 additions and 24 deletions
|
@ -143,7 +143,7 @@ namespace Pixi.Images
|
||||||
//position.y = zero_y;
|
//position.y = zero_y;
|
||||||
}
|
}
|
||||||
Logging.CommandLog($"Placed {img.width}x{img.height} image beside you ({blockCount} blocks total)");
|
Logging.CommandLog($"Placed {img.width}x{img.height} image beside you ({blockCount} blocks total)");
|
||||||
Logging.MetaLog($"Saved {(img.width * img.height) - blockCount} blocks ({img.width * img.height / blockCount}x) while placing {filepath}");
|
Logging.MetaLog($"Saved {(img.width * img.height) - blockCount} blocks ({blockCount / (img.width * img.height)}%) while placing {filepath}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Pixelate2DFileToTextBlock(string filepath)
|
public static void Pixelate2DFileToTextBlock(string filepath)
|
||||||
|
|
|
@ -157,11 +157,12 @@ namespace Pixi.Images
|
||||||
Color pixel = img.GetPixel(x, y);
|
Color pixel = img.GetPixel(x, y);
|
||||||
imgString.Append("<color=");
|
imgString.Append("<color=");
|
||||||
imgString.Append(HexPixel(pixel));
|
imgString.Append(HexPixel(pixel));
|
||||||
imgString.Append(">\u25a0</color>");
|
imgString.Append(">");
|
||||||
|
imgString.Append("\u25a0");
|
||||||
}
|
}
|
||||||
imgString.Append("<br>");
|
imgString.Append("<br>");
|
||||||
}
|
}
|
||||||
imgString.Append("</cspace></line-height>");
|
imgString.Append("</color></cspace></line-height>");
|
||||||
return imgString.ToString();
|
return imgString.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -804,6 +804,15 @@
|
||||||
<Reference Include="GamecraftModdingAPI">
|
<Reference Include="GamecraftModdingAPI">
|
||||||
<HintPath>..\..\ref\Plugins\GamecraftModdingAPI.dll</HintPath>
|
<HintPath>..\..\ref\Plugins\GamecraftModdingAPI.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="GamecraftModdingAPI">
|
||||||
|
<HintPath>..\..\ref\Plugins\GamecraftModdingAPI.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<!--End Dependencies-->
|
<!--End Dependencies-->
|
||||||
|
<ItemGroup>
|
||||||
|
<None Remove="cubes-id.json" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<EmbeddedResource Include="cubes-id.json" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -7,13 +7,10 @@ using UnityEngine;
|
||||||
using Unity.Mathematics; // float3
|
using Unity.Mathematics; // float3
|
||||||
|
|
||||||
using IllusionPlugin;
|
using IllusionPlugin;
|
||||||
using GamecraftModdingAPI;
|
|
||||||
using GamecraftModdingAPI.Commands;
|
|
||||||
using GamecraftModdingAPI.Utility;
|
using GamecraftModdingAPI.Utility;
|
||||||
using GamecraftModdingAPI.Blocks;
|
|
||||||
using GamecraftModdingAPI.Players;
|
|
||||||
|
|
||||||
using Pixi.Images;
|
using Pixi.Images;
|
||||||
|
using Pixi.Robots;
|
||||||
|
|
||||||
namespace Pixi
|
namespace Pixi
|
||||||
{
|
{
|
||||||
|
@ -29,7 +26,7 @@ namespace Pixi
|
||||||
public void OnApplicationQuit()
|
public void OnApplicationQuit()
|
||||||
{
|
{
|
||||||
// Shutdown this mod
|
// Shutdown this mod
|
||||||
GamecraftModdingAPI.Utility.Logging.LogDebug($"{Name} has shutdown");
|
Logging.LogDebug($"{Name} has shutdown");
|
||||||
|
|
||||||
// Shutdown the Gamecraft modding API last
|
// Shutdown the Gamecraft modding API last
|
||||||
GamecraftModdingAPI.Main.Shutdown();
|
GamecraftModdingAPI.Main.Shutdown();
|
||||||
|
@ -48,8 +45,11 @@ namespace Pixi
|
||||||
ImageCommands.CreateImportCommand();
|
ImageCommands.CreateImportCommand();
|
||||||
ImageCommands.CreateTextCommand();
|
ImageCommands.CreateTextCommand();
|
||||||
ImageCommands.CreateTextConsoleCommand();
|
ImageCommands.CreateTextConsoleCommand();
|
||||||
|
// Robot functionality
|
||||||
|
RobotCommands.CreateRobotCRFCommand();
|
||||||
|
RobotCommands.CreateRobotFileCommand();
|
||||||
|
|
||||||
GamecraftModdingAPI.Utility.Logging.LogDebug($"{Name} has started up");
|
Logging.LogDebug($"{Name} has started up");
|
||||||
}
|
}
|
||||||
|
|
||||||
// unused methods
|
// unused methods
|
||||||
|
|
28
Pixi/Robots/CubeInfo.cs
Normal file
28
Pixi/Robots/CubeInfo.cs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
using System;
|
||||||
|
using Unity.Mathematics;
|
||||||
|
using GamecraftModdingAPI.Blocks;
|
||||||
|
|
||||||
|
namespace Pixi.Robots
|
||||||
|
{
|
||||||
|
public struct CubeInfo
|
||||||
|
{
|
||||||
|
// so you can't inherit from structs in C#...
|
||||||
|
// this is an extension of BlockInfo
|
||||||
|
public BlockIDs block;
|
||||||
|
|
||||||
|
public BlockColors color;
|
||||||
|
|
||||||
|
public byte darkness;
|
||||||
|
|
||||||
|
public bool visible;
|
||||||
|
|
||||||
|
// additions
|
||||||
|
public float3 rotation;
|
||||||
|
|
||||||
|
public float3 position;
|
||||||
|
|
||||||
|
public float3 scale;
|
||||||
|
|
||||||
|
public string placeholder;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,373 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
using RobocraftX.Common;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Unity.Mathematics;
|
||||||
|
|
||||||
|
using GamecraftModdingAPI.Blocks;
|
||||||
|
using GamecraftModdingAPI.Utility;
|
||||||
|
|
||||||
namespace Pixi.Robots
|
namespace Pixi.Robots
|
||||||
{
|
{
|
||||||
public static class CubeUtility
|
public static class CubeUtility
|
||||||
{
|
{
|
||||||
|
private static Dictionary<uint, string> map = null;
|
||||||
|
|
||||||
|
public static RobotStruct? ParseRobotInfo(string robotInfo)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return JsonConvert.DeserializeObject<RobotStruct>(robotInfo);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logging.MetaLog(e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CubeInfo[] ParseCubes(RobotStruct robot)
|
||||||
|
{
|
||||||
|
return ParseCubes(robot.cubeData, robot.colourData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CubeInfo[] ParseCubes(string cubeData, string colourData)
|
||||||
|
{
|
||||||
|
BinaryBufferReader cubes = new BinaryBufferReader(Convert.FromBase64String(cubeData), 0);
|
||||||
|
BinaryBufferReader colours = new BinaryBufferReader(Convert.FromBase64String(colourData), 0);
|
||||||
|
uint cubeCount = cubes.ReadUint();
|
||||||
|
uint colourCount = colours.ReadUint();
|
||||||
|
if (cubeCount != colourCount)
|
||||||
|
{
|
||||||
|
Logging.MetaLog("Something is fucking broken");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Logging.MetaLog($"Detected {cubeCount} cubes");
|
||||||
|
CubeInfo[] result = new CubeInfo[cubeCount];
|
||||||
|
for (int cube = 0; cube < cubeCount; cube++)
|
||||||
|
{
|
||||||
|
result[cube] = TranslateSpacialEnumerations(
|
||||||
|
cubes.ReadUint(),
|
||||||
|
cubes.ReadByte(),
|
||||||
|
cubes.ReadByte(),
|
||||||
|
cubes.ReadByte(),
|
||||||
|
cubes.ReadByte(),
|
||||||
|
colours.ReadByte(),
|
||||||
|
colours.ReadByte(),
|
||||||
|
colours.ReadByte(),
|
||||||
|
colours.ReadByte()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static CubeInfo TranslateSpacialEnumerations(uint cubeId, byte x, byte y, byte z, byte rotation, byte colour, byte colour_x, byte colour_y, byte colour_z)
|
||||||
|
{
|
||||||
|
if (x != colour_x || z != colour_z || y != colour_y) return default;
|
||||||
|
CubeInfo result = new CubeInfo { visible = true };
|
||||||
|
TranslateBlockColour(colour, ref result);
|
||||||
|
TranslateBlockPosition(x, y, z, ref result);
|
||||||
|
TranslateBlockRotation(rotation, ref result);
|
||||||
|
TranslateBlockId(cubeId, ref result);
|
||||||
|
#if DEBUG
|
||||||
|
Logging.MetaLog($"Cube {cubeId} ({x}, {y}, {z}) rot:{rotation} decoded as {result.block} {result.position} rot: {result.rotation} color: {result.color} {result.darkness}");
|
||||||
|
#endif
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static void TranslateBlockRotation(byte rotation, ref CubeInfo result)
|
||||||
|
{
|
||||||
|
// face refers to the face of the block connected to the bottom of the current one
|
||||||
|
// nvm, they're all incorrect
|
||||||
|
switch (rotation)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
result.rotation = new float3(0, 0, 0); // top face, forwards
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
result.rotation = new float3(0, 0, 90); // left face, forwards
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
result.rotation = new float3(0, 0, 180); // bottom face, forwards
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
result.rotation = new float3(0, 0, -90); // front face, down
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
result.rotation = new float3(0, 90, 0); // top face, right
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
result.rotation = new float3(0, 90, 90); // front face, right
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
result.rotation = new float3(-90, -90, 0); // right face, backwards
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
result.rotation = new float3(0, 90, -90); // back face, right
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
result.rotation = new float3(0, -90, 90); // back face, left
|
||||||
|
break;
|
||||||
|
case 9:
|
||||||
|
result.rotation = new float3(0, -90, -90); // front face, left
|
||||||
|
break;
|
||||||
|
case 10:
|
||||||
|
result.rotation = new float3(90, -90, 0); // left face, down
|
||||||
|
break;
|
||||||
|
case 11:
|
||||||
|
result.rotation = new float3(90, 90, 0); // right face, forwards
|
||||||
|
break;
|
||||||
|
case 12:
|
||||||
|
result.rotation = new float3(-90, 90, 0); // left face, up
|
||||||
|
break;
|
||||||
|
case 13:
|
||||||
|
result.rotation = new float3(0, 90, 180); // bottom face, right
|
||||||
|
break;
|
||||||
|
case 14:
|
||||||
|
result.rotation = new float3(0, 180, 0); // top face, backwards
|
||||||
|
break;
|
||||||
|
case 15:
|
||||||
|
result.rotation = new float3(0, 180, 90); // right face, up
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
result.rotation = new float3(0, 180, 180); // bottom face, backwards
|
||||||
|
break;
|
||||||
|
case 17:
|
||||||
|
result.rotation = new float3(0, 180, -90); // left face, backwards
|
||||||
|
break;
|
||||||
|
case 18:
|
||||||
|
result.rotation = new float3(0, -90, 0); // top face, left
|
||||||
|
break;
|
||||||
|
case 19:
|
||||||
|
result.rotation = new float3(0, -90, 180); // bottom face, left
|
||||||
|
break;
|
||||||
|
case 20:
|
||||||
|
result.rotation = new float3(90, 0, 0); // front face, down
|
||||||
|
break;
|
||||||
|
case 21:
|
||||||
|
result.rotation = new float3(90, 180, 0); // back face, down
|
||||||
|
break;
|
||||||
|
case 22:
|
||||||
|
result.rotation = new float3(-90, 0, 0); // back face, up
|
||||||
|
break;
|
||||||
|
case 23:
|
||||||
|
result.rotation = new float3(-90, 180, 0); // front face, up
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
#if DEBUG
|
||||||
|
Logging.MetaLog($"Unknown rotation {rotation.ToString("X2")}");
|
||||||
|
#endif
|
||||||
|
result.rotation = float3.zero;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// my brain hurts after figuring out all of those rotations
|
||||||
|
// I wouldn't recommend trying to redo this
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static void TranslateBlockPosition(byte x, byte y, byte z, ref CubeInfo result)
|
||||||
|
{
|
||||||
|
// for some reason, z is forwards in garage bays
|
||||||
|
result.position = new float3(x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static void TranslateBlockColour(byte colour, ref CubeInfo result)
|
||||||
|
{
|
||||||
|
// I hope these colours are accurate, I just guessed
|
||||||
|
// TODO colour accuracy (lol that won't ever happen)
|
||||||
|
switch (colour)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
result.color = BlockColors.White;
|
||||||
|
result.darkness = 0;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
result.color = BlockColors.White;
|
||||||
|
result.darkness = 5;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
result.color = BlockColors.Orange;
|
||||||
|
result.darkness = 0;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
result.color = BlockColors.Blue;
|
||||||
|
result.darkness = 2;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
result.color = BlockColors.White;
|
||||||
|
result.darkness = 8;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
result.color = BlockColors.Red;
|
||||||
|
result.darkness = 0;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
result.color = BlockColors.Yellow;
|
||||||
|
result.darkness = 0;
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
result.color = BlockColors.Green;
|
||||||
|
result.darkness = 0;
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
result.color = BlockColors.Purple;
|
||||||
|
result.darkness = 0;
|
||||||
|
break;
|
||||||
|
case 9:
|
||||||
|
result.color = BlockColors.Blue;
|
||||||
|
result.darkness = 7;
|
||||||
|
break;
|
||||||
|
case 10:
|
||||||
|
result.color = BlockColors.Purple;
|
||||||
|
result.darkness = 5;
|
||||||
|
break;
|
||||||
|
case 11:
|
||||||
|
result.color = BlockColors.Orange;
|
||||||
|
result.darkness = 7;
|
||||||
|
break;
|
||||||
|
case 12:
|
||||||
|
result.color = BlockColors.Green;
|
||||||
|
result.darkness = 3;
|
||||||
|
break;
|
||||||
|
case 13:
|
||||||
|
result.color = BlockColors.Green;
|
||||||
|
result.darkness = 2;
|
||||||
|
break;
|
||||||
|
case 14:
|
||||||
|
result.color = BlockColors.Pink;
|
||||||
|
result.darkness = 3;
|
||||||
|
break;
|
||||||
|
case 15:
|
||||||
|
result.color = BlockColors.Pink;
|
||||||
|
result.darkness = 2;
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
result.color = BlockColors.Red;
|
||||||
|
result.darkness = 2;
|
||||||
|
break;
|
||||||
|
case 17:
|
||||||
|
result.color = BlockColors.Orange;
|
||||||
|
result.darkness = 8;
|
||||||
|
break;
|
||||||
|
case 18:
|
||||||
|
result.color = BlockColors.Red;
|
||||||
|
result.darkness = 7;
|
||||||
|
break;
|
||||||
|
case 19:
|
||||||
|
result.color = BlockColors.Pink;
|
||||||
|
result.darkness = 0;
|
||||||
|
break;
|
||||||
|
case 20:
|
||||||
|
result.color = BlockColors.Yellow;
|
||||||
|
result.darkness = 2;
|
||||||
|
break;
|
||||||
|
case 21:
|
||||||
|
result.color = BlockColors.Green;
|
||||||
|
result.darkness = 7;
|
||||||
|
break;
|
||||||
|
case 22:
|
||||||
|
result.color = BlockColors.Green;
|
||||||
|
result.darkness = 8;
|
||||||
|
break;
|
||||||
|
case 23:
|
||||||
|
result.color = BlockColors.Blue;
|
||||||
|
result.darkness = 8;
|
||||||
|
break;
|
||||||
|
case 24:
|
||||||
|
result.color = BlockColors.Aqua;
|
||||||
|
result.darkness = 7;
|
||||||
|
break;
|
||||||
|
case 25:
|
||||||
|
result.color = BlockColors.Blue;
|
||||||
|
result.darkness = 6;
|
||||||
|
break;
|
||||||
|
case 26:
|
||||||
|
result.color = BlockColors.Aqua;
|
||||||
|
result.darkness = 5;
|
||||||
|
break;
|
||||||
|
case 27:
|
||||||
|
result.color = BlockColors.Blue;
|
||||||
|
result.darkness = 4;
|
||||||
|
break;
|
||||||
|
case 28:
|
||||||
|
result.color = BlockColors.Aqua;
|
||||||
|
result.darkness = 3;
|
||||||
|
break;
|
||||||
|
case 29:
|
||||||
|
result.color = BlockColors.Blue;
|
||||||
|
result.darkness = 5;
|
||||||
|
break;
|
||||||
|
case 30:
|
||||||
|
result.color = BlockColors.Purple;
|
||||||
|
result.darkness = 3;
|
||||||
|
break;
|
||||||
|
case 31:
|
||||||
|
result.color = BlockColors.Purple;
|
||||||
|
result.darkness = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
result.color = BlockColors.Aqua;
|
||||||
|
result.darkness = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static void TranslateBlockId(uint cubeId, ref CubeInfo result)
|
||||||
|
{
|
||||||
|
if (map == null)
|
||||||
|
{
|
||||||
|
StreamReader cubemap = new StreamReader(Assembly.GetExecutingAssembly().GetManifestResourceStream("Pixi.cubes-id.json"));
|
||||||
|
map = JsonConvert.DeserializeObject<Dictionary<uint, string>>(cubemap.ReadToEnd());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!map.ContainsKey(cubeId))
|
||||||
|
{
|
||||||
|
result.block = BlockIDs.TextBlock;
|
||||||
|
result.placeholder = "Unknown cube #" + cubeId.ToString();
|
||||||
|
//result.rotation = float3.zero;
|
||||||
|
#if DEBUG
|
||||||
|
Logging.MetaLog($"Unknown cubeId {cubeId}");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
string cubeName = map[cubeId];
|
||||||
|
if (cubeName.Contains("cube"))
|
||||||
|
{
|
||||||
|
result.block = BlockIDs.AluminiumCube;
|
||||||
|
result.rotation = float3.zero;
|
||||||
|
}
|
||||||
|
else if (cubeName.Contains("prism") || cubeName.Contains("edge"))
|
||||||
|
{
|
||||||
|
result.block = BlockIDs.AluminiumSlope;
|
||||||
|
}
|
||||||
|
else if (cubeName.Contains("inner"))
|
||||||
|
{
|
||||||
|
result.block = BlockIDs.AluminiumSlicedCube;
|
||||||
|
}
|
||||||
|
else if (cubeName.Contains("tetra") || cubeName.Contains("corner"))
|
||||||
|
{
|
||||||
|
result.block = BlockIDs.AluminiumCorner;
|
||||||
|
}
|
||||||
|
else if (cubeName.Contains("pyramid"))
|
||||||
|
{
|
||||||
|
result.block = BlockIDs.AluminiumPyramidSegment;
|
||||||
|
}
|
||||||
|
else if (cubeName.Contains("cone"))
|
||||||
|
{
|
||||||
|
result.block = BlockIDs.AluminiumConeSegment;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result.block = BlockIDs.TextBlock;
|
||||||
|
result.placeholder = cubeName;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
75
Pixi/Robots/RoboAPIUtility.cs
Normal file
75
Pixi/Robots/RoboAPIUtility.cs
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
|
using System.Text;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
using GamecraftModdingAPI.Utility;
|
||||||
|
|
||||||
|
namespace Pixi.Robots
|
||||||
|
{
|
||||||
|
public static class RoboAPIUtility
|
||||||
|
{
|
||||||
|
private const string ROBOT_API_LIST_URL = "https://factory.robocraftgame.com/api/roboShopItems/list";
|
||||||
|
|
||||||
|
private const string ROBOT_API_GET_URL = "https://factory.robocraftgame.com/api/roboShopItems/get/";
|
||||||
|
|
||||||
|
private const string ROBOT_API_TOKEN = "Web eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJQdWJsaWNJZCI6IjEyMyIsIkRpc3BsYXlOYW1lIjoiVGVzdCIsIlJvYm9jcmFmdE5hbWUiOiJGYWtlQ1JGVXNlciIsIkZsYWdzIjpbXSwiaXNzIjoiRnJlZWphbSIsInN1YiI6IldlYiIsImlhdCI6MTU0NTIyMzczMiwiZXhwIjoyNTQ1MjIzNzkyfQ.ralLmxdMK9rVKPZxGng8luRIdbTflJ4YMJcd25dKlqg";
|
||||||
|
|
||||||
|
public static RobotBriefStruct[] ListRobots(string searchFilter, int pageSize = 10, bool playerFilter = false)
|
||||||
|
{
|
||||||
|
// pageSize <= 2 seems to retrieve items unreliably
|
||||||
|
string bodyJson = $"{{\"page\": 1, \"pageSize\": {pageSize}, \"order\": 0, \"playerFilter\": {playerFilter.ToString().ToLower()}, \"movementFilter\": \"100000,200000,300000,400000,500000,600000,700000,800000,900000,1000000,1100000,1200000\", \"movementCategoryFilter\": \"100000,200000,300000,400000,500000,600000,700000,800000,900000,1000000,1100000,1200000\", \"weaponFilter\": \"10000000,20000000,25000000,30000000,40000000,50000000,60000000,65000000,70100000,75000000\", \"weaponCategoryFilter\": \"10000000,20000000,25000000,30000000,40000000,50000000,60000000,65000000,70100000,75000000\", \"minimumCpu\": -1, \"maximumCpu\": -1, \"minimumRobotRanking\": 0, \"maximumRobotRanking\": 1000000000, \"textFilter\": \"{searchFilter}\", \"textSearchField\": 0, \"buyable\": true, \"prependFeaturedRobot\": false, \"featuredOnly\": false, \"defaultPage\": false}}";
|
||||||
|
byte[] reqBody = Encoding.UTF8.GetBytes(bodyJson);
|
||||||
|
#if DEBUG
|
||||||
|
Logging.MetaLog($"POST body\n{bodyJson}");
|
||||||
|
#endif
|
||||||
|
// download robot list
|
||||||
|
// FIXME this blocks main thread
|
||||||
|
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(ROBOT_API_LIST_URL);
|
||||||
|
// request
|
||||||
|
request.Method = "POST";
|
||||||
|
request.ContentLength = reqBody.Length;
|
||||||
|
request.ContentType = "application/json";
|
||||||
|
request.Headers.Add(HttpRequestHeader.Authorization, ROBOT_API_TOKEN);
|
||||||
|
request.Accept = "application/json; charset=utf-8"; // HTTP Status 500 without
|
||||||
|
Stream body;
|
||||||
|
body = request.GetRequestStream();
|
||||||
|
body.Write(reqBody, 0, reqBody.Length);
|
||||||
|
body.Close();
|
||||||
|
// response
|
||||||
|
HttpWebResponse response;
|
||||||
|
response = (HttpWebResponse)request.GetResponse();
|
||||||
|
// regular Stream was unreliable
|
||||||
|
// because they could read everything before everything was availabe
|
||||||
|
StreamReader respReader = new StreamReader(response.GetResponseStream());
|
||||||
|
string bodyStr = respReader.ReadToEnd();
|
||||||
|
RobotListResponse rlr = JsonConvert.DeserializeObject<RobotListResponse>(bodyStr);
|
||||||
|
return rlr.response.roboShopItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RobotStruct QueryRobotInfo(int robotId)
|
||||||
|
{
|
||||||
|
// download robot info
|
||||||
|
// FIXME this blocks main thread
|
||||||
|
string url = ROBOT_API_GET_URL + robotId.ToString();
|
||||||
|
Logging.MetaLog(url);
|
||||||
|
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
|
||||||
|
// request
|
||||||
|
request.Method = "GET";
|
||||||
|
request.ContentType = "application/json";
|
||||||
|
request.Accept = "application/json; charset=utf-8"; // HTTP Status 500 without
|
||||||
|
request.Headers.Add(HttpRequestHeader.Authorization, ROBOT_API_TOKEN);
|
||||||
|
// response
|
||||||
|
HttpWebResponse response;
|
||||||
|
response = (HttpWebResponse)request.GetResponse();
|
||||||
|
// regular Stream was unreliable
|
||||||
|
// because they could read everything before everything was availabe
|
||||||
|
StreamReader body = new StreamReader(response.GetResponseStream());
|
||||||
|
string bodyStr = body.ReadToEnd();
|
||||||
|
response.Close();
|
||||||
|
RobotInfoResponse rir = JsonConvert.DeserializeObject<RobotInfoResponse>(bodyStr);
|
||||||
|
return rir.response;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
49
Pixi/Robots/RobotAPIStructs.cs
Normal file
49
Pixi/Robots/RobotAPIStructs.cs
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
using System;
|
||||||
|
namespace Pixi.Robots
|
||||||
|
{
|
||||||
|
public struct RobotBriefStruct
|
||||||
|
{
|
||||||
|
public int itemId;
|
||||||
|
|
||||||
|
public string itemName;
|
||||||
|
|
||||||
|
public string itemDescription;
|
||||||
|
|
||||||
|
public string thumbnail;
|
||||||
|
|
||||||
|
public string addedBy;
|
||||||
|
|
||||||
|
public string addedByDisplayName;
|
||||||
|
|
||||||
|
public int cpu;
|
||||||
|
|
||||||
|
public int totalRobotRanking;
|
||||||
|
|
||||||
|
public string cubeData;
|
||||||
|
|
||||||
|
public string colourData;
|
||||||
|
|
||||||
|
public bool featured;
|
||||||
|
|
||||||
|
public string cubeAmounts; // this is sent incorrectly by the API server (it's actually a Dictionary<string, int>)
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct RobotList
|
||||||
|
{
|
||||||
|
public RobotBriefStruct[] roboShopItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct RobotListResponse
|
||||||
|
{
|
||||||
|
public RobotList response;
|
||||||
|
|
||||||
|
public int statusCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct RobotInfoResponse
|
||||||
|
{
|
||||||
|
public RobotStruct response;
|
||||||
|
|
||||||
|
public int statusCode;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,114 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
using Unity.Mathematics;
|
||||||
|
|
||||||
|
using GamecraftModdingAPI;
|
||||||
|
using GamecraftModdingAPI.Blocks;
|
||||||
|
using GamecraftModdingAPI.Commands;
|
||||||
|
using GamecraftModdingAPI.Players;
|
||||||
|
using GamecraftModdingAPI.Utility;
|
||||||
|
|
||||||
namespace Pixi.Robots
|
namespace Pixi.Robots
|
||||||
{
|
{
|
||||||
public static class RobotCommands
|
public static class RobotCommands
|
||||||
{
|
{
|
||||||
|
private static double blockSize = 0.2;
|
||||||
|
|
||||||
|
public static void CreateRobotFileCommand()
|
||||||
|
{
|
||||||
|
CommandBuilder.Builder()
|
||||||
|
.Name("PixiBotFile")
|
||||||
|
.Description("Converts a robot file from RCBUP into Gamecraft blocks. Larger robots will freeze your game until conversion completes. (Pixi)")
|
||||||
|
.Action<string>(ImportRobotFile)
|
||||||
|
.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void CreateRobotCRFCommand()
|
||||||
|
{
|
||||||
|
CommandBuilder.Builder()
|
||||||
|
.Name("PixiBot")
|
||||||
|
.Description("Downloads a robot from Robocraft's Factory and converts it into Gamecraft blocks. Larger robots will freeze your game until conversion completes. (Pixi)")
|
||||||
|
.Action<string>(ImportRobotOnline)
|
||||||
|
.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ImportRobotFile(string filepath)
|
||||||
|
{
|
||||||
|
string file;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
file = File.ReadAllText(filepath);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logging.CommandLogError($"Failed to load robot data. Reason: {e.Message}");
|
||||||
|
Logging.MetaLog(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
RobotStruct? robot = CubeUtility.ParseRobotInfo(file);
|
||||||
|
if (!robot.HasValue)
|
||||||
|
{
|
||||||
|
Logging.CommandLogError($"Failed to parse robot data. File format was not recognised.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
float3 position = new Player(PlayerType.Local).Position;
|
||||||
|
position.y += (float)blockSize;
|
||||||
|
CubeInfo[] cubes = CubeUtility.ParseCubes(robot.Value);
|
||||||
|
for (int c = 0; c < cubes.Length; c++) // sometimes I wish this were C++
|
||||||
|
{
|
||||||
|
CubeInfo cube = cubes[c];
|
||||||
|
float3 realPosition = (cube.position * (float)blockSize) + position;
|
||||||
|
Block newBlock = Block.PlaceNew(cube.block, realPosition, cube.rotation, cube.color, cube.darkness, scale: cube.scale);
|
||||||
|
// the goal is for this to never evaluate to true (ie all cubes are translated correctly)
|
||||||
|
if (!string.IsNullOrEmpty(cube.placeholder) && cube.block == BlockIDs.TextBlock)
|
||||||
|
{
|
||||||
|
newBlock.Specialise<TextBlock>().Text = cube.placeholder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Logging.CommandLog($"Placed {robot.Value.name} by {robot.Value.addedByDisplayName} ({cubes.Length} cubes) beside you");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ImportRobotOnline(string robotName)
|
||||||
|
{
|
||||||
|
Stopwatch timer = Stopwatch.StartNew();
|
||||||
|
// download robot data
|
||||||
|
RobotStruct robot;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
RobotBriefStruct[] botList = RoboAPIUtility.ListRobots(robotName);
|
||||||
|
if (botList.Length == 0)
|
||||||
|
throw new Exception("Failed to find robot");
|
||||||
|
robot = RoboAPIUtility.QueryRobotInfo(botList[0].itemId);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logging.CommandLogError($"Failed to download robot data. Reason: {e.Message}");
|
||||||
|
Logging.MetaLog(e);
|
||||||
|
timer.Stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
timer.Stop();
|
||||||
|
Logging.MetaLog($"Completed API calls in {timer.ElapsedMilliseconds}ms");
|
||||||
|
float3 position = new Player(PlayerType.Local).Position;
|
||||||
|
position.y += (float)blockSize;
|
||||||
|
CubeInfo[] cubes = CubeUtility.ParseCubes(robot);
|
||||||
|
for (int c = 0; c < cubes.Length; c++) // sometimes I wish this were C++
|
||||||
|
{
|
||||||
|
CubeInfo cube = cubes[c];
|
||||||
|
float3 realPosition = (cube.position * (float)blockSize) + position;
|
||||||
|
Block newBlock = Block.PlaceNew(cube.block, realPosition, cube.rotation, cube.color, cube.darkness, scale: cube.scale);
|
||||||
|
// the goal is for this to never evaluate to true (ie all cubes are translated correctly)
|
||||||
|
if (!string.IsNullOrEmpty(cube.placeholder) && cube.block == BlockIDs.TextBlock)
|
||||||
|
{
|
||||||
|
newBlock.Specialise<TextBlock>().Text = cube.placeholder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Logging.CommandLog($"Placed {robot.name} by {robot.addedByDisplayName} ({cubes.Length} cubes) beside you");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
30
Pixi/Robots/RobotStruct.cs
Normal file
30
Pixi/Robots/RobotStruct.cs
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
using System;
|
||||||
|
namespace Pixi.Robots
|
||||||
|
{
|
||||||
|
public struct RobotStruct
|
||||||
|
{
|
||||||
|
public int id;
|
||||||
|
|
||||||
|
public string name;
|
||||||
|
|
||||||
|
public string description;
|
||||||
|
|
||||||
|
public string thumbnail;
|
||||||
|
|
||||||
|
public string addedBy;
|
||||||
|
|
||||||
|
public string addedByDisplayName;
|
||||||
|
|
||||||
|
public int cpu;
|
||||||
|
|
||||||
|
public int totalRobotRanking;
|
||||||
|
|
||||||
|
public string cubeData;
|
||||||
|
|
||||||
|
public string colourData;
|
||||||
|
|
||||||
|
public bool featured;
|
||||||
|
|
||||||
|
public string cubeAmounts; // this is sent incorrectly by the API server (it's actually a Dictionary<string, int>)
|
||||||
|
}
|
||||||
|
}
|
1
Pixi/cubes-id.json
Normal file
1
Pixi/cubes-id.json
Normal file
File diff suppressed because one or more lines are too long
43
README.md
43
README.md
|
@ -15,17 +15,36 @@ Since Pixi places vanilla Gamecraft blocks, imported images should be visible wi
|
||||||
|
|
||||||
### Commands
|
### Commands
|
||||||
|
|
||||||
`PixiScale [width] [height]` sets the block canvas size (usually you'll want this to be the same size as your image).
|
`PixiText @"[image]"` converts an image to text and places a text block with that text beside you.
|
||||||
When conversion using `Pixi2D` is done, if the canvas is larger than your image the image will be repeated.
|
|
||||||
If the canvas is smaller than your image, the image will be cropped.
|
|
||||||
|
|
||||||
`Pixi2D "[image]"` converts an image to blocks and places it beside where you're standing (along the xy-plane).
|
`PixiConsole @"[image]" "[text block id]"` converts an image to text and places a console block beside you which changes the specified text block.
|
||||||
|
|
||||||
|
`Pixi2D @"[image]"` converts an image to blocks and places it beside you (along the xy-plane).
|
||||||
|
|
||||||
|
Anything between `[` and `]` characters is a command argument you must provide by replacing everything inside and including the square brackets.
|
||||||
|
An argument like `[dog name]` is an argument named "dog name" and could be a value like `Clifford` or `doggo`,
|
||||||
|
and `@"[dog name]"` could be a value like `@"Clifford"` or `@"doggo"`.
|
||||||
|
|
||||||
|
For example, if you want to add an image called `pixel_art.png`, stored in Gamecraft's installation directory,
|
||||||
|
execute the command `Pixi2D @"pixel_art.png"` to load the image as blocks.
|
||||||
|
It's important to include the file extension, since Pixi isn't psychic (yet).
|
||||||
|
|
||||||
|
**EXPERIMENTAL**
|
||||||
|
|
||||||
|
`PixiBot @"[bot]"` downloads a bot from Robocraft's community Factory and places it beside you.
|
||||||
|
|
||||||
|
`PixiBotFile @"[bot]"` converts a `.bot` file from [rcbup](https://github.com/NGnius/rcbup) to blocks and places it beside you.
|
||||||
|
|
||||||
|
**NOTE**
|
||||||
|
|
||||||
|
Do not forget the `@"` before and `"` after the command argument, otherwise the command won't work.
|
||||||
If your image is not stored in the same folder as Gamecraft, you should specify the full filepath (eg `C:\path\to\image.png`) to the image.
|
If your image is not stored in the same folder as Gamecraft, you should specify the full filepath (eg `C:\path\to\image.png`) to the image.
|
||||||
This works best with `.PNG` images, but `.JPG` also works -- you just won't be able to use transparency-based features.
|
This works best with `.PNG` images, but `.JPG` also works -- you just won't be able to use transparency-based features.
|
||||||
|
Optionally, if you know your command argument won't have a backslash `\` in it, you can omit the `@` symbol.
|
||||||
|
|
||||||
For example, if you want to add an image called `pixel_art.png`,
|
`PixiThicc [depth]` sets the block thickness for `Pixi2D` image conversion.
|
||||||
with a resolution of 1920x1080, stored in Gamecraft's installation directory,
|
The depth should be a positive whole number, like 3 or 42, and not 3.14 or -42.
|
||||||
execute the command `PixiScale 1920 1080` to set the size and then `Pixi2D "pixel_art.png"` to load the image.
|
The default thickness is 1.
|
||||||
|
|
||||||
### Behaviour
|
### Behaviour
|
||||||
|
|
||||||
|
@ -75,3 +94,13 @@ I'd recommend Visual Studio Community Edition or JetBrains Rider for Windows and
|
||||||
|
|
||||||
If you've successfully completed setup, you should be able to build the Pixi project without errors.
|
If you've successfully completed setup, you should be able to build the Pixi project without errors.
|
||||||
If it doesn't work and you can't figure out why, ask for help on the [Exmods Discord server](https://discord.gg/xjnFxQV).
|
If it doesn't work and you can't figure out why, ask for help on the [Exmods Discord server](https://discord.gg/xjnFxQV).
|
||||||
|
|
||||||
|
# Disclaimer
|
||||||
|
|
||||||
|
Pixi, Exmods and NGnius are not endorsed or supported by Gamecraft or FreeJam.
|
||||||
|
Modify Gamecraft at your own risk.
|
||||||
|
Read the LICENSE file for licensing information.
|
||||||
|
Please don't sue this project's contributors (that's what all disclaimers boil down to, right?).
|
||||||
|
|
||||||
|
Pixi is not a psychic overlord which secretly rules the world.
|
||||||
|
Well, not this world at least.
|
||||||
|
|
Loading…
Reference in a new issue