Files
Towbar/tests/validate-dual-mode-42.20.ps1
T
2026-08-19 12:12:12 -04:00

89 lines
5.0 KiB
PowerShell

$ErrorActionPreference = "Stop"
$repositoryRoot = Split-Path -Parent $PSScriptRoot
$releaseRoot = Join-Path $repositoryRoot "42.20"
$failures = [Collections.Generic.List[string]]::new()
function Assert-True {
param([bool]$Condition, [string]$Message)
if (-not $Condition) { $script:failures.Add($Message) }
}
function Read-Source {
param([string]$Path)
if (-not (Test-Path -LiteralPath $Path)) {
$script:failures.Add("Missing dual-mode source: $Path")
return ""
}
return Get-Content -LiteralPath $Path -Raw
}
$compatibilityPath = Join-Path $releaseRoot "media/lua/shared/TowBar/VehicleCompatibility.lua"
$hooking = Read-Source (Join-Path $releaseRoot "media/lua/client/TowBar/TowingHooking.lua")
$btTow = Read-Source (Join-Path $releaseRoot "media/lua/server/BTTow.lua")
$template = Read-Source (Join-Path $releaseRoot "media/scripts/vehicles/template_towbar.txt")
$compatibility = Read-Source $compatibilityPath
# The boundary must be an exact mod-ID + exact VehicleScript full-name map.
# Model scale remains part of the legacy renderer only; it cannot decide KI5 ownership.
Assert-True ($compatibility -match 'TowBarMod\.Compatibility') "Classifier must expose TowBarMod.Compatibility."
Assert-True ($compatibility -match 'function Compatibility\.isKi5Vehicle\(vehicle\)') "Classifier must expose isKi5Vehicle(vehicle)."
foreach ($identity in @(
'91range', 'Base.91range',
'87toyotaCorolla', 'Base.87toyotaCorollaAE92levin',
'76chevyKseries', 'Base.76chevyK30CCwrecker'
)) {
Assert-True ($compatibility.Contains($identity)) "Classifier is missing verified exact KI5 identity: $identity"
}
Assert-True ($compatibility -match '(?s)getActivatedMods\(\).*?contains') "KI5 mode must require an exact active mod ID."
Assert-True ($compatibility -match 'getFullName\(\)') "KI5 mode must match the complete VehicleScript name."
Assert-True ($compatibility -notmatch 'getModelScale|modelScale|attachmentExist|string\.find|string\.match') "KI5 ownership must not be inferred from scale, attachments, or substring patterns."
# The visual carrier is classified independently. KI5 keeps current B42.20
# dynamic hitbox placement; every other vehicle uses the exact B42.13 selector.
foreach ($source in @($hooking, $btTow)) {
Assert-True ($source -match 'Compatibility\.isKi5Vehicle\(vehicle\)') "Each visual path must classify the vehicle carrying the towbar."
Assert-True ($source -match 'getTowbarFrontEdgeZ') "KI5 visual path must retain hitbox-derived placement."
Assert-True ($source -match 'TowbarVisualScale\s*=\s*2\.5') "KI5 visual path must retain 2.5x scale compensation."
Assert-True ($source -match 'getTowbarIndexVanilla') "Legacy visual path must restore the B42.13 vanilla slot formula."
Assert-True ($source -match 'getTowbarIndexSmallScale') "Legacy non-KI5 path must restore B42.13 small-scale slot selection."
Assert-True ($source -match 'VanillaScaleMin\s*=\s*1\.5' -and $source -match 'VanillaScaleMax\s*=\s*2\.0') "Legacy visual classification must preserve the B42.13 scale window."
}
# One bank cannot represent both scales. Restore the original `towbar` bank for
# legacy vehicles and isolate today's enlarged KI5 mesh in its own part.
Assert-True ($template -match '(?s)model towbarModel\s*\{.*?scale\s*=\s*0\.01,') "Vanilla and normal-scale non-KI5 vehicles must restore the B42.13 0.01 model."
Assert-True ($template -match '(?s)model towbarModelKI5\s*\{.*?scale\s*=\s*0\.025,') "KI5 towbar bank must remain exactly 2.5x its original 0.01 scale."
Assert-True ($template -match '(?s)model towbarModelLarge\s*\{.*?scale\s*=\s*0\.02022,') "Small-scale non-KI5 vehicles must retain the B42.13 large model scale."
Assert-True ($template -match 'part towbarKI5') "Template must provide an isolated KI5 part without resizing the legacy towbar part."
function Get-Ki5DynamicSlot {
param([double]$ShapeZ, [double]$CenterZ)
$meshHalfLength = (0.9714089036 * 2.5) / 2
$center = $CenterZ + ($ShapeZ / 2) + $meshHalfLength
return [Math]::Max(0, [Math]::Min(23, [int][Math]::Floor((($center - 1.0) / 0.1) + 0.5)))
}
function Get-LegacyVanillaSlot {
param([double]$ShapeZ)
$z = ($ShapeZ / 2) - 0.1
return [Math]::Max(0, [Math]::Min(23, [int][Math]::Floor((($z * 2 / 3) - 1) * 10)))
}
function Get-LegacySmallScaleSlot {
param([double]$MaxAbsAttachmentZ)
return [Math]::Max(0, [Math]::Min(23, [int][Math]::Floor(($MaxAbsAttachmentZ + 0.1 - 1.0) * 10)))
}
Assert-True ((Get-Ki5DynamicSlot 4.3111 -0.0444) -eq 23) "KI5 Corolla must retain B42.20 dynamic slot 23."
Assert-True ((Get-Ki5DynamicSlot 4.4444 -0.2556) -eq 22) "KI5 Range Rover must retain B42.20 dynamic slot 22."
Assert-True ((Get-LegacyVanillaSlot 2.6044) -eq 0) "B42 Ranger must return to the B42.13 legacy-normal slot 0, not dynamic slot 15."
Assert-True ((Get-LegacySmallScaleSlot 2.3222) -eq 14) "Non-KI5 small-scale geometry must retain the B42.13 attachment-derived slot."
if ($failures.Count -gt 0) {
$failures | ForEach-Object { Write-Host "FAIL: $_" -ForegroundColor Red }
exit 1
}
Write-Output "PASS: Build 42.20 exact-KI5 dual-mode contracts."