@@ -372,7 +372,7 @@ namespace GamecraftModdingAPI | |||
public SimBody GetSimBody() | |||
{ | |||
return BlockEngine.GetBlockInfo(this, | |||
(GridConnectionsEntityStruct st) => new SimBody(st.machineRigidBodyId)); | |||
(GridConnectionsEntityStruct st) => new SimBody(st.machineRigidBodyId, st.clusterId)); | |||
} | |||
private void OnPlacedInit(object sender, BlockPlacedRemovedEventArgs e) | |||
@@ -1,5 +1,6 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using Gamecraft.Wires; | |||
using RobocraftX.Blocks; | |||
@@ -9,6 +10,7 @@ using RobocraftX.Physics; | |||
using RobocraftX.Scene.Simulation; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS; | |||
using Svelto.ECS.Hybrid; | |||
using GamecraftModdingAPI.Engines; | |||
@@ -90,28 +92,19 @@ namespace GamecraftModdingAPI.Blocks | |||
{ | |||
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; | |||
return GetBlockInitInfo(block, getter, def); | |||
} | |||
public U GetBlockInfoViewStruct<T, U>(Block block, Func<T, U> getter, | |||
U def = default) where T : struct, INeedEGID, IEntityComponent | |||
U def = default) where T : struct, IEntityViewComponent | |||
{ | |||
if (entitiesDB.Exists<T>(block.Id)) | |||
{ | |||
// TODO: optimize by using EntitiesDB internal calls instead of iterating over everything | |||
EntityCollection<T> entities = entitiesDB.QueryEntities<T>(block.Id.groupID); | |||
for (int i = 0; i < entities.count; i++) | |||
{ | |||
if (entities[i].ID == block.Id) | |||
{ | |||
return getter(entities[i]); | |||
} | |||
} | |||
} | |||
return getter(entitiesDB.QueryEntity<T>(block.Id)); | |||
return GetBlockInitInfo(block, getter, def); | |||
} | |||
private U GetBlockInitInfo<T, U>(Block block, Func<T, U> getter, U def) where T : struct, IEntityComponent | |||
{ | |||
if (block.InitData.Group == null) return def; | |||
var initializer = new EntityComponentInitializer(block.Id, block.InitData.Group); | |||
if (initializer.Has<T>()) | |||
@@ -121,35 +114,26 @@ namespace GamecraftModdingAPI.Blocks | |||
public delegate void Setter<T, U>(ref T component, U value) where T : struct, IEntityComponent; | |||
public void SetBlockInfoViewStruct<T, U>(Block block, Setter<T, U> setter, U value) where T : struct, INeedEGID, IEntityComponent | |||
public void SetBlockInfoViewStruct<T, U>(Block block, Setter<T, U> setter, U value) where T : struct, IEntityViewComponent | |||
{ | |||
if (entitiesDB.Exists<T>(block.Id)) | |||
{ | |||
EntityCollection<T> entities = entitiesDB.QueryEntities<T>(block.Id.groupID); | |||
for (int i = 0; i < entities.count; i++) | |||
{ | |||
if (entities[i].ID == block.Id) | |||
{ | |||
setter(ref entities[i], value); | |||
return; | |||
} | |||
} | |||
} | |||
else if (block.InitData.Group != null) | |||
{ | |||
var initializer = new EntityComponentInitializer(block.Id, block.InitData.Group); | |||
T component = initializer.Has<T>() ? initializer.Get<T>() : default; | |||
ref T structRef = ref component; | |||
setter(ref structRef, value); | |||
initializer.Init(structRef); | |||
} | |||
setter(ref entitiesDB.QueryEntity<T>(block.Id), value); | |||
else | |||
SetBlockInitInfo(block, setter, value); | |||
} | |||
public void SetBlockInfo<T, U>(Block block, Setter<T, U> setter, U value) where T : unmanaged, IEntityComponent | |||
{ | |||
if (entitiesDB.Exists<T>(block.Id)) | |||
setter(ref entitiesDB.QueryEntity<T>(block.Id), value); | |||
else if (block.InitData.Group != null) | |||
else | |||
SetBlockInitInfo(block, setter, value); | |||
} | |||
private void SetBlockInitInfo<T, U>(Block block, Setter<T, U> setter, U value) | |||
where T : struct, IEntityComponent | |||
{ | |||
if (block.InitData.Group != null) | |||
{ | |||
var initializer = new EntityComponentInitializer(block.Id, block.InitData.Group); | |||
T component = initializer.Has<T>() ? initializer.Get<T>() : default; | |||
@@ -222,6 +206,22 @@ namespace GamecraftModdingAPI.Blocks | |||
return list.ToArray(); | |||
} | |||
public SimBody[] GetClusterBodies(uint cid) | |||
{ | |||
var groups = entitiesDB.QueryEntities<GridConnectionsEntityStruct>(); | |||
var bodies = new HashSet<uint>(); | |||
foreach (var (coll, _) in groups) | |||
{ | |||
foreach (var conn in coll) | |||
{ | |||
if (conn.clusterId == cid) | |||
bodies.Add(conn.machineRigidBodyId); | |||
} | |||
} | |||
return bodies.Select(id => new SimBody(id)).ToArray(); | |||
} | |||
public EGID? FindBlockEGID(uint id) | |||
{ | |||
var groups = entitiesDB.FindGroups<DBEntityStruct>(); | |||
@@ -234,6 +234,21 @@ namespace GamecraftModdingAPI.Blocks | |||
return null; | |||
} | |||
public Cluster GetCluster(uint sbid) | |||
{ | |||
var groups = entitiesDB.QueryEntities<GridConnectionsEntityStruct>(); | |||
foreach (var (coll, _) in groups) | |||
{ | |||
foreach (var conn in coll) | |||
{ | |||
if (conn.machineRigidBodyId == sbid) | |||
return new Cluster(conn.clusterId); | |||
} | |||
} | |||
return null; | |||
} | |||
#if DEBUG | |||
public EntitiesDB GetEntitiesDB() | |||
{ | |||
@@ -192,6 +192,9 @@ namespace GamecraftModdingAPI.Blocks | |||
PlayerFilter, | |||
TeamFilter, | |||
Number2Text, //193 | |||
DestructionManager = 260, | |||
ChunkHealthModifier, | |||
ClusterHealthModifier, //262 | |||
BeachTree1 = 200, | |||
BeachTree2, | |||
BeachTree3, | |||
@@ -243,6 +246,8 @@ namespace GamecraftModdingAPI.Blocks | |||
AdvancedRotator, | |||
MusicBlock, //256 | |||
PlasmaCannonBlock, | |||
QuantumRiflePickup = 300, | |||
QuantumRifleAmmoPickup, | |||
MagmaRockCube=777, | |||
MagmaRockCubeSliced, | |||
MagmaRockSlope, | |||
@@ -0,0 +1,41 @@ | |||
using Gamecraft.Damage; | |||
using RobocraftX.Common; | |||
using Svelto.ECS; | |||
namespace GamecraftModdingAPI | |||
{ | |||
/// <summary> | |||
/// Represnts a cluster of blocks in time running mode, meaning blocks that are connected either directly or via joints. | |||
/// </summary> | |||
public class Cluster | |||
{ | |||
public EGID Id { get; } | |||
public Cluster(EGID id) | |||
{ | |||
Id = id; | |||
} | |||
public Cluster(uint id) : this(new EGID(id, CommonExclusiveGroups.SIMULATION_CLUSTERS_GROUP)) | |||
{ | |||
} | |||
public float InitialHealth | |||
{ | |||
get => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(Id).initialHealth; | |||
set => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(Id).initialHealth = value; | |||
} | |||
public float CurrentHealth | |||
{ | |||
get => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(Id).currentHealth; | |||
set => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(Id).currentHealth = value; | |||
} | |||
public float HealthMultiplier | |||
{ | |||
get => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(Id).healthMultiplier; | |||
set => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(Id).healthMultiplier = value; | |||
} | |||
} | |||
} |
@@ -3,6 +3,7 @@ using Svelto.ECS; | |||
using Unity.Mathematics; | |||
using UnityEngine; | |||
using Gamecraft.Damage; | |||
using RobocraftX.Common; | |||
using RobocraftX.Physics; | |||
@@ -15,6 +16,14 @@ namespace GamecraftModdingAPI | |||
{ | |||
public EGID Id { get; } | |||
/// <summary> | |||
/// The cluster this chunk belongs to, or null if the chunk doesn't exist. Get the SimBody from a Block if possible for good performance here. | |||
/// </summary> | |||
public Cluster Cluster => cluster ?? (cluster = clusterId == uint.MaxValue ? Block.BlockEngine.GetCluster(Id.entityID) : new Cluster(clusterId)); | |||
private Cluster cluster; | |||
private uint clusterId; | |||
public SimBody(EGID id) | |||
{ | |||
Id = id; | |||
@@ -24,6 +33,11 @@ namespace GamecraftModdingAPI | |||
{ | |||
} | |||
internal SimBody(uint id, uint clusterID) : this(id) | |||
{ | |||
clusterId = clusterID; | |||
} | |||
/// <summary> | |||
/// The position of this body. When setting the position, update the position of the connected bodies as well, | |||
/// otherwise unexpected forces may arise. | |||
@@ -70,6 +84,29 @@ namespace GamecraftModdingAPI | |||
//set => GetStruct().physicsMass.CenterOfMass = value; | |||
} | |||
public float Volume | |||
{ | |||
get => GetStruct().volume; | |||
} | |||
public float InitialHealth | |||
{ | |||
get => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(Id).initialHealth; | |||
set => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(Id).initialHealth = value; | |||
} | |||
public float CurrentHealth | |||
{ | |||
get => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(Id).currentHealth; | |||
set => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(Id).currentHealth = value; | |||
} | |||
public float HealthMultiplier | |||
{ | |||
get => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(Id).healthMultiplier; | |||
set => Block.BlockEngine.GetBlockInfo<HealthEntityComponent>(Id).healthMultiplier = value; | |||
} | |||
/// <summary> | |||
/// Whether the body can be moved or static. | |||
/// </summary> | |||
@@ -268,6 +268,16 @@ namespace GamecraftModdingAPI.Tests | |||
}) | |||
.Build(); | |||
CommandBuilder.Builder("TestChunkHealth", "Sets the chunk looked at to the given health.") | |||
.Action((float val, float max) => | |||
{ | |||
var body = new Player(PlayerType.Local).GetSimBodyLookedAt(); | |||
if (body == null) return; | |||
body.CurrentHealth = val; | |||
body.InitialHealth = max; | |||
Logging.CommandLog("Health set to: " + val); | |||
}).Build(); | |||
GameClient.SetDebugInfo("InstalledMods", InstalledMods); | |||
Block.Placed += (sender, args) => | |||
Logging.MetaDebugLog("Placed block " + args.Type + " with ID " + args.ID); | |||
@@ -3,6 +3,7 @@ using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Reflection; | |||
using System.Reflection.Emit; | |||
using System.Text; | |||
using System.Text.Formatting; | |||
using GamecraftModdingAPI.Blocks; | |||
using GamecraftModdingAPI.Engines; | |||
@@ -46,9 +47,9 @@ namespace GamecraftModdingAPI.Utility | |||
var array = new CodeInstruction[] | |||
{ | |||
new CodeInstruction(OpCodes.Ldloc_0), //StringBuffer | |||
new CodeInstruction(OpCodes.Call, ((Action<StringBuffer>)AddInfo).Method) | |||
new CodeInstruction(OpCodes.Call, ((Action<StringBuilder>)AddInfo).Method) | |||
}; | |||
list.InsertRange(index, array); | |||
list.InsertRange(index - 1, array); //-1: ldloc.1 ("local") before ldfld | |||
} | |||
catch (Exception e) | |||
{ | |||
@@ -58,13 +59,15 @@ namespace GamecraftModdingAPI.Utility | |||
return list; | |||
} | |||
public static void AddInfo(StringBuffer sb) | |||
public static void AddInfo(StringBuilder sb) | |||
{ | |||
foreach (var info in _extraInfo) | |||
{ | |||
try | |||
{ | |||
sb.Append(info.Value() + "\n"); | |||
string text = info.Value().Trim(); | |||
if (text.Length != 0) | |||
sb.Append(text + "\n"); | |||
} | |||
catch (Exception e) | |||
{ | |||