From 0a3a39e3d22b28412248e85567dc1841d86580e1 Mon Sep 17 00:00:00 2001 From: Luis Gonzalez Date: Thu, 18 Jun 2026 00:36:21 -0700 Subject: [PATCH] Slice 2 (WIP): WarriorCone ability + class tests; Warrior path validated Authored the WarriorCone AbilityDefinition (archetype Cone, dmg 22, range 2.2, ~130deg arc, 22-tick cd) and added it to the gameplay subscene's AbilityDatabase (re-baked). ClassTraitsTests cover the class->ability mapping + the asymmetric seed folds. 348/348. Play-validated the Warrior end-to-end, server==client: AbilityRef=WarriorCone, 4 seeds, eff MaxHP 130 / MoveSpd 5.1 / cone dmg 22 / coneRad 1.13; conns=1 (re-bake handshake intact); zero runtime errors. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../Abilities/Ability_WarriorCone.asset | 24 +++++++ .../Abilities/Ability_WarriorCone.asset.meta | 8 +++ Assets/_Project/Subscenes/Gameplay.unity | 1 + .../Tests/EditMode/ClassTraitsTests.cs | 65 +++++++++++++++++++ .../Tests/EditMode/ClassTraitsTests.cs.meta | 2 + 5 files changed, 100 insertions(+) create mode 100644 Assets/_Project/Abilities/Ability_WarriorCone.asset create mode 100644 Assets/_Project/Abilities/Ability_WarriorCone.asset.meta create mode 100644 Assets/_Project/Tests/EditMode/ClassTraitsTests.cs create mode 100644 Assets/_Project/Tests/EditMode/ClassTraitsTests.cs.meta diff --git a/Assets/_Project/Abilities/Ability_WarriorCone.asset b/Assets/_Project/Abilities/Ability_WarriorCone.asset new file mode 100644 index 000000000..1b4283772 --- /dev/null +++ b/Assets/_Project/Abilities/Ability_WarriorCone.asset @@ -0,0 +1,24 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 84b20ca889a744e888c8c3b3b723ec69, type: 3} + m_Name: Ability_WarriorCone + m_EditorClassIdentifier: ProjectM.Authoring::ProjectM.Authoring.AbilityDefinition + Id: 4 + Archetype: 2 + DisplayName: Warrior Cone + Damage: 22 + ProjectileSpeed: 0 + Range: 2.2 + AutoTargetRange: 0 + AutoTargetConeDegrees: 65 + CooldownTicks: 22 + ProjectilePrefab: {fileID: 0} diff --git a/Assets/_Project/Abilities/Ability_WarriorCone.asset.meta b/Assets/_Project/Abilities/Ability_WarriorCone.asset.meta new file mode 100644 index 000000000..028f85a0c --- /dev/null +++ b/Assets/_Project/Abilities/Ability_WarriorCone.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4cf5f3ead961b1242b0e3c79de96fa44 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/_Project/Subscenes/Gameplay.unity b/Assets/_Project/Subscenes/Gameplay.unity index fa5e8d698..b474b9d11 100644 --- a/Assets/_Project/Subscenes/Gameplay.unity +++ b/Assets/_Project/Subscenes/Gameplay.unity @@ -1006,6 +1006,7 @@ MonoBehaviour: - {fileID: 11400000, guid: 013954d16c1ff4c1dad82495e38b4657, type: 2} - {fileID: 11400000, guid: b594c2953f8304189b91ca8c96a9d0c4, type: 2} - {fileID: 11400000, guid: 601a090742e5341cc9ee1f25d215136b, type: 2} + - {fileID: 11400000, guid: 4cf5f3ead961b1242b0e3c79de96fa44, type: 2} Characters: - {fileID: 11400000, guid: e675b529048144a41a2054c729180bce, type: 2} --- !u!4 &409538539 diff --git a/Assets/_Project/Tests/EditMode/ClassTraitsTests.cs b/Assets/_Project/Tests/EditMode/ClassTraitsTests.cs new file mode 100644 index 000000000..f18471bec --- /dev/null +++ b/Assets/_Project/Tests/EditMode/ClassTraitsTests.cs @@ -0,0 +1,65 @@ +using NUnit.Framework; +using ProjectM.Simulation; +using Unity.Collections; +using Unity.Entities; + +namespace ProjectM.Tests +{ + /// + /// Slice 2 — the pure class mapping: which Fire ability each class gets, and that the DRG-asymmetric trait + /// seeds fold the right direction (Warrior = melee bruiser / tankier / slower; Ranger = ranged / faster / + /// squishier + a wider auto-assist co-op hook), all on the reserved range. + /// + public class ClassTraitsTests + { + static DynamicBuffer SeededBuffer(World world, byte classId) + { + var em = world.EntityManager; + var e = em.CreateEntity(); + em.AddBuffer(e); + var ecb = new EntityCommandBuffer(Allocator.Temp); + ClassTraits.AppendSeeds(classId, e, ecb); + ecb.Playback(em); + ecb.Dispose(); + return em.GetBuffer(e); + } + + [Test] + public void AbilityFor_And_Normalize_DefaultToWarrior() + { + Assert.AreEqual((byte)AbilityId.WarriorCone, ClassTraits.AbilityFor(ClassTraits.WarriorClass)); + Assert.AreEqual((byte)AbilityId.Primary, ClassTraits.AbilityFor(ClassTraits.RangerClass)); + Assert.AreEqual((byte)AbilityId.WarriorCone, ClassTraits.AbilityFor(0), "unknown class -> Warrior cone"); + Assert.AreEqual(ClassTraits.WarriorClass, ClassTraits.Normalize(0)); + Assert.AreEqual(ClassTraits.WarriorClass, ClassTraits.Normalize(99)); + Assert.AreEqual(ClassTraits.RangerClass, ClassTraits.Normalize(ClassTraits.RangerClass)); + } + + [Test] + public void Warrior_Seeds_Buff_Melee_And_Tankiness_And_Slow() + { + using var world = new World("ClassTraitsWarrior"); + var mods = SeededBuffer(world, ClassTraits.WarriorClass); + Assert.AreEqual(4, mods.Length, "Warrior seeds 4 trait modifiers."); + Assert.Greater(StatMath.Apply(10f, StatTarget.MeleeDamage, mods), 10f, "Warrior hits harder in melee."); + Assert.Greater(StatMath.Apply(2.6f, StatTarget.MeleeRange, mods), 2.6f, "Warrior reaches further in melee."); + Assert.Less(StatMath.Apply(5f, StatTarget.MoveSpeed, mods), 5f, "Warrior moves slower."); + Assert.Greater(StatMath.Apply(100f, StatTarget.MaxHealth, mods), 100f, "Warrior is tankier."); + for (int i = 0; i < mods.Length; i++) + Assert.GreaterOrEqual(mods[i].SourceId, Tuning.ClassSourceId, "class seeds use the reserved SourceId range."); + } + + [Test] + public void Ranger_Seeds_Buff_Range_Speed_Assist_And_Leave_Melee_Weak() + { + using var world = new World("ClassTraitsRanger"); + var mods = SeededBuffer(world, ClassTraits.RangerClass); + Assert.AreEqual(4, mods.Length, "Ranger seeds 4 trait modifiers."); + Assert.Greater(StatMath.Apply(5f, StatTarget.MoveSpeed, mods), 5f, "Ranger moves faster."); + Assert.Less(StatMath.Apply(100f, StatTarget.MaxHealth, mods), 100f, "Ranger is squishier."); + Assert.Greater(StatMath.Apply(20f, StatTarget.Range, mods), 20f, "Ranger has longer projectile range."); + Assert.Greater(StatMath.Apply(0f, StatTarget.AutoTargetRange, mods), 0f, "Ranger has a wider auto-assist (the co-op hook)."); + Assert.AreEqual(10f, StatMath.Apply(10f, StatTarget.MeleeDamage, mods), 0.001f, "Ranger melee is unbuffed (weaker than the Warrior)."); + } + } +} diff --git a/Assets/_Project/Tests/EditMode/ClassTraitsTests.cs.meta b/Assets/_Project/Tests/EditMode/ClassTraitsTests.cs.meta new file mode 100644 index 000000000..c6d8041f9 --- /dev/null +++ b/Assets/_Project/Tests/EditMode/ClassTraitsTests.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: c97bc5f5f2142144f85272e159e3c7b1 \ No newline at end of file