diff --git a/GamecraftModdingAPI/Block.cs b/GamecraftModdingAPI/Block.cs
index 6a5b2a1..88932da 100644
--- a/GamecraftModdingAPI/Block.cs
+++ b/GamecraftModdingAPI/Block.cs
@@ -14,6 +14,7 @@ namespace GamecraftModdingAPI
{
///
/// A single (perhaps scaled) block. Properties may return default values if the block is removed and then setting them is ignored.
+ /// For specific block type operations, use the specialised block classes in the GamecraftModdingAPI.Blocks namespace.
///
public class Block
{
@@ -81,7 +82,7 @@ namespace GamecraftModdingAPI
DeterministicStepCompositionRootPatch.SubmitEntitiesNow();
}
- public EGID Id { get; }
+ public EGID Id { get; protected set; }
///
/// The block's current position or zero if the block no longer exists.
@@ -213,6 +214,13 @@ namespace GamecraftModdingAPI
GameEngineManager.AddGameEngine(BlockEngine);
}
+ ///
+ /// Convert the block to a specialised block class.
+ ///
+ /// The block.
+ /// Force an entity sync when true.
+ /// Only set this to false when the block was not placed the same tick this was called.
+ /// The specialised block type.
public T Specialise(bool sameTick = true) where T : Block
{
// What have I gotten myself into?
diff --git a/GamecraftModdingAPI/Blocks/SpawnPoint.cs b/GamecraftModdingAPI/Blocks/SpawnPoint.cs
new file mode 100644
index 0000000..8fd1fda
--- /dev/null
+++ b/GamecraftModdingAPI/Blocks/SpawnPoint.cs
@@ -0,0 +1,132 @@
+using System;
+
+using RobocraftX.Blocks;
+using Gamecraft.CharacterVulnerability;
+using Svelto.ECS;
+using Unity.Mathematics;
+
+using GamecraftModdingAPI;
+using GamecraftModdingAPI.Utility;
+
+namespace GamecraftModdingAPI.Blocks
+{
+ public class SpawnPoint : Block
+ {
+ ///
+ /// Places a new spawn point.
+ /// Any valid spawn block type is accepted.
+ /// This re-implements Block.PlaceNew(...)
+ ///
+ 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 {typeof(SpawnPoint).Name} block");
+ }
+ if (PlacementEngine.IsInGame && GameState.IsBuildMode())
+ {
+ try
+ {
+ EGID id = PlacementEngine.PlaceBlock(block, color, darkness,
+ position, uscale, scale, player, rotation);
+ Sync();
+ return new SpawnPoint(id);
+ }
+ catch (Exception e)
+ {
+ Logging.MetaDebugLog(e);
+ }
+ }
+
+ return null;
+ }
+
+ public SpawnPoint(EGID id) : base(id)
+ {
+ if (!BlockEngine.GetBlockInfoExists(this.Id))
+ {
+ throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
+ }
+ }
+
+ public SpawnPoint(uint id) : base(id)
+ {
+ if (!BlockEngine.GetBlockInfoExists(this.Id))
+ {
+ throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
+ }
+ }
+
+ // custom spawn point properties
+
+ ///
+ /// The lives the player spawns in with.
+ ///
+ public uint Lives
+ {
+ get
+ {
+ return BlockEngine.GetBlockInfo(Id).lives;
+ }
+
+ set
+ {
+ ref SpawnPointStatsEntityStruct spses = ref BlockEngine.GetBlockInfo(Id);
+ spses.lives = value;
+ }
+ }
+
+ ///
+ /// Whether the spawned player can take damage.
+ ///
+ public bool Damageable
+ {
+ get
+ {
+ return BlockEngine.GetBlockInfo(Id).canTakeDamage;
+ }
+
+ set
+ {
+ ref SpawnPointStatsEntityStruct spses = ref BlockEngine.GetBlockInfo(Id);
+ spses.canTakeDamage = value;
+ }
+ }
+
+ ///
+ /// Whether the game over screen will be displayed
+ ///
+ public bool GameOverEnabled
+ {
+ get
+ {
+ return BlockEngine.GetBlockInfo(Id).gameOverScreen;
+ }
+
+ set
+ {
+ ref SpawnPointStatsEntityStruct spses = ref BlockEngine.GetBlockInfo(Id);
+ spses.gameOverScreen = value;
+ }
+ }
+
+ ///
+ /// The team id for players who spawn here.
+ ///
+ public byte Team
+ {
+ get
+ {
+ return BlockEngine.GetBlockInfo(Id).teamId;
+ }
+
+ set
+ {
+ ref SpawnPointIdsEntityStruct spses = ref BlockEngine.GetBlockInfo(Id);
+ spses.teamId = value;
+ }
+ }
+ }
+}
diff --git a/GamecraftModdingAPI/Blocks/Timer.cs b/GamecraftModdingAPI/Blocks/Timer.cs
new file mode 100644
index 0000000..b31f9f8
--- /dev/null
+++ b/GamecraftModdingAPI/Blocks/Timer.cs
@@ -0,0 +1,127 @@
+using System;
+
+using RobocraftX.Blocks;
+using Gamecraft.Blocks.TimerBlock;
+using Svelto.ECS;
+using Unity.Mathematics;
+
+using GamecraftModdingAPI;
+using GamecraftModdingAPI.Utility;
+
+namespace GamecraftModdingAPI.Blocks
+{
+ public class Timer : Block
+ {
+ ///
+ /// Places a new timer block.
+ ///
+ 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())
+ {
+
+ try
+ {
+ EGID id = PlacementEngine.PlaceBlock(BlockIDs.Timer, color, darkness,
+ position, uscale, scale, player, rotation);
+ Sync();
+ return new Timer(id);
+ }
+ catch (Exception e)
+ {
+ Logging.MetaDebugLog(e);
+ }
+ }
+
+ return null;
+ }
+
+ public Timer(EGID id) : base(id)
+ {
+ if (!BlockEngine.GetBlockInfoExists(this.Id))
+ {
+ throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
+ }
+ }
+
+ public Timer(uint id) : base(id)
+ {
+ if (!BlockEngine.GetBlockInfoExists(this.Id))
+ {
+ throw new BlockTypeException($"Block is not a {this.GetType().Name} block");
+ }
+ }
+
+ // custom timer properties
+
+ ///
+ /// The player-specified start time.
+ ///
+ public float Start
+ {
+ get
+ {
+ return BlockEngine.GetBlockInfo(Id).startTime;
+ }
+
+ set
+ {
+ ref TimerBlockDataStruct tbds = ref BlockEngine.GetBlockInfo(Id);
+ tbds.startTime = value;
+ }
+ }
+
+ ///
+ /// The player-specified end time.
+ ///
+ public float End
+ {
+ get
+ {
+ return BlockEngine.GetBlockInfo(Id).endTime;
+ }
+
+ set
+ {
+ ref TimerBlockDataStruct tbds = ref BlockEngine.GetBlockInfo(Id);
+ tbds.endTime = value;
+ }
+ }
+
+ ///
+ /// Whether to display time with millisecond precision.
+ ///
+ public bool DisplayMilliseconds
+ {
+ get
+ {
+ return BlockEngine.GetBlockInfo(Id).outputFormatHasMS;
+ }
+
+ set
+ {
+ ref TimerBlockDataStruct tbds = ref BlockEngine.GetBlockInfo(Id);
+ tbds.outputFormatHasMS = value;
+ }
+ }
+
+ ///
+ /// Current time (as of the last video frame), in milliseconds.
+ ///
+ public int CurrentTime
+ {
+ get
+ {
+ return BlockEngine.GetBlockInfo(Id).timeLastRenderFrameMS;
+ }
+
+ set
+ {
+ ref TimerBlockLabelCacheEntityStruct tblces = ref BlockEngine.GetBlockInfo(Id);
+ tblces.timeLastRenderFrameMS = value;
+ }
+ }
+ }
+}