Merge branch 'delegating' into preview
This commit is contained in:
commit
c5e9599c46
22 changed files with 464 additions and 596 deletions
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Reflection;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Linq;
|
||||||
|
using System.Reflection.Emit;
|
||||||
|
|
||||||
using Svelto.ECS;
|
using Svelto.ECS;
|
||||||
using Svelto.ECS.EntityStructs;
|
using Svelto.ECS.EntityStructs;
|
||||||
|
@ -54,23 +55,13 @@ namespace GamecraftModdingAPI
|
||||||
float3 rotation = default, BlockColors color = BlockColors.Default, byte darkness = 0,
|
float3 rotation = default, BlockColors color = BlockColors.Default, byte darkness = 0,
|
||||||
int uscale = 1, float3 scale = default, Player player = null)
|
int uscale = 1, float3 scale = default, Player player = null)
|
||||||
{
|
{
|
||||||
if (PlacementEngine.IsInGame && GameState.IsBuildMode())
|
return PlaceNew<Block>(block, position, rotation, color, darkness, uscale, scale, player);
|
||||||
{
|
|
||||||
return new Block(PlacementEngine.PlaceBlock(block, color, darkness,
|
|
||||||
position, uscale, scale, player, rotation));
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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 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.
|
/// 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.
|
/// The placed block will be a complete block with a placement grid and collision which will be saved along with the game.
|
||||||
/// <para></para>
|
|
||||||
/// <para>This method waits for the block to be constructed in the game which may take a significant amount of time.
|
|
||||||
/// Only use this to place a single block.
|
|
||||||
/// For placing multiple blocks, use PlaceNew() then AsyncUtils.WaitForSubmission() when done with placing blocks.</para>
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="block">The block's type</param>
|
/// <param name="block">The block's type</param>
|
||||||
/// <param name="color">The block's color</param>
|
/// <param name="color">The block's color</param>
|
||||||
|
@ -81,23 +72,18 @@ namespace GamecraftModdingAPI
|
||||||
/// <param name="scale">The block's non-uniform scale - 0 means <paramref name="uscale"/> is used</param>
|
/// <param name="scale">The block's non-uniform scale - 0 means <paramref name="uscale"/> is used</param>
|
||||||
/// <param name="player">The player who placed the block</param>
|
/// <param name="player">The player who placed the block</param>
|
||||||
/// <returns>The placed block or null if failed</returns>
|
/// <returns>The placed block or null if failed</returns>
|
||||||
public static async Task<Block> PlaceNewAsync(BlockIDs block, float3 position,
|
public static T PlaceNew<T>(BlockIDs block, float3 position,
|
||||||
float3 rotation = default, BlockColors color = BlockColors.Default, byte darkness = 0,
|
float3 rotation = default, BlockColors color = BlockColors.Default, byte darkness = 0,
|
||||||
int uscale = 1, float3 scale = default, Player player = null)
|
int uscale = 1, float3 scale = default, Player player = null) where T : Block
|
||||||
{
|
{
|
||||||
if (PlacementEngine.IsInGame && GameState.IsBuildMode())
|
if (PlacementEngine.IsInGame && GameState.IsBuildMode())
|
||||||
{
|
{
|
||||||
try
|
var egid = PlacementEngine.PlaceBlock(block, color, darkness,
|
||||||
{
|
position, uscale, scale, player, rotation, out var initializer);
|
||||||
var ret = new Block(PlacementEngine.PlaceBlock(block, color, darkness,
|
var bl = New<T>(egid.entityID, egid.groupID);
|
||||||
position, uscale, scale, player, rotation));
|
bl.InitData.Group = BlockEngine.InitGroup(initializer);
|
||||||
await AsyncUtils.WaitForSubmission();
|
Placed += bl.OnPlacedInit;
|
||||||
return ret;
|
return bl;
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Logging.MetaDebugLog(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -109,7 +95,7 @@ namespace GamecraftModdingAPI
|
||||||
/// <returns>The block object</returns>
|
/// <returns>The block object</returns>
|
||||||
public static Block GetLastPlacedBlock()
|
public static Block GetLastPlacedBlock()
|
||||||
{
|
{
|
||||||
return new Block(BlockIdentifiers.LatestBlockID);
|
return New<Block>(BlockIdentifiers.LatestBlockID);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -130,9 +116,91 @@ namespace GamecraftModdingAPI
|
||||||
remove => BlockEventsEngine.Removed -= value;
|
remove => BlockEventsEngine.Removed -= value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Dictionary<Type, Func<EGID, Block>> initializers = new Dictionary<Type, Func<EGID, Block>>();
|
||||||
|
|
||||||
|
private static Dictionary<Type, ExclusiveGroupStruct[]> typeToGroup =
|
||||||
|
new Dictionary<Type, ExclusiveGroupStruct[]>
|
||||||
|
{
|
||||||
|
{typeof(ConsoleBlock), new[] {CommonExclusiveGroups.BUILD_CONSOLE_BLOCK_GROUP}},
|
||||||
|
{typeof(Motor), new[] {CommonExclusiveGroups.BUILD_MOTOR_BLOCK_GROUP}},
|
||||||
|
{typeof(Piston), new[] {CommonExclusiveGroups.BUILD_PISTON_BLOCK_GROUP}},
|
||||||
|
{typeof(Servo), new[] {CommonExclusiveGroups.BUILD_SERVO_BLOCK_GROUP}},
|
||||||
|
{
|
||||||
|
typeof(SpawnPoint),
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
CommonExclusiveGroups.BUILD_SPAWNPOINT_BLOCK_GROUP,
|
||||||
|
CommonExclusiveGroups.BUILD_BUILDINGSPAWN_BLOCK_GROUP
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{typeof(TextBlock), new[] {CommonExclusiveGroups.BUILD_TEXT_BLOCK_GROUP}},
|
||||||
|
{typeof(Timer), new[] {CommonExclusiveGroups.BUILD_TIMER_BLOCK_GROUP}}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs a new instance of T with the given ID and group using dynamically created delegates.
|
||||||
|
/// It's equivalent to new T(EGID) with a minimal overhead thanks to caching the created delegates.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">The block ID</param>
|
||||||
|
/// <param name="group">The block group</param>
|
||||||
|
/// <typeparam name="T">The block's type or Block itself</typeparam>
|
||||||
|
/// <returns>An instance of the provided type</returns>
|
||||||
|
/// <exception cref="BlockTypeException">The block group doesn't match or cannot be found</exception>
|
||||||
|
/// <exception cref="MissingMethodException">The block class doesn't have the needed constructor</exception>
|
||||||
|
private static T New<T>(uint id, ExclusiveGroupStruct? group = null) where T : Block
|
||||||
|
{
|
||||||
|
var type = typeof(T);
|
||||||
|
EGID egid;
|
||||||
|
if (!group.HasValue)
|
||||||
|
{
|
||||||
|
if (typeToGroup.TryGetValue(type, out var gr) && gr.Length == 1)
|
||||||
|
egid = new EGID(id, gr[0]);
|
||||||
|
else
|
||||||
|
egid = BlockEngine.FindBlockEGID(id) ?? throw new BlockTypeException("Could not find block group!");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
egid = new EGID(id, group.Value);
|
||||||
|
if (typeToGroup.TryGetValue(type, out var gr)
|
||||||
|
&& gr.All(egs => egs != group.Value)) //If this subclass has a specific group, then use that - so Block should still work
|
||||||
|
throw new BlockTypeException($"Incompatible block type! Type {type.Name} belongs to group {gr.Select(g => g.ToString()).Aggregate((a, b) => a + ", " + b)} instead of {group.Value}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (initializers.TryGetValue(type, out var func))
|
||||||
|
{
|
||||||
|
var bl = (T) func(egid);
|
||||||
|
return bl;
|
||||||
|
}
|
||||||
|
|
||||||
|
//https://stackoverflow.com/a/10593806/2703239
|
||||||
|
var ctor = type.GetConstructor(new[] {typeof(EGID)});
|
||||||
|
if (ctor == null)
|
||||||
|
throw new MissingMethodException("There is no constructor with an EGID parameter for this object");
|
||||||
|
DynamicMethod dynamic = new DynamicMethod(string.Empty,
|
||||||
|
type,
|
||||||
|
new[] {typeof(EGID)},
|
||||||
|
type);
|
||||||
|
ILGenerator il = dynamic.GetILGenerator();
|
||||||
|
|
||||||
|
il.DeclareLocal(type);
|
||||||
|
il.Emit(OpCodes.Ldarg_0); //Load EGID and pass to constructor
|
||||||
|
il.Emit(OpCodes.Newobj, ctor); //Call constructor
|
||||||
|
//il.Emit(OpCodes.Stloc_0); - doesn't seem like we need these
|
||||||
|
//il.Emit(OpCodes.Ldloc_0);
|
||||||
|
il.Emit(OpCodes.Ret);
|
||||||
|
|
||||||
|
func = (Func<EGID, T>) dynamic.CreateDelegate(typeof(Func<EGID, T>));
|
||||||
|
initializers.Add(type, func);
|
||||||
|
var block = (T) func(egid);
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
public Block(EGID id)
|
public Block(EGID id)
|
||||||
{
|
{
|
||||||
Id = id;
|
Id = id;
|
||||||
|
if (typeToGroup.TryGetValue(GetType(), out var groups) && groups.All(gr => gr != id.groupID))
|
||||||
|
throw new BlockTypeException("The block has the wrong group! The type is " + GetType() +
|
||||||
|
" while the group is " + id.groupID);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -147,16 +215,18 @@ namespace GamecraftModdingAPI
|
||||||
|
|
||||||
public EGID Id { get; }
|
public EGID Id { get; }
|
||||||
|
|
||||||
|
internal BlockEngine.BlockInitData InitData;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The block's current position or zero if the block no longer exists.
|
/// 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.
|
/// A block is 0.2 wide by default in terms of position.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float3 Position
|
public float3 Position
|
||||||
{
|
{
|
||||||
get => Exists ? MovementEngine.GetPosition(Id) : float3.zero;
|
get => MovementEngine.GetPosition(Id, InitData);
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (Exists) MovementEngine.MoveBlock(Id, value);
|
MovementEngine.MoveBlock(Id, InitData, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,10 +235,10 @@ namespace GamecraftModdingAPI
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float3 Rotation
|
public float3 Rotation
|
||||||
{
|
{
|
||||||
get => Exists ? RotationEngine.GetRotation(Id) : float3.zero;
|
get => RotationEngine.GetRotation(Id, InitData);
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (Exists) RotationEngine.RotateBlock(Id, value);
|
RotationEngine.RotateBlock(Id, InitData, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,12 +248,11 @@ namespace GamecraftModdingAPI
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float3 Scale
|
public float3 Scale
|
||||||
{
|
{
|
||||||
get => BlockEngine.GetBlockInfo<ScalingEntityStruct>(Id).scale;
|
get => BlockEngine.GetBlockInfo(this, (ScalingEntityStruct st) => st.scale);
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
|
BlockEngine.SetBlockInfo(this, (ref ScalingEntityStruct st, float3 val) => st.scale = val, value);
|
||||||
if (!Exists) return; //UpdateCollision needs the block to exist
|
if (!Exists) return; //UpdateCollision needs the block to exist
|
||||||
ref var scaling = ref BlockEngine.GetBlockInfo<ScalingEntityStruct>(Id);
|
|
||||||
scaling.scale = value;
|
|
||||||
ScalingEngine.UpdateCollision(Id);
|
ScalingEngine.UpdateCollision(Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -194,11 +263,11 @@ namespace GamecraftModdingAPI
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int UniformScale
|
public int UniformScale
|
||||||
{
|
{
|
||||||
get => BlockEngine.GetBlockInfo<UniformBlockScaleEntityStruct>(Id).scaleFactor;
|
get => BlockEngine.GetBlockInfo(this, (UniformBlockScaleEntityStruct st) => st.scaleFactor);
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
ref var scaleStruct = ref BlockEngine.GetBlockInfo<UniformBlockScaleEntityStruct>(Id);
|
BlockEngine.SetBlockInfo(this, (ref UniformBlockScaleEntityStruct st, int val) => st.scaleFactor = val,
|
||||||
scaleStruct.scaleFactor = value;
|
value);
|
||||||
Scale = new float3(value, value, value);
|
Scale = new float3(value, value, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -210,8 +279,7 @@ namespace GamecraftModdingAPI
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var id = (BlockIDs) BlockEngine.GetBlockInfo<DBEntityStruct>(Id, out var exists).DBID;
|
return BlockEngine.GetBlockInfo(this, (DBEntityStruct st) => (BlockIDs) st.DBID, BlockIDs.Invalid);
|
||||||
return exists ? id : BlockIDs.Invalid;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,17 +290,19 @@ namespace GamecraftModdingAPI
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
byte index = BlockEngine.GetBlockInfo<ColourParameterEntityStruct>(Id, out var exists).indexInPalette;
|
byte index = BlockEngine.GetBlockInfo(this, (ColourParameterEntityStruct st) => st.indexInPalette,
|
||||||
if (!exists) index = byte.MaxValue;
|
byte.MaxValue);
|
||||||
return new BlockColor(index);
|
return new BlockColor(index);
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
ref var color = ref BlockEngine.GetBlockInfo<ColourParameterEntityStruct>(Id);
|
BlockEngine.SetBlockInfo(this, (ref ColourParameterEntityStruct color, BlockColor val) =>
|
||||||
color.indexInPalette = (byte)(value.Color + value.Darkness * 10);
|
{
|
||||||
color.overridePaletteColour = false;
|
color.indexInPalette = (byte) (val.Color + val.Darkness * 10);
|
||||||
color.needsUpdate = true;
|
color.overridePaletteColour = false;
|
||||||
BlockEngine.SetBlockColorFromPalette(ref color);
|
color.needsUpdate = true;
|
||||||
|
BlockEngine.SetBlockColorFromPalette(ref color);
|
||||||
|
}, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,32 +311,37 @@ namespace GamecraftModdingAPI
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float4 CustomColor
|
public float4 CustomColor
|
||||||
{
|
{
|
||||||
get => BlockEngine.GetBlockInfo<ColourParameterEntityStruct>(Id).overriddenColour;
|
get => BlockEngine.GetBlockInfo(this, (ColourParameterEntityStruct st) => st.overriddenColour);
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
ref var color = ref BlockEngine.GetBlockInfo<ColourParameterEntityStruct>(Id);
|
BlockEngine.SetBlockInfo(this, (ref ColourParameterEntityStruct color, float4 val) =>
|
||||||
color.overriddenColour = value;
|
{
|
||||||
color.overridePaletteColour = true;
|
color.overriddenColour = val;
|
||||||
color.needsUpdate = true;
|
color.overridePaletteColour = true;
|
||||||
|
color.needsUpdate = true;
|
||||||
|
}, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The short text displayed on the block if applicable, or null.
|
/// The text displayed on the block if applicable, or null.
|
||||||
/// Setting it is temporary to the session, it won't be saved.
|
/// Setting it is temporary to the session, it won't be saved.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Label
|
public string Label
|
||||||
{
|
{
|
||||||
get => BlockEngine.GetBlockInfo<TextLabelEntityViewStruct>(Id).textLabelComponent?.text;
|
get => BlockEngine.GetBlockInfo(this, (TextLabelEntityViewStruct st) => st.textLabelComponent?.text);
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
ref var text = ref BlockEngine.GetBlockInfo<TextLabelEntityViewStruct>(Id);
|
BlockEngine.SetBlockInfo(this, (ref TextLabelEntityViewStruct text, string val) =>
|
||||||
if (text.textLabelComponent != null) text.textLabelComponent.text = value;
|
{
|
||||||
|
if (text.textLabelComponent != null) text.textLabelComponent.text = val;
|
||||||
|
}, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the block exists. The other properties will return a default value if the block doesn't exist.
|
/// 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>
|
/// </summary>
|
||||||
public bool Exists => BlockEngine.BlockExists(Id);
|
public bool Exists => BlockEngine.BlockExists(Id);
|
||||||
|
|
||||||
|
@ -282,14 +357,21 @@ namespace GamecraftModdingAPI
|
||||||
public bool Remove() => RemovalEngine.RemoveBlock(Id);
|
public bool Remove() => RemovalEngine.RemoveBlock(Id);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the rigid body of the cluster of blocks this one belongs to during simulation.
|
/// 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.
|
/// Can be used to apply forces or move the block around while the simulation is running.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The SimBody of the cluster or null if the block doesn't exist.</returns>
|
/// <returns>The SimBody of the chunk or null if the block doesn't exist.</returns>
|
||||||
public SimBody GetSimBody()
|
public SimBody GetSimBody()
|
||||||
{
|
{
|
||||||
uint id = BlockEngine.GetBlockInfo<GridConnectionsEntityStruct>(Id, out var exists).machineRigidBodyId;
|
return BlockEngine.GetBlockInfo(this,
|
||||||
return exists ? new SimBody(id) : null;
|
(GridConnectionsEntityStruct st) => new SimBody(st.machineRigidBodyId));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPlacedInit(object sender, BlockPlacedRemovedEventArgs e)
|
||||||
|
{ //Member method instead of lambda to avoid constantly creating delegates
|
||||||
|
if (e.ID != Id) return;
|
||||||
|
Placed -= OnPlacedInit; //And we can reference it
|
||||||
|
InitData = default; //Remove initializer as it's no longer valid - if the block gets removed it shouldn't be used again
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
|
@ -344,12 +426,11 @@ namespace GamecraftModdingAPI
|
||||||
// C# can't cast to a child of Block unless the object was originally that child type
|
// C# can't cast to a child of Block unless the object was originally that child type
|
||||||
// And C# doesn't let me make implicit cast operators for child types
|
// And C# doesn't let me make implicit cast operators for child types
|
||||||
// So thanks to Microsoft, we've got this horrible implementation using reflection
|
// So thanks to Microsoft, we've got this horrible implementation using reflection
|
||||||
ConstructorInfo ctor = typeof(T).GetConstructor(types: new System.Type[] { typeof(EGID) });
|
|
||||||
if (ctor == null)
|
//Lets improve that using delegates
|
||||||
{
|
var block = New<T>(Id.entityID, Id.groupID);
|
||||||
throw new BlockSpecializationException("Specialized block constructor does not accept an EGID");
|
block.InitData = this.InitData;
|
||||||
}
|
return block;
|
||||||
return (T)ctor.Invoke(new object[] { Id });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
using Gamecraft.Wires;
|
using Gamecraft.Wires;
|
||||||
|
@ -10,14 +11,13 @@ using Svelto.DataStructures;
|
||||||
using Svelto.ECS;
|
using Svelto.ECS;
|
||||||
|
|
||||||
using GamecraftModdingAPI.Engines;
|
using GamecraftModdingAPI.Engines;
|
||||||
using GamecraftModdingAPI.Utility;
|
|
||||||
|
|
||||||
namespace GamecraftModdingAPI.Blocks
|
namespace GamecraftModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Engine for executing general block actions
|
/// Engine for executing general block actions
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class BlockEngine : IApiEngine
|
public partial class BlockEngine : IApiEngine
|
||||||
{
|
{
|
||||||
public string Name { get; } = "GamecraftModdingAPIBlockGameEngine";
|
public string Name { get; } = "GamecraftModdingAPIBlockGameEngine";
|
||||||
|
|
||||||
|
@ -25,8 +25,6 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
|
|
||||||
public bool isRemovable => false;
|
public bool isRemovable => false;
|
||||||
|
|
||||||
internal bool Synced = true;
|
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -61,66 +59,55 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
color.paletteColour = paletteEntry.Colour;
|
color.paletteColour = paletteEntry.Colour;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get a struct of a block. Can be used to set properties.
|
|
||||||
/// Returns a default value if not found.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="blockID">The block's ID</param>
|
|
||||||
/// <typeparam name="T">The struct to query</typeparam>
|
|
||||||
/// <returns>An editable reference to the struct</returns>
|
|
||||||
public ref T GetBlockInfo<T>(EGID blockID) where T : struct, IEntityComponent
|
public ref T GetBlockInfo<T>(EGID blockID) where T : struct, IEntityComponent
|
||||||
{
|
{
|
||||||
if (!Synced)
|
|
||||||
{
|
|
||||||
Sync();
|
|
||||||
Synced = true;
|
|
||||||
}
|
|
||||||
if (entitiesDB.Exists<T>(blockID))
|
if (entitiesDB.Exists<T>(blockID))
|
||||||
return ref entitiesDB.QueryEntity<T>(blockID);
|
return ref entitiesDB.QueryEntity<T>(blockID);
|
||||||
T[] structHolder = new T[1]; //Create something that can be referenced
|
T[] structHolder = new T[1]; //Create something that can be referenced
|
||||||
return ref structHolder[0]; //Gets a default value automatically
|
return ref structHolder[0]; //Gets a default value automatically
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public U GetBlockInfo<T, U>(Block block, Func<T, U> getter,
|
||||||
/// Get a struct of a block. Can be used to set properties.
|
U def = default) where T : struct, IEntityComponent
|
||||||
/// Returns a default value if not found.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="blockID">The block's ID</param>
|
|
||||||
/// <param name="exists">Whether the specified struct exists for the block</param>
|
|
||||||
/// <typeparam name="T">The struct to query</typeparam>
|
|
||||||
/// <returns>An editable reference to the struct</returns>
|
|
||||||
public ref T GetBlockInfo<T>(EGID blockID, out bool exists) where T : struct, IEntityComponent
|
|
||||||
{
|
{
|
||||||
if (!Synced)
|
if (entitiesDB.Exists<T>(block.Id))
|
||||||
|
return getter(entitiesDB.QueryEntity<T>(block.Id));
|
||||||
|
if (block.InitData.Group == null) return def;
|
||||||
|
var initializer = new EntityComponentInitializer(block.Id, block.InitData.Group);
|
||||||
|
if (initializer.Has<T>())
|
||||||
|
return getter(initializer.Get<T>());
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
|
||||||
|
public delegate void Setter<T, U>(ref T component, U value) where T : struct, IEntityComponent;
|
||||||
|
|
||||||
|
public void SetBlockInfo<T, U>(Block block, Setter<T, U> setter, U value) where T : struct, IEntityComponent
|
||||||
|
{
|
||||||
|
if (entitiesDB.Exists<T>(block.Id))
|
||||||
|
setter(ref entitiesDB.QueryEntity<T>(block.Id), value);
|
||||||
|
else if (block.InitData.Group != null)
|
||||||
{
|
{
|
||||||
Sync();
|
var initializer = new EntityComponentInitializer(block.Id, block.InitData.Group);
|
||||||
Synced = true;
|
T component = initializer.Has<T>() ? initializer.Get<T>() : default;
|
||||||
|
ref T structRef = ref component;
|
||||||
|
setter(ref structRef, value);
|
||||||
|
initializer.Init(structRef);
|
||||||
}
|
}
|
||||||
exists = entitiesDB.Exists<T>(blockID);
|
|
||||||
if (exists)
|
|
||||||
return ref entitiesDB.QueryEntity<T>(blockID);
|
|
||||||
T[] structHolder = new T[1];
|
|
||||||
return ref structHolder[0];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool BlockExists(EGID id)
|
public bool BlockExists(EGID blockID)
|
||||||
{
|
{
|
||||||
if (!Synced)
|
return entitiesDB.Exists<DBEntityStruct>(blockID);
|
||||||
{
|
|
||||||
Sync();
|
|
||||||
Synced = true;
|
|
||||||
}
|
|
||||||
return entitiesDB.Exists<DBEntityStruct>(id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool GetBlockInfoExists<T>(EGID blockID) where T : struct, IEntityComponent
|
public bool GetBlockInfoExists<T>(Block block) where T : struct, IEntityComponent
|
||||||
{
|
{
|
||||||
if (!Synced)
|
if (entitiesDB.Exists<T>(block.Id))
|
||||||
{
|
return true;
|
||||||
Sync();
|
if (block.InitData.Group == null)
|
||||||
Synced = true;
|
return false;
|
||||||
}
|
var init = new EntityComponentInitializer(block.Id, block.InitData.Group);
|
||||||
return entitiesDB.Exists<T>(blockID);
|
return init.Has<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public SimBody[] GetSimBodiesFromID(byte id)
|
public SimBody[] GetSimBodiesFromID(byte id)
|
||||||
|
@ -183,17 +170,6 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Synchronize newly created entity components with entities DB.
|
|
||||||
/// This forces a partial game tick, so it may be slow.
|
|
||||||
/// This also has the potential to make Gamecraft unstable.
|
|
||||||
/// Use this sparingly.
|
|
||||||
/// </summary>
|
|
||||||
private static void Sync()
|
|
||||||
{
|
|
||||||
DeterministicStepCompositionRootPatch.SubmitEntitiesNow();
|
|
||||||
}
|
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
public EntitiesDB GetEntitiesDB()
|
public EntitiesDB GetEntitiesDB()
|
||||||
{
|
{
|
||||||
|
|
52
GamecraftModdingAPI/Blocks/BlockEngineInit.cs
Normal file
52
GamecraftModdingAPI/Blocks/BlockEngineInit.cs
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
using System;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
|
||||||
|
using Svelto.DataStructures;
|
||||||
|
using Svelto.ECS;
|
||||||
|
using Svelto.ECS.Internal;
|
||||||
|
|
||||||
|
namespace GamecraftModdingAPI.Blocks
|
||||||
|
{
|
||||||
|
public partial class BlockEngine
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Holds information needed to construct a component initializer
|
||||||
|
/// </summary>
|
||||||
|
internal struct BlockInitData
|
||||||
|
{
|
||||||
|
public FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> Group;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal delegate FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> GetInitGroup(
|
||||||
|
EntityComponentInitializer initializer);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Accesses the group field of the initializer
|
||||||
|
/// </summary>
|
||||||
|
internal GetInitGroup InitGroup = CreateAccessor<GetInitGroup>("_group");
|
||||||
|
|
||||||
|
//https://stackoverflow.com/questions/55878525/unit-testing-ref-structs-with-private-fields-via-reflection
|
||||||
|
internal static TDelegate CreateAccessor<TDelegate>(string memberName) where TDelegate : Delegate
|
||||||
|
{
|
||||||
|
var invokeMethod = typeof(TDelegate).GetMethod("Invoke");
|
||||||
|
if (invokeMethod == null)
|
||||||
|
throw new InvalidOperationException($"{typeof(TDelegate)} signature could not be determined.");
|
||||||
|
|
||||||
|
var delegateParameters = invokeMethod.GetParameters();
|
||||||
|
if (delegateParameters.Length != 1)
|
||||||
|
throw new InvalidOperationException("Delegate must have a single parameter.");
|
||||||
|
|
||||||
|
var paramType = delegateParameters[0].ParameterType;
|
||||||
|
|
||||||
|
var objParam = Expression.Parameter(paramType, "obj");
|
||||||
|
var memberExpr = Expression.PropertyOrField(objParam, memberName);
|
||||||
|
Expression returnExpr = memberExpr;
|
||||||
|
if (invokeMethod.ReturnType != memberExpr.Type)
|
||||||
|
returnExpr = Expression.ConvertChecked(memberExpr, invokeMethod.ReturnType);
|
||||||
|
|
||||||
|
var lambda =
|
||||||
|
Expression.Lambda<TDelegate>(returnExpr, $"Access{paramType.Name}_{memberName}", new[] {objParam});
|
||||||
|
return lambda.Compile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,11 @@
|
||||||
using System;
|
using System;
|
||||||
using GamecraftModdingAPI.Engines;
|
|
||||||
using GamecraftModdingAPI.Utility;
|
|
||||||
using RobocraftX.Common;
|
using RobocraftX.Common;
|
||||||
using Svelto.ECS;
|
using Svelto.ECS;
|
||||||
|
|
||||||
|
using GamecraftModdingAPI.Engines;
|
||||||
|
using GamecraftModdingAPI.Utility;
|
||||||
|
|
||||||
namespace GamecraftModdingAPI.Blocks
|
namespace GamecraftModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
public class BlockEventsEngine : IReactionaryEngine<DBEntityStruct>
|
public class BlockEventsEngine : IReactionaryEngine<DBEntityStruct>
|
||||||
|
@ -16,6 +18,7 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
}
|
}
|
||||||
|
|
||||||
public EntitiesDB entitiesDB { get; set; }
|
public EntitiesDB entitiesDB { get; set; }
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -23,14 +26,21 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
public string Name { get; } = "GamecraftModdingAPIBlockEventsEngine";
|
public string Name { get; } = "GamecraftModdingAPIBlockEventsEngine";
|
||||||
public bool isRemovable { get; } = false;
|
public bool isRemovable { get; } = false;
|
||||||
|
|
||||||
|
private bool shouldAddRemove;
|
||||||
public void Add(ref DBEntityStruct entityComponent, EGID egid)
|
public void Add(ref DBEntityStruct entityComponent, EGID egid)
|
||||||
{
|
{
|
||||||
ExceptionUtil.InvokeEvent(Placed, this, new BlockPlacedRemovedEventArgs {ID = egid, Type = (BlockIDs) entityComponent.DBID});
|
if (!(shouldAddRemove = !shouldAddRemove))
|
||||||
|
return;
|
||||||
|
ExceptionUtil.InvokeEvent(Placed, this,
|
||||||
|
new BlockPlacedRemovedEventArgs {ID = egid, Type = (BlockIDs) entityComponent.DBID});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Remove(ref DBEntityStruct entityComponent, EGID egid)
|
public void Remove(ref DBEntityStruct entityComponent, EGID egid)
|
||||||
{
|
{
|
||||||
ExceptionUtil.InvokeEvent(Removed, this, new BlockPlacedRemovedEventArgs {ID = egid, Type = (BlockIDs) entityComponent.DBID});
|
if (!(shouldAddRemove = !shouldAddRemove))
|
||||||
|
return;
|
||||||
|
ExceptionUtil.InvokeEvent(Removed, this,
|
||||||
|
new BlockPlacedRemovedEventArgs {ID = egid, Type = (BlockIDs) entityComponent.DBID});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,5 +48,8 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
public EGID ID;
|
public EGID ID;
|
||||||
public BlockIDs Type;
|
public BlockIDs Type;
|
||||||
|
private Block block;
|
||||||
|
|
||||||
|
public Block Block => block ?? (block = new Block(ID));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,7 +6,7 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
public enum BlockIDs : ushort
|
public enum BlockIDs : ushort
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A custom value for the API. Doesn't exist for Gamecraft.
|
/// Called "nothing" in Gamecraft. (DBID.NOTHING)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Invalid = ushort.MaxValue,
|
Invalid = ushort.MaxValue,
|
||||||
AluminiumCube = 0,
|
AluminiumCube = 0,
|
||||||
|
|
|
@ -22,20 +22,19 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
}
|
}
|
||||||
|
|
||||||
[APITestCase(TestType.EditMode)]
|
[APITestCase(TestType.EditMode)]
|
||||||
public static void TestSync()
|
public static void TestInitProperty()
|
||||||
{
|
{
|
||||||
Block newBlock = Block.PlaceNew(BlockIDs.AluminiumCube, Unity.Mathematics.float3.zero + 2);
|
Block newBlock = Block.PlaceNew(BlockIDs.AluminiumCube, Unity.Mathematics.float3.zero + 2);
|
||||||
if (!Assert.CloseTo(newBlock.Position, (Unity.Mathematics.float3.zero + 2), $"Newly placed block at {newBlock.Position} is expected at {Unity.Mathematics.float3.zero + 2}.", "Newly placed block position matches.")) return;
|
if (!Assert.CloseTo(newBlock.Position, (Unity.Mathematics.float3.zero + 2), $"Newly placed block at {newBlock.Position} is expected at {Unity.Mathematics.float3.zero + 2}.", "Newly placed block position matches.")) return;
|
||||||
Assert.Equal(newBlock.Exists, true, "Newly placed block does not exist, possibly because Sync() skipped/missed/failed.", "Newly placed block exists, Sync() successful.");
|
//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)]
|
[APITestCase(TestType.EditMode)]
|
||||||
public static void TestTextBlock()
|
public static void TestTextBlock()
|
||||||
{
|
{
|
||||||
Block newBlock = Block.PlaceNew(BlockIDs.TextBlock, Unity.Mathematics.float3.zero + 1);
|
|
||||||
TextBlock textBlock = null; // Note: the assignment operation is a lambda, which slightly confuses the compiler
|
TextBlock textBlock = null; // Note: the assignment operation is a lambda, which slightly confuses the compiler
|
||||||
Assert.Errorless(() => { textBlock = newBlock.Specialise<TextBlock>(); }, "Block.Specialize<TextBlock>() raised an exception: ", "Block.Specialize<TextBlock>() completed without issue.");
|
Assert.Errorless(() => { textBlock = Block.PlaceNew<TextBlock>(BlockIDs.TextBlock, Unity.Mathematics.float3.zero + 1); }, "Block.PlaceNew<TextBlock>() raised an exception: ", "Block.PlaceNew<TextBlock>() completed without issue.");
|
||||||
if (!Assert.NotNull(textBlock, "Block.Specialize<TextBlock>() returned null, possibly because it failed silently.", "Specialized TextBlock is not null.")) return;
|
if (!Assert.NotNull(textBlock, "Block.PlaceNew<TextBlock>() returned null, possibly because it failed silently.", "Specialized TextBlock is not null.")) return;
|
||||||
if (!Assert.NotNull(textBlock.Text, "TextBlock.Text is null, possibly because it failed silently.", "TextBlock.Text is not null.")) return;
|
if (!Assert.NotNull(textBlock.Text, "TextBlock.Text is null, possibly because it failed silently.", "TextBlock.Text is not null.")) return;
|
||||||
if (!Assert.NotNull(textBlock.TextBlockId, "TextBlock.TextBlockId is null, possibly because it failed silently.", "TextBlock.TextBlockId is not null.")) return;
|
if (!Assert.NotNull(textBlock.TextBlockId, "TextBlock.TextBlockId is null, possibly because it failed silently.", "TextBlock.TextBlockId is not null.")) return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,79 +12,64 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
public class ConsoleBlock : Block
|
public class ConsoleBlock : Block
|
||||||
{
|
{
|
||||||
public static ConsoleBlock PlaceNew(float3 position,
|
public ConsoleBlock(EGID id): base(id)
|
||||||
float3 rotation = default, BlockColors color = BlockColors.Default, byte darkness = 0,
|
|
||||||
int uscale = 1, float3 scale = default, Player player = null)
|
|
||||||
{
|
{
|
||||||
if (PlacementEngine.IsInGame && GameState.IsBuildMode())
|
|
||||||
{
|
|
||||||
EGID id = PlacementEngine.PlaceBlock(BlockIDs.ConsoleBlock, color, darkness,
|
|
||||||
position, uscale, scale, player, rotation);
|
|
||||||
return new ConsoleBlock(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConsoleBlock(EGID id): base(id)
|
public ConsoleBlock(uint id): base(new EGID(id, CommonExclusiveGroups.BUILD_CONSOLE_BLOCK_GROUP))
|
||||||
{
|
{
|
||||||
if (!BlockEngine.GetBlockInfoExists<ConsoleBlockEntityStruct>(this.Id))
|
|
||||||
{
|
|
||||||
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConsoleBlock(uint id): base(new EGID(id, CommonExclusiveGroups.BUILD_CONSOLE_BLOCK_GROUP))
|
|
||||||
{
|
|
||||||
if (!BlockEngine.GetBlockInfoExists<ConsoleBlockEntityStruct>(this.Id))
|
|
||||||
{
|
|
||||||
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// custom console block properties
|
// custom console block properties
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Setting a nonexistent command will crash the game when switching to simulation
|
||||||
|
/// </summary>
|
||||||
public string Command
|
public string Command
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return BlockEngine.GetBlockInfo<ConsoleBlockEntityStruct>(Id).commandName;
|
return BlockEngine.GetBlockInfo(this, (ConsoleBlockEntityStruct st) => st.commandName);
|
||||||
}
|
}
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
BlockEngine.GetBlockInfo<ConsoleBlockEntityStruct>(Id).commandName.Set(value);
|
BlockEngine.SetBlockInfo(this, (ref ConsoleBlockEntityStruct st, string val) => st.commandName.Set(val),
|
||||||
|
value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Arg1
|
public string Arg1
|
||||||
{
|
{
|
||||||
get => BlockEngine.GetBlockInfo<ConsoleBlockEntityStruct>(Id).arg1;
|
get => BlockEngine.GetBlockInfo(this, (ConsoleBlockEntityStruct st) => st.arg1);
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
BlockEngine.GetBlockInfo<ConsoleBlockEntityStruct>(Id).arg1.Set(value);
|
BlockEngine.SetBlockInfo(this, (ref ConsoleBlockEntityStruct st, string val) => st.arg1.Set(val),
|
||||||
|
value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Arg2
|
public string Arg2
|
||||||
{
|
{
|
||||||
get => BlockEngine.GetBlockInfo<ConsoleBlockEntityStruct>(Id).arg2;
|
get => BlockEngine.GetBlockInfo(this, (ConsoleBlockEntityStruct st) => st.arg2);
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
BlockEngine.GetBlockInfo<ConsoleBlockEntityStruct>(Id).arg2.Set(value);
|
BlockEngine.SetBlockInfo(this, (ref ConsoleBlockEntityStruct st, string val) => st.arg2.Set(val),
|
||||||
}
|
value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Arg3
|
public string Arg3
|
||||||
{
|
{
|
||||||
get => BlockEngine.GetBlockInfo<ConsoleBlockEntityStruct>(Id).arg3;
|
get => BlockEngine.GetBlockInfo(this, (ConsoleBlockEntityStruct st) => st.arg3);
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
BlockEngine.GetBlockInfo<ConsoleBlockEntityStruct>(Id).arg3.Set(value);
|
BlockEngine.SetBlockInfo(this, (ref ConsoleBlockEntityStruct st, string val) => st.arg3.Set(val),
|
||||||
}
|
value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,43 +11,12 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
public class Motor : Block
|
public class Motor : Block
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Places a new motor.
|
|
||||||
/// Any valid motor type is accepted.
|
|
||||||
/// This re-implements Block.PlaceNew(...)
|
|
||||||
/// </summary>
|
|
||||||
public static new Motor PlaceNew(BlockIDs block, float3 position,
|
|
||||||
float3 rotation = default, BlockColors color = BlockColors.Default, byte darkness = 0,
|
|
||||||
int uscale = 1, float3 scale = default, Player player = null)
|
|
||||||
{
|
|
||||||
if (!(block == BlockIDs.MotorS || block == BlockIDs.MotorM))
|
|
||||||
{
|
|
||||||
throw new BlockTypeException($"Block is not a {typeof(Motor).Name} block");
|
|
||||||
}
|
|
||||||
if (PlacementEngine.IsInGame && GameState.IsBuildMode())
|
|
||||||
{
|
|
||||||
EGID id = PlacementEngine.PlaceBlock(block, color, darkness,
|
|
||||||
position, uscale, scale, player, rotation);
|
|
||||||
return new Motor(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Motor(EGID id) : base(id)
|
public Motor(EGID id) : base(id)
|
||||||
{
|
{
|
||||||
if (!BlockEngine.GetBlockInfoExists<MotorReadOnlyStruct>(this.Id))
|
|
||||||
{
|
|
||||||
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Motor(uint id): base(new EGID(id, CommonExclusiveGroups.BUILD_MOTOR_BLOCK_GROUP))
|
public Motor(uint id): base(new EGID(id, CommonExclusiveGroups.BUILD_MOTOR_BLOCK_GROUP))
|
||||||
{
|
{
|
||||||
if (!BlockEngine.GetBlockInfoExists<MotorReadOnlyStruct>(this.Id))
|
|
||||||
{
|
|
||||||
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// custom motor properties
|
// custom motor properties
|
||||||
|
@ -59,13 +28,12 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return BlockEngine.GetBlockInfo<MotorReadOnlyStruct>(Id).maxVelocity;
|
return BlockEngine.GetBlockInfo(this, (MotorReadOnlyStruct st) => st.maxVelocity);
|
||||||
}
|
}
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
ref MotorReadOnlyStruct motor = ref BlockEngine.GetBlockInfo<MotorReadOnlyStruct>(Id);
|
BlockEngine.SetBlockInfo(this, (ref MotorReadOnlyStruct st, float val) => st.maxVelocity = val, value);
|
||||||
motor.maxVelocity = value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,13 +44,12 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return BlockEngine.GetBlockInfo<MotorReadOnlyStruct>(Id).maxForce;
|
return BlockEngine.GetBlockInfo(this, (MotorReadOnlyStruct st) => st.maxForce);
|
||||||
}
|
}
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
ref MotorReadOnlyStruct motor = ref BlockEngine.GetBlockInfo<MotorReadOnlyStruct>(Id);
|
BlockEngine.SetBlockInfo(this, (ref MotorReadOnlyStruct st, float val) => st.maxForce = val, value);
|
||||||
motor.maxForce = value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,13 +60,12 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return BlockEngine.GetBlockInfo<MotorReadOnlyStruct>(Id).reverse;
|
return BlockEngine.GetBlockInfo(this, (MotorReadOnlyStruct st) => st.reverse);
|
||||||
}
|
}
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
ref MotorReadOnlyStruct motor = ref BlockEngine.GetBlockInfo<MotorReadOnlyStruct>(Id);
|
BlockEngine.SetBlockInfo(this, (ref MotorReadOnlyStruct st, bool val) => st.reverse = val, value);
|
||||||
motor.reverse = value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,8 +35,17 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
|
|
||||||
// implementations for Movement static class
|
// implementations for Movement static class
|
||||||
|
|
||||||
public float3 MoveBlock(EGID blockID, float3 vector)
|
internal float3 MoveBlock(EGID blockID, BlockEngine.BlockInitData data, float3 vector)
|
||||||
{
|
{
|
||||||
|
if (!entitiesDB.Exists<PositionEntityStruct>(blockID))
|
||||||
|
{
|
||||||
|
if (data.Group == null) return float3.zero;
|
||||||
|
var init = new EntityComponentInitializer(blockID, data.Group);
|
||||||
|
init.Init(new PositionEntityStruct {position = vector});
|
||||||
|
init.Init(new GridRotationStruct {position = vector});
|
||||||
|
init.Init(new LocalTransformEntityStruct {position = vector});
|
||||||
|
return vector;
|
||||||
|
}
|
||||||
ref PositionEntityStruct posStruct = ref this.entitiesDB.QueryEntity<PositionEntityStruct>(blockID);
|
ref PositionEntityStruct posStruct = ref this.entitiesDB.QueryEntity<PositionEntityStruct>(blockID);
|
||||||
ref GridRotationStruct gridStruct = ref this.entitiesDB.QueryEntity<GridRotationStruct>(blockID);
|
ref GridRotationStruct gridStruct = ref this.entitiesDB.QueryEntity<GridRotationStruct>(blockID);
|
||||||
ref LocalTransformEntityStruct transStruct = ref this.entitiesDB.QueryEntity<LocalTransformEntityStruct>(blockID);
|
ref LocalTransformEntityStruct transStruct = ref this.entitiesDB.QueryEntity<LocalTransformEntityStruct>(blockID);
|
||||||
|
@ -56,8 +65,14 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
return posStruct.position;
|
return posStruct.position;
|
||||||
}
|
}
|
||||||
|
|
||||||
public float3 GetPosition(EGID blockID)
|
internal float3 GetPosition(EGID blockID, BlockEngine.BlockInitData data)
|
||||||
{
|
{
|
||||||
|
if (!entitiesDB.Exists<PositionEntityStruct>(blockID))
|
||||||
|
{
|
||||||
|
if (data.Group == null) return float3.zero;
|
||||||
|
var init = new EntityComponentInitializer(blockID, data.Group);
|
||||||
|
return init.Has<PositionEntityStruct>() ? init.Get<PositionEntityStruct>().position : float3.zero;
|
||||||
|
}
|
||||||
ref PositionEntityStruct posStruct = ref this.entitiesDB.QueryEntity<PositionEntityStruct>(blockID);
|
ref PositionEntityStruct posStruct = ref this.entitiesDB.QueryEntity<PositionEntityStruct>(blockID);
|
||||||
return posStruct.position;
|
return posStruct.position;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,27 +8,22 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
public ObjectIdentifier(EGID id) : base(id)
|
public ObjectIdentifier(EGID id) : base(id)
|
||||||
{
|
{
|
||||||
if (!BlockEngine.GetBlockInfoExists<ObjectIdEntityStruct>(Id))
|
|
||||||
{
|
|
||||||
throw new BlockTypeException($"Block is not a {GetType().Name} block");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ObjectIdentifier(uint id) : base(new EGID(id, CommonExclusiveGroups.BUILD_OBJID_BLOCK_GROUP))
|
public ObjectIdentifier(uint id) : base(new EGID(id, CommonExclusiveGroups.BUILD_OBJID_BLOCK_GROUP))
|
||||||
{
|
{
|
||||||
if (!BlockEngine.GetBlockInfoExists<ObjectIdEntityStruct>(Id))
|
|
||||||
{
|
|
||||||
throw new BlockTypeException($"Block is not a {GetType().Name} block");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public char Identifier
|
public char Identifier
|
||||||
{
|
{
|
||||||
get => (char) (BlockEngine.GetBlockInfo<ObjectIdEntityStruct>(Id).objectId + 'A');
|
get => (char) BlockEngine.GetBlockInfo(this, (ObjectIdEntityStruct st) => st.objectId + 'A');
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
BlockEngine.GetBlockInfo<ObjectIdEntityStruct>(Id).objectId = (byte) (value - 'A');
|
BlockEngine.SetBlockInfo(this, (ref ObjectIdEntityStruct st, char val) =>
|
||||||
Label = value + ""; //The label isn't updated automatically
|
{
|
||||||
|
st.objectId = (byte) (val - 'A');
|
||||||
|
Label = val + ""; //The label isn't updated automatically
|
||||||
|
}, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +32,7 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public byte SimID
|
public byte SimID
|
||||||
{
|
{
|
||||||
get => BlockEngine.GetBlockInfo<ObjectIdEntityStruct>(Id).simObjectId;
|
get => BlockEngine.GetBlockInfo(this, (ObjectIdEntityStruct st) => st.simObjectId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -11,43 +11,12 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
public class Piston : Block
|
public class Piston : Block
|
||||||
{
|
{
|
||||||
/// <summary>
|
public Piston(EGID id) : base(id)
|
||||||
/// Places a new piston.
|
|
||||||
/// Any valid piston type is accepted.
|
|
||||||
/// This re-implements Block.PlaceNew(...)
|
|
||||||
/// </summary>
|
|
||||||
public static new Piston PlaceNew(BlockIDs block, float3 position,
|
|
||||||
float3 rotation = default, BlockColors color = BlockColors.Default, byte darkness = 0,
|
|
||||||
int uscale = 1, float3 scale = default, Player player = null)
|
|
||||||
{
|
{
|
||||||
if (!(block == BlockIDs.ServoPiston || block == BlockIDs.StepperPiston || block == BlockIDs.PneumaticPiston))
|
|
||||||
{
|
|
||||||
throw new BlockTypeException($"Block is not a {typeof(Piston).Name} block");
|
|
||||||
}
|
|
||||||
if (PlacementEngine.IsInGame && GameState.IsBuildMode())
|
|
||||||
{
|
|
||||||
EGID id = PlacementEngine.PlaceBlock(block, color, darkness,
|
|
||||||
position, uscale, scale, player, rotation);
|
|
||||||
return new Piston(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Piston(EGID id) : base(id)
|
|
||||||
{
|
|
||||||
if (!BlockEngine.GetBlockInfoExists<PistonReadOnlyStruct>(this.Id))
|
|
||||||
{
|
|
||||||
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Piston(uint id) : base(new EGID(id, CommonExclusiveGroups.BUILD_PISTON_BLOCK_GROUP))
|
public Piston(uint id) : base(new EGID(id, CommonExclusiveGroups.BUILD_PISTON_BLOCK_GROUP))
|
||||||
{
|
{
|
||||||
if (!BlockEngine.GetBlockInfoExists<PistonReadOnlyStruct>(this.Id))
|
|
||||||
{
|
|
||||||
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// custom piston properties
|
// custom piston properties
|
||||||
|
@ -56,13 +25,13 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
/// The piston's max extension distance.
|
/// The piston's max extension distance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float MaximumExtension
|
public float MaximumExtension
|
||||||
{
|
{
|
||||||
get => BlockEngine.GetBlockInfo<PistonReadOnlyStruct>(Id).maxDeviation;
|
get => BlockEngine.GetBlockInfo(this, (PistonReadOnlyStruct st) => st.maxDeviation);
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
ref PistonReadOnlyStruct piston = ref BlockEngine.GetBlockInfo<PistonReadOnlyStruct>(Id);
|
BlockEngine.SetBlockInfo(this, (ref PistonReadOnlyStruct st, float val) => st.maxDeviation = val,
|
||||||
piston.maxDeviation = value;
|
value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,13 +39,12 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
/// The piston's max extension force.
|
/// The piston's max extension force.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float MaximumForce
|
public float MaximumForce
|
||||||
{
|
{
|
||||||
get => BlockEngine.GetBlockInfo<PistonReadOnlyStruct>(Id).maxForce;
|
get => BlockEngine.GetBlockInfo(this, (PistonReadOnlyStruct st) => st.maxForce);
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
ref PistonReadOnlyStruct piston = ref BlockEngine.GetBlockInfo<PistonReadOnlyStruct>(Id);
|
BlockEngine.SetBlockInfo(this, (ref PistonReadOnlyStruct st, float val) => st.maxForce = val, value);
|
||||||
piston.maxForce = value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,15 +40,16 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
private static BlockEntityFactory _blockEntityFactory; //Injected from PlaceBlockEngine
|
private static BlockEntityFactory _blockEntityFactory; //Injected from PlaceBlockEngine
|
||||||
|
|
||||||
public EGID PlaceBlock(BlockIDs block, BlockColors color, byte darkness, float3 position, int uscale,
|
public EGID PlaceBlock(BlockIDs block, BlockColors color, byte darkness, float3 position, int uscale,
|
||||||
float3 scale, Player player, float3 rotation)
|
float3 scale, Player player, float3 rotation, out EntityComponentInitializer initializer)
|
||||||
{ //It appears that only the non-uniform scale has any visible effect, but if that's not given here it will be set to the uniform one
|
{ //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)
|
if (darkness > 9)
|
||||||
throw new Exception("That is too dark. Make sure to use 0-9 as darkness. (0 is default.)");
|
throw new Exception("That is too dark. Make sure to use 0-9 as darkness. (0 is default.)");
|
||||||
return BuildBlock((ushort) block, (byte) (color + darkness * 10), position, uscale, scale, rotation,
|
initializer = BuildBlock((ushort) block, (byte) (color + darkness * 10), position, uscale, scale, rotation,
|
||||||
(player ?? new Player(PlayerType.Local)).Id);
|
(player ?? new Player(PlayerType.Local)).Id);
|
||||||
|
return initializer.EGID;
|
||||||
}
|
}
|
||||||
|
|
||||||
private EGID BuildBlock(ushort block, byte color, float3 position, int uscale, float3 scale, float3 rot, uint playerId)
|
private EntityComponentInitializer BuildBlock(ushort block, byte color, float3 position, int uscale, float3 scale, float3 rot, uint playerId)
|
||||||
{
|
{
|
||||||
if (_blockEntityFactory == null)
|
if (_blockEntityFactory == null)
|
||||||
throw new Exception("The factory is null.");
|
throw new Exception("The factory is null.");
|
||||||
|
@ -106,8 +107,7 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
ref PickedBlockExtraDataStruct pickedBlock = ref entitiesDB.QueryEntity<PickedBlockExtraDataStruct>(playerEGID);
|
ref PickedBlockExtraDataStruct pickedBlock = ref entitiesDB.QueryEntity<PickedBlockExtraDataStruct>(playerEGID);
|
||||||
pickedBlock.placedBlockEntityID = structInitializer.EGID;
|
pickedBlock.placedBlockEntityID = structInitializer.EGID;
|
||||||
pickedBlock.placedBlockWasAPickedBlock = false;
|
pickedBlock.placedBlockWasAPickedBlock = false;
|
||||||
Block.BlockEngine.Synced = false; // Block entities will need to be submitted before properties can be used
|
return structInitializer;
|
||||||
return structInitializer.EGID;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Name { get; } = "GamecraftModdingAPIPlacementGameEngine";
|
public string Name { get; } = "GamecraftModdingAPIPlacementGameEngine";
|
||||||
|
|
|
@ -35,23 +35,32 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
|
|
||||||
// implementations for Rotation static class
|
// implementations for Rotation static class
|
||||||
|
|
||||||
public float3 RotateBlock(EGID blockID, Vector3 vector)
|
internal float3 RotateBlock(EGID blockID, BlockEngine.BlockInitData data, Vector3 vector)
|
||||||
{
|
{
|
||||||
|
if (!entitiesDB.Exists<RotationEntityStruct>(blockID))
|
||||||
|
{
|
||||||
|
if (data.Group == null) return float3.zero;
|
||||||
|
var init = new EntityComponentInitializer(blockID, data.Group);
|
||||||
|
init.Init(new RotationEntityStruct {rotation = new Quaternion {eulerAngles = vector}});
|
||||||
|
init.Init(new GridRotationStruct {rotation = new Quaternion {eulerAngles = vector}});
|
||||||
|
init.Init(new LocalTransformEntityStruct {rotation = new Quaternion {eulerAngles = vector}});
|
||||||
|
return vector;
|
||||||
|
}
|
||||||
ref RotationEntityStruct rotStruct = ref this.entitiesDB.QueryEntity<RotationEntityStruct>(blockID);
|
ref RotationEntityStruct rotStruct = ref this.entitiesDB.QueryEntity<RotationEntityStruct>(blockID);
|
||||||
ref GridRotationStruct gridStruct = ref this.entitiesDB.QueryEntity<GridRotationStruct>(blockID);
|
ref GridRotationStruct gridStruct = ref this.entitiesDB.QueryEntity<GridRotationStruct>(blockID);
|
||||||
ref LocalTransformEntityStruct transStruct = ref this.entitiesDB.QueryEntity<LocalTransformEntityStruct>(blockID);
|
ref LocalTransformEntityStruct transStruct = ref this.entitiesDB.QueryEntity<LocalTransformEntityStruct>(blockID);
|
||||||
ref UECSPhysicsEntityStruct phyStruct = ref this.entitiesDB.QueryEntity<UECSPhysicsEntityStruct>(blockID);
|
ref UECSPhysicsEntityStruct phyStruct = ref this.entitiesDB.QueryEntity<UECSPhysicsEntityStruct>(blockID);
|
||||||
// main (persistent) position
|
// main (persistent) position
|
||||||
Quaternion newRotation = (Quaternion)rotStruct.rotation;
|
Quaternion newRotation = rotStruct.rotation;
|
||||||
newRotation.eulerAngles += vector;
|
newRotation.eulerAngles = vector;
|
||||||
rotStruct.rotation = (quaternion)newRotation;
|
rotStruct.rotation = newRotation;
|
||||||
// placement grid rotation
|
// placement grid rotation
|
||||||
Quaternion newGridRotation = (Quaternion)gridStruct.rotation;
|
Quaternion newGridRotation = gridStruct.rotation;
|
||||||
newGridRotation.eulerAngles += vector;
|
newGridRotation.eulerAngles = vector;
|
||||||
gridStruct.rotation = (quaternion)newGridRotation;
|
gridStruct.rotation = newGridRotation;
|
||||||
// rendered position
|
// rendered position
|
||||||
Quaternion newTransRotation = (Quaternion)rotStruct.rotation;
|
Quaternion newTransRotation = rotStruct.rotation;
|
||||||
newTransRotation.eulerAngles += vector;
|
newTransRotation.eulerAngles = vector;
|
||||||
transStruct.rotation = newTransRotation;
|
transStruct.rotation = newTransRotation;
|
||||||
// collision position
|
// collision position
|
||||||
FullGameFields._physicsWorld.EntityManager.SetComponentData(phyStruct.uecsEntity, new Unity.Transforms.Rotation
|
FullGameFields._physicsWorld.EntityManager.SetComponentData(phyStruct.uecsEntity, new Unity.Transforms.Rotation
|
||||||
|
@ -63,8 +72,17 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public float3 GetRotation(EGID blockID)
|
internal float3 GetRotation(EGID blockID, BlockEngine.BlockInitData data)
|
||||||
{
|
{
|
||||||
|
if (!entitiesDB.Exists<RotationEntityStruct>(blockID))
|
||||||
|
{
|
||||||
|
if (data.Group == null) return float3.zero;
|
||||||
|
var init = new EntityComponentInitializer(blockID, data.Group);
|
||||||
|
return init.Has<RotationEntityStruct>()
|
||||||
|
? (float3) ((Quaternion) init.Get<RotationEntityStruct>().rotation).eulerAngles
|
||||||
|
: float3.zero;
|
||||||
|
}
|
||||||
|
|
||||||
ref RotationEntityStruct rotStruct = ref entitiesDB.QueryEntity<RotationEntityStruct>(blockID);
|
ref RotationEntityStruct rotStruct = ref entitiesDB.QueryEntity<RotationEntityStruct>(blockID);
|
||||||
return ((Quaternion) rotStruct.rotation).eulerAngles;
|
return ((Quaternion) rotStruct.rotation).eulerAngles;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,43 +11,12 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
public class Servo : Block
|
public class Servo : Block
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Places a new servo.
|
|
||||||
/// Any valid servo type is accepted.
|
|
||||||
/// This re-implements Block.PlaceNew(...)
|
|
||||||
/// </summary>
|
|
||||||
public static new Servo PlaceNew(BlockIDs block, float3 position,
|
|
||||||
float3 rotation = default, BlockColors color = BlockColors.Default, byte darkness = 0,
|
|
||||||
int uscale = 1, float3 scale = default, Player player = null)
|
|
||||||
{
|
|
||||||
if (!(block == BlockIDs.ServoAxle || block == BlockIDs.ServoHinge || block == BlockIDs.ServoPiston))
|
|
||||||
{
|
|
||||||
throw new BlockTypeException($"Block is not a {nameof(Servo)} block");
|
|
||||||
}
|
|
||||||
if (PlacementEngine.IsInGame && GameState.IsBuildMode())
|
|
||||||
{
|
|
||||||
EGID id = PlacementEngine.PlaceBlock(block, color, darkness,
|
|
||||||
position, uscale, scale, player, rotation);
|
|
||||||
return new Servo(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Servo(EGID id) : base(id)
|
public Servo(EGID id) : base(id)
|
||||||
{
|
{
|
||||||
if (!BlockEngine.GetBlockInfoExists<ServoReadOnlyStruct>(this.Id))
|
|
||||||
{
|
|
||||||
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Servo(uint id) : base(new EGID(id, CommonExclusiveGroups.BUILD_SERVO_BLOCK_GROUP))
|
public Servo(uint id) : base(new EGID(id, CommonExclusiveGroups.BUILD_SERVO_BLOCK_GROUP))
|
||||||
{
|
{
|
||||||
if (!BlockEngine.GetBlockInfoExists<ServoReadOnlyStruct>(this.Id))
|
|
||||||
{
|
|
||||||
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// custom servo properties
|
// custom servo properties
|
||||||
|
@ -56,13 +25,12 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
/// The servo's minimum angle.
|
/// The servo's minimum angle.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float MinimumAngle
|
public float MinimumAngle
|
||||||
{
|
{
|
||||||
get => BlockEngine.GetBlockInfo<ServoReadOnlyStruct>(Id).minDeviation;
|
get => BlockEngine.GetBlockInfo(this, (ServoReadOnlyStruct st) => st.minDeviation);
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
ref ServoReadOnlyStruct servo = ref BlockEngine.GetBlockInfo<ServoReadOnlyStruct>(Id);
|
BlockEngine.SetBlockInfo(this, (ref ServoReadOnlyStruct st, float val) => st.minDeviation = val, value);
|
||||||
servo.minDeviation = value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,13 +39,12 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float MaximumAngle
|
public float MaximumAngle
|
||||||
{
|
{
|
||||||
get => BlockEngine.GetBlockInfo<ServoReadOnlyStruct>(Id).maxDeviation;
|
get => BlockEngine.GetBlockInfo(this, (ServoReadOnlyStruct st) => st.maxDeviation);
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
ref ServoReadOnlyStruct servo = ref BlockEngine.GetBlockInfo<ServoReadOnlyStruct>(Id);
|
BlockEngine.SetBlockInfo(this, (ref ServoReadOnlyStruct st, float val) => st.maxDeviation = val, value);
|
||||||
servo.maxDeviation = value;
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -85,13 +52,12 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float MaximumForce
|
public float MaximumForce
|
||||||
{
|
{
|
||||||
get => BlockEngine.GetBlockInfo<ServoReadOnlyStruct>(Id).maxForce;
|
get => BlockEngine.GetBlockInfo(this, (ServoReadOnlyStruct st) => st.maxForce);
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
ref ServoReadOnlyStruct servo = ref BlockEngine.GetBlockInfo<ServoReadOnlyStruct>(Id);
|
BlockEngine.SetBlockInfo(this, (ref ServoReadOnlyStruct st, float val) => st.maxForce = val, value);
|
||||||
servo.maxForce = value;
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -99,13 +65,12 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool Reverse
|
public bool Reverse
|
||||||
{
|
{
|
||||||
get => BlockEngine.GetBlockInfo<ServoReadOnlyStruct>(Id).reverse;
|
get => BlockEngine.GetBlockInfo(this, (ServoReadOnlyStruct st) => st.reverse);
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
ref ServoReadOnlyStruct servo = ref BlockEngine.GetBlockInfo<ServoReadOnlyStruct>(Id);
|
BlockEngine.SetBlockInfo(this, (ref ServoReadOnlyStruct st, bool val) => st.reverse = val, value);
|
||||||
servo.reverse = value;
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,28 +14,9 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class SignalingBlock : Block
|
public class SignalingBlock : Block
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Places a new signaling block.
|
|
||||||
/// Any valid functional block type with IO ports will work.
|
|
||||||
/// This re-implements Block.PlaceNew(...)
|
|
||||||
/// </summary>
|
|
||||||
public static new SignalingBlock PlaceNew(BlockIDs block, float3 position,
|
|
||||||
float3 rotation = default, BlockColors color = BlockColors.Default, byte darkness = 0,
|
|
||||||
int uscale = 1, float3 scale = default, Player player = null)
|
|
||||||
{
|
|
||||||
if (PlacementEngine.IsInGame && GameState.IsBuildMode())
|
|
||||||
{
|
|
||||||
EGID id = PlacementEngine.PlaceBlock(block, color, darkness,
|
|
||||||
position, uscale, scale, player, rotation);
|
|
||||||
return new SignalingBlock(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SignalingBlock(EGID id) : base(id)
|
public SignalingBlock(EGID id) : base(id)
|
||||||
{
|
{
|
||||||
if (!BlockEngine.GetBlockInfoExists<BlockPortsStruct>(this.Id))
|
if (!BlockEngine.GetBlockInfoExists<BlockPortsStruct>(this))
|
||||||
{
|
{
|
||||||
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
|
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
|
||||||
}
|
}
|
||||||
|
@ -43,17 +24,12 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
|
|
||||||
public SignalingBlock(uint id) : base(id)
|
public SignalingBlock(uint id) : base(id)
|
||||||
{
|
{
|
||||||
if (!BlockEngine.GetBlockInfoExists<BlockPortsStruct>(this.Id))
|
if (!BlockEngine.GetBlockInfoExists<BlockPortsStruct>(this))
|
||||||
{
|
{
|
||||||
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
|
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ref BlockPortsStruct GetBlockPortsStruct()
|
|
||||||
{
|
|
||||||
return ref BlockEngine.GetBlockInfo<BlockPortsStruct>(Id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates the input port identifiers.
|
/// Generates the input port identifiers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -72,16 +48,6 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
return SignalEngine.GetSignalOutputs(Id);
|
return SignalEngine.GetSignalOutputs(Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the port struct.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The port struct.</returns>
|
|
||||||
/// <param name="portId">Port identifier.</param>
|
|
||||||
protected ref PortEntityStruct GetPortStruct(EGID portId)
|
|
||||||
{
|
|
||||||
return ref BlockEngine.GetBlockInfo<PortEntityStruct>(portId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the connected wire.
|
/// Gets the connected wire.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -108,16 +74,16 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
/// The input port count.
|
/// The input port count.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public uint InputCount
|
public uint InputCount
|
||||||
{
|
{
|
||||||
get => GetBlockPortsStruct().inputCount;
|
get => BlockEngine.GetBlockInfo(this, (BlockPortsStruct st) => st.inputCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The output port count.
|
/// The output port count.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public uint OutputCount
|
public uint OutputCount
|
||||||
{
|
{
|
||||||
get => GetBlockPortsStruct().outputCount;
|
get => BlockEngine.GetBlockInfo(this, (BlockPortsStruct st) => st.outputCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,43 +13,12 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
public class SpawnPoint : Block
|
public class SpawnPoint : Block
|
||||||
{
|
{
|
||||||
/// <summary>
|
public SpawnPoint(EGID id) : base(id)
|
||||||
/// Places a new spawn point.
|
|
||||||
/// Any valid spawn block type is accepted.
|
|
||||||
/// This re-implements Block.PlaceNew(...)
|
|
||||||
/// </summary>
|
|
||||||
public static new SpawnPoint PlaceNew(BlockIDs block, float3 position,
|
|
||||||
float3 rotation = default, BlockColors color = BlockColors.Default, byte darkness = 0,
|
|
||||||
int uscale = 1, float3 scale = default, Player player = null)
|
|
||||||
{
|
{
|
||||||
if (!(block == BlockIDs.LargeSpawn || block == BlockIDs.SmallSpawn || block == BlockIDs.MediumSpawn || block == BlockIDs.PlayerSpawn))
|
|
||||||
{
|
|
||||||
throw new BlockTypeException($"Block is not a {nameof(SpawnPoint)} block");
|
|
||||||
}
|
|
||||||
if (PlacementEngine.IsInGame && GameState.IsBuildMode())
|
|
||||||
{
|
|
||||||
EGID id = PlacementEngine.PlaceBlock(block, color, darkness,
|
|
||||||
position, uscale, scale, player, rotation);
|
|
||||||
return new SpawnPoint(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SpawnPoint(EGID id) : base(id)
|
|
||||||
{
|
|
||||||
if (!BlockEngine.GetBlockInfoExists<SpawnPointStatsEntityStruct>(this.Id))
|
|
||||||
{
|
|
||||||
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public SpawnPoint(uint id) : base(new EGID(id, CommonExclusiveGroups.BUILD_SPAWNPOINT_BLOCK_GROUP))
|
public SpawnPoint(uint id) : base(new EGID(id, CommonExclusiveGroups.BUILD_SPAWNPOINT_BLOCK_GROUP))
|
||||||
{
|
{
|
||||||
if (!BlockEngine.GetBlockInfoExists<SpawnPointStatsEntityStruct>(this.Id))
|
|
||||||
{
|
|
||||||
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// custom spawn point properties
|
// custom spawn point properties
|
||||||
|
@ -59,16 +28,12 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public uint Lives
|
public uint Lives
|
||||||
{
|
{
|
||||||
get
|
get => BlockEngine.GetBlockInfo(this, (SpawnPointStatsEntityStruct st) => st.lives);
|
||||||
{
|
|
||||||
return BlockEngine.GetBlockInfo<SpawnPointStatsEntityStruct>(Id).lives;
|
|
||||||
}
|
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
ref SpawnPointStatsEntityStruct spses = ref BlockEngine.GetBlockInfo<SpawnPointStatsEntityStruct>(Id);
|
BlockEngine.SetBlockInfo(this, (ref SpawnPointStatsEntityStruct st, uint val) => st.lives = val, value);
|
||||||
spses.lives = value;
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -76,16 +41,12 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool Damageable
|
public bool Damageable
|
||||||
{
|
{
|
||||||
get
|
get => BlockEngine.GetBlockInfo(this, (SpawnPointStatsEntityStruct st) => st.canTakeDamage);
|
||||||
{
|
|
||||||
return BlockEngine.GetBlockInfo<SpawnPointStatsEntityStruct>(Id).canTakeDamage;
|
|
||||||
}
|
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
ref SpawnPointStatsEntityStruct spses = ref BlockEngine.GetBlockInfo<SpawnPointStatsEntityStruct>(Id);
|
BlockEngine.SetBlockInfo(this, (ref SpawnPointStatsEntityStruct st, bool val) => st.canTakeDamage = val, value);
|
||||||
spses.canTakeDamage = value;
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -93,16 +54,12 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool GameOverEnabled
|
public bool GameOverEnabled
|
||||||
{
|
{
|
||||||
get
|
get => BlockEngine.GetBlockInfo(this, (SpawnPointStatsEntityStruct st) => st.gameOverScreen);
|
||||||
{
|
|
||||||
return BlockEngine.GetBlockInfo<SpawnPointStatsEntityStruct>(Id).gameOverScreen;
|
|
||||||
}
|
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
ref SpawnPointStatsEntityStruct spses = ref BlockEngine.GetBlockInfo<SpawnPointStatsEntityStruct>(Id);
|
BlockEngine.SetBlockInfo(this, (ref SpawnPointStatsEntityStruct st, bool val) => st.gameOverScreen = val, value);
|
||||||
spses.gameOverScreen = value;
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -110,16 +67,12 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public byte Team
|
public byte Team
|
||||||
{
|
{
|
||||||
get
|
get => BlockEngine.GetBlockInfo(this, (SpawnPointIdsEntityStruct st) => st.teamId);
|
||||||
{
|
|
||||||
return BlockEngine.GetBlockInfo<SpawnPointIdsEntityStruct>(Id).teamId;
|
|
||||||
}
|
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
ref SpawnPointIdsEntityStruct spses = ref BlockEngine.GetBlockInfo<SpawnPointIdsEntityStruct>(Id);
|
BlockEngine.SetBlockInfo(this, (ref SpawnPointIdsEntityStruct st, byte val) => st.teamId = val, value);
|
||||||
spses.teamId = value;
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,35 +12,12 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
public class TextBlock : Block
|
public class TextBlock : Block
|
||||||
{
|
{
|
||||||
|
|
||||||
public static TextBlock PlaceNew(float3 position,
|
|
||||||
float3 rotation = default, BlockColors color = BlockColors.Default, byte darkness = 0,
|
|
||||||
int uscale = 1, float3 scale = default, Player player = null)
|
|
||||||
{
|
|
||||||
if (PlacementEngine.IsInGame && GameState.IsBuildMode())
|
|
||||||
{
|
|
||||||
EGID id = PlacementEngine.PlaceBlock(BlockIDs.TextBlock, color, darkness,
|
|
||||||
position, uscale, scale, player, rotation);
|
|
||||||
return new TextBlock(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TextBlock(EGID id) : base(id)
|
public TextBlock(EGID id) : base(id)
|
||||||
{
|
{
|
||||||
if (!BlockEngine.GetBlockInfoExists<TextBlockDataStruct>(this.Id))
|
|
||||||
{
|
|
||||||
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public TextBlock(uint id) : base(new EGID(id, CommonExclusiveGroups.BUILD_TEXT_BLOCK_GROUP))
|
public TextBlock(uint id) : base(new EGID(id, CommonExclusiveGroups.BUILD_TEXT_BLOCK_GROUP))
|
||||||
{
|
{
|
||||||
if (!BlockEngine.GetBlockInfoExists<TextBlockDataStruct>(this.Id))
|
|
||||||
{
|
|
||||||
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// custom text block properties
|
// custom text block properties
|
||||||
|
@ -50,35 +27,34 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Text
|
public string Text
|
||||||
{
|
{
|
||||||
get
|
get => BlockEngine.GetBlockInfo(this, (TextBlockDataStruct st) => st.textCurrent);
|
||||||
{
|
|
||||||
return BlockEngine.GetBlockInfo<TextBlockDataStruct>(Id).textCurrent;
|
|
||||||
}
|
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
ref TextBlockDataStruct tbds = ref BlockEngine.GetBlockInfo<TextBlockDataStruct>(Id);
|
BlockEngine.SetBlockInfo(this, (ref TextBlockDataStruct tbds, string val) =>
|
||||||
tbds.textCurrent.Set(value);
|
{
|
||||||
tbds.textStored.Set(value);
|
tbds.textCurrent.Set(val);
|
||||||
BlockEngine.GetBlockInfo<TextBlockNetworkDataStruct>(Id).newTextBlockStringContent.Set(value);
|
tbds.textStored.Set(val);
|
||||||
}
|
}, value);
|
||||||
}
|
BlockEngine.SetBlockInfo(this,
|
||||||
|
(ref TextBlockNetworkDataStruct st, string val) => st.newTextBlockStringContent.Set(val), value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The text block's current text block ID (used in ChangeTextBlockCommand).
|
/// The text block's current text block ID (used in ChangeTextBlockCommand).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string TextBlockId
|
public string TextBlockId
|
||||||
{
|
{
|
||||||
get
|
get => BlockEngine.GetBlockInfo(this, (TextBlockDataStruct st) => st.textBlockID);
|
||||||
{
|
|
||||||
return BlockEngine.GetBlockInfo<TextBlockDataStruct>(Id).textBlockID;
|
|
||||||
}
|
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
BlockEngine.GetBlockInfo<TextBlockDataStruct>(Id).textBlockID.Set(value);
|
BlockEngine.SetBlockInfo(this, (ref TextBlockDataStruct tbds, string val) =>
|
||||||
BlockEngine.GetBlockInfo<TextBlockNetworkDataStruct>(Id).newTextBlockID.Set(value);
|
tbds.textBlockID.Set(val), value);
|
||||||
}
|
BlockEngine.SetBlockInfo(this,
|
||||||
|
(ref TextBlockNetworkDataStruct st, string val) => st.newTextBlockID.Set(val), value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,37 +13,12 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
{
|
{
|
||||||
public class Timer : Block
|
public class Timer : Block
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Places a new timer block.
|
|
||||||
/// </summary>
|
|
||||||
public static Timer PlaceNew(float3 position,
|
|
||||||
float3 rotation = default, BlockColors color = BlockColors.Default, byte darkness = 0,
|
|
||||||
int uscale = 1, float3 scale = default, Player player = null)
|
|
||||||
{
|
|
||||||
if (PlacementEngine.IsInGame && GameState.IsBuildMode())
|
|
||||||
{
|
|
||||||
EGID id = PlacementEngine.PlaceBlock(BlockIDs.Timer, color, darkness,
|
|
||||||
position, uscale, scale, player, rotation);
|
|
||||||
return new Timer(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Timer(EGID id) : base(id)
|
public Timer(EGID id) : base(id)
|
||||||
{
|
{
|
||||||
if (!BlockEngine.GetBlockInfoExists<TimerBlockDataStruct>(this.Id))
|
|
||||||
{
|
|
||||||
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Timer(uint id) : base(new EGID(id, CommonExclusiveGroups.BUILD_TIMER_BLOCK_GROUP))
|
public Timer(uint id) : base(new EGID(id, CommonExclusiveGroups.BUILD_TIMER_BLOCK_GROUP))
|
||||||
{
|
{
|
||||||
if (!BlockEngine.GetBlockInfoExists<TimerBlockDataStruct>(this.Id))
|
|
||||||
{
|
|
||||||
throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// custom timer properties
|
// custom timer properties
|
||||||
|
@ -53,16 +28,13 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float Start
|
public float Start
|
||||||
{
|
{
|
||||||
get
|
get => BlockEngine.GetBlockInfo(this, (TimerBlockDataStruct st) => st.startTime);
|
||||||
{
|
|
||||||
return BlockEngine.GetBlockInfo<TimerBlockDataStruct>(Id).startTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
ref TimerBlockDataStruct tbds = ref BlockEngine.GetBlockInfo<TimerBlockDataStruct>(Id);
|
BlockEngine.SetBlockInfo(this, (ref TimerBlockDataStruct tbds, float val) => tbds.startTime = val,
|
||||||
tbds.startTime = value;
|
value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -70,16 +42,13 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float End
|
public float End
|
||||||
{
|
{
|
||||||
get
|
get => BlockEngine.GetBlockInfo(this, (TimerBlockDataStruct st) => st.endTime);
|
||||||
{
|
|
||||||
return BlockEngine.GetBlockInfo<TimerBlockDataStruct>(Id).endTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
ref TimerBlockDataStruct tbds = ref BlockEngine.GetBlockInfo<TimerBlockDataStruct>(Id);
|
BlockEngine.SetBlockInfo(this, (ref TimerBlockDataStruct tbds, float val) => tbds.endTime = val,
|
||||||
tbds.endTime = value;
|
value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -87,16 +56,13 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool DisplayMilliseconds
|
public bool DisplayMilliseconds
|
||||||
{
|
{
|
||||||
get
|
get => BlockEngine.GetBlockInfo(this, (TimerBlockDataStruct st) => st.outputFormatHasMS);
|
||||||
{
|
|
||||||
return BlockEngine.GetBlockInfo<TimerBlockDataStruct>(Id).outputFormatHasMS;
|
|
||||||
}
|
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
ref TimerBlockDataStruct tbds = ref BlockEngine.GetBlockInfo<TimerBlockDataStruct>(Id);
|
BlockEngine.SetBlockInfo(this, (ref TimerBlockDataStruct tbds, bool val) => tbds.outputFormatHasMS = val,
|
||||||
tbds.outputFormatHasMS = value;
|
value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -104,16 +70,13 @@ namespace GamecraftModdingAPI.Blocks
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int CurrentTime
|
public int CurrentTime
|
||||||
{
|
{
|
||||||
get
|
get => BlockEngine.GetBlockInfo(this, (TimerBlockLabelCacheEntityStruct st) => st.timeLastRenderFrameMS);
|
||||||
{
|
|
||||||
return BlockEngine.GetBlockInfo<TimerBlockLabelCacheEntityStruct>(Id).timeLastRenderFrameMS;
|
|
||||||
}
|
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
ref TimerBlockLabelCacheEntityStruct tblces = ref BlockEngine.GetBlockInfo<TimerBlockLabelCacheEntityStruct>(Id);
|
BlockEngine.SetBlockInfo(this, (ref TimerBlockLabelCacheEntityStruct tbds, int val) => tbds.timeLastRenderFrameMS = val,
|
||||||
tblces.timeLastRenderFrameMS = value;
|
value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ using RobocraftX.Physics;
|
||||||
namespace GamecraftModdingAPI
|
namespace GamecraftModdingAPI
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A rigid body (like a cluster of connected blocks) during simulation.
|
/// A rigid body (like a chunk of connected blocks) during simulation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class SimBody : IEquatable<SimBody>, IEquatable<EGID>
|
public class SimBody : IEquatable<SimBody>, IEquatable<EGID>
|
||||||
{
|
{
|
||||||
|
|
|
@ -83,32 +83,12 @@ namespace GamecraftModdingAPI.Tests
|
||||||
{
|
{
|
||||||
if (err == null) err = $"{nameof(T)} '{obj1}' is not equal to '{obj2}'.";
|
if (err == null) err = $"{nameof(T)} '{obj1}' is not equal to '{obj2}'.";
|
||||||
if (success == null) success = $"{nameof(T)} '{obj1}' is equal to '{obj2}'.";
|
if (success == null) success = $"{nameof(T)} '{obj1}' is equal to '{obj2}'.";
|
||||||
if (obj1 == null && obj2 == null)
|
if ((obj1 == null && obj2 == null)
|
||||||
|
|| (obj1 != null && obj2 != null && obj1.Equals(obj2) && obj2.Equals(obj1)))
|
||||||
{
|
{
|
||||||
// pass
|
// pass
|
||||||
Log(PASS + success);
|
Log(PASS + success);
|
||||||
TestRoot.TestsPassed = true;
|
TestRoot.TestsPassed = true;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (!(obj1 == null && obj2 == null) && obj1.Equals(obj2) && obj2.Equals(obj1))
|
|
||||||
{
|
|
||||||
// pass
|
|
||||||
Log(PASS + success);
|
|
||||||
TestRoot.TestsPassed = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (obj1 != null && (obj1 != null && !obj1.Equals(obj2)))
|
|
||||||
{
|
|
||||||
// pass
|
|
||||||
Log(PASS + success);
|
|
||||||
TestRoot.TestsPassed = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (obj2 != null && !obj2.Equals(obj1))
|
|
||||||
{
|
|
||||||
// pass
|
|
||||||
Log(PASS + success);
|
|
||||||
TestRoot.TestsPassed = true;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -232,6 +232,28 @@ namespace GamecraftModdingAPI.Tests
|
||||||
}
|
}
|
||||||
}).Build();
|
}).Build();
|
||||||
|
|
||||||
|
CommandBuilder.Builder()
|
||||||
|
.Name("PlaceConsole")
|
||||||
|
.Description("Place a bunch of console block with a given text - entering simulation with them crashes the game as the cmd doesn't exist")
|
||||||
|
.Action((float x, float y, float z) =>
|
||||||
|
{
|
||||||
|
Stopwatch sw = new Stopwatch();
|
||||||
|
sw.Start();
|
||||||
|
for (int i = 0; i < 100; i++)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < 100; j++)
|
||||||
|
{
|
||||||
|
var block = Block.PlaceNew<ConsoleBlock>(BlockIDs.ConsoleBlock,
|
||||||
|
new float3(x + i, y, z + j));
|
||||||
|
block.Command = "test_command";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sw.Stop();
|
||||||
|
Logging.CommandLog($"Blocks placed in {sw.ElapsedMilliseconds} ms");
|
||||||
|
})
|
||||||
|
.Build();
|
||||||
|
|
||||||
GameClient.SetDebugInfo("InstalledMods", InstalledMods);
|
GameClient.SetDebugInfo("InstalledMods", InstalledMods);
|
||||||
Block.Placed += (sender, args) =>
|
Block.Placed += (sender, args) =>
|
||||||
Logging.MetaDebugLog("Placed block " + args.Type + " with ID " + args.ID);
|
Logging.MetaDebugLog("Placed block " + args.Type + " with ID " + args.ID);
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
using RobocraftX.StateSync;
|
|
||||||
using Svelto.ECS;
|
|
||||||
|
|
||||||
using HarmonyLib;
|
|
||||||
|
|
||||||
namespace GamecraftModdingAPI.Utility
|
|
||||||
{
|
|
||||||
[HarmonyPatch(typeof(DeterministicStepCompositionRoot), "ResetWorld")]
|
|
||||||
public static class DeterministicStepCompositionRootPatch
|
|
||||||
{
|
|
||||||
private static SimpleEntitiesSubmissionScheduler engineRootScheduler;
|
|
||||||
public static void Postfix(SimpleEntitiesSubmissionScheduler scheduler)
|
|
||||||
{
|
|
||||||
engineRootScheduler = scheduler;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void SubmitEntitiesNow()
|
|
||||||
{
|
|
||||||
if (engineRootScheduler != null)
|
|
||||||
engineRootScheduler.SubmitEntities();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue