8f099c32ba
AbilityDefBlob/AbilityDefinition gain WindupTicks. AbilityFireSystem's socket loop resolves each socket at the historical command (serverTick - WindupTicks) via GetDataAtTick — a pure fn of replicated data on both worlds (Build Spec §4/ DB-6: no local fireAtTick that would diverge under reprediction; fireCount from that command, spawn uses the current tick's aim/pos; history gap = no-fire). WindupTicks set on the 4 non-Blink Sparks (Blink is movement, resolved by BlinkSystem). 497 EditMode green (no regression). Client telegraph VFX + the reprediction L3 test are operator-eyes / netcode-world follow-ups. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
97 lines
4.5 KiB
C#
97 lines
4.5 KiB
C#
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,
|
|
Archetype = (byte)def.Archetype,
|
|
AutoTargetConeRadians = math.radians(def.AutoTargetConeDegrees),
|
|
CooldownTicks = def.CooldownTicks,
|
|
EffectFlags = (byte)(def.Reel ? ProjectileEffectFlag.Reel : (byte)0),
|
|
DurationTicks = def.EffectDurationTicks,
|
|
WindupTicks = def.WindupTicks,
|
|
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),
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|