From 35c262c84cda059b4314cf91f4b0337632b066e7 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 5 Feb 2026 15:59:07 +0000 Subject: [PATCH] fix: reduce combat separation to SC2-level tightness MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In-range attacking units were still spreading because processAttackingUnit applies separation at combatSpreadSpeedMultiplier * maxSpeed when force exceeds combatSeparationThreshold. With strength 0.3 and 8+ tightly-packed allies, force easily exceeded the 0.3 threshold, moving units at 0.75 units/sec - accumulating to 3.75 units of drift over 5 seconds of fighting. Changes: - strengthCombat: 0.3 → 0.1 (near-zero per-neighbor separation force) - spreadSpeedMultiplier: 0.25 → 0.1 (much slower combat spreading) - separationThreshold: 0.3 → 0.8 (only move when severely overlapping) With these values, 8 neighbors at typical combat distance generate ~0.39 force - well below the 0.8 threshold. Units effectively stay put while fighting, matching SC2 behavior where attacking units stack tightly. https://claude.ai/code/session_012Sc49F3UHGXxttLJpBPggB --- public/config/collision.config.json | 12 ++++++------ src/data/collisionConfig.ts | 10 ++++++---- tests/data/collisionConfig.test.ts | 15 +++++++++++---- 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/public/config/collision.config.json b/public/config/collision.config.json index 3fcb3ee2..6e3f27c1 100644 --- a/public/config/collision.config.json +++ b/public/config/collision.config.json @@ -20,8 +20,8 @@ "strengthArriving": 1.0, "_strengthArriving_description": "Separation at destination. Gentle spread, not sudden explosion.", - "strengthCombat": 0.3, - "_strengthCombat_description": "Separation during combat. Minimal - units should focus on attacking, not spreading.", + "strengthCombat": 0.1, + "_strengthCombat_description": "Separation during combat. Near-zero so units stack tightly like SC2 while fighting.", "maxForce": 2.0, "_maxForce_description": "Maximum separation force. Low cap prevents jerky movement.", @@ -62,11 +62,11 @@ "combat": { "_description": "Combat movement - minimal spreading, focus on attacking", - "spreadSpeedMultiplier": 0.25, - "_spreadSpeedMultiplier_description": "Speed when spreading in combat. Slow to maintain formation.", + "spreadSpeedMultiplier": 0.1, + "_spreadSpeedMultiplier_description": "Speed when spreading in combat. Very slow - SC2 units barely move while attacking.", - "separationThreshold": 0.3, - "_separationThreshold_description": "High threshold - don't spread for minor overlaps during combat." + "separationThreshold": 0.8, + "_separationThreshold_description": "High threshold - only spread when severely overlapping. Most combat units stay put." }, "arrival": { diff --git a/src/data/collisionConfig.ts b/src/data/collisionConfig.ts index d6856bf4..1c9042e5 100644 --- a/src/data/collisionConfig.ts +++ b/src/data/collisionConfig.ts @@ -89,7 +89,7 @@ const DEFAULT_CONFIG: CollisionConfig = { strengthMoving: 0.15, strengthIdle: 1.2, strengthArriving: 1.2, - strengthCombat: 0.3, + strengthCombat: 0.1, maxForce: 2.0, flyingMultiplier: 1.2, }, @@ -105,8 +105,8 @@ const DEFAULT_CONFIG: CollisionConfig = { settleThreshold: 0.85, }, combat: { - spreadSpeedMultiplier: 0.25, - separationThreshold: 0.3, + spreadSpeedMultiplier: 0.1, + separationThreshold: 0.8, }, arrival: { spreadRadius: 3.0, @@ -157,7 +157,9 @@ class CollisionConfigLoader { try { const response = await fetch('/config/collision.config.json'); if (!response.ok) { - debugInitialization.warn('[CollisionConfig] Failed to load collision.config.json, using defaults'); + debugInitialization.warn( + '[CollisionConfig] Failed to load collision.config.json, using defaults' + ); this.config = DEFAULT_CONFIG; return; } diff --git a/tests/data/collisionConfig.test.ts b/tests/data/collisionConfig.test.ts index 35902223..a05b6926 100644 --- a/tests/data/collisionConfig.test.ts +++ b/tests/data/collisionConfig.test.ts @@ -332,21 +332,28 @@ describe('Collision Configuration', () => { }); describe('config value relationships', () => { - it('idle separation is stricter than combat', () => { + it('combat separation threshold is higher than idle (more overlap-tolerant)', () => { const config = collisionConfig.getConfig(); - expect(config.idle.separationThreshold).toBeGreaterThan(config.combat.separationThreshold); + // Combat units tolerate more overlap before spreading - SC2-style tight stacking + expect(config.combat.separationThreshold).toBeGreaterThanOrEqual( + config.idle.separationThreshold + ); }); it('arriving strength is strongest', () => { const config = collisionConfig.getConfig(); - expect(config.separation.strengthArriving).toBeGreaterThanOrEqual(config.separation.strengthIdle); + expect(config.separation.strengthArriving).toBeGreaterThanOrEqual( + config.separation.strengthIdle + ); expect(config.separation.strengthArriving).toBeGreaterThan(config.separation.strengthMoving); expect(config.separation.strengthArriving).toBeGreaterThan(config.separation.strengthCombat); }); it('soft margin is larger than hard margin for buildings', () => { const config = collisionConfig.getConfig(); - expect(config.buildingAvoidance.softMargin).toBeGreaterThan(config.buildingAvoidance.hardMargin); + expect(config.buildingAvoidance.softMargin).toBeGreaterThan( + config.buildingAvoidance.hardMargin + ); }); it('building avoidance is strong compared to separation', () => {