From 005f25d44fb3d69cc194e4ebb446da4a5d77c3fd Mon Sep 17 00:00:00 2001 From: ViTeXFTW Date: Sat, 18 Jul 2026 15:29:28 +0200 Subject: [PATCH 1/2] Validate Weapon field values --- crates/analysis/tests/spec/WeaponValues.ini | 31 ++++ .../tests/spec/WeaponValues.spec.toml | 44 +++++ crates/schema/schema.json | 152 ++++++++++++++---- crates/schema/src/lib.rs | 19 +++ 4 files changed, 218 insertions(+), 28 deletions(-) create mode 100644 crates/analysis/tests/spec/WeaponValues.ini create mode 100644 crates/analysis/tests/spec/WeaponValues.spec.toml diff --git a/crates/analysis/tests/spec/WeaponValues.ini b/crates/analysis/tests/spec/WeaponValues.ini new file mode 100644 index 0000000..843b4b0 --- /dev/null +++ b/crates/analysis/tests/spec/WeaponValues.ini @@ -0,0 +1,31 @@ +Object LaserObject +End + +Weapon ValidWeapon + LaserName = LaserObject + LaserBoneName = WeaponA + DamageStatusType = BURNED + RadiusDamageAffects = SELF ALLIES NOT_AIRBORNE + ProjectileCollidesWith = ALLIES PROJECTILES CONTROLLED_STRUCTURES + AntiAirborneVehicle = Yes + AntiGround = Yes + AntiProjectile = No + AntiSmallMissile = No + AntiMine = No + AntiParachute = No + AntiAirborneInfantry = Yes + AntiBallisticMissile = No + DelayBetweenShots = 500 + DelayBetweenShots = Min:100 Max:200 + WeaponBonus = GARRISONED DAMAGE 125% +End + +Weapon InvalidWeapon + LaserName = MissingLaser + DamageStatusType = CRANKY + RadiusDamageAffects = FRIENDS + ProjectileCollidesWith = CIVILIANS + AntiAirborneVehicle = Maybe + DelayBetweenShots = eventually + WeaponBonus = SOMETIMES POWER lots +End diff --git a/crates/analysis/tests/spec/WeaponValues.spec.toml b/crates/analysis/tests/spec/WeaponValues.spec.toml new file mode 100644 index 0000000..c0bdd93 --- /dev/null +++ b/crates/analysis/tests/spec/WeaponValues.spec.toml @@ -0,0 +1,44 @@ +[[diag]] +severity = "warning" +code = "unresolved-reference" +on = "MissingLaser" + +[[diag]] +severity = "error" +code = "bad-enum" +on = "CRANKY" + +[[diag]] +severity = "error" +code = "bad-flag" +on = "FRIENDS" + +[[diag]] +severity = "error" +code = "bad-flag" +on = "CIVILIANS" + +[[diag]] +severity = "error" +code = "bad-bool" +on = "Maybe" + +[[diag]] +severity = "error" +code = "bad-number" +on = "eventually" + +[[diag]] +severity = "error" +code = "bad-enum" +on = "SOMETIMES" + +[[diag]] +severity = "error" +code = "bad-enum" +on = "POWER" + +[[diag]] +severity = "error" +code = "bad-percent" +on = "lots" diff --git a/crates/schema/schema.json b/crates/schema/schema.json index 6d4fe35..804c9ab 100644 --- a/crates/schema/schema.json +++ b/crates/schema/schema.json @@ -16021,7 +16021,8 @@ { "name": "LaserName", "value_type": { - "kind": "ascii_string" + "kind": "reference", + "ref_kind": "object" }, "parse_fn": "INI::parseAsciiString", "doc": null @@ -16029,7 +16030,7 @@ { "name": "LaserBoneName", "value_type": { - "kind": "ascii_string" + "kind": "w3d_model_member" }, "parse_fn": "INI::parseAsciiString", "doc": null @@ -16150,8 +16151,8 @@ { "name": "DamageStatusType", "value_type": { - "kind": "unknown", - "parse_fn": "ObjectStatusMaskType::parseSingleBitFromINI" + "kind": "enum", + "value_set": "object_status" }, "parse_fn": "ObjectStatusMaskType::parseSingleBitFromINI", "doc": null @@ -16294,8 +16295,31 @@ { "name": "DelayBetweenShots", "value_type": { - "kind": "unknown", - "parse_fn": "WeaponTemplate::parseShotDelay" + "kind": "one_of", + "variants": [ + { + "kind": "duration" + }, + { + "kind": "token_list", + "tokens": [ + { + "kind": "prefixed", + "prefix": "Min", + "value_type": { + "kind": "duration" + } + }, + { + "kind": "prefixed", + "prefix": "Max", + "value_type": { + "kind": "duration" + } + } + ] + } + ] }, "parse_fn": "WeaponTemplate::parseShotDelay", "doc": null @@ -16303,8 +16327,8 @@ { "name": "RadiusDamageAffects", "value_type": { - "kind": "unknown", - "parse_fn": "INI::parseBitString32" + "kind": "bit_flags", + "value_set": "weapon_affects" }, "parse_fn": "INI::parseBitString32", "doc": null @@ -16312,8 +16336,8 @@ { "name": "ProjectileCollidesWith", "value_type": { - "kind": "unknown", - "parse_fn": "INI::parseBitString32" + "kind": "bit_flags", + "value_set": "weapon_collide" }, "parse_fn": "INI::parseBitString32", "doc": null @@ -16321,8 +16345,7 @@ { "name": "AntiAirborneVehicle", "value_type": { - "kind": "unknown", - "parse_fn": "INI::parseBitInInt32" + "kind": "bool" }, "parse_fn": "INI::parseBitInInt32", "doc": null @@ -16330,8 +16353,7 @@ { "name": "AntiGround", "value_type": { - "kind": "unknown", - "parse_fn": "INI::parseBitInInt32" + "kind": "bool" }, "parse_fn": "INI::parseBitInInt32", "doc": null @@ -16339,8 +16361,7 @@ { "name": "AntiProjectile", "value_type": { - "kind": "unknown", - "parse_fn": "INI::parseBitInInt32" + "kind": "bool" }, "parse_fn": "INI::parseBitInInt32", "doc": null @@ -16348,8 +16369,7 @@ { "name": "AntiSmallMissile", "value_type": { - "kind": "unknown", - "parse_fn": "INI::parseBitInInt32" + "kind": "bool" }, "parse_fn": "INI::parseBitInInt32", "doc": null @@ -16357,8 +16377,7 @@ { "name": "AntiMine", "value_type": { - "kind": "unknown", - "parse_fn": "INI::parseBitInInt32" + "kind": "bool" }, "parse_fn": "INI::parseBitInInt32", "doc": null @@ -16366,8 +16385,7 @@ { "name": "AntiParachute", "value_type": { - "kind": "unknown", - "parse_fn": "INI::parseBitInInt32" + "kind": "bool" }, "parse_fn": "INI::parseBitInInt32", "doc": null @@ -16375,8 +16393,7 @@ { "name": "AntiAirborneInfantry", "value_type": { - "kind": "unknown", - "parse_fn": "INI::parseBitInInt32" + "kind": "bool" }, "parse_fn": "INI::parseBitInInt32", "doc": null @@ -16384,8 +16401,7 @@ { "name": "AntiBallisticMissile", "value_type": { - "kind": "unknown", - "parse_fn": "INI::parseBitInInt32" + "kind": "bool" }, "parse_fn": "INI::parseBitInInt32", "doc": null @@ -16402,8 +16418,20 @@ { "name": "WeaponBonus", "value_type": { - "kind": "unknown", - "parse_fn": "WeaponTemplate::parseWeaponBonusSet" + "kind": "token_list", + "tokens": [ + { + "kind": "enum", + "value_set": "weapon_bonus_condition" + }, + { + "kind": "enum", + "value_set": "weapon_bonus_field" + }, + { + "kind": "percent" + } + ] }, "parse_fn": "WeaponTemplate::parseWeaponBonusSet", "doc": null @@ -67985,6 +68013,32 @@ ], "doc": "TheWeaponBonusNames (Weapon.h)" }, + { + "id": "weapon_bonus_field", + "members": [ + { + "name": "DAMAGE", + "value": 0 + }, + { + "name": "RADIUS", + "value": 1 + }, + { + "name": "RANGE", + "value": 2 + }, + { + "name": "RATE_OF_FIRE", + "value": 3 + }, + { + "name": "PRE_ATTACK", + "value": 4 + } + ], + "doc": "TheWeaponBonusFieldNames (Weapon.h)" + }, { "id": "ocl_create_location", "members": [ @@ -69446,6 +69500,48 @@ } ] }, + { + "id": "weapon_collide", + "members": [ + { + "name": "ALLIES", + "value": 0 + }, + { + "name": "ENEMIES", + "value": 1 + }, + { + "name": "STRUCTURES", + "value": 2 + }, + { + "name": "SHRUBBERY", + "value": 3 + }, + { + "name": "PROJECTILES", + "value": 4 + }, + { + "name": "WALLS", + "value": 5 + }, + { + "name": "SMALL_MISSILES", + "value": 6 + }, + { + "name": "BALLISTIC_MISSILES", + "value": 7 + }, + { + "name": "CONTROLLED_STRUCTURES", + "value": 8 + } + ], + "doc": "TheWeaponCollideMaskNames (Weapon.h)" + }, { "id": "terrain_class", "members": [ diff --git a/crates/schema/src/lib.rs b/crates/schema/src/lib.rs index e6e2384..408028f 100644 --- a/crates/schema/src/lib.rs +++ b/crates/schema/src/lib.rs @@ -687,6 +687,25 @@ mod tests { } } + #[test] + fn weapon_fields_have_concrete_value_types() { + let schema = embedded(); + let weapon = schema + .index() + .block("Weapon") + .expect("Weapon block missing"); + for field in &weapon.fields { + assert!( + !contains(&field.value_type, |ty| matches!( + ty, + ValueType::Unknown { .. } + )), + "Weapon.{} still has an unknown value type", + field.name + ); + } + } + #[test] fn object_backed_module_fields_are_object_references() { let schema = embedded(); From 034ad783b6000db32950973a2a22693b6146a4a3 Mon Sep 17 00:00:00 2001 From: ViTeXFTW Date: Sat, 18 Jul 2026 15:37:35 +0200 Subject: [PATCH 2/2] Avoid invalid laser bone validation --- crates/schema/schema.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/schema/schema.json b/crates/schema/schema.json index 804c9ab..bca6033 100644 --- a/crates/schema/schema.json +++ b/crates/schema/schema.json @@ -16030,10 +16030,10 @@ { "name": "LaserBoneName", "value_type": { - "kind": "w3d_model_member" + "kind": "ascii_string" }, "parse_fn": "INI::parseAsciiString", - "doc": null + "doc": "Bone on the firing object's model; a shared Weapon template does not identify that object statically." }, { "name": "LeechRangeWeapon",