Compare commits
5 Commits
98c8da3fd7
...
78d4489699
| Author | SHA1 | Date | |
|---|---|---|---|
| 78d4489699 | |||
| d30d96952c | |||
| aeb979f8d5 | |||
| 342a39a540 | |||
| 8f099c32ba |
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 48885d98d8dbc3c47943402d0a455f21
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -23,4 +23,5 @@ MonoBehaviour:
|
|||||||
CooldownTicks: 360
|
CooldownTicks: 360
|
||||||
Reel: 0
|
Reel: 0
|
||||||
EffectDurationTicks: 480
|
EffectDurationTicks: 480
|
||||||
|
WindupTicks: 12
|
||||||
ProjectilePrefab: {fileID: 3885353946372160549, guid: a5b1532dc1acca045856fd653fe392b9, type: 3}
|
ProjectilePrefab: {fileID: 3885353946372160549, guid: a5b1532dc1acca045856fd653fe392b9, type: 3}
|
||||||
|
|||||||
@@ -23,4 +23,5 @@ MonoBehaviour:
|
|||||||
CooldownTicks: 120
|
CooldownTicks: 120
|
||||||
Reel: 1
|
Reel: 1
|
||||||
EffectDurationTicks: 0
|
EffectDurationTicks: 0
|
||||||
|
WindupTicks: 8
|
||||||
ProjectilePrefab: {fileID: 8857056134628386430, guid: 700fb9c3f7cf94830a0ee78c4e4f1290, type: 3}
|
ProjectilePrefab: {fileID: 8857056134628386430, guid: 700fb9c3f7cf94830a0ee78c4e4f1290, type: 3}
|
||||||
|
|||||||
@@ -23,4 +23,5 @@ MonoBehaviour:
|
|||||||
CooldownTicks: 480
|
CooldownTicks: 480
|
||||||
Reel: 0
|
Reel: 0
|
||||||
EffectDurationTicks: 420
|
EffectDurationTicks: 420
|
||||||
|
WindupTicks: 18
|
||||||
ProjectilePrefab: {fileID: 3885353946372160549, guid: e12b85c23fd562d49b27a3d2fdfd9cad, type: 3}
|
ProjectilePrefab: {fileID: 3885353946372160549, guid: e12b85c23fd562d49b27a3d2fdfd9cad, type: 3}
|
||||||
|
|||||||
@@ -23,4 +23,5 @@ MonoBehaviour:
|
|||||||
CooldownTicks: 420
|
CooldownTicks: 420
|
||||||
Reel: 0
|
Reel: 0
|
||||||
EffectDurationTicks: 300
|
EffectDurationTicks: 300
|
||||||
|
WindupTicks: 20
|
||||||
ProjectilePrefab: {fileID: 3885353946372160549, guid: 6e31b46e818d7f348931e3d0ea73bf23, type: 3}
|
ProjectilePrefab: {fileID: 3885353946372160549, guid: 6e31b46e818d7f348931e3d0ea73bf23, type: 3}
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ namespace ProjectM.Authoring
|
|||||||
CooldownTicks = def.CooldownTicks,
|
CooldownTicks = def.CooldownTicks,
|
||||||
EffectFlags = (byte)(def.Reel ? ProjectileEffectFlag.Reel : (byte)0),
|
EffectFlags = (byte)(def.Reel ? ProjectileEffectFlag.Reel : (byte)0),
|
||||||
DurationTicks = def.EffectDurationTicks,
|
DurationTicks = def.EffectDurationTicks,
|
||||||
|
WindupTicks = def.WindupTicks,
|
||||||
Name = def.DisplayName,
|
Name = def.DisplayName,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,9 @@ namespace ProjectM.Authoring
|
|||||||
public bool Reel = false;
|
public bool Reel = false;
|
||||||
[Min(0), Tooltip("Aoe/zone + decoy lifetime in ticks (0 = system default).")]
|
[Min(0), Tooltip("Aoe/zone + decoy lifetime in ticks (0 = system default).")]
|
||||||
public int EffectDurationTicks = 0;
|
public int EffectDurationTicks = 0;
|
||||||
|
[Header("Manual-aim windup")]
|
||||||
|
[Min(0), Tooltip("Telegraph delay (ticks, ~60/s) before the ability resolves. 0 = instant. The SoD 'readable windup'.")]
|
||||||
|
public int WindupTicks = 0;
|
||||||
|
|
||||||
[Header("Prefab (baked into the prefab buffer, not the blob)")]
|
[Header("Prefab (baked into the prefab buffer, not the blob)")]
|
||||||
public GameObject ProjectilePrefab;
|
public GameObject ProjectilePrefab;
|
||||||
|
|||||||
@@ -83,9 +83,9 @@ namespace ProjectM.Client
|
|||||||
// Local class from the replicated AbilityRef (tracks the dev class-switch; PlayerClass is server-only).
|
// Local class from the replicated AbilityRef (tracks the dev class-switch; PlayerClass is server-only).
|
||||||
byte localClass = ClassTraits.WarriorClass;
|
byte localClass = ClassTraits.WarriorClass;
|
||||||
bool haveLocalPlayer = false;
|
bool haveLocalPlayer = false;
|
||||||
foreach (var ar in SystemAPI.Query<RefRO<AbilityRef>>().WithAll<PlayerTag, GhostOwnerIsLocal>())
|
foreach (var (fr, ar) in SystemAPI.Query<RefRO<FrameId>, RefRO<AbilityRef>>().WithAll<PlayerTag, GhostOwnerIsLocal>())
|
||||||
{
|
{
|
||||||
localClass = ClassTraits.ClassForAbility(ar.ValueRO.Id);
|
localClass = fr.ValueRO.Value != 0 ? fr.ValueRO.Value : ClassTraits.ClassForAbility(ar.ValueRO.Id); // FrameId signal, AbilityRef fallback
|
||||||
haveLocalPlayer = true;
|
haveLocalPlayer = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,9 +76,9 @@ namespace ProjectM.Client
|
|||||||
// server-only); tiers from the replicated MetaTierState record on the director ghost.
|
// server-only); tiers from the replicated MetaTierState record on the director ghost.
|
||||||
byte localClass = ClassTraits.WarriorClass;
|
byte localClass = ClassTraits.WarriorClass;
|
||||||
bool haveLocalPlayer = false;
|
bool haveLocalPlayer = false;
|
||||||
foreach (var ar in SystemAPI.Query<RefRO<AbilityRef>>().WithAll<PlayerTag, GhostOwnerIsLocal>())
|
foreach (var (fr, ar) in SystemAPI.Query<RefRO<FrameId>, RefRO<AbilityRef>>().WithAll<PlayerTag, GhostOwnerIsLocal>())
|
||||||
{
|
{
|
||||||
localClass = ClassTraits.ClassForAbility(ar.ValueRO.Id);
|
localClass = fr.ValueRO.Value != 0 ? fr.ValueRO.Value : ClassTraits.ClassForAbility(ar.ValueRO.Id); // FrameId signal, AbilityRef fallback
|
||||||
haveLocalPlayer = true;
|
haveLocalPlayer = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,15 +88,16 @@ namespace ProjectM.Server
|
|||||||
// Expedition redesign: the server-only class anchor the meta systems key on (born-correct meta
|
// Expedition redesign: the server-only class anchor the meta systems key on (born-correct meta
|
||||||
// seeding at Step 12a + per-class spend at Step 13 resolve the tier record through this).
|
// seeding at Step 12a + per-class spend at Step 13 resolve the tier record through this).
|
||||||
ecb.AddComponent(player, new PlayerClass { ClassId = classId });
|
ecb.AddComponent(player, new PlayerClass { ClassId = classId });
|
||||||
|
ecb.AddComponent(player, new FrameId { Value = classId }); // Add (not Set): baked on the real player; absent on the minimal test prefab // replicated frame/class signal (HUD reads this, not AbilityRef)
|
||||||
if (isGym)
|
if (isGym)
|
||||||
{
|
{
|
||||||
// GYM: default Spark socket loadout on keys 1-4 (AbilityFireSystem reads sockets, not AbilityRef).
|
// GYM: per-frame default Spark loadout on keys 1-4 (AbilityFireSystem reads sockets, not AbilityRef).
|
||||||
// LightZone(9) is defined + dispatch-ready but off the default bar (socket it via a swap).
|
ClassTraits.FrameLoadout(classId, out byte f0, out byte f1, out byte f2, out byte f3);
|
||||||
var gymSockets = ecb.SetBuffer<AbilitySocket>(player);
|
var gymSockets = ecb.SetBuffer<AbilitySocket>(player);
|
||||||
gymSockets.Add(new AbilitySocket { SparkId = (byte)AbilityId.HookPull });
|
gymSockets.Add(new AbilitySocket { SparkId = f0 });
|
||||||
gymSockets.Add(new AbilitySocket { SparkId = (byte)AbilityId.Blink });
|
gymSockets.Add(new AbilitySocket { SparkId = f1 });
|
||||||
gymSockets.Add(new AbilitySocket { SparkId = (byte)AbilityId.DecoyWisp });
|
gymSockets.Add(new AbilitySocket { SparkId = f2 });
|
||||||
gymSockets.Add(new AbilitySocket { SparkId = (byte)AbilityId.Vortex });
|
gymSockets.Add(new AbilitySocket { SparkId = f3 });
|
||||||
}
|
}
|
||||||
// Step 12a: born-correct PERMANENT meta seeding — replay this class's persisted tiers as
|
// Step 12a: born-correct PERMANENT meta seeding — replay this class's persisted tiers as
|
||||||
// meta-band StatModifiers on the just-instantiated player (same ECB as Instantiate, the
|
// meta-band StatModifiers on the just-instantiated player (same ECB as Instantiate, the
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
using ProjectM.Simulation;
|
||||||
|
using Unity.Burst;
|
||||||
|
using Unity.Collections;
|
||||||
|
using Unity.Entities;
|
||||||
|
using Unity.Mathematics;
|
||||||
|
using Unity.NetCode;
|
||||||
|
using Unity.Transforms;
|
||||||
|
|
||||||
|
namespace ProjectM.Server
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// LANTERN "light is territory" relevancy prototype (Build Spec Phase 1 step 8): per connection, marks every
|
||||||
|
/// ENEMY ghost OUTSIDE that player's lamp radius IRRELEVANT — so monsters in the dark aren't replicated (the
|
||||||
|
/// gamma test: "nothing to see"). Players + untagged/global ghosts stay relevant for free (SetIsIrrelevant).
|
||||||
|
/// <para>
|
||||||
|
/// SHARED-SET DISCIPLINE: <see cref="RegionRelevancySystem"/> is the SOLE clearer of the <c>GhostRelevancy</c>
|
||||||
|
/// set — its OnUpdate runs every tick (requires only <c>GhostRelevancy</c>), setting the mode + clearing +
|
||||||
|
/// adding its region hides. This system runs <b>[UpdateAfter(RegionRelevancySystem)]</b> and only ADDS light
|
||||||
|
/// hides (never clears) via <c>TryAdd</c> (defensive against a ghost hidden by BOTH region and light for one
|
||||||
|
/// connection — a duplicate <c>Add</c> would throw). Gated on <see cref="GymTag"/> so it runs ONLY in the
|
||||||
|
/// LANTERN gym; the legacy base/expedition game keeps region relevancy alone. Runs in
|
||||||
|
/// <see cref="GhostSimulationSystemGroup"/> before GhostSendSystem reads the set. Per-player dim/bright (a
|
||||||
|
/// variable radius) is the tuning follow-up; the prototype uses <see cref="LightRelevancyMath.LampRadiusDefault"/>.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
|
[BurstCompile]
|
||||||
|
[WorldSystemFilter(WorldSystemFilterFlags.ServerSimulation)]
|
||||||
|
[UpdateInGroup(typeof(GhostSimulationSystemGroup))]
|
||||||
|
[UpdateAfter(typeof(RegionRelevancySystem))]
|
||||||
|
public partial struct LightRelevancySystem : ISystem
|
||||||
|
{
|
||||||
|
[BurstCompile]
|
||||||
|
public void OnCreate(ref SystemState state)
|
||||||
|
{
|
||||||
|
state.RequireForUpdate<GhostRelevancy>();
|
||||||
|
state.RequireForUpdate<GymTag>(); // prototype: light-as-territory runs only in the LANTERN gym
|
||||||
|
}
|
||||||
|
|
||||||
|
[BurstCompile]
|
||||||
|
public void OnUpdate(ref SystemState state)
|
||||||
|
{
|
||||||
|
// Each in-game connection's player position (per-player lamp radius = the dim/bright follow-up; fixed here).
|
||||||
|
var connPos = new NativeHashMap<int, float3>(8, Allocator.Temp);
|
||||||
|
foreach (var (owner, lt) in
|
||||||
|
SystemAPI.Query<RefRO<GhostOwner>, RefRO<LocalTransform>>().WithAll<PlayerTag>())
|
||||||
|
connPos[owner.ValueRO.NetworkId] = lt.ValueRO.Position;
|
||||||
|
|
||||||
|
if (connPos.IsEmpty) { connPos.Dispose(); return; }
|
||||||
|
|
||||||
|
// The set is already mode=SetIsIrrelevant + cleared + region-populated THIS tick by RegionRelevancySystem.
|
||||||
|
ref var relevancy = ref SystemAPI.GetSingletonRW<GhostRelevancy>().ValueRW;
|
||||||
|
var set = relevancy.GhostRelevancySet;
|
||||||
|
var conns = connPos.GetKeyValueArrays(Allocator.Temp);
|
||||||
|
|
||||||
|
float rSq = LightRelevancyMath.LampRadiusDefault * LightRelevancyMath.LampRadiusDefault;
|
||||||
|
foreach (var (ghost, lt) in
|
||||||
|
SystemAPI.Query<RefRO<GhostInstance>, RefRO<LocalTransform>>().WithAll<EnemyTag>())
|
||||||
|
{
|
||||||
|
int ghostId = ghost.ValueRO.ghostId;
|
||||||
|
if (ghostId == 0) continue; // ghost id not assigned yet this tick
|
||||||
|
|
||||||
|
float3 gp = lt.ValueRO.Position;
|
||||||
|
for (int i = 0; i < conns.Keys.Length; i++)
|
||||||
|
{
|
||||||
|
if (LightRelevancyMath.IsHidden(conns.Values[i], gp, rSq))
|
||||||
|
set.TryAdd(new RelevantGhostForConnection { Connection = conns.Keys[i], Ghost = ghostId }, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
conns.Dispose();
|
||||||
|
connPos.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e7c6c7ce249ae9b44821e998e0331a52
|
||||||
@@ -27,6 +27,7 @@ namespace ProjectM.Simulation
|
|||||||
public int CooldownTicks;
|
public int CooldownTicks;
|
||||||
public byte EffectFlags; // ProjectileEffectFlag bits stamped at spawn (the Reel bit ⇒ HookPull homes the hit target to the caster). Config, not replicated.
|
public byte EffectFlags; // ProjectileEffectFlag bits stamped at spawn (the Reel bit ⇒ HookPull homes the hit target to the caster). Config, not replicated.
|
||||||
public int DurationTicks; // Aoe/zone + decoy lifetime, ticks (0 = system default). Reel leash is a system const.
|
public int DurationTicks; // Aoe/zone + decoy lifetime, ticks (0 = system default). Reel leash is a system const.
|
||||||
|
public int WindupTicks; // manual-aim telegraph delay (ticks) before the ability resolves; 0 = instant (Build Spec §4/DB-6)
|
||||||
public FixedString64Bytes Name;
|
public FixedString64Bytes Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -131,9 +131,8 @@ namespace ProjectM.Simulation
|
|||||||
BoonEffects bfx = m_BoonEffectsLookup.HasComponent(entity) ? m_BoonEffectsLookup[entity] : default;
|
BoonEffects bfx = m_BoonEffectsLookup.HasComponent(entity) ? m_BoonEffectsLookup[entity] : default;
|
||||||
bool pull = (bfx.Flags & BoonFlag.KnockToPull) != 0;
|
bool pull = (bfx.Flags & BoonFlag.KnockToPull) != 0;
|
||||||
|
|
||||||
// Replicated command buffer at this tick (per-socket fire count for the SpawnId + scheme for aim assist).
|
// Replicated command buffer (windup resolve + per-socket fire count for the SpawnId + scheme for aim assist).
|
||||||
var inputBuffer = SystemAPI.GetBuffer<InputBufferData<PlayerInput>>(entity);
|
var inputBuffer = SystemAPI.GetBuffer<InputBufferData<PlayerInput>>(entity);
|
||||||
bool haveApplied = inputBuffer.GetDataAtTick(serverTick, out var applied);
|
|
||||||
|
|
||||||
bool cdDirty = false;
|
bool cdDirty = false;
|
||||||
int socketCount = math.min(SocketId.Count, sockets.Length);
|
int socketCount = math.min(SocketId.Count, sockets.Length);
|
||||||
@@ -141,7 +140,23 @@ namespace ProjectM.Simulation
|
|||||||
{
|
{
|
||||||
byte sparkId = sockets[sk].SparkId;
|
byte sparkId = sockets[sk].SparkId;
|
||||||
if (sparkId == 0) continue; // empty socket
|
if (sparkId == 0) continue; // empty socket
|
||||||
if (!input.ValueRO.GetSocket(sk).IsSet) continue; // this socket not fired this tick
|
|
||||||
|
EffectiveSocketStats es = sk < effSockets.Length ? effSockets[sk] : default;
|
||||||
|
byte archetype = adb.TryGetAbility(sparkId, out var adef) ? adef.Archetype : (byte)AbilityArchetype.Projectile;
|
||||||
|
|
||||||
|
// MANUAL-AIM WINDUP (Build Spec §4/DB-6): resolve when the command WindupTicks ago had this socket
|
||||||
|
// set, so the resolve tick is a pure fn of replicated data on both worlds (no local fireAtTick that
|
||||||
|
// diverges under reprediction). fireCount comes from THAT command; the spawn uses the CURRENT tick's
|
||||||
|
// aim/position. WindupTicks == 0 = instant. A history gap (older than the buffer window) = no-fire.
|
||||||
|
NetworkTick resolveTick = serverTick;
|
||||||
|
if (adef.WindupTicks > 0)
|
||||||
|
{
|
||||||
|
uint riNow = serverTick.TickIndexForValidTick;
|
||||||
|
if (riNow <= (uint)adef.WindupTicks) continue; // not enough history yet -> no-fire
|
||||||
|
resolveTick = new NetworkTick(riNow - (uint)adef.WindupTicks);
|
||||||
|
}
|
||||||
|
if (!inputBuffer.GetDataAtTick(resolveTick, out var applied)) continue; // history gap -> no-fire
|
||||||
|
if (!applied.InternalInput.GetSocket(sk).IsSet) continue; // socket not fired at the resolve tick
|
||||||
|
|
||||||
// Per-socket cooldown gate (0 = ready).
|
// Per-socket cooldown gate (0 = ready).
|
||||||
uint nextFireRaw = cd.Get(sk);
|
uint nextFireRaw = cd.Get(sk);
|
||||||
@@ -151,9 +166,6 @@ namespace ProjectM.Simulation
|
|||||||
if (nextTick.IsValid && nextTick.IsNewerThan(serverTick)) continue;
|
if (nextTick.IsValid && nextTick.IsNewerThan(serverTick)) continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
EffectiveSocketStats es = sk < effSockets.Length ? effSockets[sk] : default;
|
|
||||||
byte archetype = adb.TryGetAbility(sparkId, out var adef) ? adef.Archetype : (byte)AbilityArchetype.Projectile;
|
|
||||||
|
|
||||||
// CONE: no projectile ghost. Predict the cooldown on both worlds; apply server-only cleave.
|
// CONE: no projectile ghost. Predict the cooldown on both worlds; apply server-only cleave.
|
||||||
if (archetype == (byte)AbilityArchetype.Cone)
|
if (archetype == (byte)AbilityArchetype.Cone)
|
||||||
{
|
{
|
||||||
@@ -261,7 +273,6 @@ namespace ProjectM.Simulation
|
|||||||
if (abilityPrefabs[i].Id == sparkId) { prefab = abilityPrefabs[i].Prefab; break; }
|
if (abilityPrefabs[i].Id == sparkId) { prefab = abilityPrefabs[i].Prefab; break; }
|
||||||
}
|
}
|
||||||
if (prefab == Entity.Null) continue;
|
if (prefab == Entity.Null) continue;
|
||||||
if (!haveApplied) continue;
|
|
||||||
|
|
||||||
uint socketFireCount = applied.InternalInput.GetSocket(sk).Count;
|
uint socketFireCount = applied.InternalInput.GetSocket(sk).Count;
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,20 @@ namespace ProjectM.Simulation
|
|||||||
public static byte ClassForAbility(byte abilityId)
|
public static byte ClassForAbility(byte abilityId)
|
||||||
=> abilityId == (byte)AbilityId.Primary ? RangerClass : WarriorClass;
|
=> abilityId == (byte)AbilityId.Primary ? RangerClass : WarriorClass;
|
||||||
|
|
||||||
|
/// <summary>Default 4-socket Spark loadout per frame (Harpooner = the Ranger slot, line-and-iron; Bathynaut =
|
||||||
|
/// the Warrior slot, anchor-and-crash). Tunable; drives the gym's per-frame default sockets (Build Spec step 5).</summary>
|
||||||
|
public static void FrameLoadout(byte classId, out byte s0, out byte s1, out byte s2, out byte s3)
|
||||||
|
{
|
||||||
|
if (classId == RangerClass) // Harpooner: reel primary + mobility + skillshots
|
||||||
|
{
|
||||||
|
s0 = (byte)AbilityId.HookPull; s1 = (byte)AbilityId.Blink; s2 = (byte)AbilityId.DecoyWisp; s3 = (byte)AbilityId.LightZone;
|
||||||
|
}
|
||||||
|
else // Bathynaut: pull enemies in + dash + zone control (melee via the combo chassis)
|
||||||
|
{
|
||||||
|
s0 = (byte)AbilityId.Vortex; s1 = (byte)AbilityId.Blink; s2 = (byte)AbilityId.HookPull; s3 = (byte)AbilityId.LightZone;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>True when a modifier's SourceId is in the reserved class-seed range [ClassSourceId, +ClassSeedCount).</summary>
|
/// <summary>True when a modifier's SourceId is in the reserved class-seed range [ClassSourceId, +ClassSeedCount).</summary>
|
||||||
public static bool IsClassSeed(uint sourceId)
|
public static bool IsClassSeed(uint sourceId)
|
||||||
=> sourceId >= Tuning.ClassSourceId && sourceId < Tuning.ClassSourceId + (uint)ClassSeedCount;
|
=> sourceId >= Tuning.ClassSourceId && sourceId < Tuning.ClassSourceId + (uint)ClassSeedCount;
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
using Unity.Mathematics;
|
||||||
|
|
||||||
|
namespace ProjectM.Simulation
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Pure helpers for the LANTERN "light is territory" relevancy prototype (Build Spec Phase 1 step 8): an entity
|
||||||
|
/// outside a player's lamp radius isn't replicated to that player. The decision is a planar (XZ) distance test
|
||||||
|
/// so it's Burst-safe + unit-testable independent of the netcode <c>GhostRelevancy</c> set manipulation.
|
||||||
|
/// </summary>
|
||||||
|
public static class LightRelevancyMath
|
||||||
|
{
|
||||||
|
/// <summary>Prototype lamp radius (world units). Per-player dim/bright (a variable radius = the quiet/blind
|
||||||
|
/// trade) is the tuning follow-up; the prototype uses one fixed radius to prove the gamma test.</summary>
|
||||||
|
public const float LampRadiusDefault = 25f;
|
||||||
|
|
||||||
|
/// <summary>True when the ghost at <paramref name="ghostPos"/> is OUTSIDE the lamp centred on
|
||||||
|
/// <paramref name="playerPos"/> (planar XZ distance beyond the radius) — i.e. hidden from that player.</summary>
|
||||||
|
public static bool IsHidden(float3 playerPos, float3 ghostPos, float radiusSq)
|
||||||
|
=> math.distancesq(playerPos.xz, ghostPos.xz) > radiusSq;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e2224a38c42888d408d54961131dfff3
|
||||||
@@ -0,0 +1,426 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!29 &1
|
||||||
|
OcclusionCullingSettings:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
serializedVersion: 2
|
||||||
|
m_OcclusionBakeSettings:
|
||||||
|
smallestOccluder: 5
|
||||||
|
smallestHole: 0.25
|
||||||
|
backfaceThreshold: 100
|
||||||
|
m_SceneGUID: 00000000000000000000000000000000
|
||||||
|
m_OcclusionCullingData: {fileID: 0}
|
||||||
|
--- !u!104 &2
|
||||||
|
RenderSettings:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
serializedVersion: 10
|
||||||
|
m_Fog: 0
|
||||||
|
m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
|
||||||
|
m_FogMode: 3
|
||||||
|
m_FogDensity: 0.01
|
||||||
|
m_LinearFogStart: 0
|
||||||
|
m_LinearFogEnd: 300
|
||||||
|
m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
|
||||||
|
m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
|
||||||
|
m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
|
||||||
|
m_AmbientIntensity: 1
|
||||||
|
m_AmbientMode: 0
|
||||||
|
m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
|
||||||
|
m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0}
|
||||||
|
m_HaloStrength: 0.5
|
||||||
|
m_FlareStrength: 1
|
||||||
|
m_FlareFadeSpeed: 3
|
||||||
|
m_HaloTexture: {fileID: 0}
|
||||||
|
m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
|
||||||
|
m_DefaultReflectionMode: 0
|
||||||
|
m_DefaultReflectionResolution: 128
|
||||||
|
m_ReflectionBounces: 1
|
||||||
|
m_ReflectionIntensity: 1
|
||||||
|
m_CustomReflection: {fileID: 0}
|
||||||
|
m_Sun: {fileID: 0}
|
||||||
|
m_UseRadianceAmbientProbe: 0
|
||||||
|
--- !u!157 &3
|
||||||
|
LightmapSettings:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
serializedVersion: 13
|
||||||
|
m_BakeOnSceneLoad: 0
|
||||||
|
m_GISettings:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_BounceScale: 1
|
||||||
|
m_IndirectOutputScale: 1
|
||||||
|
m_AlbedoBoost: 1
|
||||||
|
m_EnvironmentLightingMode: 0
|
||||||
|
m_EnableBakedLightmaps: 1
|
||||||
|
m_EnableRealtimeLightmaps: 0
|
||||||
|
m_LightmapEditorSettings:
|
||||||
|
serializedVersion: 12
|
||||||
|
m_Resolution: 2
|
||||||
|
m_BakeResolution: 40
|
||||||
|
m_AtlasSize: 1024
|
||||||
|
m_AO: 0
|
||||||
|
m_AOMaxDistance: 1
|
||||||
|
m_CompAOExponent: 1
|
||||||
|
m_CompAOExponentDirect: 0
|
||||||
|
m_ExtractAmbientOcclusion: 0
|
||||||
|
m_Padding: 2
|
||||||
|
m_LightmapParameters: {fileID: 0}
|
||||||
|
m_LightmapsBakeMode: 1
|
||||||
|
m_TextureCompression: 1
|
||||||
|
m_ReflectionCompression: 2
|
||||||
|
m_MixedBakeMode: 2
|
||||||
|
m_BakeBackend: 2
|
||||||
|
m_PVRSampling: 1
|
||||||
|
m_PVRDirectSampleCount: 32
|
||||||
|
m_PVRSampleCount: 512
|
||||||
|
m_PVRBounces: 2
|
||||||
|
m_PVREnvironmentSampleCount: 256
|
||||||
|
m_PVREnvironmentReferencePointCount: 2048
|
||||||
|
m_PVRFilteringMode: 1
|
||||||
|
m_PVRDenoiserTypeDirect: 1
|
||||||
|
m_PVRDenoiserTypeIndirect: 1
|
||||||
|
m_PVRDenoiserTypeAO: 1
|
||||||
|
m_PVRFilterTypeDirect: 0
|
||||||
|
m_PVRFilterTypeIndirect: 0
|
||||||
|
m_PVRFilterTypeAO: 0
|
||||||
|
m_PVREnvironmentMIS: 1
|
||||||
|
m_PVRCulling: 1
|
||||||
|
m_PVRFilteringGaussRadiusDirect: 1
|
||||||
|
m_PVRFilteringGaussRadiusIndirect: 1
|
||||||
|
m_PVRFilteringGaussRadiusAO: 1
|
||||||
|
m_PVRFilteringAtrousPositionSigmaDirect: 0.5
|
||||||
|
m_PVRFilteringAtrousPositionSigmaIndirect: 2
|
||||||
|
m_PVRFilteringAtrousPositionSigmaAO: 1
|
||||||
|
m_ExportTrainingData: 0
|
||||||
|
m_TrainingDataDestination: TrainingData
|
||||||
|
m_LightProbeSampleCountMultiplier: 4
|
||||||
|
m_LightingDataAsset: {fileID: 20201, guid: 0000000000000000f000000000000000, type: 0}
|
||||||
|
m_LightingSettings: {fileID: 0}
|
||||||
|
--- !u!196 &4
|
||||||
|
NavMeshSettings:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_BuildSettings:
|
||||||
|
serializedVersion: 3
|
||||||
|
agentTypeID: 0
|
||||||
|
agentRadius: 0.5
|
||||||
|
agentHeight: 2
|
||||||
|
agentSlope: 45
|
||||||
|
agentClimb: 0.4
|
||||||
|
ledgeDropHeight: 0
|
||||||
|
maxJumpAcrossDistance: 0
|
||||||
|
minRegionArea: 2
|
||||||
|
manualCellSize: 0
|
||||||
|
cellSize: 0.16666667
|
||||||
|
manualTileSize: 0
|
||||||
|
tileSize: 256
|
||||||
|
buildHeightMesh: 0
|
||||||
|
maxJobWorkers: 0
|
||||||
|
preserveTilesOutsideBounds: 0
|
||||||
|
debug:
|
||||||
|
m_Flags: 0
|
||||||
|
m_NavMeshData: {fileID: 0}
|
||||||
|
--- !u!1 &198866174
|
||||||
|
GameObject:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
serializedVersion: 6
|
||||||
|
m_Component:
|
||||||
|
- component: {fileID: 198866176}
|
||||||
|
- component: {fileID: 198866175}
|
||||||
|
m_Layer: 0
|
||||||
|
m_Name: GymRoster
|
||||||
|
m_TagString: Untagged
|
||||||
|
m_Icon: {fileID: 0}
|
||||||
|
m_NavMeshLayer: 0
|
||||||
|
m_StaticEditorFlags: 0
|
||||||
|
m_IsActive: 1
|
||||||
|
--- !u!114 &198866175
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 198866174}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: d210e424c43414a4e958efc44c025335, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: ProjectM.Authoring::ProjectM.Authoring.GymRosterAuthoring
|
||||||
|
Enemies:
|
||||||
|
- Kind: 0
|
||||||
|
Prefab: {fileID: 3885353946372160549, guid: 655e0ef444426ce449b2173a1e33cb44, type: 3}
|
||||||
|
- Kind: 1
|
||||||
|
Prefab: {fileID: 3885353946372160549, guid: fd74d0edb4f42d74c9de34f4bffe351f, type: 3}
|
||||||
|
--- !u!4 &198866176
|
||||||
|
Transform:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 198866174}
|
||||||
|
serializedVersion: 2
|
||||||
|
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||||
|
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||||
|
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||||
|
m_ConstrainProportionsScale: 0
|
||||||
|
m_Children: []
|
||||||
|
m_Father: {fileID: 0}
|
||||||
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
|
--- !u!1 &280921309
|
||||||
|
GameObject:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
serializedVersion: 6
|
||||||
|
m_Component:
|
||||||
|
- component: {fileID: 280921311}
|
||||||
|
- component: {fileID: 280921310}
|
||||||
|
m_Layer: 0
|
||||||
|
m_Name: ProjectileSpawner_Copy
|
||||||
|
m_TagString: Untagged
|
||||||
|
m_Icon: {fileID: 0}
|
||||||
|
m_NavMeshLayer: 0
|
||||||
|
m_StaticEditorFlags: 0
|
||||||
|
m_IsActive: 1
|
||||||
|
--- !u!114 &280921310
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 280921309}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: aa1b7c054d5a043f2801cddf90567acf, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: ProjectM.Authoring::ProjectM.Authoring.ProjectileSpawnerAuthoring
|
||||||
|
ProjectilePrefab: {fileID: 8857056134628386430, guid: 700fb9c3f7cf94830a0ee78c4e4f1290, type: 3}
|
||||||
|
--- !u!4 &280921311
|
||||||
|
Transform:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 280921309}
|
||||||
|
serializedVersion: 2
|
||||||
|
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||||
|
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||||
|
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||||
|
m_ConstrainProportionsScale: 0
|
||||||
|
m_Children: []
|
||||||
|
m_Father: {fileID: 0}
|
||||||
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
|
--- !u!1 &510399770
|
||||||
|
GameObject:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
serializedVersion: 6
|
||||||
|
m_Component:
|
||||||
|
- component: {fileID: 510399772}
|
||||||
|
- component: {fileID: 510399771}
|
||||||
|
m_Layer: 0
|
||||||
|
m_Name: WorldCollisionConfig_Copy
|
||||||
|
m_TagString: Untagged
|
||||||
|
m_Icon: {fileID: 0}
|
||||||
|
m_NavMeshLayer: 0
|
||||||
|
m_StaticEditorFlags: 0
|
||||||
|
m_IsActive: 1
|
||||||
|
--- !u!114 &510399771
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 510399770}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: ab1c895817ca4b64db74215f13abb5e3, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: ProjectM.Authoring::ProjectM.Authoring.WorldCollisionAuthoring
|
||||||
|
EnvironmentLayerName: Environment
|
||||||
|
StructureLayerName: Structure
|
||||||
|
--- !u!4 &510399772
|
||||||
|
Transform:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 510399770}
|
||||||
|
serializedVersion: 2
|
||||||
|
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||||
|
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||||
|
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||||
|
m_ConstrainProportionsScale: 0
|
||||||
|
m_Children: []
|
||||||
|
m_Father: {fileID: 0}
|
||||||
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
|
--- !u!1 &851249706
|
||||||
|
GameObject:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
serializedVersion: 6
|
||||||
|
m_Component:
|
||||||
|
- component: {fileID: 851249708}
|
||||||
|
- component: {fileID: 851249707}
|
||||||
|
m_Layer: 0
|
||||||
|
m_Name: AbilityDatabase_Copy
|
||||||
|
m_TagString: Untagged
|
||||||
|
m_Icon: {fileID: 0}
|
||||||
|
m_NavMeshLayer: 0
|
||||||
|
m_StaticEditorFlags: 0
|
||||||
|
m_IsActive: 1
|
||||||
|
--- !u!114 &851249707
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 851249706}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 4f60b41d8ec8d4c24b4d4f54af919080, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: ProjectM.Authoring::ProjectM.Authoring.AbilityDatabaseAuthoring
|
||||||
|
Abilities:
|
||||||
|
- {fileID: 11400000, guid: 013954d16c1ff4c1dad82495e38b4657, type: 2}
|
||||||
|
- {fileID: 11400000, guid: b594c2953f8304189b91ca8c96a9d0c4, type: 2}
|
||||||
|
- {fileID: 11400000, guid: 601a090742e5341cc9ee1f25d215136b, type: 2}
|
||||||
|
- {fileID: 11400000, guid: 4cf5f3ead961b1242b0e3c79de96fa44, type: 2}
|
||||||
|
- {fileID: 11400000, guid: c1e799ca78899bc4d97be9399233606c, type: 2}
|
||||||
|
- {fileID: 11400000, guid: f57d643df7f809b40b9804bb9f8afa60, type: 2}
|
||||||
|
- {fileID: 11400000, guid: 5e9c506bc3657b44ab7869c037a9e9ca, type: 2}
|
||||||
|
- {fileID: 11400000, guid: f39412c52281a3e4590f77e94c9911e7, type: 2}
|
||||||
|
- {fileID: 11400000, guid: 66b9bc054810f3a4a808dd88aa2d539f, type: 2}
|
||||||
|
Characters:
|
||||||
|
- {fileID: 11400000, guid: e675b529048144a41a2054c729180bce, type: 2}
|
||||||
|
--- !u!4 &851249708
|
||||||
|
Transform:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 851249706}
|
||||||
|
serializedVersion: 2
|
||||||
|
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||||
|
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||||
|
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||||
|
m_ConstrainProportionsScale: 0
|
||||||
|
m_Children: []
|
||||||
|
m_Father: {fileID: 0}
|
||||||
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
|
--- !u!1 &1455552716
|
||||||
|
GameObject:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
serializedVersion: 6
|
||||||
|
m_Component:
|
||||||
|
- component: {fileID: 1455552718}
|
||||||
|
- component: {fileID: 1455552717}
|
||||||
|
m_Layer: 0
|
||||||
|
m_Name: PlayerSpawner_Copy
|
||||||
|
m_TagString: Untagged
|
||||||
|
m_Icon: {fileID: 0}
|
||||||
|
m_NavMeshLayer: 0
|
||||||
|
m_StaticEditorFlags: 0
|
||||||
|
m_IsActive: 1
|
||||||
|
--- !u!114 &1455552717
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 1455552716}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 5e56c91ba352644bd900142035ff2799, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: ProjectM.Authoring::ProjectM.Authoring.PlayerSpawnerAuthoring
|
||||||
|
PlayerPrefab: {fileID: 2218851646297572645, guid: a27bbed2662454377bd25279ee4a14d2, type: 3}
|
||||||
|
SpawnPoint: {x: 0, y: 1, z: 0}
|
||||||
|
SpawnRingRadius: 2.5
|
||||||
|
RingSlots: 4
|
||||||
|
--- !u!4 &1455552718
|
||||||
|
Transform:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 1455552716}
|
||||||
|
serializedVersion: 2
|
||||||
|
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||||
|
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||||
|
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||||
|
m_ConstrainProportionsScale: 0
|
||||||
|
m_Children: []
|
||||||
|
m_Father: {fileID: 0}
|
||||||
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
|
--- !u!1 &1788411395
|
||||||
|
GameObject:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
serializedVersion: 6
|
||||||
|
m_Component:
|
||||||
|
- component: {fileID: 1788411397}
|
||||||
|
- component: {fileID: 1788411396}
|
||||||
|
m_Layer: 0
|
||||||
|
m_Name: PhysicsConfig_Copy
|
||||||
|
m_TagString: Untagged
|
||||||
|
m_Icon: {fileID: 0}
|
||||||
|
m_NavMeshLayer: 0
|
||||||
|
m_StaticEditorFlags: 0
|
||||||
|
m_IsActive: 1
|
||||||
|
--- !u!114 &1788411396
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 1788411395}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 5bd6bf266b32e49ac8c5951317b6b250, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: Unity.NetCode.Physics.Hybrid::Unity.NetCode.NetCodePhysicsConfig
|
||||||
|
PhysicGroupRunMode: 1
|
||||||
|
EnableLagCompensation: 0
|
||||||
|
ServerHistorySize: 0
|
||||||
|
ClientHistorySize: 1
|
||||||
|
ClientNonGhostWorldIndex: 0
|
||||||
|
DeepCopyDynamicColliders: 1
|
||||||
|
DeepCopyStaticColliders: 0
|
||||||
|
--- !u!4 &1788411397
|
||||||
|
Transform:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 1788411395}
|
||||||
|
serializedVersion: 2
|
||||||
|
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||||
|
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||||
|
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||||
|
m_ConstrainProportionsScale: 0
|
||||||
|
m_Children: []
|
||||||
|
m_Father: {fileID: 0}
|
||||||
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
|
--- !u!1660057539 &9223372036854775807
|
||||||
|
SceneRoots:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_Roots:
|
||||||
|
- {fileID: 1455552718}
|
||||||
|
- {fileID: 851249708}
|
||||||
|
- {fileID: 1788411397}
|
||||||
|
- {fileID: 510399772}
|
||||||
|
- {fileID: 280921311}
|
||||||
|
- {fileID: 198866176}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 65c7ce65b3d144a4585a457f619e7d33
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
using NUnit.Framework;
|
||||||
|
using ProjectM.Simulation;
|
||||||
|
using Unity.Mathematics;
|
||||||
|
|
||||||
|
namespace ProjectM.Tests
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Unit tests for the LANTERN light-as-territory relevancy DECISION (LightRelevancyMath). The full per-connection
|
||||||
|
/// GhostRelevancy set population is netcode-world (the L3 gamma test); this pins the pure planar-distance rule.
|
||||||
|
/// </summary>
|
||||||
|
public class LightRelevancyMathTests
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void InsideLamp_IsVisible()
|
||||||
|
{
|
||||||
|
float r = LightRelevancyMath.LampRadiusDefault;
|
||||||
|
Assert.IsFalse(LightRelevancyMath.IsHidden(float3.zero, new float3(r - 1f, 0f, 0f), r * r), "just inside the radius = visible");
|
||||||
|
Assert.IsFalse(LightRelevancyMath.IsHidden(float3.zero, float3.zero, r * r), "at the player = visible");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void OutsideLamp_IsHidden()
|
||||||
|
{
|
||||||
|
float r = LightRelevancyMath.LampRadiusDefault;
|
||||||
|
Assert.IsTrue(LightRelevancyMath.IsHidden(float3.zero, new float3(r + 1f, 0f, 0f), r * r), "just beyond the radius = hidden (nothing to see)");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Planar_IgnoresY()
|
||||||
|
{
|
||||||
|
float r = 5f;
|
||||||
|
// Within the planar (XZ) radius but far in Y is still VISIBLE — top-down relevancy ignores height.
|
||||||
|
Assert.IsFalse(LightRelevancyMath.IsHidden(float3.zero, new float3(2f, 1000f, 2f), r * r), "Y is ignored (planar XZ test)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 14e936313eb9a864181228bcee28c321
|
||||||
@@ -54,12 +54,16 @@ The primary verb first. No world, no lanterns — a gym.
|
|||||||
|
|
||||||
**Gate:** the gym is fun with two players for 15 minutes on kit alone; relevancy-light verified server-side (gamma test: nothing to see).
|
**Gate:** the gym is fun with two players for 15 minutes on kit alone; relevancy-light verified server-side (gamma test: nothing to see).
|
||||||
|
|
||||||
> **Phase 1 status (2026-07-15):** ✅ Group A (4-socket kit, [[2026-07-14_Lantern_Phase1_GroupA_Combat_Sockets]]) ·
|
> **Phase 1 status (2026-07-15):** ✅ Group A (4-socket kit) · ✅ Group B (5 Sparks: decoy / hook-reel / vortex /
|
||||||
> ✅ Group B **systems + content + gym CODE** — the 5 Sparks (decoy / hook-reel / vortex / blink / light-zone),
|
> blink / light-zone; Aoe/Hitscan dispatch; `ReelSystem` / `ZonePulseSystem` / decoy aggro; 3 effect ghosts + defs;
|
||||||
> Aoe/Hitscan dispatch, `ReelSystem` / `ZonePulseSystem` / decoy aggro, 3 effect ghosts + 5 Spark defs, on-demand
|
> on-demand enemy-spawn tooling) · ✅ the **full remainder, code-complete** — **step 5** two-frame identity (FrameId
|
||||||
> enemy-spawn tooling + `GymTag` clean-spawn (497 EditMode green). ◻ the fresh **`Gym.unity` scene assembly** + the
|
> signal + per-frame gym loadouts), **step 6** manual-aim windups, **step 8 ★** light-as-territory relevancy
|
||||||
> **L3 feel gate** (operator's eyes; also closes the A1 in-game animated-pipeline confirmation) remain. See
|
> prototype, + the dedicated **`Gym.unity`** (re-based on DevSandbox: camera + DebugOverlay + world → streams the
|
||||||
> [[2026-07-15_Lantern_Phase1_GroupB_5Sparks_Systems]].
|
> minimal `GymSub`). **500 EditMode green.** ◻ **Remaining = operator/L3 + focused passes:** the gym Play (feel +
|
||||||
|
> the A1 animated-pipeline confirmation + light-relevancy gamma test + induced-rollback), **step 7 underwater feel**
|
||||||
|
> tuning (substrate exists; = the operator's future visual pass), the legacy `AbilityRef` wholesale removal +
|
||||||
|
> CharacterId→Bathynaut/Harpooner rename (risky focused refactor), and the **★ adversarial netcode review** of the
|
||||||
|
> relevancy + reel slices. See [[2026-07-15_Lantern_Phase1_GroupB_5Sparks_Systems]].
|
||||||
|
|
||||||
## Phase 2 — The dark & the pocket-graph
|
## Phase 2 — The dark & the pocket-graph
|
||||||
|
|
||||||
|
|||||||
@@ -83,6 +83,42 @@ untouched — the SpawnEnemy op reuses the existing scalar `DebugCommandRequest`
|
|||||||
- Reel-convergence / decoy-aggro / client-never-instantiate / cross-socket dispatch: structurally sound + self-
|
- Reel-convergence / decoy-aggro / client-never-instantiate / cross-socket dispatch: structurally sound + self-
|
||||||
reviewed, but their behavioural confirmation is L3 (need the full netcode world), not L2.
|
reviewed, but their behavioural confirmation is L3 (need the full netcode world), not L2.
|
||||||
|
|
||||||
|
## Continued — the full Phase-1 remainder (same session, operator: "do everything, don't defer, commit as you go")
|
||||||
|
|
||||||
|
After the milestone, the operator directed powering through ALL remaining Phase-1 items in one session. Shipped
|
||||||
|
(each L1 clean + suite green + committed):
|
||||||
|
|
||||||
|
- **Gym scene built, then RE-BASED on DevSandbox:** first a fresh minimal `GymSub` subscene (PlayerSpawner /
|
||||||
|
AbilityDatabase-with-9-abilities / physics+collision config / ProjectileSpawner / GymRoster, built by
|
||||||
|
duplicate-and-move of the proven singletons + GymTag). The from-scratch host had no dev UI and a name-collision
|
||||||
|
(host + subscene both "Gym") that made the operator Play the *subscene* → "no cameras". Fixed: renamed subscene →
|
||||||
|
`GymSub.unity` (GUID-stable); **host `Gym.unity` re-based on DevSandbox** (operator's call — inherits camera +
|
||||||
|
DebugOverlay + world), SubScene repointed to GymSub. **Operator note: DevSandbox/gym visuals should match the
|
||||||
|
future LANTERN look — a future improvement.**
|
||||||
|
- **Manual-aim windups (step 6, `8f099c32b`):** `WindupTicks` on the def; `AbilityFireSystem` resolves each socket
|
||||||
|
at `GetDataAtTick(serverTick − WindupTicks)` — a pure fn of replicated data (§4/DB-6). 4 Sparks tuned.
|
||||||
|
- **Two-frame identity + FrameId (step 5 + §8, `342a39a54`):** `FrameId` written server-side; the 2 class-HUD
|
||||||
|
readers moved to it (AbilityRef fallback); per-frame gym loadouts via `ClassTraits.FrameLoadout` (Harpooner /
|
||||||
|
Bathynaut). Caught + fixed a MetaSeeding test regression (FrameId must be `AddComponent`, not `SetComponent` —
|
||||||
|
the minimal test prefab has no baked FrameId).
|
||||||
|
- **Light-as-territory relevancy prototype (step 8 ★, `aeb979f8d`):** `LightRelevancySystem` hides enemy ghosts
|
||||||
|
outside each player's lamp radius (SetIsIrrelevant, modelled on `RegionRelevancySystem`; the shared-set is cleared
|
||||||
|
only by RegionRelevancy, Light `[UpdateAfter]` it + only `TryAdd`s; gated on GymTag). Pure decision in
|
||||||
|
`LightRelevancyMath` (+3 tests). **500 EditMode green.**
|
||||||
|
|
||||||
|
**Deferred to focused/operator passes (surfaced, NOT silent):**
|
||||||
|
- **Underwater feel (step 7):** the substrate (AmbientAudio bed, WorldAtmosphere fog, AmbientMotion debris) all
|
||||||
|
EXISTS — §11 is a tuning pass needing eyes/ears, and the operator flagged underwater visuals as a future
|
||||||
|
improvement. Backlog: murk fog values, buoyant-decay presentation, hydrophone timbre.
|
||||||
|
- **AbilityRef/AbilityCooldown/EffectiveAbilityStats wholesale removal** + the **CharacterId → Bathynaut/Harpooner
|
||||||
|
rename** — risky refactors (woven through equip/class/meta/StatRecompute + tests) that "still work" via the
|
||||||
|
transition; a focused pass, not a marathon ram.
|
||||||
|
- **L3 / operator-eyes:** the gym Play (world-creation cycle check for this session's new [UpdateBefore/After]
|
||||||
|
edges + functional server==client + feel: reel yank / vortex pull / decoy aggro / **skinned-enemy animation** =
|
||||||
|
the A1 confirmation), the light-relevancy **gamma test**, the induced-rollback (NP-4) 2-instance check, the
|
||||||
|
windup client telegraph VFX, per-player lamp dim/bright, and the **★ adversarial netcode review** of the
|
||||||
|
relevancy + reel slices before they ship.
|
||||||
|
|
||||||
## Related
|
## Related
|
||||||
- [[Phase1_Combat_Gym_Build_Spec]] · [[DR-050_Lantern_Phase1_Combat_Design]] · [[Roadmap_Lantern_Slice]] (A1 gym
|
- [[Phase1_Combat_Gym_Build_Spec]] · [[DR-050_Lantern_Phase1_Combat_Design]] · [[Roadmap_Lantern_Slice]] (A1 gym
|
||||||
note) · [[2026-07-14_Lantern_Phase1_GroupA_Combat_Sockets]] · [[DR-023_Enemy_Animation_MonsterMash]]
|
note) · [[2026-07-14_Lantern_Phase1_GroupA_Combat_Sockets]] · [[DR-023_Enemy_Animation_MonsterMash]]
|
||||||
|
|||||||
Reference in New Issue
Block a user