param( [string]$SkillFile = "Unity/Assets/Scripts/TH1_Logic/Skill/AllSkill/HakureiNorwayHeroSkill.cs", [string]$UnitLogicFile = "Unity/Assets/Scripts/TH1_Logic/Unit/UnitLogic.cs" ) $ErrorActionPreference = "Stop" $repoRoot = git rev-parse --show-toplevel 2>$null if (-not $repoRoot) { throw "Not inside a git repository." } $repoRoot = [System.IO.Path]::GetFullPath($repoRoot.Trim()) function Read-RepoFile([string]$relativePath) { $fullPath = Join-Path $repoRoot $relativePath if (-not (Test-Path -LiteralPath $fullPath)) { throw "Required file not found: $fullPath" } return Get-Content -LiteralPath $fullPath -Raw -Encoding UTF8 } function Assert-Contains([string]$text, [string]$needle, [string]$label) { if (-not $text.Contains($needle)) { throw "Suika falling splash targeting guardrail failed: $label missing '$needle'." } } $skillText = Read-RepoFile $SkillFile $unitLogicText = Read-RepoFile $UnitLogicFile Assert-Contains $skillText 'private static bool CanUseGridAsSuikaFallingSplashTarget' 'shared target grid predicate' Assert-Contains $skillText 'grid.Terrain is TerrainType.ShallowSea or TerrainType.DeepSea' 'water target rejection' Assert-Contains $skillText 'public static bool IsSuikaFallingSplashAttackAttempt' 'falling splash attack attempt predicate' Assert-Contains $skillText 'public override bool IsCanAttackTargetUnit' 'authoritative UnitAttack CheckCan gate' Assert-Contains $unitLogicText 'HakureiNorwayHeroSkillUtil.CanSuikaFlyAttackTarget' 'move/attack UI and AI entrypoint' $flyGridMethod = [regex]::Match($skillText, '(?ms)public static bool CanSuikaFlyToGrid\(MapData map, UnitData suika, GridData target\).*?^\s*public static bool CanSuikaFlyAttackTarget') if (-not $flyGridMethod.Success) { throw "Suika falling splash targeting guardrail failed: cannot locate CanSuikaFlyToGrid body." } if ($flyGridMethod.Value -notmatch 'CanUseGridAsSuikaFallingSplashTarget\(map, suika, target\)[\s\S]*?CanUnitLandOnGrid\(map, suika, target\)') { throw "Suika falling splash targeting guardrail failed: ground-target path must reject water before landing/occupancy checks." } $flyAttackMethod = [regex]::Match($skillText, '(?ms)public static bool CanSuikaFlyAttackTarget\(MapData map, UnitData suika, UnitData target\).*?^\s*public static bool IsSuikaFallingSplashAttackAttempt') if (-not $flyAttackMethod.Success) { throw "Suika falling splash targeting guardrail failed: cannot locate CanSuikaFlyAttackTarget body." } if ($flyAttackMethod.Value -notmatch 'IsSuikaFallingSplashAttackAttempt\(map, suika, target, out var targetGrid\)[\s\S]*?CanUseGridAsSuikaFallingSplashTarget\(map, suika, targetGrid\)') { throw "Suika falling splash targeting guardrail failed: unit-target path must reuse the same water target predicate." } $skillClassMethod = [regex]::Match($skillText, '(?ms)public partial class SuikaFallingSplashSkill.*?^\s*public partial class SuikaThrowReadySkill') if (-not $skillClassMethod.Success) { throw "Suika falling splash targeting guardrail failed: cannot locate SuikaFallingSplashSkill body." } if ($skillClassMethod.Value -notmatch 'public override bool IsCanAttackTargetUnit\(MapData map, UnitData self, UnitData target\)[\s\S]*?IsSuikaFallingSplashAttackAttempt\(map, self, target, out _\)[\s\S]*?CanSuikaFlyAttackTarget\(map, self, target\)') { throw "Suika falling splash targeting guardrail failed: SuikaFallingSplashSkill must block invalid falling-splash UnitAttack params in CheckCan." } Write-Host "Suika falling splash targeting guardrail passed."