Files
AIGame/AIClient/AgentScripts/VerifyCampInteractionsAndPets.cs

255 lines
11 KiB
C#

#if UNITY_EDITOR
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
public static class VerifyCampInteractionsAndPets
{
const string ScenePath = "Assets/Bundles/Scenes/Town/HomeCamp/HomeCamp.unity";
public static string Run()
{
var sb = new StringBuilder();
sb.AppendLine("=== Automated Verification: Camp Interactions, Building Nav & Pet Wandering ===");
bool allPassed = true;
var scene = EditorSceneManager.OpenScene(ScenePath, OpenSceneMode.Additive);
try
{
var root = scene.GetRootGameObjects().FirstOrDefault(g => g.name == "HomeCamp");
if (root == null)
{
sb.AppendLine("[FAIL] HomeCamp root not found in " + ScenePath);
return sb.ToString();
}
// Phase 1: Entities Existence, Scale & Component Checks
sb.AppendLine("\n[Phase 1: Scene Entities, 4x Scale & Component Checks]");
var explorerTf = root.transform.Find("Explorer");
if (explorerTf == null)
{
sb.AppendLine("[FAIL] Explorer missing in scene");
allPassed = false;
}
else
{
var ctrl = explorerTf.GetComponent<HotUpdate.Gameplay.CampExplorerController>();
bool scale4x = Mathf.Approximately(explorerTf.localScale.x, 4.0f);
if (scale4x)
{
sb.AppendLine($"[PASS] Explorer scaled 4x ({explorerTf.localScale}), pos={explorerTf.position}, Controller={ctrl != null}, Layer={explorerTf.gameObject.layer}");
}
else
{
sb.AppendLine($"[FAIL] Explorer scale is {explorerTf.localScale}, expected (4, 4, 4)");
allPassed = false;
}
}
var petNames = new[] { "Pet_FireCat", "Pet_RockBall", "Pet_BudBud" };
foreach (var petName in petNames)
{
var petTf = root.transform.Find(petName);
if (petTf == null)
{
sb.AppendLine($"[FAIL] Companion {petName} missing in scene");
allPassed = false;
continue;
}
var wanderer = petTf.GetComponent<HotUpdate.Gameplay.CampPetWanderer>();
var anim = petTf.GetComponentInChildren<Animator>();
bool hasWanderer = wanderer != null;
bool hasAnim = anim != null && anim.runtimeAnimatorController != null;
bool correctLayer = petTf.gameObject.layer == 29;
bool isEnlarged = petTf.localScale.x >= 2.8f;
if (hasWanderer && hasAnim && correctLayer && isEnlarged)
{
sb.AppendLine($"[PASS] {petName} scaled 4x ({petTf.localScale}), Controller={anim.runtimeAnimatorController.name}, Layer={petTf.gameObject.layer}, WanderRadius={wanderer.wanderRadius}m");
}
else
{
sb.AppendLine($"[FAIL] {petName} check failed: hasWanderer={hasWanderer}, hasAnim={hasAnim}, correctLayer={correctLayer}, isEnlarged={isEnlarged}");
allPassed = false;
}
}
// Phase 2: Companion Forward-Walking Facing Verification
sb.AppendLine("\n[Phase 2: Companion Forward-Walking Facing Verification]");
var fireCatTf = root.transform.Find("Pet_FireCat");
if (fireCatTf != null)
{
var wanderer = fireCatTf.GetComponent<HotUpdate.Gameplay.CampPetWanderer>();
var anim = fireCatTf.GetComponentInChildren<Animator>();
var motionHash = Animator.StringToHash("Motion");
// Initial state
if (!wanderer.IsMoving)
{
sb.AppendLine("[PASS] Pet_FireCat initially idle (IsMoving=False)");
}
else
{
sb.AppendLine("[FAIL] Pet_FireCat initially moving");
allPassed = false;
}
// Trigger Wander MoveTo
Vector3 moveDelta = new Vector3(1.2f, 0, 0.8f);
Vector3 petTarget = fireCatTf.position + moveDelta;
wanderer.MoveTo(petTarget);
// Rotate towards movement target using wanderer facing convention
Vector3 lookDir = wanderer.modelFacesMinusZ ? -moveDelta.normalized : moveDelta.normalized;
fireCatTf.rotation = Quaternion.LookRotation(lookDir, Vector3.up);
// Companion front face is local -Z (Vector3.back in model space)
Vector3 visualFront = fireCatTf.TransformDirection(Vector3.back);
float facingError = Vector3.Angle(visualFront, moveDelta.normalized);
if (wanderer.IsMoving && anim.GetInteger(motionHash) == 1 && facingError < 1.0f)
{
sb.AppendLine($"[PASS] Pet Walk Facing verified: Model front face (-Z) aligns with move direction (+{moveDelta.normalized}), facingError={facingError:F2}° (NOT walking backwards)");
}
else
{
sb.AppendLine($"[FAIL] Pet Walk Facing incorrect: facingError={facingError:F2}°, IsMoving={wanderer.IsMoving}");
allPassed = false;
}
// Stop Pet
wanderer.Stop();
if (!wanderer.IsMoving && anim.GetInteger(motionHash) == 0)
{
sb.AppendLine($"[PASS] Pet Stop triggered: IsMoving=False, Motion parameter = 0 (Idle)");
}
else
{
sb.AppendLine($"[FAIL] Pet Stop did not return to Motion=0: IsMoving={wanderer.IsMoving}, Motion={anim.GetInteger(motionHash)}");
allPassed = false;
}
// Social greeting simulation
if (explorerTf != null)
{
explorerTf.position = fireCatTf.position + new Vector3(1.0f, 0, 0.8f);
Vector3 toExplorer = explorerTf.position - fireCatTf.position;
toExplorer.y = 0;
if (toExplorer.magnitude < wanderer.greetingDistance)
{
Vector3 greetDir = wanderer.modelFacesMinusZ ? -toExplorer.normalized : toExplorer.normalized;
fireCatTf.rotation = Quaternion.LookRotation(greetDir, Vector3.up);
Vector3 greetFront = fireCatTf.TransformDirection(Vector3.back);
float greetError = Vector3.Angle(greetFront, toExplorer.normalized);
if (greetError < 1.0f)
{
sb.AppendLine($"[PASS] Social Greeting: Pet front face directly faces Explorer (dist={toExplorer.magnitude:F2}m, greetError={greetError:F2}°)");
}
else
{
sb.AppendLine($"[FAIL] Social Greeting face error: {greetError:F2}°");
allPassed = false;
}
}
}
}
// Phase 3: Building Interaction & Arrival Callback Simulation
sb.AppendLine("\n[Phase 3: Building Navigation & Arrival Callback Simulation]");
if (explorerTf != null)
{
var explorerCtrl = explorerTf.GetComponent<HotUpdate.Gameplay.CampExplorerController>();
bool arrivalCallbackFired = false;
Vector3 mainHouseEntrance = new Vector3(0f, 0f, 3.5f);
Vector3 mainHouseFacing = Vector3.forward;
explorerTf.position = new Vector3(0f, 0f, -3.5f);
explorerCtrl.MoveTo(mainHouseEntrance, () => { arrivalCallbackFired = true; }, mainHouseFacing);
if (explorerCtrl.IsMoving)
{
sb.AppendLine($"[PASS] MoveTo building entrance initiated: IsMoving=True, Target={explorerCtrl.TargetPosition}");
}
else
{
sb.AppendLine("[FAIL] MoveTo building entrance failed to start");
allPassed = false;
}
// Simulate movement until arrival
int steps = 0;
while (explorerCtrl.IsMoving && steps < 150)
{
Vector3 toTgt = explorerCtrl.TargetPosition - explorerTf.position;
toTgt.y = 0;
float dist = toTgt.magnitude;
if (dist <= explorerCtrl.stopDistance)
{
explorerCtrl.Stop();
break;
}
explorerTf.position += toTgt.normalized * Mathf.Min(explorerCtrl.moveSpeed * 0.05f, dist);
steps++;
}
float finalDist = Vector2.Distance(new Vector2(explorerTf.position.x, explorerTf.position.z), new Vector2(mainHouseEntrance.x, mainHouseEntrance.z));
float facingAngleDiff = Vector3.Angle(explorerTf.forward, mainHouseFacing);
if (!explorerCtrl.IsMoving && finalDist <= explorerCtrl.stopDistance + 0.05f && arrivalCallbackFired && facingAngleDiff < 5.0f)
{
sb.AppendLine($"[PASS] Explorer reached MainHouse entrance in {steps} steps! ArrivalCallbackFired=True, FacingAngleDiff={facingAngleDiff:F1}°, FinalPos={explorerTf.position}");
}
else
{
sb.AppendLine($"[FAIL] Building navigation arrival failed: finalDist={finalDist:F3}, callback={arrivalCallbackFired}, facingDiff={facingAngleDiff:F1}°");
allPassed = false;
}
}
// Phase 4: Click Indicator Validation
sb.AppendLine("\n[Phase 4: Click Indicator Validation]");
if (explorerTf != null)
{
var explorerCtrl = explorerTf.GetComponent<HotUpdate.Gameplay.CampExplorerController>();
explorerCtrl.MoveTo(new Vector3(1.5f, 0, -2.0f));
var indicator = root.transform.Find("ClickIndicator");
if (indicator != null && indicator.gameObject.activeSelf)
{
sb.AppendLine($"[PASS] ClickIndicator instantiated and active at: {indicator.position}");
}
else
{
sb.AppendLine("[FAIL] ClickIndicator not active during movement");
allPassed = false;
}
explorerCtrl.Stop();
if (indicator != null && !indicator.gameObject.activeSelf)
{
sb.AppendLine("[PASS] ClickIndicator deactivated upon arrival/stop");
}
else
{
sb.AppendLine("[FAIL] ClickIndicator failed to deactivate upon stop");
allPassed = false;
}
}
}
finally
{
EditorSceneManager.CloseScene(scene, true);
}
sb.AppendLine($"\nOverall Result: {(allPassed ? "ALL TESTS PASSED" : "TESTS FAILED")}");
return sb.ToString();
}
}
#endif