using System; using System.IO; using System.Linq; using HotUpdate.Gameplay; using Newtonsoft.Json.Linq; using TMPro; using UnityEditor; using UnityEngine; using UnityEngine.UI; public static class VerifyProgression { static void Check(bool pass,string message) { if (!pass) throw new Exception(message); } public static string Domain() { string repo=Directory.GetParent(Application.dataPath).Parent.FullName; var config=JsonUtility.FromJson(File.ReadAllText(Path.Combine(repo,"Config/Gameplay/greenfield.json"))); Check(PrototypeConfig.Valid(config),"Invalid canonical config"); Check(config.pets.Length==10&&config.stages.Length==20,"Expected ten partners and twenty stages"); Check(config.capture.weights.Length==10&&config.rewards.chests[1].choices.Length==10,"Capture pool or chest choices not widened"); foreach(var stage in config.stages) Check(!string.IsNullOrEmpty(stage.chapter),"Stage missing chapter"); var vectors=(JArray)JObject.Parse(File.ReadAllText(Path.Combine(repo,"Tools/output/progression_parity.json")))["entries"]; Check(vectors.Count>=50,"Missing C++ parity vectors"); int wins=0,losses=0; foreach(var v in vectors) { var s=JsonUtility.FromJson(v["snapshot"].ToString()); var result=JsonUtility.FromJson(v["result"].ToString()); // Replay snapshots are presentation-only saves; give them a schema matching their roster width. int replaySchema=s.stages.Length>=10?9:s.stages.Length==5?3:1; var save=new PrototypeSave { schema=replaySchema,level=s.level,stages=s.stages,shards=new int[s.stages.Length],formation=s.formation }; if(replaySchema>=2){save.selectedStage=1;save.clearedStages=new int[replaySchema>=9?20:5];save.clearedStages[0]=1;} if(replaySchema>=4){save.campLayout=new[]{-1,-1,-1};save.decorations=new int[3];save.restPet=0;} if(replaySchema>=5){save.dailyDay=1;save.signinDays=1;save.dailyProgress=new int[3];save.dailyClaimed=new int[3];save.signinClaimed=new int[7];} if(replaySchema>=6)save.shopBought=new int[3]; if(replaySchema>=7)save.mails=new PrototypeMail[0]; if(replaySchema>=8){save.chests=new int[3];save.chestDailyClaimed=0;} var game=new PrototypeGame(save); var battle=new PrototypeBattle("parity",game,s.legacy?null:s.config,s.legacy?1:s.stageId); for(int tick=0;tick<2500&&!battle.Finished;tick++) battle.Tick(); Check(battle.Finished && battle.Won==result.won && battle.Durability==result.durability && battle.Defeated==result.defeated && Mathf.Abs(battle.Time-result.duration)<.002f, "C#/C++ drift at stage "+s.stageId+" level "+s.level+" party "+string.Join(",",s.formation)+"; Unity="+battle.Won+"/"+battle.Time+" server="+result.won+"/"+result.duration); if(battle.Won) wins++;else losses++; } Check(wins>0&&losses>0,"Parity must cover wins and losses"); foreach(string screen in new[]{"Stages","Pinball","Capture","Pets","Formation","Battle","Camp","Build","Tasks","Signin","Bag","Shop","Mail","Chest","Settings"}) { var prefab=AssetDatabase.LoadAssetAtPath("Assets/Bundles/UI/Prefab/UI_Prototype"+screen+".prefab"); Check(prefab!=null,"Missing "+screen); Check(prefab.GetComponentsInChildren(true).All(t=>t.font!=null),"Missing TMP font "+screen); foreach(var image in prefab.GetComponentsInChildren(true).Where(i=>i.type==Image.Type.Sliced)) Check(image.sprite!=null&&image.sprite.border.x>0&&image.sprite.border.y>0,"Invalid nine-slice "+screen); } return "PASS: "+vectors.Count+" C++/Unity parity cases including "+wins+" wins / "+losses+" losses and pending migration; sixteen authored UI prefabs, fonts, nine-slices."; } }