PlayerDecisionController .cs 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using Unity.VisualScripting;
  4. using UnityEngine;
  5. using UnityEngine.UIElements;
  6. using UnityEngine.AI;
  7. public class PlayerDecisionController : MonoBehaviour
  8. {
  9. [Header("References")]
  10. public LayerMask enemyLayerMask = 1 << 10; // Enemy layer
  11. public LayerMask playerLayerMask = 1 << 9; // Player layer
  12. [Header("State")]
  13. private Character selectedCharacter;
  14. private bool isDragging = false;
  15. private Vector3 dragStartPosition;
  16. [Header("Settings")]
  17. public float enemySnapDistance = 1f;
  18. private List<Character> playerCharacters = new List<Character>();
  19. private TargetingLine targetingLine;
  20. private Camera mainCamera;
  21. private bool isEnabled = true;
  22. private List<TargetingLine> activeActionLines = new List<TargetingLine>();
  23. [Header("Character Stats UI")]
  24. public VisualTreeAsset characterStatsUXML;
  25. public StyleSheet characterStatsUSS;
  26. private UIDocument uiDocument;
  27. private VisualElement statsPanel;
  28. private bool isStatsPanelVisible = false;
  29. void Awake()
  30. {
  31. mainCamera = Camera.main;
  32. targetingLine = GetComponent<TargetingLine>();
  33. if (targetingLine == null)
  34. {
  35. targetingLine = gameObject.AddComponent<TargetingLine>();
  36. }
  37. }
  38. void Start()
  39. {
  40. InitializePlayerCharacters();
  41. SetupCharacterStatsUI();
  42. // Wait a moment for battle setup to complete, then refresh stats display
  43. StartCoroutine(RefreshStatsAfterSetup());
  44. }
  45. private IEnumerator RefreshStatsAfterSetup()
  46. {
  47. // Wait for battle setup to complete
  48. yield return new WaitForSeconds(0.5f);
  49. // Refresh stats display if panel is visible and character is selected
  50. if (isStatsPanelVisible && selectedCharacter != null)
  51. {
  52. Debug.Log("🔄 Refreshing stats after battle setup completion");
  53. UpdateCharacterStatsDisplay(selectedCharacter);
  54. }
  55. }
  56. void Update()
  57. {
  58. HandleInput();
  59. }
  60. private void InitializePlayerCharacters()
  61. {
  62. // Get player characters from GameManager if available
  63. if (GameManager.Instance != null)
  64. {
  65. playerCharacters.Clear();
  66. foreach (GameObject playerGO in GameManager.Instance.playerCharacters)
  67. {
  68. Character character = playerGO.GetComponent<Character>();
  69. if (character != null)
  70. {
  71. playerCharacters.Add(character);
  72. }
  73. }
  74. }
  75. else
  76. {
  77. // Fallback: find all characters (for testing without GameManager)
  78. Character[] characters = FindObjectsByType<Character>(FindObjectsSortMode.None);
  79. playerCharacters.AddRange(characters);
  80. }
  81. foreach (Character character in playerCharacters)
  82. {
  83. character.actionData.Reset();
  84. character.SetVisualState(ActionDecisionState.NoAction);
  85. }
  86. }
  87. private void HandleInput()
  88. {
  89. if (!isEnabled) return;
  90. // Toggle character stats panel with 'C' key
  91. if (Input.GetKeyDown(KeyCode.C))
  92. {
  93. ToggleStatsPanel();
  94. }
  95. if (Input.GetMouseButtonDown(0)) // Left click
  96. {
  97. HandleLeftClickDown();
  98. }
  99. else if (Input.GetMouseButton(0) && isDragging) // Left drag
  100. {
  101. HandleLeftDrag();
  102. }
  103. else if (Input.GetMouseButtonUp(0)) // Left release
  104. {
  105. HandleLeftClickUp();
  106. }
  107. else if (Input.GetMouseButtonDown(1)) // Right click
  108. {
  109. HandleRightClick();
  110. }
  111. }
  112. /// <summary>
  113. /// Check if we're currently in move targeting mode (move action selected but not yet targeted)
  114. /// </summary>
  115. private bool IsInMoveTargetingMode()
  116. {
  117. if (selectedCharacter == null) return false;
  118. var actionData = selectedCharacter.GetEnhancedActionData<EnhancedCharacterActionData>();
  119. return actionData != null &&
  120. actionData.actionType == BattleActionType.Move &&
  121. actionData.state == ActionDecisionState.NoAction;
  122. }
  123. private void HandleLeftClickDown()
  124. {
  125. Vector3 mouseWorldPosition = GetMouseWorldPosition();
  126. Character clickedCharacter = GetPlayerCharacterAtPosition(mouseWorldPosition);
  127. if (clickedCharacter != null)
  128. {
  129. // Start drag mode from character - this allows both move and attack by dragging
  130. selectedCharacter = clickedCharacter;
  131. isDragging = true;
  132. dragStartPosition = clickedCharacter.transform.position;
  133. Debug.Log($"🖱️ Starting drag from character {clickedCharacter.CharacterName}");
  134. // Update character stats display if panel is visible
  135. UpdateCharacterStatsDisplay(clickedCharacter);
  136. // Clear any existing action lines before starting new targeting
  137. ClearActionLineForCharacter(clickedCharacter);
  138. // Start targeting line for drag operations
  139. targetingLine.StartTargeting(dragStartPosition);
  140. // CinemachineCameraController.Instance.FocusOnCharacter(clickedCharacter.transform);
  141. }
  142. else
  143. {
  144. // Clicked empty space - start drag if we have a selected character and are in move mode
  145. if (selectedCharacter != null && IsInMoveTargetingMode())
  146. {
  147. isDragging = true;
  148. targetingLine.StartTargeting(selectedCharacter.transform.position);
  149. Debug.Log($"🎯 Started move targeting for {selectedCharacter.CharacterName}");
  150. }
  151. }
  152. }
  153. private void HandleLeftDrag()
  154. {
  155. if (selectedCharacter == null) return;
  156. Vector3 mouseWorldPos = GetMouseWorldPosition();
  157. GameObject enemyAtMouse = GetEnemyAtPosition(mouseWorldPos);
  158. if (enemyAtMouse != null)
  159. {
  160. // Snap to enemy
  161. Vector3 enemyPos = enemyAtMouse.transform.position;
  162. targetingLine.UpdateTargeting(dragStartPosition, enemyPos, true);
  163. }
  164. else
  165. {
  166. // Follow mouse
  167. targetingLine.UpdateTargeting(dragStartPosition, mouseWorldPos, false);
  168. }
  169. }
  170. private void HandleLeftClickUp()
  171. {
  172. Vector3 mouseWorldPos = GetMouseWorldPosition();
  173. GameObject enemyAtMouse = GetEnemyAtPosition(mouseWorldPos);
  174. // Handle different cases based on current state
  175. if (isDragging && selectedCharacter != null)
  176. {
  177. // We're in active targeting mode
  178. if (enemyAtMouse != null)
  179. {
  180. // Attack target selected
  181. selectedCharacter.actionData.SetAttackTarget(enemyAtMouse);
  182. UpdateEnhancedActionData(selectedCharacter, BattleActionType.Attack, enemyAtMouse, mouseWorldPos);
  183. selectedCharacter.SetVisualState(selectedCharacter.actionData.state);
  184. Debug.Log($"⚔️ Attack target set for {selectedCharacter.CharacterName}");
  185. }
  186. else
  187. {
  188. // Check if user actually dragged (minimum distance threshold) OR if we're in action wheel move mode
  189. float dragDistance = Vector3.Distance(dragStartPosition, mouseWorldPos);
  190. const float minDragDistance = 0.5f; // Minimum distance to consider it a drag vs click
  191. bool isActionWheelMove = IsInMoveTargetingMode();
  192. bool isDragMove = dragDistance > minDragDistance;
  193. if (isActionWheelMove || isDragMove)
  194. {
  195. // Move target selected
  196. selectedCharacter.actionData.SetMoveTarget(mouseWorldPos);
  197. UpdateEnhancedActionData(selectedCharacter, BattleActionType.Move, null, mouseWorldPos);
  198. selectedCharacter.SetVisualState(selectedCharacter.actionData.state);
  199. if (isActionWheelMove)
  200. {
  201. Debug.Log($"👟 Move target set for {selectedCharacter.CharacterName} (action wheel mode)");
  202. }
  203. else
  204. {
  205. Debug.Log($"👟 Move target set for {selectedCharacter.CharacterName} (dragged {dragDistance:F1} units)");
  206. }
  207. }
  208. else
  209. {
  210. // User just clicked on character without dragging - don't set any action
  211. Debug.Log($"🖱️ Character {selectedCharacter.CharacterName} selected (clicked without dragging)");
  212. }
  213. }
  214. // Notify BattleActionIntegration that targeting is complete
  215. var integration = FindFirstObjectByType<BattleActionIntegration>();
  216. if (integration != null)
  217. {
  218. integration.OnTargetingComplete(selectedCharacter);
  219. }
  220. // Cleanup targeting
  221. targetingLine.StopTargeting();
  222. isDragging = false;
  223. selectedCharacter = null; // Clear selection after action is set
  224. }
  225. else if (selectedCharacter != null)
  226. {
  227. // Just a character selection click - notify integration but don't set actions
  228. var integration = FindFirstObjectByType<BattleActionIntegration>();
  229. if (integration != null)
  230. {
  231. integration.OnTargetingComplete(selectedCharacter);
  232. }
  233. Debug.Log($"👆 Character {selectedCharacter.CharacterName} selected (no action set)");
  234. // Don't clear selectedCharacter here - let integration handle it
  235. }
  236. // Cleanup
  237. targetingLine.StopTargeting();
  238. selectedCharacter = null;
  239. isDragging = false;
  240. }
  241. private void HandleRightClick()
  242. {
  243. if (isDragging && selectedCharacter != null)
  244. {
  245. // Cancel current action
  246. selectedCharacter.actionData.Reset();
  247. selectedCharacter.SetVisualState(ActionDecisionState.NoAction);
  248. targetingLine.StopTargeting();
  249. selectedCharacter = null;
  250. isDragging = false;
  251. }
  252. else
  253. {
  254. // Right click on character to reset their action
  255. Vector3 mouseWorldPos = GetMouseWorldPosition();
  256. Character clickedCharacter = GetPlayerCharacterAtPosition(mouseWorldPos);
  257. if (clickedCharacter != null)
  258. {
  259. clickedCharacter.actionData.Reset();
  260. clickedCharacter.SetVisualState(ActionDecisionState.NoAction);
  261. }
  262. }
  263. }
  264. private Vector3 GetMouseWorldPosition()
  265. {
  266. Ray ray = mainCamera.ScreenPointToRay(Input.mousePosition);
  267. RaycastHit hit;
  268. // Raycast against a ground plane or existing colliders
  269. if (Physics.Raycast(ray, out hit, 200f))
  270. {
  271. return hit.point;
  272. }
  273. // Fallback: project onto a horizontal plane at y=0
  274. Plane groundPlane = new Plane(Vector3.up, Vector3.zero);
  275. if (groundPlane.Raycast(ray, out float distance))
  276. {
  277. Vector3 position = ray.GetPoint(distance);
  278. return position;
  279. }
  280. return Vector3.zero;
  281. }
  282. private Character GetPlayerCharacterAtPosition(Vector3 worldPosition)
  283. {
  284. Ray ray = mainCamera.ScreenPointToRay(Input.mousePosition);
  285. RaycastHit hit;
  286. if (Physics.Raycast(ray, out hit, 200f, playerLayerMask))
  287. {
  288. return hit.collider.GetComponent<Character>();
  289. }
  290. // Try raycast without layer mask to see what we're hitting
  291. if (Physics.Raycast(ray, out hit, 200f))
  292. {
  293. }
  294. else
  295. {
  296. }
  297. return null;
  298. }
  299. private GameObject GetEnemyAtPosition(Vector3 worldPosition)
  300. {
  301. Ray ray = mainCamera.ScreenPointToRay(Input.mousePosition);
  302. RaycastHit hit;
  303. if (Physics.Raycast(ray, out hit, 200f, enemyLayerMask))
  304. {
  305. Debug.Log($"🎯 Enemy detected: {hit.collider.gameObject.name} on layer {hit.collider.gameObject.layer}");
  306. return hit.collider.gameObject;
  307. }
  308. // Debug: Check what we're hitting without layer mask
  309. if (Physics.Raycast(ray, out hit, 200f))
  310. {
  311. Debug.Log($"🔍 Hit object: {hit.collider.gameObject.name} on layer {hit.collider.gameObject.layer} (not enemy layer)");
  312. }
  313. else
  314. {
  315. Debug.Log($"❌ No raycast hit detected at mouse position");
  316. }
  317. return null;
  318. }
  319. public bool AllCharactersHaveActions()
  320. {
  321. foreach (var character in playerCharacters)
  322. {
  323. // Characters need actions if they have no action set
  324. if (character.actionData.state == ActionDecisionState.NoAction)
  325. return false;
  326. }
  327. return true;
  328. }
  329. public void ResetAllCharacterActions()
  330. {
  331. foreach (var character in playerCharacters)
  332. {
  333. character.actionData.Reset();
  334. character.SetVisualState(ActionDecisionState.NoAction);
  335. }
  336. }
  337. public void UpdateVisualStates()
  338. {
  339. foreach (var character in playerCharacters)
  340. {
  341. // Update visual states based on current action status
  342. if (character.actionData.state == ActionDecisionState.NoAction)
  343. {
  344. character.SetVisualState(ActionDecisionState.NoAction); // Pink
  345. }
  346. else if (character.HasActionSelected())
  347. {
  348. character.SetVisualState(ActionDecisionState.ActionSelected); // Green
  349. }
  350. }
  351. }
  352. public void RefreshPlayerCharacters()
  353. {
  354. InitializePlayerCharacters();
  355. }
  356. public void SetEnabled(bool enabled)
  357. {
  358. isEnabled = enabled;
  359. if (!enabled)
  360. {
  361. if (isDragging)
  362. {
  363. // Cancel any current dragging operation
  364. targetingLine.StopTargeting();
  365. selectedCharacter = null;
  366. isDragging = false;
  367. }
  368. // Don't automatically hide action lines when disabling - let caller decide
  369. }
  370. }
  371. /// <summary>
  372. /// Check if PlayerDecisionController is currently in targeting/dragging mode
  373. /// </summary>
  374. public bool IsInTargetingMode => isDragging && selectedCharacter != null;
  375. /// <summary>
  376. /// Get the currently selected character (for action wheel integration)
  377. /// </summary>
  378. public Character GetSelectedCharacter() => selectedCharacter;
  379. /// <summary>
  380. /// Reset the controller state (for debugging/recovery)
  381. /// </summary>
  382. public void ResetState()
  383. {
  384. Debug.Log("🔄 PlayerDecisionController: Resetting state");
  385. if (isDragging && targetingLine != null)
  386. {
  387. targetingLine.StopTargeting();
  388. }
  389. selectedCharacter = null;
  390. isDragging = false;
  391. isEnabled = true;
  392. Debug.Log("✅ PlayerDecisionController: State reset complete");
  393. }
  394. public void ShowActiveActionLines()
  395. {
  396. HideActiveActionLines(); // Clear any existing lines first
  397. foreach (var character in playerCharacters)
  398. {
  399. if (character.HasActionSelected() && !character.IsActionComplete())
  400. {
  401. GameObject lineObject = new GameObject($"ActionLine_{character.name}");
  402. TargetingLine line = lineObject.AddComponent<TargetingLine>();
  403. activeActionLines.Add(line);
  404. Vector3 startPos = character.transform.position + Vector3.up * 0.5f;
  405. if (character.actionData.targetEnemy != null)
  406. {
  407. Vector3 endPos = character.actionData.targetEnemy.transform.position + Vector3.up * 0.5f;
  408. line.StartTargeting(startPos);
  409. line.UpdateTargeting(startPos, endPos, true); // true for enemy target (red line)
  410. }
  411. else if (character.actionData.targetPosition != Vector3.zero)
  412. {
  413. line.StartTargeting(startPos);
  414. line.UpdateTargeting(startPos, character.actionData.targetPosition, false); // false for movement (blue line)
  415. }
  416. }
  417. }
  418. }
  419. public void HideActiveActionLines()
  420. {
  421. foreach (var line in activeActionLines)
  422. {
  423. if (line != null)
  424. {
  425. line.StopTargeting();
  426. Destroy(line.gameObject);
  427. }
  428. }
  429. activeActionLines.Clear();
  430. }
  431. public void ClearActionLineForCharacter(Character character)
  432. {
  433. for (int i = activeActionLines.Count - 1; i >= 0; i--)
  434. {
  435. var line = activeActionLines[i];
  436. if (line != null && line.gameObject.name == $"ActionLine_{character.name}")
  437. {
  438. line.StopTargeting();
  439. Destroy(line.gameObject);
  440. activeActionLines.RemoveAt(i);
  441. break;
  442. }
  443. }
  444. }
  445. private void UpdateEnhancedActionData(Character character, BattleActionType actionType, GameObject targetEnemy, Vector3 targetPosition)
  446. {
  447. // Check if character has enhanced action data
  448. var enhancedData = character.GetEnhancedActionData<EnhancedCharacterActionData>();
  449. if (enhancedData != null)
  450. {
  451. Debug.Log($"🔄 Updating enhanced action data for {character.CharacterName}: {actionType}");
  452. // Update the enhanced action data to match the targeting result
  453. enhancedData.actionType = actionType;
  454. enhancedData.state = ActionDecisionState.ActionSelected;
  455. if (actionType == BattleActionType.Attack && targetEnemy != null)
  456. {
  457. enhancedData.targetEnemy = targetEnemy;
  458. enhancedData.targetPosition = Vector3.zero;
  459. Debug.Log($"🗡️ Enhanced data updated for attack on {targetEnemy.name}");
  460. }
  461. else if (actionType == BattleActionType.Move)
  462. {
  463. enhancedData.targetPosition = targetPosition;
  464. enhancedData.targetEnemy = null;
  465. Debug.Log($"👟 Enhanced data updated for move to {targetPosition}");
  466. }
  467. }
  468. else
  469. {
  470. Debug.Log($"ℹ️ No enhanced action data found for {character.CharacterName}");
  471. }
  472. }
  473. /// <summary>
  474. /// Manually start targeting mode for a specific character and action type
  475. /// Called by the action wheel system
  476. /// </summary>
  477. public void StartTargetingForCharacter(Character character, BattleActionType actionType)
  478. {
  479. Debug.Log($"🎯 StartTargetingForCharacter called: {character.CharacterName} -> {actionType}");
  480. selectedCharacter = character;
  481. dragStartPosition = character.transform.position;
  482. // Update character stats display if panel is visible
  483. if (isStatsPanelVisible && selectedCharacter != null)
  484. {
  485. UpdateCharacterStatsDisplay(selectedCharacter);
  486. }
  487. // Store the action type for later reference
  488. var enhancedData = character.GetEnhancedActionData<EnhancedCharacterActionData>();
  489. if (enhancedData == null)
  490. {
  491. enhancedData = new EnhancedCharacterActionData();
  492. character.SetEnhancedActionData(enhancedData);
  493. }
  494. enhancedData.actionType = actionType;
  495. enhancedData.state = ActionDecisionState.NoAction; // Waiting for target
  496. // Start targeting mode - user needs to click to set target
  497. isDragging = true;
  498. // Clear any existing action lines before starting new targeting
  499. ClearActionLineForCharacter(character);
  500. // Start the targeting line
  501. targetingLine.StartTargeting(dragStartPosition);
  502. Debug.Log($"✅ Targeting mode active for {character.CharacterName} - {actionType}");
  503. }
  504. /// <summary>
  505. /// Cancel current targeting operation
  506. /// </summary>
  507. public void CancelTargeting()
  508. {
  509. if (isDragging && selectedCharacter != null)
  510. {
  511. selectedCharacter.actionData.Reset();
  512. selectedCharacter.SetVisualState(ActionDecisionState.NoAction);
  513. targetingLine.StopTargeting();
  514. selectedCharacter = null;
  515. isDragging = false;
  516. Debug.Log("❌ Targeting cancelled");
  517. }
  518. }
  519. #region Character Stats UI
  520. private void SetupCharacterStatsUI()
  521. {
  522. // Get or create UIDocument component
  523. uiDocument = GetComponent<UIDocument>();
  524. if (uiDocument == null)
  525. {
  526. uiDocument = gameObject.AddComponent<UIDocument>();
  527. }
  528. // Set PanelSettings to mainSettings
  529. if (uiDocument.panelSettings == null)
  530. {
  531. SetupPanelSettings(uiDocument);
  532. }
  533. // Try to load UXML asset
  534. if (characterStatsUXML != null)
  535. {
  536. uiDocument.visualTreeAsset = characterStatsUXML;
  537. Debug.Log("✓ Character Stats UI: UXML asset applied");
  538. }
  539. // Check if we have content from UXML or need to create programmatically
  540. bool hasValidContent = uiDocument.rootVisualElement != null &&
  541. uiDocument.rootVisualElement.childCount > 0 &&
  542. uiDocument.rootVisualElement.Q("character-stats-container") != null;
  543. // Create UI structure programmatically only if UXML not found or invalid
  544. if (characterStatsUXML == null || !hasValidContent)
  545. {
  546. CreateCharacterStatsPanelProgrammatically();
  547. }
  548. // Get the stats panel
  549. statsPanel = uiDocument.rootVisualElement.Q("character-stats-container");
  550. if (statsPanel == null)
  551. {
  552. Debug.LogWarning("Stats panel not found, creating fallback");
  553. CreateCharacterStatsPanelProgrammatically();
  554. statsPanel = uiDocument.rootVisualElement.Q("character-stats-container");
  555. }
  556. // Apply stylesheet if available and not already applied
  557. if (characterStatsUSS != null && !uiDocument.rootVisualElement.styleSheets.Contains(characterStatsUSS))
  558. {
  559. uiDocument.rootVisualElement.styleSheets.Add(characterStatsUSS);
  560. Debug.Log("✓ Character Stats UI: USS stylesheet applied");
  561. }
  562. // Hide panel initially
  563. if (statsPanel != null)
  564. {
  565. statsPanel.style.display = DisplayStyle.None;
  566. }
  567. Debug.Log("Character Stats UI setup complete");
  568. }
  569. private void CreateCharacterStatsPanelProgrammatically()
  570. {
  571. var root = uiDocument.rootVisualElement;
  572. root.Clear();
  573. // Create main container
  574. var container = new VisualElement();
  575. container.name = "character-stats-container";
  576. container.style.position = Position.Absolute;
  577. container.style.top = 10;
  578. container.style.right = 10;
  579. container.style.width = 280;
  580. container.style.backgroundColor = new Color(0, 0, 0, 0.8f);
  581. container.style.borderTopColor = container.style.borderBottomColor =
  582. container.style.borderLeftColor = container.style.borderRightColor = new Color(1, 1, 1, 0.3f);
  583. container.style.borderTopWidth = container.style.borderBottomWidth =
  584. container.style.borderLeftWidth = container.style.borderRightWidth = 1;
  585. container.style.borderTopLeftRadius = container.style.borderTopRightRadius =
  586. container.style.borderBottomLeftRadius = container.style.borderBottomRightRadius = 5;
  587. container.style.paddingTop = container.style.paddingBottom =
  588. container.style.paddingLeft = container.style.paddingRight = 10;
  589. container.style.color = Color.white;
  590. // Title
  591. var title = new Label("Character Stats");
  592. title.name = "title-label";
  593. title.style.fontSize = 16;
  594. title.style.color = new Color(1f, 0.84f, 0f); // Gold
  595. title.style.marginBottom = 10;
  596. title.style.unityFontStyleAndWeight = FontStyle.Bold;
  597. title.style.unityTextAlign = TextAnchor.MiddleCenter;
  598. container.Add(title);
  599. // Character info section
  600. var charInfo = new VisualElement();
  601. charInfo.name = "character-info";
  602. var charName = new Label("Character Name");
  603. charName.name = "character-name";
  604. charName.style.fontSize = 14;
  605. charName.style.color = new Color(0.56f, 0.93f, 0.56f); // Light green
  606. charName.style.unityFontStyleAndWeight = FontStyle.Bold;
  607. var charLevel = new Label("Level 1");
  608. charLevel.name = "character-level";
  609. charLevel.style.fontSize = 11;
  610. charLevel.style.color = new Color(0.87f, 0.63f, 0.87f); // Plum
  611. charLevel.style.marginBottom = 5;
  612. charInfo.Add(charName);
  613. charInfo.Add(charLevel);
  614. container.Add(charInfo);
  615. // Attributes section
  616. var attrSection = CreateStatsSection("Attributes", new string[]
  617. {
  618. "str-stat:STR: 10",
  619. "dex-stat:DEX: 10",
  620. "con-stat:CON: 10",
  621. "wis-stat:WIS: 10",
  622. "per-stat:PER: 10"
  623. });
  624. container.Add(attrSection);
  625. // Combat section
  626. var combatSection = CreateStatsSection("Combat Stats", new string[]
  627. {
  628. "health-stat:Health: 100/100",
  629. "attack-stat:Attack: 15",
  630. "ac-stat:AC: 12",
  631. "movement-stat:Movement: 10"
  632. });
  633. container.Add(combatSection);
  634. // Debug section
  635. var debugSection = CreateStatsSection("Debug Info", new string[]
  636. {
  637. "agent-speed-stat:Agent Speed: 3.5"
  638. });
  639. container.Add(debugSection);
  640. // Controls
  641. var controls = new VisualElement();
  642. var helpText = new Label("Press 'C' to toggle stats panel");
  643. helpText.style.fontSize = 10;
  644. helpText.style.color = new Color(0.8f, 0.8f, 0.8f);
  645. helpText.style.unityTextAlign = TextAnchor.MiddleCenter;
  646. helpText.style.marginTop = 5;
  647. controls.Add(helpText);
  648. container.Add(controls);
  649. root.Add(container);
  650. }
  651. private VisualElement CreateStatsSection(string title, string[] stats)
  652. {
  653. var section = new VisualElement();
  654. section.style.marginBottom = 8;
  655. var header = new Label(title);
  656. header.style.fontSize = 12;
  657. header.style.color = new Color(0.53f, 0.81f, 0.92f); // Sky blue
  658. header.style.marginBottom = 4;
  659. header.style.unityFontStyleAndWeight = FontStyle.Bold;
  660. section.Add(header);
  661. foreach (string stat in stats)
  662. {
  663. string[] parts = stat.Split(':');
  664. var label = new Label(parts.Length > 1 ? parts[1] : stat);
  665. if (parts.Length > 1) label.name = parts[0];
  666. label.style.fontSize = 11;
  667. label.style.color = Color.white;
  668. label.style.marginLeft = 5;
  669. label.style.marginBottom = 2;
  670. section.Add(label);
  671. }
  672. return section;
  673. }
  674. // New method to force refresh stats display
  675. public void RefreshCharacterStatsDisplay()
  676. {
  677. if (selectedCharacter != null && isStatsPanelVisible)
  678. {
  679. Debug.Log("🔄 Force refreshing character stats display");
  680. UpdateCharacterStatsDisplay(selectedCharacter);
  681. }
  682. }
  683. private void ToggleStatsPanel()
  684. {
  685. if (statsPanel == null) return;
  686. isStatsPanelVisible = !isStatsPanelVisible;
  687. statsPanel.style.display = isStatsPanelVisible ? DisplayStyle.Flex : DisplayStyle.None;
  688. Debug.Log($"📊 Character stats panel: {(isStatsPanelVisible ? "Shown" : "Hidden")}");
  689. // Update display when showing panel
  690. if (isStatsPanelVisible && selectedCharacter != null)
  691. {
  692. UpdateCharacterStatsDisplay(selectedCharacter);
  693. }
  694. }
  695. private void UpdateCharacterStatsDisplay(Character character)
  696. {
  697. if (statsPanel == null || !isStatsPanelVisible) return;
  698. Debug.Log($"🔄 Updating stats display for {character.CharacterName}");
  699. Debug.Log($" Character component reference: {character.GetInstanceID()}");
  700. Debug.Log($" Character object name: {character.gameObject.name}");
  701. Debug.Log($" Character stats: STR:{character.Strength} DEX:{character.Dexterity} CON:{character.Constitution} WIS:{character.Wisdom} PER:{character.Perception}");
  702. Debug.Log($" Movement Speed: {character.MovementSpeed}");
  703. // Update character info
  704. var nameLabel = statsPanel.Q<Label>("character-name");
  705. var levelLabel = statsPanel.Q<Label>("character-level");
  706. if (nameLabel != null) nameLabel.text = character.CharacterName;
  707. if (levelLabel != null) levelLabel.text = $"Level {character.Level}";
  708. // Update attributes
  709. UpdateStatLabel("str-stat", $"STR: {character.Strength}");
  710. UpdateStatLabel("dex-stat", $"DEX: {character.Dexterity}");
  711. UpdateStatLabel("con-stat", $"CON: {character.Constitution}");
  712. UpdateStatLabel("wis-stat", $"WIS: {character.Wisdom}");
  713. UpdateStatLabel("per-stat", $"PER: {character.Perception}");
  714. // Update combat stats
  715. UpdateStatLabel("health-stat", $"Health: {character.CurrentHealth}/{character.MaxHealth}");
  716. UpdateStatLabel("attack-stat", $"Attack: {character.Attack}");
  717. UpdateStatLabel("ac-stat", $"AC: {character.ArmorClass}");
  718. UpdateStatLabel("movement-stat", $"Movement: {character.MovementSpeed}");
  719. // Update debug info - get NavMeshAgent speed
  720. var agent = character.GetComponent<NavMeshAgent>();
  721. float agentSpeed = agent != null ? agent.speed : 0f;
  722. UpdateStatLabel("agent-speed-stat", $"Agent Speed: {agentSpeed:F1}");
  723. Debug.Log($"📊 Updated stats display for {character.CharacterName}");
  724. }
  725. private void UpdateStatLabel(string elementName, string text)
  726. {
  727. var label = statsPanel.Q<Label>(elementName);
  728. if (label != null) label.text = text;
  729. }
  730. /// <summary>
  731. /// Setup PanelSettings with multiple fallback methods
  732. /// </summary>
  733. private void SetupPanelSettings(UIDocument uiDoc)
  734. {
  735. // Try Resources first (original approach)
  736. var mainSettings = Resources.Load<PanelSettings>("MainSettings");
  737. if (mainSettings != null)
  738. {
  739. uiDoc.panelSettings = mainSettings;
  740. Debug.Log("✓ Character Stats UI: Applied MainSettings from Resources");
  741. return;
  742. }
  743. // Try to find and reuse existing panel settings from other UI
  744. var existingUI = GameObject.Find("TravelUI")?.GetComponent<UIDocument>();
  745. if (existingUI?.panelSettings != null)
  746. {
  747. uiDoc.panelSettings = existingUI.panelSettings;
  748. Debug.Log("✓ Character Stats UI: Panel Settings assigned from TravelUI");
  749. return;
  750. }
  751. // Try to find from MainTeamSelectScript
  752. var mainTeamSelect = FindFirstObjectByType<MainTeamSelectScript>();
  753. if (mainTeamSelect != null)
  754. {
  755. var mainUIDoc = mainTeamSelect.GetComponent<UIDocument>();
  756. if (mainUIDoc?.panelSettings != null)
  757. {
  758. uiDoc.panelSettings = mainUIDoc.panelSettings;
  759. Debug.Log("✓ Character Stats UI: Panel Settings assigned from MainTeamSelectScript");
  760. return;
  761. }
  762. }
  763. // Try to find any UIDocument in the scene with PanelSettings
  764. var allUIDocuments = FindObjectsByType<UIDocument>(FindObjectsSortMode.None);
  765. foreach (var doc in allUIDocuments)
  766. {
  767. if (doc.panelSettings != null && doc != uiDoc)
  768. {
  769. uiDoc.panelSettings = doc.panelSettings;
  770. Debug.Log($"✓ Character Stats UI: Panel Settings assigned from {doc.gameObject.name}");
  771. return;
  772. }
  773. }
  774. #if UNITY_EDITOR
  775. // If still no panel settings, try to find any PanelSettings asset using Editor API
  776. var panelSettingsGuids = UnityEditor.AssetDatabase.FindAssets("t:PanelSettings");
  777. if (panelSettingsGuids.Length > 0)
  778. {
  779. var path = UnityEditor.AssetDatabase.GUIDToAssetPath(panelSettingsGuids[0]);
  780. var settings = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.UIElements.PanelSettings>(path);
  781. if (settings != null)
  782. {
  783. uiDoc.panelSettings = settings;
  784. Debug.Log($"✓ Character Stats UI: Panel Settings auto-assigned: {settings.name}");
  785. return;
  786. }
  787. }
  788. #endif
  789. Debug.LogWarning("⚠️ Could not assign Panel Settings to Character Stats UI. You may need to assign it manually.");
  790. }
  791. #endregion
  792. }