using System; using System.Collections.Generic; using System.Linq; using Gamecraft.Wires; using Gamecraft.Wires.ChannelsCommon; using GamecraftModdingAPI; using GamecraftModdingAPI.Blocks; using GamecraftModdingAPI.Commands; using GamecraftModdingAPI.Players; using GamecraftModdingAPI.Utility; using IllusionPlugin; using RobocraftX.CommandLine.Custom; using Unity.Mathematics; using uREPL; using Main = GamecraftModdingAPI.Main; namespace BlockMod { public class BlockMod : IPlugin { private Block[] blocks = new Block[0]; private Block refBlock; public void OnApplicationStart() { Main.Init(); GameClient.SetDebugInfo("BlockModInfo", GetBlockInfo); RegisterBlockCommand("scaleBlocks", "Scales the blocks you're looking at, relative to current size (current scale * new scale)." + " The block you're looking at stays where it is, everything else is moved next to it.", (scaleX, scaleY, scaleZ, blocks, refBlock) => { if (!GameState.IsBuildMode()) return; //Scaling & positioning is weird in simulation if (CheckNoBlocks(blocks)) return; // ReSharper disable once PossibleNullReferenceException float3 reference = refBlock.Position; float3 scale = new float3(scaleX, scaleY, scaleZ); foreach (var block in blocks) { block.Scale *= scale; block.Position = reference + (block.Position - reference) * scale; } Logging.CommandLog("Blocks scaled."); }); RegisterBlockCommand("scaleIndividually", "Scales the blocks you're looking at, but doesn't move them." + "The scale is relative, 1 means no change. Look at a block previously scaled to scale all of the blocks that were connected to it.", (scaleX, scaleY, scaleZ, blocks, refBlock) => { if (!GameState.IsBuildMode()) return; //Scaling & positioning is weird in simulation float3 scale = new float3(scaleX, scaleY, scaleZ); foreach (var block in blocks) block.Scale *= scale; }); RegisterBlockCommand("moveBlocks", "Moves the blocks around.", (x, y, z, blocks, refBlock) => { if (GameState.IsBuildMode()) foreach (var block in blocks) block.Position += new float3(x, y, z); else if (GameState.IsSimulationMode()) foreach (var body in GetSimBodies(blocks)) body.Position += new float3(x, y, z); }); RegisterBlockCommand("colorBlocks", "Colors the blocks you're looking at", (color, darkness, blocks, refBlock) => { if (!Enum.TryParse(color, true, out BlockColors clr)) { Logging.CommandLogWarning("Color " + color + " not found"); } foreach (var block in blocks) block.Color = new BlockColor {Color = clr, Darkness = darkness}; }); CommandBuilder.Builder("selectBlocksLookedAt", "Selects blocks (1 or more) to change. Only works in build mode, however the blocks can be changed afterwards." + " Paramter: whether one (true) or all connected (false) blocks should be selected.") .Action(single => { refBlock = new Player(PlayerType.Local).GetBlockLookedAt(); blocks = single ? new[] {refBlock} : refBlock?.GetConnectedCubes() ?? new Block[0]; Logging.CommandLog(blocks == null ? "Selection cleared." : blocks.Length + " blocks selected."); }).Build(); CommandBuilder.Builder("selectBlocksWithID", "Selects blocks with a specific object ID.") .Action(id => blocks = (refBlock = ObjectIdentifier.GetByID(id).FirstOrDefault())?.GetConnectedCubes() ?? new Block[0]).Build(); CommandBuilder.Builder("selectSelectedBlocks", "Selects blocks that are box selected by the player.") .Action(() => { blocks = new Player(PlayerType.Local).GetSelectedBlocks(); refBlock = blocks.Length > 0 ? blocks[0] : null; }).Build(); ConsoleCommands.RegisterWithChannel("selectSendSignal", ch => { }, ChannelType.Object, "Sends a signal for selecting a given object ID for a command block."); RegisterBlockCommand("pushBlocks", "Adds velocity to the selected blocks. Only works in simulation.", (x, y, z, blocks, refBlock) => { foreach (var block in GetSimBodies(blocks)) block.Velocity += new float3(x, y, z); }); RegisterBlockCommand("pushRotateBlocks", "Adds angular velocity to the selected blocks. Only works in simulation.", (x, y, z, blocks, refBlock) => { foreach (var block in GetSimBodies(blocks)) block.AngularVelocity += new float3(x, y, z); }); CommandBuilder.Builder("pushPlayer", "Adds velocity to the player.") .Action((x, y, z) => { new Player(PlayerType.Local).Velocity += new float3(x, y, z); }).Build(); CommandBuilder.Builder("pushRotatePlayer", "Adds angular velocity to the player.") .Action((x, y, z) => { new Player(PlayerType.Local).AngularVelocity += new float3(x, y, z); }).Build(); } private string GetBlockInfo() { if (GameState.IsBuildMode()) return GetBlockInfoInBuildMode(); if (GameState.IsSimulationMode()) return GetBodyInfoInSimMode(); return "Switching modes..."; } private static string GetBlockInfoInBuildMode() { var block = new Player(PlayerType.Local).GetBlockLookedAt(); if (block == null) return ""; float3 pos = block.Position; float3 rot = block.Rotation; float3 scale = block.Scale; return $"Block: {block.Type} at {pos.x:F} {pos.y:F} {pos.z:F}\n" + $"- Rotation: {rot.x:F}° {rot.y:F}° {rot.z:F}°\n" + $"- Color: {block.Color.Color} darkness: {block.Color.Darkness}\n" + $"- Scale: {scale.x:F} {scale.y:F} {scale.z:F}\n" + $"- Label: {block.Label}\n" + $"- ID: {block.Id}"; } private static string GetBodyInfoInSimMode() { var body = new Player(PlayerType.Local).GetSimBodyLookedAt(); if (body == null) return GetBlockInfoInBuildMode(); float3 pos = body.Position; float3 rot = body.Rotation; float3 vel = body.Velocity; float3 ave = body.AngularVelocity; float3 com = body.CenterOfMass; return $"Body at {pos.x:F} {pos.y:F} {pos.z:F}\n" + $"- Rotation: {rot.x:F}° {rot.y:F}° {rot.z:F}°\n" + $"- Velocity: {vel.x:F} {vel.y:F} {vel.z:F}\n" + $"- Angular velocity: {ave.x:F} {ave.y:F} {ave.z:F}\n" + $"- {(body.Static ? "Static body" : $"Mass: {body.Mass:F} center: {com.x:F} {com.y:F} {com.z:F}")}"; } private bool CheckNoBlocks(Block[] blocks) { if (blocks.Length == 0) { Logging.CommandLogWarning("No blocks selected. Use selectBlocks first."); return true; } return false; } private IEnumerable GetSimBodies(Block[] blocks) => blocks.Select(block => block.GetSimBody()).Distinct(); private Block[] SelectBlocks(byte id) { var blocks = ObjectIdentifier.GetBySimID(id).SelectMany(block => block.GetConnectedCubes()).ToArray(); return blocks; } private Block[] SelectBlocks(char id) { var blocks = ObjectIdentifier.GetByID(id).SelectMany(oid => oid.GetConnectedCubes()) .ToArray(); return blocks; } private void RegisterBlockCommandInternal(string name, string desc, Action action) { RuntimeCommands.Register(name, a1 => { action(a1, blocks, refBlock); }, desc); ConsoleCommands.RegisterWithChannel(name, (a1, ch) => { Console.WriteLine($"Command {name} with args {a1} and channel {ch} executing"); var blks = SelectBlocks(ch); action(a1, blks, blks[0]); }, ChannelType.Object, desc); } private void RegisterBlockCommand(string name, string desc, Action action) { RegisterBlockCommandInternal(name, desc, (args, bs, b) => { var argsa = args.Split(' '); if (argsa.Length < 3) { Log.Error("Too few arguments. Needed arguments are: and [id] is optional."); return; } if (!float.TryParse(argsa[0], out float x) || !float.TryParse(argsa[1], out float y) || !float.TryParse(argsa[2], out float z)) { Log.Error("Could not parse arguments as floats."); return; } if (argsa.Length > 3) { if (argsa[3].Length == 0) { Log.Error("Missing channel."); return; } var blocks = SelectBlocks(argsa[3][0]); if (CheckNoBlocks(blocks)) return; action(x, y, z, blocks, blocks[0]); } else if (!CheckNoBlocks(bs)) action(x, y, z, bs, b); }); } private void RegisterBlockCommand(string name, string desc, Action action) { RegisterBlockCommandInternal(name, desc, (args, bs, b) => { var argsa = args.Split(' '); if (argsa.Length < 2) { Log.Error("Too few arguments. Needed arguments are: and [id] is optional."); return; } if (!byte.TryParse(argsa[1], out byte darkness)) { Log.Error("Could not parse color darkness."); return; } if (argsa.Length > 2) { if (argsa[2].Length == 0) { Log.Error("Missing channel."); return; } var blocks = SelectBlocks(argsa[2][0]); if (CheckNoBlocks(blocks)) return; action(argsa[0], darkness, blocks, blocks[0]); } else if(!CheckNoBlocks(bs)) action(argsa[0], darkness, bs, b); }); } public void OnApplicationQuit() { } public void OnLevelWasLoaded(int level) { } public void OnLevelWasInitialized(int level) { } public void OnUpdate() { } public void OnFixedUpdate() { } public string Name { get; } = "BlockMod"; public string Version { get; } = "v1.0.0"; } }