param( [string]$PlayerDataFile = "Unity/Assets/Scripts/TH1_Data/PlayerData.cs", [string]$SkillFile = "Unity/Assets/Scripts/TH1_Logic/Skill/AllSkill/HakureiNorwayHeroSkill.cs", [string]$ActionFile = "Unity/Assets/Scripts/TH1_Logic/Action/ActionLogic.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 "Movement sight refresh guardrail failed: $label missing '$needle'." } } $playerDataText = Read-RepoFile $PlayerDataFile $skillText = Read-RepoFile $SkillFile $actionText = Read-RepoFile $ActionFile $updateSightByPath = [regex]::Match($playerDataText, '(?ms)public bool UpdateSightByPath\(UnitData unit,PlayerData player,Vector2Int pos,MapData map\)\s*\{[\s\S]*?return true;\s*\}') if (-not $updateSightByPath.Success) { throw "Movement sight refresh guardrail failed: cannot locate UpdateSightByPath." } Assert-Contains $updateSightByPath.Value 'GetAroundGridIdList(range, grid, remainCenter: true)' 'UpdateSightByPath must reveal the unit standing grid' Assert-Contains $updateSightByPath.Value 'unit.HeroTask(map)?.OnExploredGrids(map, unit, count);' 'UpdateSightByPath must keep hero exploration task accounting' Assert-Contains $updateSightByPath.Value 'OnAnyExploredGrids(map, unit, count)' 'UpdateSightByPath must keep global hero exploration task accounting' $suikaLandingSight = [regex]::Match($skillText, '(?ms)public static List CollectSuikaFallingSplashNewSightGrids\(MapData map, UnitData suika,\s*PlayerData player, GridData landingGrid\).*?^\s*private static void ExecuteSuikaFallingSplashDamage', [System.Text.RegularExpressions.RegexOptions]::Multiline) if (-not $suikaLandingSight.Success) { throw "Movement sight refresh guardrail failed: cannot locate CollectSuikaFallingSplashNewSightGrids." } Assert-Contains $suikaLandingSight.Value 'GetAroundGridIdList(range, landingGrid, remainCenter: true)' 'Suika landing sight refresh must include final landing grid' $suikaFlyAfterAttack = [regex]::Match($skillText, '(?ms)public static bool TryExecuteSuikaFlyAfterAttack\(MapData map, AttackInfo attackInfo\).*?^\s*public static List CollectSuikaFallingSplashNewSightGrids', [System.Text.RegularExpressions.RegexOptions]::Multiline) if (-not $suikaFlyAfterAttack.Success) { throw "Movement sight refresh guardrail failed: cannot locate TryExecuteSuikaFlyAfterAttack." } if ($suikaFlyAfterAttack.Value -notmatch 'CollectSuikaFallingSplashNewSightGrids\(map, suika, attackInfo\.OriginPlayer,\s*landingGrid\)[\s\S]*?TryRepositionUnitWithoutMoveSideEffects\(map, suika, landingGrid\)[\s\S]*?UpdateSightByPath\(suika, attackInfo\.OriginPlayer, landingGrid\.Pos\.V2\(\), map\)') { throw "Movement sight refresh guardrail failed: Suika attack landing must cache new sight before data reposition and update sight at final landing grid." } $unitAttackAction = [regex]::Match($actionText, '(?ms)private static bool TryCreateSuikaFallingSplashFragment\(.*?^\s*public override bool CheckCan', [System.Text.RegularExpressions.RegexOptions]::Multiline) if (-not $unitAttackAction.Success) { throw "Movement sight refresh guardrail failed: cannot locate Suika falling splash fragment handoff in UnitAttackAction." } Assert-Contains $unitAttackAction.Value 'sightRefreshGrids: attackInfo.SuikaFallingSplashSightRefreshGrids' 'UnitAttackAction must hand cached landing sight grids to Suika fragment' Write-Host "Movement sight refresh guardrail passed."