Files

99 lines
11 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using HotUpdate.Gameplay;
using HotUpdate.Net;
using HotUpdate.UI;
using TMPro;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
public static class VerifyDaily
{
static PrototypeFlow Flow=>UnityEngine.Object.FindObjectOfType<PrototypeFlow>();
static void Check(bool b,string message){if(!b)throw new Exception(message);}
static T Get<T>(string name)=>(T)typeof(PrototypeFlow).GetField(name,BindingFlags.NonPublic|BindingFlags.Instance).GetValue(Flow);
static void Set(string name,object value)=>typeof(PrototypeFlow).GetField(name,BindingFlags.NonPublic|BindingFlags.Instance).SetValue(Flow,value);
static async Task Until(Func<bool> condition,int seconds=20){var end=DateTime.UtcNow.AddSeconds(seconds);while(!condition()){Check(DateTime.UtcNow<end,"Daily timed out: "+Flow?.Page);await Task.Delay(100);}}
static Button Button(string name)=>UnityEngine.Object.FindObjectsOfType<Button>().Single(b=>b.name=="Btn_"+name&&b.gameObject.activeInHierarchy);
static void Click(string name)
{
var b=Button(name);Check(b.interactable,"Disabled "+name);Canvas.ForceUpdateCanvases();var rect=b.GetComponent<RectTransform>();
var pointer=new PointerEventData(EventSystem.current){position=RectTransformUtility.WorldToScreenPoint(null,rect.TransformPoint(rect.rect.center)),button=PointerEventData.InputButton.Left};
var hits=new List<RaycastResult>();EventSystem.current.RaycastAll(pointer,hits);Check(hits.Count>0&&ExecuteEvents.GetEventHandler<IPointerClickHandler>(hits[0].gameObject)==b.gameObject,"Click blocked "+name);ExecuteEvents.Execute(b.gameObject,pointer,ExecuteEvents.pointerClickHandler);
}
static bool Dot(string name)=>Get<MainHudView>("_hud").GetComponentsInChildren<Transform>(true).Single(t=>t.name==name).gameObject.activeSelf;
static async Task Capture(string name)
{
await Task.Delay(150);var type=AppDomain.CurrentDomain.GetAssemblies().Select(a=>a.GetType("Unity.Pipeline.Editor.Commands.Capture.CaptureCommands")).First(t=>t!=null);
type.GetMethod("CaptureGameView",BindingFlags.Static|BindingFlags.Public).Invoke(null,new object[]{1080,1920,null,"Screenshots/daily_"+name+".png",false,0,"screen"});
}
static async Task Shot(){int count=Flow.Game.State.shots;Click("Fire");await Until(()=>!Flow.IsBusy&&Flow.Game.State.shots==count+1);}
static async Task SyncHome(){Flow.Handle("Home");Set("_needsSync",true);Flow.Handle("Home");await Until(()=>!Flow.IsBusy);await Task.Delay(150);}
public static async Task<string> Start()
{
await Until(()=>Flow?.Game!=null&&!Flow.IsBusy);SessionState.SetString("AIGame.G1.original",JsonUtility.ToJson(Flow.Game.State));SessionState.SetString("AIGame.G1.originalEndpoint",Get<string>("_httpBase"));
AuthResponse account=null;string error=null;bool done=false;string user="g1live_daily_"+Guid.NewGuid().ToString("N");
Flow.StartCoroutine(AuthApi.Register("http://127.0.0.1:18081",user,"isolated-g1-test-password",(r,e)=>{account=r;error=e;done=true;}));await Until(()=>done);Check(account!=null,"Register failed "+error);
File.WriteAllText(Path.Combine(Application.dataPath,"../../Tools/output/g1-live/account.txt"),user);var session=new SessionData{Pid=account.pid,Token=account.token,Username=user};
Set("_session",new Func<SessionData>(()=>session));Set("_profile",new Func<long>(()=>session.Pid));Set("_httpBase","http://127.0.0.1:18081");Set("_boundProfile",0L);Flow.Handle("Home");await Until(()=>!Flow.IsBusy);await Task.Delay(150);
Check(Flow.Game.State.schema>=5&&Flow.Game.State.signinDays==1&&Dot("Dot_Daily")&&!Dot("Dot_Tasks"),"Fresh daily/sign-in state or red dots wrong");await Capture("home");
Click("Daily");await Until(()=>Flow.Page=="UI_PrototypeSignin");Check(Button("Signin0").interactable&&!Button("Signin1").interactable,"Sign-in locks wrong");await Capture("signin1");
int balls=Flow.Game.State.balls;Click("Signin0");await Until(()=>!Flow.IsBusy&&Flow.Game.State.signinClaimed[0]==1);Check(Flow.Game.State.balls==balls+10&&!Dot("Dot_Daily"),"Day1 reward or red dot wrong");await Capture("signin_reward");Click("Ok");
Set("_needsSync",true);Flow.Handle("Home");await Until(()=>!Flow.IsBusy);Check(Flow.Game.State.signinDays==1&&!Button("Signin0").interactable,"Same-day reconnect advances sign-in");
Click("ToTasks");await Until(()=>Flow.Page=="UI_PrototypeTasks");Check(!Button("ClaimDaily0").interactable&&!Button("ClaimGoal").interactable,"Incomplete task claimable");await Capture("tasks_empty");
Click("GoDaily0");await Until(()=>Flow.Page=="UI_PrototypePinball");await Shot();await Shot();await Shot();Check(Flow.Game.State.dailyProgress[0]==3&&Dot("Dot_Tasks"),"Real shot events not counted");
Click("Back");await Task.Delay(150);Click("Tasks");await Until(()=>Flow.Page=="UI_PrototypeTasks");await Capture("shot_ready");balls=Flow.Game.State.balls;
Click("ClaimDaily0");await Until(()=>!Flow.IsBusy&&Flow.Game.State.dailyClaimed[0]==1);Check(Flow.Game.State.balls==balls+5&&!Dot("Dot_Tasks"),"Shot claim repeated or dot retained");Click("Ok");
SessionState.SetInt("Daily.coins",Flow.Game.State.coins);SessionState.SetInt("Daily.candy",Flow.Game.State.candy);SessionState.SetInt("Daily.balls",Flow.Game.State.balls);
Click("GoDaily1");await Until(()=>Flow.Page=="UI_PrototypeStages");Click("Stage1");await Until(()=>Flow.Page=="UI_PrototypeFormation");Click("StartBattle");await Until(()=>Flow.Page=="UI_PrototypeBattle");Click("Speed");await Until(()=>Flow.Battle.Time>=12);
return "PASS live first login +10 once, locked future days, same-day reconnect stable, three real shots, daily +5 once and reward dots; tutorial battle running.";
}
public static async Task<string> Finish()
{
if(Flow.Battle==null||!Flow.Battle.Finished||Flow.IsBusy)return "RUNNING time="+Flow?.Battle?.Time;
Check(Flow.Battle.Won&&Flow.Game.State.dailyProgress[1]==1&&Flow.Game.State.balls==SessionState.GetInt("Daily.balls",0)+3&&Flow.Game.State.coins==SessionState.GetInt("Daily.coins",0)+80&&Flow.Game.State.candy==SessionState.GetInt("Daily.candy",0)+40,"Battle/task settlement mismatch");
Click("Home");await Task.Delay(150);Click("Tasks");await Until(()=>Flow.Page=="UI_PrototypeTasks");await Capture("battle_ready");
int balls=Flow.Game.State.balls;Click("ClaimGoal");await Until(()=>!Flow.IsBusy&&Flow.Game.State.goalClaimed);Check(Flow.Game.State.balls==balls+5,"Teaching goal wrong");Click("Ok");
Click("ClaimDaily1");await Until(()=>!Flow.IsBusy&&Flow.Game.State.dailyClaimed[1]==1);Check(Flow.Game.State.balls==balls+10,"Daily battle claim wrong");Click("Ok");
return "PASS live normally finished battle counted, exact first-clear reward, teaching +5 and daily battle +5 separately once.";
}
public static async Task<string> RestAndClaim()
{
await SyncHome();Click("NavHome");await Until(()=>Flow.Page=="UI_PrototypeCamp");int balls=Flow.Game.State.balls;Click("RestPet0");Click("Interact");await Until(()=>!Flow.IsBusy&&Flow.Game.State.dailyProgress[2]==1);
Check(Flow.Game.State.balls==balls,"Interaction directly gave resource");Click("Back");await Task.Delay(150);Click("Tasks");await Until(()=>Flow.Page=="UI_PrototypeTasks");await Capture("rest_ready");
Click("ClaimDaily2");await Until(()=>!Flow.IsBusy&&Flow.Game.State.dailyClaimed[2]==1);Check(Flow.Game.State.balls==balls+5&&!Dot("Dot_Tasks"),"Interaction task award or red dot wrong");Click("Ok");await Capture("tasks_claimed");
return "PASS actual rest interaction has no direct reward; third daily task gives +5 once; all three claimed and task red dot cleared.";
}
public static async Task<string> EarnedSignin()
{
await SyncHome();Check(Flow.Game.State.signinDays==3&&Dot("Dot_Daily"),"Earned sign-in fixture missing");Click("Daily");await Until(()=>Flow.Page=="UI_PrototypeSignin");
Check(Button("Signin1").interactable&&Button("Signin2").interactable&&!Button("Signin3").interactable,"Earned sign-in lock/preservation wrong");await Capture("signin3");
var api=Get<PrototypeApi>("_api");var command=api.Command("signin.claim",Flow.Game.State.revision,item:1);int balls=Flow.Game.State.balls;PrototypeReply response=null;string error=null;
Flow.StartCoroutine(api.Send(command,(r,e)=>{response=r;error=e;}));await Until(()=>response!=null||error!=null);Check(response?.ok==true,"Sign-in before simulated lost ack failed");
var session=Get<Func<SessionData>>("_session")();string key="AIGame.G1.pending."+Uri.EscapeDataString("http://127.0.0.1:18081")+"."+session.Pid;
PlayerPrefs.SetString(key,JsonUtility.ToJson(command));PlayerPrefs.Save();Set("_needsSync",true);Flow.Handle("Home");await Until(()=>!Flow.IsBusy&&Get<PrototypeReply>("_reply").replayed);
Check(Flow.Game.State.balls==balls+5&&Flow.Game.State.signinClaimed[1]==1&&!api.HasPending,"Sign-in retry granted twice");
var dialog=UiManager.Instance.Get(ConfirmDialogView.PrefabName).GetComponent<ConfirmDialogView>().CaptureState();Check(dialog.Visible&&dialog.Title=="奖励已领取","Original sign-in receipt not shown");await Capture("recovery");Click("Ok");
Click("Signin2");await Until(()=>!Flow.IsBusy&&Flow.Game.State.signinClaimed[2]==1);Check(Flow.Game.State.balls==balls+10&&!Dot("Dot_Daily"),"Earned reward preservation or red dot wrong");Click("Ok");
Set("_boundProfile",0L);Flow.Handle("Home");await Until(()=>!Flow.IsBusy);Check(Flow.Game.State.signinDays==3&&Flow.Game.State.signinClaimed.Take(3).All(n=>n==1),"Sign-in state lost on reattach");
return "PASS earned unclaimed login rewards remain, future day locked, lost-ack +5 granted once with receipt and durable journal cleared; profile reattach retains claims.";
}
public static async Task<string> Completed()
{
await SyncHome();Click("Daily");await Until(()=>Flow.Page=="UI_PrototypeSignin");Check(Flow.Game.State.signinDays==7&&!Dot("Dot_Daily")&&Enumerable.Range(0,7).All(i=>!Button("Signin"+i).interactable),"Completed seven-day course restarted");await Capture("signin_complete");
return "PASS completed seven-day view has all rewards claimed, no red dot and no repeated course.";
}
public static async Task<string> FinalViews()
{
await SyncHome();Click("Tasks");await Until(()=>Flow.Page=="UI_PrototypeTasks");Check(Enumerable.Range(0,3).All(i=>!Button("ClaimDaily"+i).interactable)&&!Dot("Dot_Tasks"),"Final daily claim state wrong");await Capture("tasks_claimed");
Click("ToSignin");await Until(()=>Flow.Page=="UI_PrototypeSignin");Check(Enumerable.Range(0,7).All(i=>!Button("Signin"+i).interactable)&&!Dot("Dot_Daily"),"Final sign-in claim state wrong");await Capture("signin_complete");
Click("Back");await Until(()=>Flow.Page=="Home");await Capture("home_claimed");return "PASS final task/sign-in copy, disabled claimed rewards and cleared home dots.";
}
}