Files
vnengine/internal/vn/flightcheck.go
2026-02-12 15:25:40 -03:00

121 lines
2.8 KiB
Go

package vn
import (
"fmt"
"os"
"path/filepath"
"sort"
"strings"
)
// FlightCheckResult contains script integrity validation results.
type FlightCheckResult struct {
Errors []string
}
func (r *FlightCheckResult) OK() bool {
return len(r.Errors) == 0
}
func (r *FlightCheckResult) Summary(texts *Texts) string {
if r.OK() {
return texts.FlightCheckPassed
}
if len(r.Errors) == 1 {
return fmt.Sprintf(texts.FlightCheckOneFmt, r.Errors[0])
}
return fmt.Sprintf(texts.FlightCheckManyFmt, len(r.Errors))
}
func RunFlightCheck(script *Script, texts *Texts) *FlightCheckResult {
result := &FlightCheckResult{}
seenFiles := map[string]bool{}
for _, file := range script.LoadedFiles {
if seenFiles[file] {
continue
}
seenFiles[file] = true
if _, err := os.Stat(file); err != nil {
result.Errors = append(result.Errors, fmt.Sprintf(texts.FlightMissingScriptFmt, file))
}
}
sceneByID := map[string]Scene{}
for _, scene := range script.Scenes {
if _, ok := sceneByID[scene.ID]; ok {
result.Errors = append(result.Errors, fmt.Sprintf(texts.FlightDuplicateSceneFmt, scene.ID))
continue
}
sceneByID[scene.ID] = scene
}
if _, ok := sceneByID[script.Start]; !ok {
result.Errors = append(result.Errors, fmt.Sprintf(texts.FlightStartMissingFmt, script.Start))
}
assets := map[string]bool{}
var walk func(sceneID string, cmds []Command)
walk = func(sceneID string, cmds []Command) {
for _, cmd := range cmds {
switch strings.ToLower(cmd.Type) {
case "goto":
if cmd.Target != "" {
if _, ok := sceneByID[cmd.Target]; !ok {
result.Errors = append(result.Errors, fmt.Sprintf(texts.FlightGotoMissingFmt, sceneID, cmd.Target))
}
}
case "choice":
for _, c := range cmd.Choices {
if _, ok := sceneByID[c.Target]; !ok {
result.Errors = append(result.Errors, fmt.Sprintf(texts.FlightChoiceMissingFmt, sceneID, c.Target))
}
}
case "if":
walk(sceneID, cmd.Then)
walk(sceneID, cmd.Else)
case "menu":
for _, item := range cmd.MenuItems {
if item.Sprite != "" {
assets[item.Sprite] = true
}
walk(sceneID, item.Commands)
}
}
if cmd.BackgroundImage != "" {
assets[cmd.BackgroundImage] = true
}
if looksLikeImagePath(cmd.Background) {
assets[cmd.Background] = true
}
if cmd.Sprite != "" {
assets[cmd.Sprite] = true
}
}
}
for _, scene := range script.Scenes {
walk(scene.ID, scene.Commands)
}
keys := make([]string, 0, len(assets))
for a := range assets {
keys = append(keys, a)
}
sort.Strings(keys)
for _, rel := range keys {
if strings.Contains(rel, "{") {
continue
}
path := rel
if !filepath.IsAbs(path) {
path = filepath.Join(script.BaseDir, rel)
}
if _, err := os.Stat(path); err != nil {
result.Errors = append(result.Errors, fmt.Sprintf(texts.FlightMissingAssetFmt, rel))
}
}
return result
}