feat: introduce dedicated armor classes for air and fortifications

Add `air` and `fortification` armor classes to enable more nuanced
balance. Aircraft now use `air` class instead of light/medium, and
defensive turrets use `fortification` instead of `structure`, allowing
weapons to specialize against planes and defenses without affecting
other targets.

- Add armor classes to all 12 weapons with appropriate multipliers
- Adjust weapon stats (sniperrifle, rocketpod, shoulderrocket)
- Reduce HP on several combat units for balance
- Reclassify aircraft and defensive turrets to new armor classes
This commit is contained in:
Brian Fertig 2026-08-01 14:02:22 -06:00
parent ac063d34a6
commit 71e455650b
1 changed files with 76 additions and 42 deletions

View File

@ -20,7 +20,15 @@
"aircraft is an ordinary ground target that anything can shoot, and it cannot fire back.", "aircraft is an ordinary ground target that anything can shoot, and it cannot fire back.",
"`domain` and the unit's `moveClass` have to agree: an air unit takes a move class flagged", "`domain` and the unit's `moveClass` have to agree: an air unit takes a move class flagged",
"`\"air\": true`. Terrain passability comes from the move class COST TABLE, not from the terrain's", "`\"air\": true`. Terrain passability comes from the move class COST TABLE, not from the terrain's",
"own `blocksMove` \u2014 that is how `hover` crosses water without a per-terrain exception." "own `blocksMove` \u2014 that is how `hover` crosses water without a per-terrain exception.",
"",
"Armor classes are the whole balance surface: every weapon lists a multiplier for EVERY class,",
"so a roster change is a table edit rather than a code change. Two of them exist purely so a",
"counter can be expressed at all \u2014 `air` (aircraft, separate from light/medium so a weapon can",
"be lethal to planes without also being lethal to jeeps and tanks) and `fortification` (Laser",
"Tower and Missile Launcher, separate from `structure` so a weapon can specialise in cracking",
"defences without flattening generators). Adding a class means adding a key to all 12 weapons;",
"compileRules refuses to start if one is missing, which is the point."
], ],
"version": 1, "version": 1,
"constants": { "constants": {
@ -80,7 +88,9 @@
"light", "light",
"medium", "medium",
"heavy", "heavy",
"structure" "structure",
"fortification",
"air"
], ],
"moveClasses": { "moveClasses": {
"foot": { "foot": {
@ -264,11 +274,13 @@
"ground" "ground"
], ],
"armorMul": { "armorMul": {
"infantry": 1.3, "infantry": 1.0,
"light": 0.85, "light": 0.5,
"medium": 0.3, "medium": 0.25,
"heavy": 0.12, "heavy": 0.15,
"structure": 0.2 "structure": 0.7,
"fortification": 0.55,
"air": 0.3
}, },
"fx": { "fx": {
"style": "tracer", "style": "tracer",
@ -292,11 +304,13 @@
"ground" "ground"
], ],
"armorMul": { "armorMul": {
"infantry": 1.3, "infantry": 2.2,
"light": 0.85, "light": 0.7,
"medium": 0.3, "medium": 0.3,
"heavy": 0.12, "heavy": 0.15,
"structure": 0.2 "structure": 0.5,
"fortification": 0.4,
"air": 0.35
}, },
"fx": { "fx": {
"style": "tracer", "style": "tracer",
@ -310,20 +324,22 @@
"id": "sniperrifle", "id": "sniperrifle",
"name": "Marksman Rifle", "name": "Marksman Rifle",
"kind": "hitscan", "kind": "hitscan",
"damage": 55, "damage": 70,
"reload": 2.0, "reload": 2.2,
"burst": 1, "burst": 1,
"range": 460, "range": 580,
"spread": 0.0, "spread": 0.0,
"targets": [ "targets": [
"ground" "ground"
], ],
"armorMul": { "armorMul": {
"infantry": 2.2, "infantry": 3.2,
"light": 0.9, "light": 0.35,
"medium": 0.3, "medium": 0.12,
"heavy": 0.1, "heavy": 0.08,
"structure": 0.1 "structure": 0.08,
"fortification": 0.08,
"air": 0.15
}, },
"fx": { "fx": {
"style": "beam", "style": "beam",
@ -354,7 +370,9 @@
"light": 1.15, "light": 1.15,
"medium": 1.0, "medium": 1.0,
"heavy": 0.8, "heavy": 0.8,
"structure": 1.2 "structure": 1.2,
"fortification": 1.1,
"air": 1.0
}, },
"fx": { "fx": {
"style": "shell", "style": "shell",
@ -368,7 +386,7 @@
"id": "rocketpod", "id": "rocketpod",
"name": "Rocket Pod", "name": "Rocket Pod",
"kind": "guided", "kind": "guided",
"damage": 40, "damage": 48,
"reload": 4.0, "reload": 4.0,
"burst": 2, "burst": 2,
"burstDelay": 0.25, "burstDelay": 0.25,
@ -387,7 +405,9 @@
"light": 0.6, "light": 0.6,
"medium": 1.0, "medium": 1.0,
"heavy": 1.4, "heavy": 1.4,
"structure": 1.5 "structure": 1.5,
"fortification": 1.6,
"air": 2.6
}, },
"fx": { "fx": {
"style": "rocket", "style": "rocket",
@ -402,10 +422,10 @@
"id": "shoulderrocket", "id": "shoulderrocket",
"name": "Shoulder Rocket", "name": "Shoulder Rocket",
"kind": "guided", "kind": "guided",
"damage": 32, "damage": 40,
"reload": 2.2, "reload": 2.2,
"burst": 1, "burst": 1,
"range": 320, "range": 380,
"minRange": 60, "minRange": 60,
"speed": 260, "speed": 260,
"turnRate": 2.0, "turnRate": 2.0,
@ -416,11 +436,13 @@
"air" "air"
], ],
"armorMul": { "armorMul": {
"infantry": 0.35, "infantry": 0.3,
"light": 0.8, "light": 0.9,
"medium": 1.1, "medium": 1.3,
"heavy": 1.3, "heavy": 1.2,
"structure": 1.0 "structure": 1.2,
"fortification": 1.9,
"air": 2.8
}, },
"fx": { "fx": {
"style": "rocket", "style": "rocket",
@ -448,7 +470,9 @@
"light": 1.0, "light": 1.0,
"medium": 0.8, "medium": 0.8,
"heavy": 0.6, "heavy": 0.6,
"structure": 0.5 "structure": 0.5,
"fortification": 0.45,
"air": 0.8
}, },
"fx": { "fx": {
"style": "beam", "style": "beam",
@ -476,7 +500,9 @@
"light": 1, "light": 1,
"medium": 1, "medium": 1,
"heavy": 1, "heavy": 1,
"structure": 1 "structure": 1,
"fortification": 1,
"air": 1
}, },
"fx": { "fx": {
"style": "dgun", "style": "dgun",
@ -503,7 +529,9 @@
"light": 1.2, "light": 1.2,
"medium": 0.9, "medium": 0.9,
"heavy": 0.55, "heavy": 0.55,
"structure": 0.4 "structure": 0.4,
"fortification": 0.35,
"air": 1.0
}, },
"fx": { "fx": {
"style": "beam", "style": "beam",
@ -535,7 +563,9 @@
"light": 0.7, "light": 0.7,
"medium": 1.1, "medium": 1.1,
"heavy": 1.5, "heavy": 1.5,
"structure": 1.3 "structure": 1.3,
"fortification": 1.2,
"air": 2.8
}, },
"fx": { "fx": {
"style": "rocket", "style": "rocket",
@ -561,7 +591,9 @@
"light": 1.0, "light": 1.0,
"medium": 0.85, "medium": 0.85,
"heavy": 0.6, "heavy": 0.6,
"structure": 0.2 "structure": 0.2,
"fortification": 0.2,
"air": 1.0
}, },
"fx": { "fx": {
"style": "tracer", "style": "tracer",
@ -591,7 +623,9 @@
"light": 1.0, "light": 1.0,
"medium": 1.0, "medium": 1.0,
"heavy": 0.85, "heavy": 0.85,
"structure": 1.6 "structure": 1.6,
"fortification": 1.5,
"air": 1.0
}, },
"fx": { "fx": {
"style": "shell", "style": "shell",
@ -696,7 +730,7 @@
"role": "combat", "role": "combat",
"size": "small", "size": "small",
"radius": 16, "radius": 16,
"hp": 200, "hp": 130,
"speed": 46, "speed": 46,
"turnRate": 5.0, "turnRate": 5.0,
"moveClass": "foot", "moveClass": "foot",
@ -726,7 +760,7 @@
"role": "combat", "role": "combat",
"size": "small", "size": "small",
"radius": 16, "radius": 16,
"hp": 240, "hp": 150,
"speed": 50, "speed": 50,
"turnRate": 5.5, "turnRate": 5.5,
"moveClass": "foot", "moveClass": "foot",
@ -818,7 +852,7 @@
"role": "artillery", "role": "artillery",
"size": "medium", "size": "medium",
"radius": 28, "radius": 28,
"hp": 1250, "hp": 1050,
"speed": 75, "speed": 75,
"turnRate": 1.8, "turnRate": 1.8,
"moveClass": "tread", "moveClass": "tread",
@ -893,7 +927,7 @@
"speed": 290, "speed": 290,
"turnRate": 3.0, "turnRate": 3.0,
"moveClass": "air", "moveClass": "air",
"armorClass": "light", "armorClass": "air",
"sight": 620, "sight": 620,
"cost": { "cost": {
"energy": 900, "energy": 900,
@ -932,7 +966,7 @@
"speed": 215, "speed": 215,
"turnRate": 1.7, "turnRate": 1.7,
"moveClass": "air", "moveClass": "air",
"armorClass": "medium", "armorClass": "air",
"sight": 560, "sight": 560,
"cost": { "cost": {
"energy": 1700, "energy": 1700,
@ -1140,7 +1174,7 @@
"h": 1 "h": 1
}, },
"hp": 1700, "hp": 1700,
"armorClass": "structure", "armorClass": "fortification",
"sight": 300, "sight": 300,
"cost": { "cost": {
"energy": 420, "energy": 420,
@ -1171,7 +1205,7 @@
"h": 2 "h": 2
}, },
"hp": 3100, "hp": 3100,
"armorClass": "structure", "armorClass": "fortification",
"sight": 420, "sight": 420,
"cost": { "cost": {
"energy": 1100, "energy": 1100,