Initial Combat Implementation
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7329bc1c607064c54a087b67292b8ff5
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,92 @@
|
||||
using System.Collections.Generic;
|
||||
using ProjectM.Simulation;
|
||||
using Unity.Collections;
|
||||
using Unity.Entities;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ProjectM.Authoring
|
||||
{
|
||||
/// <summary>
|
||||
/// Bakes the designer-authored ability + character definitions into a single AbilityDatabase blob
|
||||
/// singleton (immutable, shared, Burst-fast) plus a companion AbilityPrefabElement buffer holding the
|
||||
/// per-ability projectile ghost prefab entity refs (entity refs cannot live in a blob). Place ONE of
|
||||
/// these in the gameplay subscene; it streams identically into the client and server worlds (config,
|
||||
/// not replicated). DependsOn each definition so a value change in an SO re-bakes the blob.
|
||||
/// </summary>
|
||||
public class AbilityDatabaseAuthoring : MonoBehaviour
|
||||
{
|
||||
[Tooltip("All ability definitions available in the game. Indexed at runtime by AbilityId.")]
|
||||
public List<AbilityDefinition> Abilities = new List<AbilityDefinition>();
|
||||
|
||||
[Tooltip("All character-stats definitions. Indexed at runtime by CharacterId.")]
|
||||
public List<CharacterStatsDefinition> Characters = new List<CharacterStatsDefinition>();
|
||||
|
||||
private class DatabaseBaker : Baker<AbilityDatabaseAuthoring>
|
||||
{
|
||||
public override void Bake(AbilityDatabaseAuthoring authoring)
|
||||
{
|
||||
var entity = GetEntity(TransformUsageFlags.None);
|
||||
|
||||
int abilityCount = authoring.Abilities != null ? authoring.Abilities.Count : 0;
|
||||
int charCount = authoring.Characters != null ? authoring.Characters.Count : 0;
|
||||
|
||||
var builder = new BlobBuilder(Allocator.Temp);
|
||||
ref var root = ref builder.ConstructRoot<AbilityDatabaseBlob>();
|
||||
|
||||
var abilityArray = builder.Allocate(ref root.Abilities, abilityCount);
|
||||
for (int i = 0; i < abilityCount; i++)
|
||||
{
|
||||
var def = authoring.Abilities[i];
|
||||
if (def == null) { abilityArray[i] = default; continue; }
|
||||
DependsOn(def);
|
||||
abilityArray[i] = new AbilityDefBlob
|
||||
{
|
||||
Id = (byte)def.Id,
|
||||
Damage = def.Damage,
|
||||
ProjectileSpeed = def.ProjectileSpeed,
|
||||
Range = def.Range,
|
||||
AutoTargetRange = def.AutoTargetRange,
|
||||
AutoTargetConeRadians = math.radians(def.AutoTargetConeDegrees),
|
||||
CooldownTicks = def.CooldownTicks,
|
||||
Name = def.DisplayName,
|
||||
};
|
||||
}
|
||||
|
||||
var charArray = builder.Allocate(ref root.Characters, charCount);
|
||||
for (int i = 0; i < charCount; i++)
|
||||
{
|
||||
var def = authoring.Characters[i];
|
||||
if (def == null) { charArray[i] = default; continue; }
|
||||
DependsOn(def);
|
||||
charArray[i] = new CharacterStatsBlob
|
||||
{
|
||||
Id = (byte)def.Id,
|
||||
MoveSpeed = def.MoveSpeed,
|
||||
TurnRateRadiansPerSec = math.radians(def.TurnRateDegreesPerSec),
|
||||
MaxHealth = def.MaxHealth,
|
||||
Name = def.DisplayName,
|
||||
};
|
||||
}
|
||||
|
||||
var blob = builder.CreateBlobAssetReference<AbilityDatabaseBlob>(Allocator.Persistent);
|
||||
builder.Dispose();
|
||||
AddBlobAsset(ref blob, out _);
|
||||
AddComponent(entity, new AbilityDatabase { Value = blob });
|
||||
|
||||
// Companion entity-ref buffer: per-ability projectile ghost prefab (resolved via GetEntity).
|
||||
var prefabBuffer = AddBuffer<AbilityPrefabElement>(entity);
|
||||
for (int i = 0; i < abilityCount; i++)
|
||||
{
|
||||
var def = authoring.Abilities[i];
|
||||
if (def == null || def.ProjectilePrefab == null) continue;
|
||||
prefabBuffer.Add(new AbilityPrefabElement
|
||||
{
|
||||
Id = (byte)def.Id,
|
||||
Prefab = GetEntity(def.ProjectilePrefab, TransformUsageFlags.Dynamic),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4f60b41d8ec8d4c24b4d4f54af919080
|
||||
@@ -0,0 +1,33 @@
|
||||
using ProjectM.Simulation;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ProjectM.Authoring
|
||||
{
|
||||
/// <summary>
|
||||
/// Designer-facing definition of one ability. Numeric fields are baked into the AbilityDatabase blob
|
||||
/// (immutable, Burst-fast runtime config); the projectile prefab is baked into the companion
|
||||
/// AbilityPrefabElement buffer (entity refs cannot live inside a blob). UI fields (icon/description)
|
||||
/// are deliberately deferred to a later managed lookup keyed by id.
|
||||
/// </summary>
|
||||
[CreateAssetMenu(menuName = "Project M/Ability Definition", fileName = "Ability_")]
|
||||
public class AbilityDefinition : ScriptableObject
|
||||
{
|
||||
public AbilityId Id = AbilityId.Primary;
|
||||
public string DisplayName = "Ability";
|
||||
|
||||
[Header("Combat")]
|
||||
[Min(0f)] public float Damage = 20f;
|
||||
[Min(0f)] public float ProjectileSpeed = 25f;
|
||||
[Min(0f)] public float Range = 20f;
|
||||
|
||||
[Header("Auto-target assist")]
|
||||
[Min(0f)] public float AutoTargetRange = 12f;
|
||||
[Min(0f)] public float AutoTargetConeDegrees = 35f;
|
||||
|
||||
[Header("Timing")]
|
||||
[Min(1)] public int CooldownTicks = 12;
|
||||
|
||||
[Header("Prefab (baked into the prefab buffer, not the blob)")]
|
||||
public GameObject ProjectilePrefab;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 84b20ca889a744e888c8c3b3b723ec69
|
||||
@@ -0,0 +1,21 @@
|
||||
using ProjectM.Simulation;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ProjectM.Authoring
|
||||
{
|
||||
/// <summary>
|
||||
/// Designer-facing definition of a character's base stats (movement + survivability), baked into the
|
||||
/// AbilityDatabase blob and looked up at runtime by CharacterStatsRef. The single source of these
|
||||
/// values - PlayerAuthoring also seeds the player's starting Health from MaxHealth.
|
||||
/// </summary>
|
||||
[CreateAssetMenu(menuName = "Project M/Character Stats Definition", fileName = "Character_")]
|
||||
public class CharacterStatsDefinition : ScriptableObject
|
||||
{
|
||||
public CharacterId Id = CharacterId.Default;
|
||||
public string DisplayName = "Character";
|
||||
|
||||
[Min(0f)] public float MoveSpeed = 6f;
|
||||
[Min(0f)] public float TurnRateDegreesPerSec = 720f;
|
||||
[Min(0f)] public float MaxHealth = 100f;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c63201f1fd9f24efd9ccae3e20cd2364
|
||||
@@ -0,0 +1,38 @@
|
||||
using ProjectM.Simulation;
|
||||
using Unity.Entities;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ProjectM.Authoring
|
||||
{
|
||||
/// <summary>
|
||||
/// Authoring for the projectile ghost prefab fired by the player's primary ability. Bakes the
|
||||
/// baked-once tunables (<see cref="Projectile.Speed"/>, <see cref="Projectile.Damage"/>,
|
||||
/// <see cref="Projectile.Range"/>) onto the entity; the replicated <c>Direction</c>/<c>SpawnId</c>
|
||||
/// and the integrated <c>DistanceTravelled</c> are left at their default 0 and written at spawn
|
||||
/// time by AbilityFireSystem. Ghost replication and <c>GhostOwner</c> are supplied by the
|
||||
/// GhostAuthoringComponent added on the same prefab GameObject (not added here, nor is Health).
|
||||
/// <c>GetEntity(TransformUsageFlags.Dynamic)</c> ensures a runtime-mutable LocalTransform exists.
|
||||
/// </summary>
|
||||
public class ProjectileAuthoring : MonoBehaviour
|
||||
{
|
||||
[Min(0f)] public float Speed = 25f;
|
||||
[Min(0f)] public float Damage = 20f;
|
||||
[Min(0f)] public float Range = 20f;
|
||||
|
||||
private class ProjectileBaker : Baker<ProjectileAuthoring>
|
||||
{
|
||||
public override void Bake(ProjectileAuthoring authoring)
|
||||
{
|
||||
var entity = GetEntity(authoring, TransformUsageFlags.Dynamic);
|
||||
|
||||
// Direction / SpawnId / DistanceTravelled default to 0 — set at spawn by AbilityFireSystem.
|
||||
AddComponent(entity, new Projectile
|
||||
{
|
||||
Speed = authoring.Speed,
|
||||
Damage = authoring.Damage,
|
||||
Range = authoring.Range
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1429bc3b3a1da44e4a11065be0733a8f
|
||||
@@ -0,0 +1,34 @@
|
||||
using ProjectM.Simulation;
|
||||
using Unity.Entities;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ProjectM.Authoring
|
||||
{
|
||||
/// <summary>
|
||||
/// Authoring placed once in the gameplay subscene. Bakes a <see cref="ProjectileSpawner"/>
|
||||
/// singleton holding the projectile ghost prefab entity, which the predicted AbilityFireSystem
|
||||
/// instantiates whenever a player fires. The spawner itself carries no transform (it is a pure
|
||||
/// data singleton) so it is baked with <c>TransformUsageFlags.None</c>, while the referenced
|
||||
/// prefab is baked with <c>TransformUsageFlags.Dynamic</c> so the spawned projectile has a
|
||||
/// runtime-mutable LocalTransform.
|
||||
/// </summary>
|
||||
public class ProjectileSpawnerAuthoring : MonoBehaviour
|
||||
{
|
||||
[Tooltip("The projectile ghost prefab spawned when a player fires.")]
|
||||
public GameObject ProjectilePrefab;
|
||||
|
||||
private class ProjectileSpawnerBaker : Baker<ProjectileSpawnerAuthoring>
|
||||
{
|
||||
public override void Bake(ProjectileSpawnerAuthoring authoring)
|
||||
{
|
||||
// The spawner itself needs no transform; it is a data singleton.
|
||||
var entity = GetEntity(authoring, TransformUsageFlags.None);
|
||||
|
||||
AddComponent(entity, new ProjectileSpawner
|
||||
{
|
||||
Prefab = GetEntity(authoring.ProjectilePrefab, TransformUsageFlags.Dynamic)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aa1b7c054d5a043f2801cddf90567acf
|
||||
@@ -0,0 +1,41 @@
|
||||
using ProjectM.Simulation;
|
||||
using Unity.Entities;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ProjectM.Authoring
|
||||
{
|
||||
/// <summary>
|
||||
/// Authoring for the training-dummy enemy prefab. Bakes a stationary, damageable auto-target
|
||||
/// candidate: <see cref="TrainingDummyTag"/> marks it for the ability auto-target cone,
|
||||
/// <see cref="Health"/> and <see cref="HitRadius"/> make it a valid projectile hit target, and a
|
||||
/// <see cref="DamageEvent"/> buffer receives server-authoritative hits. Dummies are NOT ghosts and
|
||||
/// carry no <c>GhostOwner</c>, so projectiles never treat them as the firing owner.
|
||||
/// <c>GetEntity(TransformUsageFlags.Dynamic)</c> ensures a runtime LocalTransform for hit tests
|
||||
/// and spawn placement.
|
||||
/// </summary>
|
||||
public class TrainingDummyAuthoring : MonoBehaviour
|
||||
{
|
||||
[Min(0f), Tooltip("Starting and maximum health for the dummy.")]
|
||||
public float MaxHealth = 60f;
|
||||
|
||||
[Min(0f), Tooltip("World-unit radius used by the projectile hit test.")]
|
||||
public float HitRadius = 0.8f;
|
||||
|
||||
private class TrainingDummyBaker : Baker<TrainingDummyAuthoring>
|
||||
{
|
||||
public override void Bake(TrainingDummyAuthoring authoring)
|
||||
{
|
||||
var entity = GetEntity(authoring, TransformUsageFlags.Dynamic);
|
||||
|
||||
AddComponent<TrainingDummyTag>(entity);
|
||||
AddComponent(entity, new Health
|
||||
{
|
||||
Current = authoring.MaxHealth,
|
||||
Max = authoring.MaxHealth
|
||||
});
|
||||
AddComponent(entity, new HitRadius { Value = authoring.HitRadius });
|
||||
AddBuffer<DamageEvent>(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c32aebeb7bfbb464898dfee8e6e87e6c
|
||||
@@ -0,0 +1,48 @@
|
||||
using ProjectM.Simulation;
|
||||
using Unity.Entities;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ProjectM.Authoring
|
||||
{
|
||||
/// <summary>
|
||||
/// Authoring for the baked <see cref="TrainingDummySpawner"/> singleton. Place this on a single
|
||||
/// GameObject in the gameplay subscene; at runtime the server-only
|
||||
/// <c>TrainingDummySpawnSystem</c> reads the singleton, instantiates <see cref="Count"/> dummies
|
||||
/// laid out along +X from <see cref="Origin"/> at <see cref="Spacing"/> intervals, then destroys
|
||||
/// the singleton so it fires exactly once. The entity itself carries no transform
|
||||
/// (<c>TransformUsageFlags.None</c>); only the referenced <see cref="DummyPrefab"/> needs a
|
||||
/// runtime-mutable LocalTransform (<c>TransformUsageFlags.Dynamic</c>).
|
||||
/// </summary>
|
||||
public class TrainingDummySpawnerAuthoring : MonoBehaviour
|
||||
{
|
||||
[Tooltip("Training dummy prefab to instantiate. Must carry TrainingDummyAuthoring.")]
|
||||
public GameObject DummyPrefab;
|
||||
|
||||
[Min(0)]
|
||||
[Tooltip("How many dummies to spawn.")]
|
||||
public int Count = 3;
|
||||
|
||||
[Min(0f)]
|
||||
[Tooltip("World-unit spacing between consecutive dummies along +X.")]
|
||||
public float Spacing = 3f;
|
||||
|
||||
[Tooltip("World-space position of the first dummy.")]
|
||||
public Vector3 Origin = new Vector3(0, 0, 8);
|
||||
|
||||
private class TrainingDummySpawnerBaker : Baker<TrainingDummySpawnerAuthoring>
|
||||
{
|
||||
public override void Bake(TrainingDummySpawnerAuthoring authoring)
|
||||
{
|
||||
var entity = GetEntity(authoring, TransformUsageFlags.None);
|
||||
|
||||
AddComponent(entity, new TrainingDummySpawner
|
||||
{
|
||||
Prefab = GetEntity(authoring.DummyPrefab, TransformUsageFlags.Dynamic),
|
||||
Count = authoring.Count,
|
||||
Spacing = authoring.Spacing,
|
||||
Origin = authoring.Origin
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 51cc543c146d84239ba6dc219221df18
|
||||
@@ -0,0 +1,43 @@
|
||||
using ProjectM.Simulation;
|
||||
using Unity.Entities;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ProjectM.Authoring
|
||||
{
|
||||
/// <summary>
|
||||
/// Authoring for an upgrade pickup ghost prefab: a world object that grants one stat modifier to the
|
||||
/// first player that overlaps it (server-authoritative, applied by <c>UpgradePickupSystem</c>) and
|
||||
/// then despawns. Bake the prefab as an interpolated ghost (add a GhostAuthoringComponent) so clients
|
||||
/// see it appear and despawn. <c>GetEntity(TransformUsageFlags.Dynamic)</c> gives it a world transform.
|
||||
/// </summary>
|
||||
public class UpgradePickupAuthoring : MonoBehaviour
|
||||
{
|
||||
[Tooltip("Which stat the granted modifier targets.")]
|
||||
public StatTarget Target = StatTarget.Damage;
|
||||
|
||||
[Tooltip("How the granted modifier combines.")]
|
||||
public ModOp Op = ModOp.Flat;
|
||||
|
||||
[Tooltip("Modifier magnitude: flat amount, or fractional percent (0.1 = +10%).")]
|
||||
public float Value = 10f;
|
||||
|
||||
[Tooltip("Overlap radius (world units) for the player pickup test.")]
|
||||
[Min(0f)] public float HitRadius = 1f;
|
||||
|
||||
private class UpgradePickupBaker : Baker<UpgradePickupAuthoring>
|
||||
{
|
||||
public override void Bake(UpgradePickupAuthoring authoring)
|
||||
{
|
||||
var entity = GetEntity(authoring, TransformUsageFlags.Dynamic);
|
||||
AddComponent(entity, new UpgradePickup
|
||||
{
|
||||
Target = (byte)authoring.Target,
|
||||
Op = (byte)authoring.Op,
|
||||
Value = authoring.Value,
|
||||
SourceId = 0u,
|
||||
});
|
||||
AddComponent(entity, new HitRadius { Value = authoring.HitRadius });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 07e6d40378fcb43c5be706ef96cb4bb2
|
||||
@@ -0,0 +1,48 @@
|
||||
using ProjectM.Simulation;
|
||||
using Unity.Entities;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ProjectM.Authoring
|
||||
{
|
||||
/// <summary>
|
||||
/// Authoring for the baked <see cref="UpgradePickupSpawner"/> singleton (mirrors
|
||||
/// TrainingDummySpawnerAuthoring). Place on a single GameObject in the gameplay subscene; the
|
||||
/// server-only <c>UpgradePickupSpawnSystem</c> reads it, spawns <see cref="Count"/> pickups along +X
|
||||
/// from <see cref="Origin"/> at <see cref="Spacing"/> intervals, then destroys the singleton so it
|
||||
/// fires exactly once. The entity carries no transform; only the prefab needs a runtime transform.
|
||||
/// </summary>
|
||||
public class UpgradePickupSpawnerAuthoring : MonoBehaviour
|
||||
{
|
||||
[Tooltip("Upgrade pickup prefab to instantiate. Must carry UpgradePickupAuthoring.")]
|
||||
public GameObject PickupPrefab;
|
||||
|
||||
[Min(0)]
|
||||
[Tooltip("How many pickups to spawn.")]
|
||||
public int Count = 2;
|
||||
|
||||
[Min(0f)]
|
||||
[Tooltip("World-unit spacing between consecutive pickups along +X.")]
|
||||
public float Spacing = 3f;
|
||||
|
||||
[Tooltip("World-space position of the first pickup.")]
|
||||
public Vector3 Origin = new Vector3(-4f, 0f, 6f);
|
||||
|
||||
private class UpgradePickupSpawnerBaker : Baker<UpgradePickupSpawnerAuthoring>
|
||||
{
|
||||
public override void Bake(UpgradePickupSpawnerAuthoring authoring)
|
||||
{
|
||||
var entity = GetEntity(authoring, TransformUsageFlags.None);
|
||||
|
||||
AddComponent(entity, new UpgradePickupSpawner
|
||||
{
|
||||
Prefab = authoring.PickupPrefab != null
|
||||
? GetEntity(authoring.PickupPrefab, TransformUsageFlags.Dynamic)
|
||||
: Entity.Null,
|
||||
Count = authoring.Count,
|
||||
Spacing = authoring.Spacing,
|
||||
Origin = authoring.Origin,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e367eb55e2c2248f18be10d6c3c9ad67
|
||||
@@ -1,21 +1,32 @@
|
||||
using ProjectM.Simulation;
|
||||
using Unity.Entities;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ProjectM.Authoring
|
||||
{
|
||||
/// <summary>
|
||||
/// Authoring for the player ghost prefab. Bakes the gameplay components onto the entity and
|
||||
/// exposes movement tunables for designers. Ghost replication, <c>GhostOwner</c> and
|
||||
/// AutoCommandTarget are supplied by the GhostAuthoringComponent added on the same prefab
|
||||
/// GameObject. <c>GetEntity(TransformUsageFlags.Dynamic)</c> ensures a runtime-mutable
|
||||
/// LocalTransform exists.
|
||||
/// Authoring for the player ghost prefab. As of M3 the numeric tunables live in data
|
||||
/// (<see cref="CharacterStatsDefinition"/> / <see cref="AbilityDefinition"/> ScriptableObjects);
|
||||
/// this authoring only selects which definitions the player uses and bakes the light id refs, the
|
||||
/// (empty) replicated modifier buffer, and the zeroed effective-stat components that
|
||||
/// StatRecomputeSystem fills each predicted tick. Health is seeded from the character definition's
|
||||
/// MaxHealth (single source). Ghost replication, <c>GhostOwner</c> and AutoCommandTarget come from
|
||||
/// the GhostAuthoringComponent on the same prefab GameObject; <c>GetEntity(TransformUsageFlags.Dynamic)</c>
|
||||
/// ensures a runtime-mutable LocalTransform exists.
|
||||
/// </summary>
|
||||
public class PlayerAuthoring : MonoBehaviour
|
||||
{
|
||||
[Min(0f)] public float MoveSpeed = 6f;
|
||||
[Min(0f)] public float TurnRateDegreesPerSec = 720f;
|
||||
[Tooltip("Character-stats definition (move speed, turn rate, max health). Single source of those values.")]
|
||||
public CharacterStatsDefinition Character;
|
||||
|
||||
[Tooltip("Ability definition occupying the player's primary slot.")]
|
||||
public AbilityDefinition PrimaryAbility;
|
||||
|
||||
[Header("Fallbacks (used only if a definition above is unassigned)")]
|
||||
[Min(0f)] public float FallbackMaxHealth = 100f;
|
||||
|
||||
/// <summary>Projectile hit-test radius for the player as a damageable target, in world units.</summary>
|
||||
[Min(0f)] public float HitRadius = 0.6f;
|
||||
|
||||
private class PlayerBaker : Baker<PlayerAuthoring>
|
||||
{
|
||||
@@ -23,14 +34,38 @@ namespace ProjectM.Authoring
|
||||
{
|
||||
var entity = GetEntity(authoring, TransformUsageFlags.Dynamic);
|
||||
|
||||
// Re-bake when a referenced definition's serialized values change.
|
||||
if (authoring.Character != null) DependsOn(authoring.Character);
|
||||
if (authoring.PrimaryAbility != null) DependsOn(authoring.PrimaryAbility);
|
||||
|
||||
byte characterId = authoring.Character != null
|
||||
? (byte)authoring.Character.Id : (byte)CharacterId.Default;
|
||||
byte abilityId = authoring.PrimaryAbility != null
|
||||
? (byte)authoring.PrimaryAbility.Id : (byte)AbilityId.Primary;
|
||||
float maxHealth = authoring.Character != null
|
||||
? authoring.Character.MaxHealth : authoring.FallbackMaxHealth;
|
||||
|
||||
AddComponent<PlayerTag>(entity);
|
||||
AddComponent(entity, new PlayerMoveStats
|
||||
{
|
||||
MoveSpeed = authoring.MoveSpeed,
|
||||
TurnRateRadiansPerSec = math.radians(authoring.TurnRateDegreesPerSec)
|
||||
});
|
||||
AddComponent<PlayerFacing>(entity);
|
||||
AddComponent<PlayerInput>(entity);
|
||||
|
||||
// Data-driven stat refs (replace M2's inlined PlayerMoveStats / AbilityStats values).
|
||||
AddComponent(entity, new CharacterStatsRef { Id = characterId });
|
||||
AddComponent(entity, new AbilityRef { Id = abilityId });
|
||||
|
||||
// Effective stats: zeroed at bake, recomputed every predicted tick by StatRecomputeSystem.
|
||||
AddComponent(entity, new EffectiveAbilityStats());
|
||||
AddComponent(entity, new EffectiveCharacterStats());
|
||||
|
||||
// Empty replicated modifier stack (grown by upgrades/pickups/debug hook, server-authoritative).
|
||||
AddBuffer<StatModifier>(entity);
|
||||
|
||||
// Combat: server-authoritative health (Current replicated for display), the player's
|
||||
// damageable hit radius, predicted cooldown state, and the per-tick damage inbox.
|
||||
AddComponent(entity, new Health { Current = maxHealth, Max = maxHealth });
|
||||
AddComponent(entity, new HitRadius { Value = authoring.HitRadius });
|
||||
AddComponent<AbilityCooldown>(entity);
|
||||
AddBuffer<DamageEvent>(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user