Seat events, and everything needed to get there
- Added support for seat enter and exit events and a test for them - Added support for entering and exiting seat from code - Changed the Id property of ECS objects to non-abstract, requiring it in the constructor, so that the Player class can inherit EcsObjectBase - Added a weird constructor to EcsObjectBase that allows running code to determine the object Id - Added interface for engines that receive entity functions - Exposed the entity submission scheduler and removed it from FullGameFields because it moved from there - Made the Game.Enter event only fire after the first entity submission so the game is fully initialized and the local player exists - Added all seat groups to the dictionary
This commit is contained in:
parent
4bd636b8ed
commit
6204b226d1
18 changed files with 284 additions and 128 deletions
|
@ -1,5 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using RobocraftX.Common;
|
||||
using RobocraftX.Schedulers;
|
||||
|
@ -11,6 +10,7 @@ using RobocraftX.Blocks;
|
|||
using RobocraftX.ScreenshotTaker;
|
||||
using TechbloxModdingAPI.Blocks;
|
||||
using TechbloxModdingAPI.Engines;
|
||||
using TechbloxModdingAPI.Tasks;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
namespace TechbloxModdingAPI.App
|
||||
|
@ -35,6 +35,12 @@ namespace TechbloxModdingAPI.App
|
|||
|
||||
public void Ready()
|
||||
{
|
||||
EnteringGame().RunOn(Scheduler.leanRunner);
|
||||
}
|
||||
|
||||
private IEnumerator<TaskContract> EnteringGame()
|
||||
{
|
||||
yield return new WaitForSubmissionEnumerator(GameLoadedEnginePatch.Scheduler).Continue();
|
||||
EnterGame.Invoke(this, new GameEventArgs { GameName = GameMode.SaveGameDetails.Name, GamePath = GameMode.SaveGameDetails.Folder });
|
||||
IsInGame = true;
|
||||
}
|
||||
|
@ -96,40 +102,29 @@ namespace TechbloxModdingAPI.App
|
|||
TimeRunningModeUtil.ToggleTimeRunningState(entitiesDB);
|
||||
}
|
||||
|
||||
public EGID[] GetAllBlocksInGame(BlockIDs filter = BlockIDs.Invalid)
|
||||
{
|
||||
var allBlocks = entitiesDB.QueryEntities<BlockTagEntityStruct>();
|
||||
List<EGID> blockEGIDs = new List<EGID>();
|
||||
if (filter == BlockIDs.Invalid)
|
||||
{
|
||||
foreach (var (blocks, _) in allBlocks)
|
||||
{
|
||||
var buffer = blocks.ToBuffer().buffer;
|
||||
for (int i = 0; i < buffer.capacity; i++)
|
||||
blockEGIDs.Add(buffer[i].ID);
|
||||
}
|
||||
public EGID[] GetAllBlocksInGame(BlockIDs filter = BlockIDs.Invalid)
|
||||
{
|
||||
var allBlocks = entitiesDB.QueryEntities<BlockTagEntityStruct>();
|
||||
List<EGID> blockEGIDs = new List<EGID>();
|
||||
foreach (var (blocks, _) in allBlocks)
|
||||
{
|
||||
var (buffer, count) = blocks.ToBuffer();
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
uint dbid;
|
||||
if (filter == BlockIDs.Invalid)
|
||||
dbid = (uint)filter;
|
||||
else
|
||||
dbid = entitiesDB.QueryEntity<DBEntityStruct>(buffer[i].ID).DBID;
|
||||
if (dbid == (ulong)filter)
|
||||
blockEGIDs.Add(buffer[i].ID);
|
||||
}
|
||||
}
|
||||
|
||||
return blockEGIDs.ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var (blocks, _) in allBlocks)
|
||||
{
|
||||
var array = blocks.ToBuffer().buffer;
|
||||
for (var index = 0; index < array.capacity; index++)
|
||||
{
|
||||
var block = array[index];
|
||||
uint dbid = entitiesDB.QueryEntity<DBEntityStruct>(block.ID).DBID;
|
||||
if (dbid == (ulong) filter)
|
||||
blockEGIDs.Add(block.ID);
|
||||
}
|
||||
}
|
||||
return blockEGIDs.ToArray();
|
||||
}
|
||||
|
||||
return blockEGIDs.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public void EnableScreenshotTaker()
|
||||
public void EnableScreenshotTaker()
|
||||
{
|
||||
ref var local = ref entitiesDB.QueryEntity<ScreenshotModeEntityStruct>(ScreenshotTakerEgids.ScreenshotTaker);
|
||||
if (local.enabled)
|
||||
|
|
|
@ -99,12 +99,16 @@ namespace TechbloxModdingAPI
|
|||
{CommonExclusiveGroups.ENGINE_BLOCK_BUILD_GROUP, (id => new Engine(id), typeof(Engine))},
|
||||
{CommonExclusiveGroups.LOGIC_BLOCK_GROUP, (id => new LogicGate(id), typeof(LogicGate))},
|
||||
{CommonExclusiveGroups.PISTON_BLOCK_GROUP, (id => new Piston(id), typeof(Piston))},
|
||||
{SeatGroups.PASSENGER_BLOCK_BUILD_GROUP, (id => new Seat(id), typeof(Seat))},
|
||||
{SeatGroups.PILOTSEAT_BLOCK_BUILD_GROUP, (id => new Seat(id), typeof(Seat))},
|
||||
{CommonExclusiveGroups.SERVO_BLOCK_GROUP, (id => new Servo(id), typeof(Servo))},
|
||||
{CommonExclusiveGroups.WHEELRIG_BLOCK_BUILD_GROUP, (id => new WheelRig(id), typeof(WheelRig))}
|
||||
};
|
||||
|
||||
static Block()
|
||||
{
|
||||
foreach (var group in SeatGroups.SEATS_BLOCK_GROUPS) // Adds driver and passenger seats, occupied and unoccupied
|
||||
GroupToConstructor.Add(group, (id => new Seat(id), typeof(Seat)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a correctly typed instance of this block. The instances are shared for a specific block.
|
||||
/// If an instance is no longer referenced a new instance is returned.
|
||||
|
@ -126,9 +130,8 @@ namespace TechbloxModdingAPI
|
|||
: GetInstance(egid, e => new Block(e));
|
||||
}
|
||||
|
||||
public Block(EGID id)
|
||||
public Block(EGID id) : base(id)
|
||||
{
|
||||
Id = id;
|
||||
Type expectedType;
|
||||
if (GroupToConstructor.ContainsKey(id.groupID) &&
|
||||
!GetType().IsAssignableFrom(expectedType = GroupToConstructor[id.groupID].Type))
|
||||
|
@ -156,17 +159,18 @@ namespace TechbloxModdingAPI
|
|||
/// <param name="player">The player who placed the block</param>
|
||||
/// <param name="force">Place even if not in build mode</param>
|
||||
public Block(BlockIDs type, float3 position, bool autoWire = false, Player player = null, bool force = false)
|
||||
: base(block =>
|
||||
{
|
||||
if (!PlacementEngine.IsInGame || !GameState.IsBuildMode() && !force)
|
||||
throw new BlockException("Blocks can only be placed in build mode.");
|
||||
var initializer = PlacementEngine.PlaceBlock(type, position, player, autoWire);
|
||||
block.InitData = initializer;
|
||||
Placed += ((Block)block).OnPlacedInit;
|
||||
return initializer.EGID;
|
||||
})
|
||||
{
|
||||
if (!PlacementEngine.IsInGame || !GameState.IsBuildMode() && !force)
|
||||
throw new BlockException("Blocks can only be placed in build mode.");
|
||||
var initializer = PlacementEngine.PlaceBlock(type, position, player, autoWire);
|
||||
Id = initializer.EGID;
|
||||
InitData = initializer;
|
||||
Placed += OnPlacedInit;
|
||||
}
|
||||
|
||||
public override EGID Id { get; }
|
||||
|
||||
private EGID copiedFrom;
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -19,17 +19,16 @@ namespace TechbloxModdingAPI
|
|||
public class BlockGroup : EcsObjectBase, ICollection<Block>, IDisposable
|
||||
{
|
||||
internal static BlueprintEngine _engine = new BlueprintEngine();
|
||||
public override EGID Id { get; }
|
||||
private readonly Block sourceBlock;
|
||||
private readonly List<Block> blocks;
|
||||
private float3 position, rotation;
|
||||
internal bool PosAndRotCalculated;
|
||||
|
||||
internal BlockGroup(int id, Block block)
|
||||
internal BlockGroup(int id, Block block) : base(new EGID((uint)id,
|
||||
BlockGroupExclusiveGroups.BlockGroupEntityGroup))
|
||||
{
|
||||
if (id == BlockGroupUtility.GROUP_UNASSIGNED)
|
||||
throw new BlockException("Cannot create a block group for blocks without a group!");
|
||||
Id = new EGID((uint) id, BlockGroupExclusiveGroups.BlockGroupEntityGroup);
|
||||
sourceBlock = block;
|
||||
blocks = new List<Block>(GetBlocks());
|
||||
Block.Removed += OnBlockRemoved;
|
||||
|
|
|
@ -76,10 +76,11 @@ namespace TechbloxModdingAPI.Blocks
|
|||
/// <param name="startPort">Starting port number, or guess if omitted.</param>
|
||||
/// <param name="endPort">Ending port number, or guess if omitted.</param>
|
||||
/// <exception cref="WireInvalidException">Guessing failed or wire does not exist.</exception>
|
||||
public Wire(Block start, Block end, byte startPort = Byte.MaxValue, byte endPort = Byte.MaxValue)
|
||||
public Wire(Block start, Block end, byte startPort = Byte.MaxValue, byte endPort = Byte.MaxValue) : base(ecs =>
|
||||
{
|
||||
startBlockEGID = start.Id;
|
||||
endBlockEGID = end.Id;
|
||||
var th = (Wire)ecs;
|
||||
th.startBlockEGID = start.Id;
|
||||
th.endBlockEGID = end.Id;
|
||||
bool flipped = false;
|
||||
// find block ports
|
||||
EGID wire = signalEngine.MatchBlocksToWire(start.Id, end.Id, startPort, endPort);
|
||||
|
@ -94,12 +95,16 @@ namespace TechbloxModdingAPI.Blocks
|
|||
|
||||
if (wire != default)
|
||||
{
|
||||
Construct(start.Id, end.Id, startPort, endPort, wire, flipped);
|
||||
th.Construct(start.Id, end.Id, startPort, endPort, wire, flipped);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new WireInvalidException("Wire not found");
|
||||
}
|
||||
|
||||
return th.wireEGID;
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -116,7 +121,7 @@ namespace TechbloxModdingAPI.Blocks
|
|||
{
|
||||
}
|
||||
|
||||
private Wire(EGID startBlock, EGID endBlock, byte startPort, byte endPort, EGID wire, bool inputToOutput)
|
||||
private Wire(EGID startBlock, EGID endBlock, byte startPort, byte endPort, EGID wire, bool inputToOutput) : base(wire)
|
||||
{
|
||||
Construct(startBlock, endBlock, startPort, endPort, wire, inputToOutput);
|
||||
}
|
||||
|
@ -139,7 +144,7 @@ namespace TechbloxModdingAPI.Blocks
|
|||
/// Construct a wire object from an existing wire connection.
|
||||
/// </summary>
|
||||
/// <param name="wireEgid">The wire ID.</param>
|
||||
public Wire(EGID wireEgid)
|
||||
public Wire(EGID wireEgid) : base(wireEgid)
|
||||
{
|
||||
WireEntityStruct wire = signalEngine.GetWire(wireEGID);
|
||||
Construct(wire.sourceBlockEGID, wire.destinationBlockEGID, wire.sourcePortUsage, wire.destinationPortUsage,
|
||||
|
@ -151,14 +156,6 @@ namespace TechbloxModdingAPI.Blocks
|
|||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The wire's in-game id.
|
||||
/// </summary>
|
||||
public override EGID Id
|
||||
{
|
||||
get => wireEGID;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The wire's signal value, as a float.
|
||||
/// </summary>
|
||||
|
|
|
@ -10,11 +10,8 @@ namespace TechbloxModdingAPI
|
|||
/// </summary>
|
||||
public class Cluster : EcsObjectBase
|
||||
{
|
||||
public override EGID Id { get; }
|
||||
|
||||
public Cluster(EGID id)
|
||||
public Cluster(EGID id) : base(id)
|
||||
{
|
||||
Id = id;
|
||||
}
|
||||
|
||||
public Cluster(uint id) : this(new EGID(id, CommonExclusiveGroups.SIMULATION_CLUSTERS_GROUP))
|
||||
|
|
|
@ -10,8 +10,8 @@ namespace TechbloxModdingAPI
|
|||
{
|
||||
public abstract class EcsObjectBase
|
||||
{
|
||||
public abstract EGID Id { get; } //Abstract to support the 'place' Block constructor
|
||||
|
||||
public EGID Id { get; }
|
||||
|
||||
private static readonly Dictionary<Type, WeakDictionary<EGID, EcsObjectBase>> _instances =
|
||||
new Dictionary<Type, WeakDictionary<EGID, EcsObjectBase>>();
|
||||
|
||||
|
@ -39,7 +39,19 @@ namespace TechbloxModdingAPI
|
|||
return (T)instance;
|
||||
}
|
||||
|
||||
protected EcsObjectBase()
|
||||
protected EcsObjectBase(EGID id)
|
||||
{
|
||||
if (!_instances.TryGetValue(GetType(), out var dict))
|
||||
{
|
||||
dict = new WeakDictionary<EGID, EcsObjectBase>();
|
||||
_instances.Add(GetType(), dict);
|
||||
}
|
||||
if (!dict.ContainsKey(id)) // Multiple instances may be created
|
||||
dict.Add(id, this);
|
||||
Id = id;
|
||||
}
|
||||
|
||||
protected EcsObjectBase(Func<EcsObjectBase, EGID> initializer)
|
||||
{
|
||||
if (!_instances.TryGetValue(GetType(), out var dict))
|
||||
{
|
||||
|
@ -47,11 +59,9 @@ namespace TechbloxModdingAPI
|
|||
_instances.Add(GetType(), dict);
|
||||
}
|
||||
|
||||
// ReSharper disable VirtualMemberCallInConstructor
|
||||
// The ID should not depend on the constructor
|
||||
if (!dict.ContainsKey(Id)) // Multiple instances may be created
|
||||
dict.Add(Id, this);
|
||||
// ReSharper enable VirtualMemberCallInConstructor
|
||||
var id = initializer(this);
|
||||
if (!dict.ContainsKey(id)) // Multiple instances may be created
|
||||
dict.Add(id, this);
|
||||
}
|
||||
|
||||
#region ECS initializer stuff
|
||||
|
|
|
@ -5,6 +5,7 @@ using RobocraftX.CR.MainGame;
|
|||
using RobocraftX.FrontEnd;
|
||||
using RobocraftX.StateSync;
|
||||
using Svelto.ECS;
|
||||
using Svelto.ECS.Schedulers;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
namespace TechbloxModdingAPI.Engines
|
||||
|
@ -12,10 +13,12 @@ namespace TechbloxModdingAPI.Engines
|
|||
[HarmonyPatch]
|
||||
class GameLoadedEnginePatch
|
||||
{
|
||||
public static EntitiesSubmissionScheduler Scheduler { get; private set; }
|
||||
public static void Postfix(StateSyncRegistrationHelper stateSyncReg)
|
||||
{
|
||||
// register all game engines, including deterministic
|
||||
GameEngineManager.RegisterEngines(stateSyncReg);
|
||||
Scheduler = stateSyncReg.enginesRoot.scheduler;
|
||||
}
|
||||
|
||||
public static MethodBase TargetMethod()
|
||||
|
|
9
TechbloxModdingAPI/Engines/IFunEngine.cs
Normal file
9
TechbloxModdingAPI/Engines/IFunEngine.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
using Svelto.ECS;
|
||||
|
||||
namespace TechbloxModdingAPI.Engines
|
||||
{
|
||||
public interface IFunEngine : IApiEngine
|
||||
{
|
||||
public IEntityFunctions Functions { set; }
|
||||
}
|
||||
}
|
30
TechbloxModdingAPI/Player.Events.cs
Normal file
30
TechbloxModdingAPI/Player.Events.cs
Normal file
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
using Svelto.ECS;
|
||||
using TechbloxModdingAPI.Blocks;
|
||||
using TechbloxModdingAPI.Utility;
|
||||
|
||||
namespace TechbloxModdingAPI
|
||||
{
|
||||
public partial class Player
|
||||
{
|
||||
internal WrappedHandler<PlayerSeatEventArgs> seatEntered;
|
||||
public event EventHandler<PlayerSeatEventArgs> SeatEntered
|
||||
{
|
||||
add => seatEntered += value;
|
||||
remove => seatEntered -= value;
|
||||
}
|
||||
|
||||
internal WrappedHandler<PlayerSeatEventArgs> seatExited;
|
||||
public event EventHandler<PlayerSeatEventArgs> SeatExited
|
||||
{
|
||||
add => seatExited += value;
|
||||
remove => seatExited -= value;
|
||||
}
|
||||
}
|
||||
|
||||
public struct PlayerSeatEventArgs
|
||||
{
|
||||
public EGID SeatId;
|
||||
public Seat Seat => (Seat)Block.New(SeatId);
|
||||
}
|
||||
}
|
|
@ -20,10 +20,11 @@ namespace TechbloxModdingAPI
|
|||
/// <summary>
|
||||
/// An in-game player character. Any Leo you see is a player.
|
||||
/// </summary>
|
||||
public class Player : IEquatable<Player>, IEquatable<EGID>
|
||||
{ //TODO: Inherit EcsObjectBase and make Id an EGID, useful for caching
|
||||
public partial class Player : EcsObjectBase, IEquatable<Player>, IEquatable<EGID>
|
||||
{
|
||||
// static functionality
|
||||
private static PlayerEngine playerEngine = new PlayerEngine();
|
||||
private static PlayerEventsEngine playerEventsEngine = new PlayerEventsEngine();
|
||||
private static Player localPlayer;
|
||||
|
||||
/// <summary>
|
||||
|
@ -79,7 +80,7 @@ namespace TechbloxModdingAPI
|
|||
/// Initializes a new instance of the <see cref="T:TechbloxModdingAPI.Player"/> class.
|
||||
/// </summary>
|
||||
/// <param name="id">The player's unique identifier.</param>
|
||||
public Player(uint id)
|
||||
public Player(uint id) : base(new EGID(id, CharacterExclusiveGroups.OnFootGroup))
|
||||
{
|
||||
this.Id = id;
|
||||
if (!Exists(id))
|
||||
|
@ -93,22 +94,31 @@ namespace TechbloxModdingAPI
|
|||
/// Initializes a new instance of the <see cref="T:TechbloxModdingAPI.Player"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player type. Chooses the first available player matching the criteria.</param>
|
||||
public Player(PlayerType player)
|
||||
public Player(PlayerType player) : base(ecs =>
|
||||
{
|
||||
switch (player)
|
||||
{
|
||||
case PlayerType.Local:
|
||||
this.Id = playerEngine.GetLocalPlayer();
|
||||
break;
|
||||
case PlayerType.Remote:
|
||||
this.Id = playerEngine.GetRemotePlayer();
|
||||
break;
|
||||
}
|
||||
if (this.Id == uint.MaxValue)
|
||||
{
|
||||
throw new PlayerNotFoundException($"No player of {player} type exists");
|
||||
}
|
||||
this.Type = player;
|
||||
uint id;
|
||||
switch (player)
|
||||
{
|
||||
case PlayerType.Local:
|
||||
id = playerEngine.GetLocalPlayer();
|
||||
break;
|
||||
case PlayerType.Remote:
|
||||
id = playerEngine.GetRemotePlayer();
|
||||
break;
|
||||
default:
|
||||
id = uint.MaxValue;
|
||||
break;
|
||||
}
|
||||
|
||||
if (id == uint.MaxValue)
|
||||
{
|
||||
throw new PlayerNotFoundException($"No player of {player} type exists");
|
||||
}
|
||||
|
||||
return new EGID(id, CharacterExclusiveGroups.OnFootGroup);
|
||||
})
|
||||
{
|
||||
this.Type = player;
|
||||
}
|
||||
|
||||
// object fields & properties
|
||||
|
@ -124,7 +134,7 @@ namespace TechbloxModdingAPI
|
|||
/// The player's unique identifier.
|
||||
/// </summary>
|
||||
/// <value>The identifier.</value>
|
||||
public uint Id { get; }
|
||||
public new uint Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The player's current position.
|
||||
|
@ -424,6 +434,16 @@ namespace TechbloxModdingAPI
|
|||
}
|
||||
playerEngine.SetLocation(Id, location, exitSeat: exitSeat);
|
||||
}
|
||||
|
||||
public void EnterSeat(Seat seat)
|
||||
{
|
||||
playerEngine.EnterSeat(Id, seat.Id);
|
||||
}
|
||||
|
||||
public void ExitSeat()
|
||||
{
|
||||
playerEngine.ExitSeat(Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the block the player is currently looking at in build mode.
|
||||
|
@ -507,6 +527,7 @@ namespace TechbloxModdingAPI
|
|||
internal static void Init()
|
||||
{
|
||||
Utility.GameEngineManager.AddGameEngine(playerEngine);
|
||||
Utility.GameEngineManager.AddGameEngine(playerEventsEngine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System.Runtime.CompilerServices;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
using RobocraftX.Character;
|
||||
using RobocraftX.Character.Movement;
|
||||
|
@ -8,10 +9,13 @@ using RobocraftX.CR.MachineEditing.BoxSelect;
|
|||
using RobocraftX.Physics;
|
||||
using RobocraftX.Blocks.Ghost;
|
||||
using Gamecraft.GUI.HUDFeedbackBlocks;
|
||||
using RobocraftX.Blocks;
|
||||
using RobocraftX.PilotSeat;
|
||||
using Svelto.ECS;
|
||||
using Techblox.Camera;
|
||||
using Unity.Mathematics;
|
||||
using Svelto.ECS.DataStructures;
|
||||
using Svelto.ECS.EntityStructs;
|
||||
using Techblox.BuildingDrone;
|
||||
|
||||
using TechbloxModdingAPI.Engines;
|
||||
|
@ -19,7 +23,7 @@ using TechbloxModdingAPI.Utility;
|
|||
|
||||
namespace TechbloxModdingAPI.Players
|
||||
{
|
||||
internal class PlayerEngine : IApiEngine, IFactoryEngine
|
||||
internal class PlayerEngine : IFunEngine
|
||||
{
|
||||
public string Name { get; } = "TechbloxModdingAPIPlayerGameEngine";
|
||||
|
||||
|
@ -27,7 +31,7 @@ namespace TechbloxModdingAPI.Players
|
|||
|
||||
public bool isRemovable => false;
|
||||
|
||||
public IEntityFactory Factory { set; private get; }
|
||||
public IEntityFunctions Functions { get; set; }
|
||||
|
||||
private bool isReady = false;
|
||||
|
||||
|
@ -101,9 +105,7 @@ namespace TechbloxModdingAPI.Players
|
|||
return false;
|
||||
if (group == CharacterExclusiveGroups.InPilotSeatGroup && exitSeat)
|
||||
{
|
||||
EGID egid = new EGID(playerId, group);
|
||||
entitiesDB.QueryEntity<CharacterPilotSeatEntityStruct>(egid).instantExit = true;
|
||||
entitiesDB.PublishEntityChange<CharacterPilotSeatEntityStruct>(egid);
|
||||
ExitSeat(playerId);
|
||||
}
|
||||
rbesOpt.Get().position = location;
|
||||
return true;
|
||||
|
@ -183,12 +185,12 @@ namespace TechbloxModdingAPI.Players
|
|||
{
|
||||
if (!entitiesDB.Exists<BoxSelectStateEntityStruct>(playerid,
|
||||
BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup))
|
||||
return new Block[0];
|
||||
return Array.Empty<Block>();
|
||||
var state = entitiesDB.QueryEntity<BoxSelectStateEntityStruct>(playerid,
|
||||
BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup);
|
||||
var blocks = entitiesDB.QueryEntity<SelectedBlocksStruct>(playerid,
|
||||
BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup);
|
||||
if (!state.active) return new Block[0];
|
||||
if (!state.active) return Array.Empty<Block>();
|
||||
var pointer = (EGID*) blocks.selectedBlocks.ToPointer();
|
||||
var ret = new Block[blocks.count];
|
||||
for (int j = 0; j < blocks.count; j++)
|
||||
|
@ -199,5 +201,31 @@ namespace TechbloxModdingAPI.Players
|
|||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void EnterSeat(uint playerId, EGID seatId)
|
||||
{
|
||||
PilotSeatGroupUtils.SwapTagTo<OCCUPIED_TAG>(Functions, seatId);
|
||||
var opt = GetCharacterStruct<CharacterPilotSeatEntityStruct>(playerId, out var group);
|
||||
if (!opt) return;
|
||||
ref CharacterPilotSeatEntityStruct charSeat = ref opt.Get();
|
||||
var charId = new EGID(playerId, group);
|
||||
charSeat.pilotSeatEntity = entitiesDB.GetEntityReference(seatId);
|
||||
charSeat.entryPositionOffset =
|
||||
entitiesDB.QueryEntity<PositionEntityStruct>(charId).position -
|
||||
entitiesDB.QueryEntity<PositionEntityStruct>(seatId).position;
|
||||
ref var seat = ref entitiesDB.QueryEntity<PilotSeatEntityStruct>(seatId);
|
||||
seat.occupyingCharacter = entitiesDB.GetEntityReference(charId);
|
||||
charSeat.followCam = entitiesDB.QueryEntity<SeatFollowCamComponent>(seatId).followCam;
|
||||
Functions.SwapEntityGroup<CharacterEntityDescriptor>(charId, CharacterExclusiveGroups.InPilotSeatGroup);
|
||||
}
|
||||
|
||||
public void ExitSeat(uint playerId)
|
||||
{
|
||||
EGID egid = new EGID(playerId, CharacterExclusiveGroups.InPilotSeatGroup);
|
||||
var opt = entitiesDB.QueryEntityOptional<CharacterPilotSeatEntityStruct>(egid);
|
||||
if (!opt) return;
|
||||
opt.Get().instantExit = true;
|
||||
entitiesDB.PublishEntityChange<CharacterPilotSeatEntityStruct>(egid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
33
TechbloxModdingAPI/Players/PlayerEventsEngine.cs
Normal file
33
TechbloxModdingAPI/Players/PlayerEventsEngine.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
using RobocraftX.Character;
|
||||
using RobocraftX.Character.Movement;
|
||||
using Svelto.ECS;
|
||||
using TechbloxModdingAPI.Engines;
|
||||
|
||||
namespace TechbloxModdingAPI.Players
|
||||
{
|
||||
public class PlayerEventsEngine : IApiEngine, IReactOnSwap<CharacterPilotSeatEntityStruct>
|
||||
{
|
||||
public void Ready()
|
||||
{
|
||||
}
|
||||
|
||||
public EntitiesDB entitiesDB { get; set; }
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public string Name => "TechbloxModdingAPIPlayerEventsEngine";
|
||||
public bool isRemovable => false;
|
||||
|
||||
public void MovedTo(ref CharacterPilotSeatEntityStruct entityComponent, ExclusiveGroupStruct previousGroup, EGID egid)
|
||||
{
|
||||
var seatId = entityComponent.pilotSeatEntity.ToEGID(entitiesDB);
|
||||
var player = EcsObjectBase.GetInstance(new EGID(egid.entityID, CharacterExclusiveGroups.OnFootGroup),
|
||||
e => new Player(e.entityID));
|
||||
if (previousGroup == CharacterExclusiveGroups.InPilotSeatGroup)
|
||||
player.seatExited.Invoke(this, new PlayerSeatEventArgs { SeatId = seatId});
|
||||
else if (egid.groupID == CharacterExclusiveGroups.InPilotSeatGroup)
|
||||
player.seatEntered.Invoke(this, new PlayerSeatEventArgs { SeatId = seatId });
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +1,11 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Svelto.Tasks;
|
||||
using Svelto.Tasks.Enumerators;
|
||||
using Unity.Mathematics;
|
||||
|
||||
using TechbloxModdingAPI;
|
||||
using TechbloxModdingAPI.App;
|
||||
using TechbloxModdingAPI.Blocks;
|
||||
using TechbloxModdingAPI.Tests;
|
||||
|
||||
namespace TechbloxModdingAPI.Players
|
||||
|
@ -30,6 +33,34 @@ namespace TechbloxModdingAPI.Players
|
|||
if (!Assert.Errorless(() => { p.Position = float3.zero + 1; }, "Player.Position = origin+1 errored: ", "Player moved to origin+1.")) return;
|
||||
Assert.CloseTo(p.Position, float3.zero + 1, "Player is not close to origin+1 despite being teleported there.", "Player.Position is at origin+1.");
|
||||
}
|
||||
|
||||
[APITestCase(TestType.Game)]
|
||||
public static void SeatEventTestBuild()
|
||||
{
|
||||
Player.LocalPlayer.SeatEntered += Assert.CallsBack<PlayerSeatEventArgs>("SeatEntered");
|
||||
Player.LocalPlayer.SeatExited += Assert.CallsBack<PlayerSeatEventArgs>("SeatExited");
|
||||
Block.PlaceNew(BlockIDs.DriverSeat, -1f);
|
||||
}
|
||||
|
||||
[APITestCase(TestType.SimulationMode)]
|
||||
public static IEnumerator<TaskContract> SeatEventTestSim()
|
||||
{
|
||||
var seats = Game.CurrentGame().GetBlocksInGame(BlockIDs.DriverSeat);
|
||||
if (seats.Length == 0)
|
||||
{
|
||||
Assert.Fail("No driver seat found!");
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (seats[0] is Seat seat)
|
||||
Assert.Errorless(() => Player.LocalPlayer.EnterSeat(seat), "Failed to enter seat.",
|
||||
"Entered seat successfully.");
|
||||
else
|
||||
Assert.Fail("Found a seat that is not a seat!");
|
||||
yield return new WaitForSecondsEnumerator(1).Continue();
|
||||
Assert.Errorless(() => Player.LocalPlayer.ExitSeat(), "Failed to exit seat.",
|
||||
"Exited seat successfully.");
|
||||
}
|
||||
|
||||
[APITestCase(TestType.Menu)]
|
||||
public static void InvalidStateTest()
|
||||
|
|
|
@ -14,8 +14,6 @@ namespace TechbloxModdingAPI
|
|||
/// </summary>
|
||||
public class SimBody : EcsObjectBase, IEquatable<SimBody>, IEquatable<EGID>
|
||||
{
|
||||
public override EGID Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The cluster this chunk belongs to, or null if no cluster destruction manager present or the chunk doesn't exist.
|
||||
/// Get the SimBody from a Block if possible for good performance here.
|
||||
|
@ -28,9 +26,8 @@ namespace TechbloxModdingAPI
|
|||
private Cluster cluster;
|
||||
private readonly uint clusterId = uint.MaxValue;
|
||||
|
||||
public SimBody(EGID id)
|
||||
public SimBody(EGID id) : base(id)
|
||||
{
|
||||
Id = id;
|
||||
}
|
||||
|
||||
public SimBody(uint id) : this(new EGID(id, CommonExclusiveGroups.SIMULATION_BODIES_GROUP))
|
||||
|
|
|
@ -58,6 +58,14 @@ namespace TechbloxModdingAPI.Tests
|
|||
|
||||
// debug/test handlers
|
||||
Client.EnterMenu += (sender, args) => throw new Exception("Test handler always throws an exception!");
|
||||
Client.EnterMenu += (sender, args) => Console.WriteLine("EnterMenu handler after erroring handler");
|
||||
Game.Enter += (s, a) =>
|
||||
{
|
||||
Player.LocalPlayer.SeatEntered += (sender, args) =>
|
||||
Console.WriteLine($"Player {Player.LocalPlayer} entered seat {args.Seat}");
|
||||
Player.LocalPlayer.SeatExited += (sender, args) =>
|
||||
Console.WriteLine($"Player {Player.LocalPlayer} exited seat {args.Seat}");
|
||||
};
|
||||
|
||||
// debug/test commands
|
||||
if (Dependency.Hell("ExtraCommands"))
|
||||
|
|
|
@ -72,14 +72,6 @@ namespace TechbloxModdingAPI.Utility
|
|||
}
|
||||
}
|
||||
|
||||
public static SimpleEntitiesSubmissionScheduler _mainGameSubmissionScheduler
|
||||
{
|
||||
get
|
||||
{
|
||||
return (SimpleEntitiesSubmissionScheduler)fgcr?.Field("_sub").Field("_mainGameSubmissionScheduler").GetValue();
|
||||
}
|
||||
}
|
||||
|
||||
public static BuildPhysicsWorld _physicsWorldSystem
|
||||
{
|
||||
get
|
||||
|
|
|
@ -26,10 +26,10 @@ namespace TechbloxModdingAPI.Utility
|
|||
{
|
||||
Logging.MetaDebugLog($"Registering Game IApiEngine {engine.Name}");
|
||||
_lastEngineRoot.AddEngine(engine);
|
||||
if (typeof(IFactoryEngine).IsAssignableFrom(engine.GetType()))
|
||||
{
|
||||
((IFactoryEngine)engine).Factory = _lastEngineRoot.GenerateEntityFactory();
|
||||
}
|
||||
if (engine is IFactoryEngine factoryEngine)
|
||||
factoryEngine.Factory = _lastEngineRoot.GenerateEntityFactory();
|
||||
if (engine is IFunEngine funEngine)
|
||||
funEngine.Functions = _lastEngineRoot.GenerateEntityFunctions();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,6 +66,7 @@ namespace TechbloxModdingAPI.Utility
|
|||
var enginesRoot = helper.enginesRoot;
|
||||
_lastEngineRoot = enginesRoot;
|
||||
IEntityFactory factory = enginesRoot.GenerateEntityFactory();
|
||||
IEntityFunctions functions = enginesRoot.GenerateEntityFunctions();
|
||||
foreach (var key in _gameEngines.Keys)
|
||||
{
|
||||
Logging.MetaDebugLog($"Registering Game IApiEngine {_gameEngines[key].Name}");
|
||||
|
@ -75,6 +76,8 @@ namespace TechbloxModdingAPI.Utility
|
|||
enginesRoot.AddEngine(_gameEngines[key]);
|
||||
if (_gameEngines[key] is IFactoryEngine factEngine)
|
||||
factEngine.Factory = factory;
|
||||
if (_gameEngines[key] is IFunEngine funEngine)
|
||||
funEngine.Functions = functions;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,10 +26,10 @@ namespace TechbloxModdingAPI.Utility
|
|||
{
|
||||
Logging.MetaDebugLog($"Registering Menu IApiEngine {engine.Name}");
|
||||
_lastEngineRoot.AddEngine(engine);
|
||||
if (typeof(IFactoryEngine).IsAssignableFrom(engine.GetType()))
|
||||
{
|
||||
((IFactoryEngine)engine).Factory = _lastEngineRoot.GenerateEntityFactory();
|
||||
}
|
||||
if (engine is IFactoryEngine factoryEngine)
|
||||
factoryEngine.Factory = _lastEngineRoot.GenerateEntityFactory();
|
||||
if (engine is IFunEngine funEngine)
|
||||
funEngine.Functions = _lastEngineRoot.GenerateEntityFunctions();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -65,14 +65,13 @@ namespace TechbloxModdingAPI.Utility
|
|||
{
|
||||
_lastEngineRoot = enginesRoot;
|
||||
IEntityFactory factory = enginesRoot.GenerateEntityFactory();
|
||||
IEntityFunctions functions = enginesRoot.GenerateEntityFunctions();
|
||||
foreach (var key in _menuEngines.Keys)
|
||||
{
|
||||
Logging.MetaDebugLog($"Registering Menu IApiEngine {_menuEngines[key].Name}");
|
||||
enginesRoot.AddEngine(_menuEngines[key]);
|
||||
if (_menuEngines[key] is IFactoryEngine factEngine)
|
||||
{
|
||||
factEngine.Factory = factory;
|
||||
}
|
||||
if (_menuEngines[key] is IFactoryEngine factEngine) factEngine.Factory = factory;
|
||||
if(_menuEngines[key] is IFunEngine funEngine) funEngine.Functions = functions;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue