PostBattleLootSystem.cs 54 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469
  1. using System.Collections.Generic;
  2. using UnityEngine;
  3. using System.Linq;
  4. using UnityEngine.UIElements;
  5. #if UNITY_EDITOR
  6. using UnityEditor;
  7. #endif
  8. /// <summary>
  9. /// Handles post-battle looting when all enemies are defeated
  10. /// Allows players to loot enemy corpses and manage inventory weight
  11. /// </summary>
  12. public class PostBattleLootSystem : MonoBehaviour
  13. {
  14. [Header("UI Toolkit References")]
  15. public UIDocument lootUIDocument;
  16. public VisualTreeAsset lootScreenTemplate;
  17. public PanelSettings panelSettings;
  18. [Header("Currency Settings")]
  19. public int baseGoldReward = 5;
  20. public int baseSilverReward = 15;
  21. public int baseCopperReward = 25;
  22. [Header("Debug Settings")]
  23. public bool showDebugLogs = true;
  24. [Header("Item Distribution Settings")]
  25. public bool enablePlayerItemSelection = true; // Allow manual item distribution
  26. public bool autoDistributeItems = false; // If true, items are distributed automatically
  27. // Events
  28. public event System.Action OnLootingComplete;
  29. private List<LootableEnemy> lootableEnemies = new List<LootableEnemy>();
  30. private bool isLootingActive = false;
  31. private VisualElement rootElement;
  32. private bool takeAllPressed = false;
  33. private Dictionary<string, int> selectedPlayerForItem = new Dictionary<string, int>(); // itemName -> playerIndex
  34. /// <summary>
  35. /// Gets whether the looting process is currently active
  36. /// </summary>
  37. public bool IsLootingActive => isLootingActive;
  38. [System.Serializable]
  39. public class LootableEnemy
  40. {
  41. public string enemyName;
  42. public List<string> dropItems = new List<string>();
  43. public int goldReward;
  44. public int silverReward;
  45. public int copperReward;
  46. public bool hasBeenLooted = false;
  47. public Vector3 corpsePosition;
  48. public LootableEnemy(string name, Vector3 position)
  49. {
  50. enemyName = name;
  51. corpsePosition = position;
  52. }
  53. }
  54. [System.Serializable]
  55. public class LootableItem
  56. {
  57. public string itemName;
  58. public string description;
  59. public int weight = 1;
  60. public int value = 1; // In copper
  61. public bool isSelected = false;
  62. public LootableItem(string name, string desc, int itemWeight = 1, int itemValue = 1)
  63. {
  64. itemName = name;
  65. description = desc;
  66. weight = itemWeight;
  67. value = itemValue;
  68. }
  69. }
  70. /// <summary>
  71. /// Initialize looting system with defeated enemies
  72. /// </summary>
  73. public void InitializeLootSystem(List<GameObject> defeatedEnemies)
  74. {
  75. lootableEnemies.Clear();
  76. foreach (var enemy in defeatedEnemies)
  77. {
  78. if (enemy == null) continue;
  79. Character enemyCharacter = enemy.GetComponent<Character>();
  80. if (enemyCharacter == null || !enemyCharacter.IsDead) continue;
  81. var lootableEnemy = new LootableEnemy(enemyCharacter.CharacterName, enemy.transform.position);
  82. // Generate loot based on enemy type and random factors
  83. GenerateEnemyLoot(lootableEnemy, enemyCharacter);
  84. lootableEnemies.Add(lootableEnemy);
  85. }
  86. if (showDebugLogs)
  87. Debug.Log($"💰 Initialized loot system with {lootableEnemies.Count} lootable enemies");
  88. }
  89. /// <summary>
  90. /// Show the loot UI and start looting phase
  91. /// </summary>
  92. public void StartLooting()
  93. {
  94. if (lootableEnemies.Count == 0)
  95. {
  96. if (showDebugLogs)
  97. Debug.Log("💰 No enemies to loot, ending battle");
  98. OnLootingComplete?.Invoke();
  99. return;
  100. }
  101. isLootingActive = true;
  102. CreateAndShowLootUI();
  103. if (showDebugLogs)
  104. Debug.Log("💰 Started post-battle looting phase");
  105. }
  106. /// <summary>
  107. /// Create and show the UI Toolkit loot interface
  108. /// </summary>
  109. private void CreateAndShowLootUI()
  110. {
  111. // Find or create UI Document
  112. if (lootUIDocument == null)
  113. {
  114. GameObject uiGO = new GameObject("LootUIDocument");
  115. uiGO.transform.SetParent(transform);
  116. lootUIDocument = uiGO.AddComponent<UIDocument>();
  117. }
  118. // Set up the UI Document with proper references
  119. SetupLootUIDocument();
  120. // Get the root element
  121. if (lootUIDocument.visualTreeAsset != null)
  122. {
  123. rootElement = lootUIDocument.rootVisualElement;
  124. // Show the overlay
  125. var overlay = rootElement.Q<VisualElement>("LootScreenOverlay");
  126. if (overlay != null)
  127. {
  128. overlay.style.display = DisplayStyle.Flex;
  129. }
  130. // Populate the UI with loot data
  131. PopulateLootUI();
  132. // Set up button callbacks
  133. SetupUICallbacks();
  134. }
  135. else
  136. {
  137. // Fallback - create basic UI if template loading failed
  138. Debug.LogWarning("PostBattleLootScreen.uxml not found. Creating basic UI.");
  139. CreateBasicLootUI();
  140. }
  141. }
  142. /// <summary>
  143. /// Set up the UIDocument with proper UXML template and panel settings
  144. /// </summary>
  145. private void SetupLootUIDocument()
  146. {
  147. if (lootUIDocument == null) return;
  148. // Load the UXML template if not assigned
  149. if (lootScreenTemplate == null)
  150. {
  151. lootScreenTemplate = Resources.Load<VisualTreeAsset>("UI/BattleSceneUI/PostBattleLootScreen");
  152. if (lootScreenTemplate == null)
  153. {
  154. #if UNITY_EDITOR
  155. // Try alternative path in editor
  156. lootScreenTemplate = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>("Assets/UI/BattleSceneUI/PostBattleLootScreen.uxml");
  157. #endif
  158. }
  159. }
  160. // Load panel settings if not assigned
  161. if (panelSettings == null)
  162. {
  163. panelSettings = Resources.Load<PanelSettings>("MainSettings");
  164. if (panelSettings == null)
  165. {
  166. // Try alternative panel settings
  167. panelSettings = Resources.Load<PanelSettings>("UI/TravelPanelSettings");
  168. if (panelSettings == null)
  169. {
  170. // Try to find any PanelSettings in the project
  171. var allPanelSettings = Resources.FindObjectsOfTypeAll<PanelSettings>();
  172. if (allPanelSettings.Length > 0)
  173. panelSettings = allPanelSettings[0];
  174. }
  175. }
  176. }
  177. // Configure the UIDocument
  178. lootUIDocument.visualTreeAsset = lootScreenTemplate;
  179. lootUIDocument.panelSettings = panelSettings;
  180. // Load and apply stylesheet
  181. var stylesheet = Resources.Load<StyleSheet>("UI/BattleSceneUI/PostBattleLootScreen");
  182. if (stylesheet == null)
  183. {
  184. #if UNITY_EDITOR
  185. stylesheet = AssetDatabase.LoadAssetAtPath<StyleSheet>("Assets/UI/BattleSceneUI/PostBattleLootScreen.uss");
  186. #endif
  187. }
  188. if (stylesheet != null && lootUIDocument.rootVisualElement != null)
  189. {
  190. lootUIDocument.rootVisualElement.styleSheets.Add(stylesheet);
  191. }
  192. if (lootScreenTemplate == null)
  193. {
  194. Debug.LogError("PostBattleLootSystem: Could not load PostBattleLootScreen.uxml template!");
  195. }
  196. if (panelSettings == null)
  197. {
  198. Debug.LogWarning("PostBattleLootSystem: No PanelSettings found. UI may not display correctly.");
  199. }
  200. }
  201. /// <summary>
  202. /// Create a basic fallback UI if the UXML template isn't available
  203. /// </summary>
  204. private void CreateBasicLootUI()
  205. {
  206. // Create a simple overlay using UI Toolkit elements
  207. rootElement = lootUIDocument.rootVisualElement;
  208. // Create overlay
  209. var overlay = new VisualElement();
  210. overlay.name = "LootScreenOverlay";
  211. overlay.style.position = Position.Absolute;
  212. overlay.style.top = 0;
  213. overlay.style.left = 0;
  214. overlay.style.right = 0;
  215. overlay.style.bottom = 0;
  216. overlay.style.backgroundColor = new Color(0, 0, 0, 0.8f);
  217. overlay.style.justifyContent = Justify.Center;
  218. overlay.style.alignItems = Align.Center;
  219. // Create background panel
  220. var background = new VisualElement();
  221. background.style.backgroundColor = new Color(0.1f, 0.1f, 0.2f, 0.95f);
  222. background.style.borderTopWidth = 2;
  223. background.style.borderBottomWidth = 2;
  224. background.style.borderLeftWidth = 2;
  225. background.style.borderRightWidth = 2;
  226. background.style.borderTopColor = new Color(0.7f, 0.5f, 0.2f);
  227. background.style.borderBottomColor = new Color(0.7f, 0.5f, 0.2f);
  228. background.style.borderLeftColor = new Color(0.7f, 0.5f, 0.2f);
  229. background.style.borderRightColor = new Color(0.7f, 0.5f, 0.2f);
  230. background.style.borderTopLeftRadius = 15;
  231. background.style.borderTopRightRadius = 15;
  232. background.style.borderBottomLeftRadius = 15;
  233. background.style.borderBottomRightRadius = 15;
  234. background.style.paddingTop = 30;
  235. background.style.paddingBottom = 30;
  236. background.style.paddingLeft = 30;
  237. background.style.paddingRight = 30;
  238. background.style.width = new Length(80, LengthUnit.Percent);
  239. background.style.maxWidth = 800;
  240. // Add title
  241. var title = new Label("🏆 VICTORY! 🏆");
  242. title.style.fontSize = 36;
  243. title.style.color = new Color(1f, 0.84f, 0f);
  244. title.style.unityTextAlign = TextAnchor.MiddleCenter;
  245. title.style.marginBottom = 20;
  246. // Add loot info
  247. var lootInfo = new Label();
  248. lootInfo.name = "LootInfo";
  249. lootInfo.style.fontSize = 14;
  250. lootInfo.style.color = Color.white;
  251. lootInfo.style.whiteSpace = WhiteSpace.Normal;
  252. lootInfo.style.marginBottom = 20;
  253. // Add continue button
  254. var continueButton = new Button(() => CompleteLootingProcess());
  255. continueButton.text = "Continue";
  256. continueButton.name = "ContinueButton";
  257. continueButton.style.fontSize = 16;
  258. continueButton.style.paddingTop = 10;
  259. continueButton.style.paddingBottom = 10;
  260. continueButton.style.paddingLeft = 20;
  261. continueButton.style.paddingRight = 20;
  262. // Assemble the UI
  263. background.Add(title);
  264. background.Add(lootInfo);
  265. background.Add(continueButton);
  266. overlay.Add(background);
  267. rootElement.Add(overlay);
  268. // Populate basic loot info
  269. PopulateBasicLootInfo(lootInfo);
  270. }
  271. /// <summary>
  272. /// Set up UI element callbacks for the loot interface
  273. /// </summary>
  274. private void SetupUICallbacks()
  275. {
  276. if (rootElement == null) return;
  277. // Set up Take All button
  278. var takeAllButton = rootElement.Q<Button>("TakeAllButton");
  279. if (takeAllButton != null)
  280. {
  281. takeAllButton.clicked += () =>
  282. {
  283. takeAllPressed = true;
  284. // Force auto-distribution and skip player selection
  285. bool originalAutoDistribute = autoDistributeItems;
  286. autoDistributeItems = true; // Temporarily force auto-distribution
  287. AutoLootAll();
  288. // Restore original setting
  289. autoDistributeItems = originalAutoDistribute;
  290. };
  291. }
  292. // Set up Continue button - this should return to map
  293. var continueButton = rootElement.Q<Button>("ContinueButton");
  294. if (continueButton != null)
  295. {
  296. continueButton.text = "Return to Map"; // Make it clear what this button does
  297. continueButton.clicked += () =>
  298. {
  299. if (!takeAllPressed)
  300. {
  301. AutoLootAll(); // Auto-loot if not already done
  302. }
  303. // CompleteLootingProcess is called by AutoLootAll, so scene transition will happen
  304. };
  305. }
  306. // Set up keyboard input for space key
  307. rootElement.RegisterCallback<KeyDownEvent>(OnKeyDown);
  308. // Make sure the root element can receive focus for keyboard events
  309. rootElement.focusable = true;
  310. rootElement.Focus();
  311. }
  312. /// <summary>
  313. /// Handle keyboard input for the loot UI
  314. /// </summary>
  315. private void OnKeyDown(KeyDownEvent evt)
  316. {
  317. if (evt.keyCode == KeyCode.Space)
  318. {
  319. if (!takeAllPressed)
  320. {
  321. takeAllPressed = true;
  322. // Force auto-distribution and skip player selection
  323. bool originalAutoDistribute = autoDistributeItems;
  324. autoDistributeItems = true; // Temporarily force auto-distribution
  325. AutoLootAll();
  326. // Restore original setting
  327. autoDistributeItems = originalAutoDistribute;
  328. }
  329. }
  330. }
  331. /// <summary>
  332. /// Populate basic loot information for fallback UI
  333. /// </summary>
  334. private void PopulateBasicLootInfo(Label lootInfo)
  335. {
  336. var lootText = "Collecting loot from defeated enemies...\n\n";
  337. int totalGold = 0, totalSilver = 0, totalCopper = 0;
  338. List<string> allItems = new List<string>();
  339. foreach (var enemy in lootableEnemies)
  340. {
  341. totalGold += enemy.goldReward;
  342. totalSilver += enemy.silverReward;
  343. totalCopper += enemy.copperReward;
  344. allItems.AddRange(enemy.dropItems);
  345. lootText += $"💀 {enemy.enemyName}: {enemy.goldReward}g {enemy.silverReward}s {enemy.copperReward}c\n";
  346. foreach (var item in enemy.dropItems)
  347. {
  348. lootText += $" 📦 {item}\n";
  349. }
  350. }
  351. lootText += $"\n💰 Total: {totalGold} gold, {totalSilver} silver, {totalCopper} copper\n";
  352. lootText += $"📦 {allItems.Count} items collected\n\n";
  353. lootText += "Click Continue to proceed or press SPACE...";
  354. lootInfo.text = lootText;
  355. }
  356. /// <summary>
  357. /// Show a temporary on-screen message for looting
  358. /// </summary>
  359. private System.Collections.IEnumerator ShowLootingMessage()
  360. {
  361. // Create a temporary UI GameObject
  362. GameObject tempUI = new GameObject("TempLootUI");
  363. tempUI.transform.SetParent(FindFirstObjectByType<Canvas>()?.transform, false);
  364. // Add background panel
  365. var canvasGroup = tempUI.AddComponent<CanvasGroup>();
  366. var image = tempUI.AddComponent<UnityEngine.UI.Image>();
  367. image.color = new Color(0, 0, 0, 0.8f); // Semi-transparent black
  368. // Set full screen size
  369. var rectTransform = tempUI.GetComponent<RectTransform>();
  370. rectTransform.anchorMin = Vector2.zero;
  371. rectTransform.anchorMax = Vector2.one;
  372. rectTransform.offsetMin = Vector2.zero;
  373. rectTransform.offsetMax = Vector2.zero;
  374. // Add text
  375. GameObject textObj = new GameObject("LootText");
  376. textObj.transform.SetParent(tempUI.transform, false);
  377. var text = textObj.AddComponent<UnityEngine.UI.Text>();
  378. text.font = Resources.GetBuiltinResource<Font>("Arial.ttf");
  379. text.fontSize = 24;
  380. text.color = Color.white;
  381. text.alignment = TextAnchor.MiddleCenter;
  382. // Set text content
  383. string lootText = "🏆 VICTORY! 🏆\n\n";
  384. lootText += "Collecting loot from defeated enemies...\n\n";
  385. int totalGold = 0, totalSilver = 0, totalCopper = 0;
  386. List<string> allItems = new List<string>();
  387. foreach (var enemy in lootableEnemies)
  388. {
  389. totalGold += enemy.goldReward;
  390. totalSilver += enemy.silverReward;
  391. totalCopper += enemy.copperReward;
  392. allItems.AddRange(enemy.dropItems);
  393. lootText += $"💀 {enemy.enemyName}: {enemy.goldReward}g {enemy.silverReward}s {enemy.copperReward}c\n";
  394. foreach (var item in enemy.dropItems)
  395. {
  396. lootText += $" 📦 {item}\n";
  397. }
  398. }
  399. lootText += $"\n💰 Total: {totalGold} gold, {totalSilver} silver, {totalCopper} copper\n";
  400. lootText += $"📦 {allItems.Count} items collected\n\n";
  401. lootText += "Press SPACE to continue...";
  402. text.text = lootText;
  403. // Set text position
  404. var textRect = textObj.GetComponent<RectTransform>();
  405. textRect.anchorMin = Vector2.zero;
  406. textRect.anchorMax = Vector2.one;
  407. textRect.offsetMin = new Vector2(50, 50);
  408. textRect.offsetMax = new Vector2(-50, -50);
  409. // Auto-loot everything
  410. AutoLootAll();
  411. // Wait for space key or 5 seconds
  412. float timer = 0f;
  413. while (timer < 5f && !Input.GetKeyDown(KeyCode.Space))
  414. {
  415. timer += Time.deltaTime;
  416. yield return null;
  417. }
  418. // Cleanup UI
  419. if (tempUI != null)
  420. Destroy(tempUI);
  421. // Complete looting
  422. CompleteLootingProcess();
  423. }
  424. /// <summary>
  425. /// Complete the looting process and trigger the callback
  426. /// </summary>
  427. private void CompleteLootingProcess()
  428. {
  429. isLootingActive = false;
  430. // Hide the loot UI
  431. if (rootElement != null)
  432. rootElement.style.display = DisplayStyle.None;
  433. if (showDebugLogs)
  434. Debug.Log("💰 Looting completed, triggering OnLootingComplete");
  435. OnLootingComplete?.Invoke();
  436. }
  437. /// <summary>
  438. /// Generate loot for a defeated enemy
  439. /// </summary>
  440. private void GenerateEnemyLoot(LootableEnemy lootableEnemy, Character enemyCharacter)
  441. {
  442. // Base currency rewards
  443. lootableEnemy.goldReward = Random.Range(0, baseGoldReward + 1);
  444. lootableEnemy.silverReward = Random.Range(0, baseSilverReward + 1);
  445. lootableEnemy.copperReward = Random.Range(baseCopperReward - 5, baseCopperReward + 10);
  446. // Basic item drops based on enemy type
  447. GenerateBasicDrops(lootableEnemy, enemyCharacter.CharacterName);
  448. // Try to get drops from enemy data if available
  449. TryGetEnemyDataDrops(lootableEnemy, enemyCharacter);
  450. if (showDebugLogs)
  451. {
  452. Debug.Log($"💰 Generated loot for {lootableEnemy.enemyName}: " +
  453. $"{lootableEnemy.goldReward}g {lootableEnemy.silverReward}s {lootableEnemy.copperReward}c, " +
  454. $"{lootableEnemy.dropItems.Count} items");
  455. }
  456. }
  457. /// <summary>
  458. /// Generate basic drops based on enemy name/type
  459. /// </summary>
  460. private void GenerateBasicDrops(LootableEnemy lootableEnemy, string enemyName)
  461. {
  462. string lowerName = enemyName.ToLower();
  463. // Skeleton-type enemies
  464. if (lowerName.Contains("skeleton"))
  465. {
  466. if (Random.Range(0f, 1f) < 0.3f) lootableEnemy.dropItems.Add("Bone");
  467. if (Random.Range(0f, 1f) < 0.2f) lootableEnemy.dropItems.Add("Rusty Sword");
  468. if (Random.Range(0f, 1f) < 0.15f) lootableEnemy.dropItems.Add("Bone Dust");
  469. }
  470. // Bandit-type enemies
  471. else if (lowerName.Contains("bandit") || lowerName.Contains("thief"))
  472. {
  473. if (Random.Range(0f, 1f) < 0.4f) lootableEnemy.dropItems.Add("Thieves' Tools");
  474. if (Random.Range(0f, 1f) < 0.3f) lootableEnemy.dropItems.Add("Rope");
  475. if (Random.Range(0f, 1f) < 0.2f) lootableEnemy.dropItems.Add("Dagger");
  476. // Bandits often have extra coin
  477. lootableEnemy.copperReward += Random.Range(5, 15);
  478. }
  479. // Orc-type enemies
  480. else if (lowerName.Contains("orc") || lowerName.Contains("goblin"))
  481. {
  482. if (Random.Range(0f, 1f) < 0.3f) lootableEnemy.dropItems.Add("Crude Axe");
  483. if (Random.Range(0f, 1f) < 0.25f) lootableEnemy.dropItems.Add("Hide Armor");
  484. if (Random.Range(0f, 1f) < 0.2f) lootableEnemy.dropItems.Add("Iron Ration");
  485. }
  486. // Default/unknown enemies
  487. else
  488. {
  489. if (Random.Range(0f, 1f) < 0.2f) lootableEnemy.dropItems.Add("Leather Scraps");
  490. if (Random.Range(0f, 1f) < 0.15f) lootableEnemy.dropItems.Add("Iron Ration");
  491. }
  492. // Common drops for all enemies
  493. if (Random.Range(0f, 1f) < 0.1f) lootableEnemy.dropItems.Add("Health Potion");
  494. if (Random.Range(0f, 1f) < 0.05f) lootableEnemy.dropItems.Add("Bandage");
  495. }
  496. /// <summary>
  497. /// Try to get drops from EnemyCharacterData if available
  498. /// </summary>
  499. private void TryGetEnemyDataDrops(LootableEnemy lootableEnemy, Character enemyCharacter)
  500. {
  501. // This would integrate with the EnemyCharacterData system
  502. // For now, we'll use the basic drop system above
  503. // TODO: Integrate with EnemyCharacterData.dropTable when available
  504. }
  505. /// <summary>
  506. /// Show the loot UI
  507. /// </summary>
  508. private void ShowLootUI()
  509. {
  510. if (rootElement != null)
  511. {
  512. // Show the UI Toolkit loot interface
  513. rootElement.style.display = DisplayStyle.Flex;
  514. PopulateLootUI();
  515. }
  516. else
  517. {
  518. // Fallback to console-based looting for now
  519. Debug.Log("💰 === POST-BATTLE LOOT ===");
  520. foreach (var enemy in lootableEnemies)
  521. {
  522. Debug.Log($"💰 {enemy.enemyName}: {enemy.goldReward}g {enemy.silverReward}s {enemy.copperReward}c");
  523. foreach (var item in enemy.dropItems)
  524. {
  525. Debug.Log($"💰 - {item}");
  526. }
  527. }
  528. Debug.Log("💰 ========================");
  529. // Auto-loot everything for now
  530. AutoLootAll();
  531. }
  532. }
  533. /// <summary>
  534. /// Populate the loot UI with available items
  535. /// </summary>
  536. private void PopulateLootUI()
  537. {
  538. if (rootElement == null) return;
  539. // Calculate totals first
  540. int totalGold = 0, totalSilver = 0, totalCopper = 0;
  541. List<string> allItems = new List<string>();
  542. foreach (var enemy in lootableEnemies)
  543. {
  544. totalGold += enemy.goldReward;
  545. totalSilver += enemy.silverReward;
  546. totalCopper += enemy.copperReward;
  547. allItems.AddRange(enemy.dropItems);
  548. }
  549. // Update currency displays with better formatting and tighter spacing
  550. var goldLabel = rootElement.Q<Label>("GoldAmount");
  551. var silverLabel = rootElement.Q<Label>("SilverAmount");
  552. var copperLabel = rootElement.Q<Label>("CopperAmount");
  553. if (goldLabel != null)
  554. {
  555. goldLabel.text = totalGold.ToString();
  556. goldLabel.style.fontSize = 18; // Slightly larger than before but not huge
  557. goldLabel.style.unityFontStyleAndWeight = FontStyle.Bold;
  558. goldLabel.style.marginBottom = 2; // Reduce spacing
  559. goldLabel.style.marginTop = 2;
  560. }
  561. if (silverLabel != null)
  562. {
  563. silverLabel.text = totalSilver.ToString();
  564. silverLabel.style.fontSize = 18;
  565. silverLabel.style.unityFontStyleAndWeight = FontStyle.Bold;
  566. silverLabel.style.marginBottom = 2;
  567. silverLabel.style.marginTop = 2;
  568. }
  569. if (copperLabel != null)
  570. {
  571. copperLabel.text = totalCopper.ToString();
  572. copperLabel.style.fontSize = 18;
  573. copperLabel.style.unityFontStyleAndWeight = FontStyle.Bold;
  574. copperLabel.style.marginBottom = 2;
  575. copperLabel.style.marginTop = 2;
  576. }
  577. // Style the currency text labels to be closer to the numbers
  578. var goldText = rootElement.Q<Label>("GoldText");
  579. var silverText = rootElement.Q<Label>("SilverText");
  580. var copperText = rootElement.Q<Label>("CopperText");
  581. if (goldText != null)
  582. {
  583. goldText.style.fontSize = 12;
  584. goldText.style.marginTop = -2; // Move closer to number
  585. goldText.style.marginBottom = 8; // Add space after currency section
  586. }
  587. if (silverText != null)
  588. {
  589. silverText.style.fontSize = 12;
  590. silverText.style.marginTop = -2;
  591. silverText.style.marginBottom = 8;
  592. }
  593. if (copperText != null)
  594. {
  595. copperText.style.fontSize = 12;
  596. copperText.style.marginTop = -2;
  597. copperText.style.marginBottom = 8;
  598. }
  599. // Also style the currency icons to be smaller
  600. var goldIcon = rootElement.Q<Label>("GoldIcon");
  601. var silverIcon = rootElement.Q<Label>("SilverIcon");
  602. var copperIcon = rootElement.Q<Label>("CopperIcon");
  603. if (goldIcon != null) goldIcon.style.fontSize = 14;
  604. if (silverIcon != null) silverIcon.style.fontSize = 14;
  605. if (copperIcon != null) copperIcon.style.fontSize = 14;
  606. // Update item count
  607. var itemCountLabel = rootElement.Q<Label>("ItemCount");
  608. if (itemCountLabel != null)
  609. itemCountLabel.text = $"{allItems.Count} items";
  610. // Populate enemy loot sections
  611. var enemyLootContainer = rootElement.Q<VisualElement>("EnemyLootContainer");
  612. if (enemyLootContainer != null)
  613. {
  614. enemyLootContainer.Clear();
  615. foreach (var enemy in lootableEnemies)
  616. {
  617. var enemySection = new VisualElement();
  618. enemySection.AddToClassList("enemy-loot-section");
  619. // Enemy header
  620. var enemyHeader = new Label($"💀 {enemy.enemyName}");
  621. enemyHeader.AddToClassList("enemy-name");
  622. enemySection.Add(enemyHeader);
  623. // Enemy currency
  624. if (enemy.goldReward > 0 || enemy.silverReward > 0 || enemy.copperReward > 0)
  625. {
  626. var currencyContainer = new VisualElement();
  627. currencyContainer.AddToClassList("enemy-currency");
  628. if (enemy.goldReward > 0)
  629. {
  630. var enemyGoldLabel = new Label($"🪙 {enemy.goldReward}");
  631. enemyGoldLabel.AddToClassList("currency-gold");
  632. currencyContainer.Add(enemyGoldLabel);
  633. }
  634. if (enemy.silverReward > 0)
  635. {
  636. var enemySilverLabel = new Label($"🪙 {enemy.silverReward}");
  637. enemySilverLabel.AddToClassList("currency-silver");
  638. currencyContainer.Add(enemySilverLabel);
  639. }
  640. if (enemy.copperReward > 0)
  641. {
  642. var enemyCopperLabel = new Label($"🪙 {enemy.copperReward}");
  643. enemyCopperLabel.AddToClassList("currency-copper");
  644. currencyContainer.Add(enemyCopperLabel);
  645. }
  646. enemySection.Add(currencyContainer);
  647. }
  648. // Enemy items
  649. if (enemy.dropItems != null && enemy.dropItems.Count > 0)
  650. {
  651. var itemsContainer = new VisualElement();
  652. itemsContainer.AddToClassList("enemy-items");
  653. foreach (var item in enemy.dropItems)
  654. {
  655. var itemLabel = new Label($"📦 {item}");
  656. itemLabel.AddToClassList("item-entry");
  657. itemsContainer.Add(itemLabel);
  658. }
  659. enemySection.Add(itemsContainer);
  660. }
  661. enemyLootContainer.Add(enemySection);
  662. }
  663. }
  664. // Populate the total items list scrollview with better visibility
  665. var itemsListContainer = rootElement.Q<ScrollView>("ItemsList");
  666. if (itemsListContainer != null)
  667. {
  668. itemsListContainer.Clear();
  669. // Set scrollview properties for better visibility and containment
  670. itemsListContainer.style.minHeight = 80;
  671. itemsListContainer.style.maxHeight = 150;
  672. itemsListContainer.style.backgroundColor = new Color(0.15f, 0.15f, 0.25f, 0.9f);
  673. itemsListContainer.style.borderTopWidth = 2;
  674. itemsListContainer.style.borderBottomWidth = 2;
  675. itemsListContainer.style.borderLeftWidth = 2;
  676. itemsListContainer.style.borderRightWidth = 2;
  677. itemsListContainer.style.borderTopColor = new Color(0.6f, 0.6f, 0.8f, 0.8f);
  678. itemsListContainer.style.borderBottomColor = new Color(0.6f, 0.6f, 0.8f, 0.8f);
  679. itemsListContainer.style.borderLeftColor = new Color(0.6f, 0.6f, 0.8f, 0.8f);
  680. itemsListContainer.style.borderRightColor = new Color(0.6f, 0.6f, 0.8f, 0.8f);
  681. itemsListContainer.style.borderTopLeftRadius = 8;
  682. itemsListContainer.style.borderTopRightRadius = 8;
  683. itemsListContainer.style.borderBottomLeftRadius = 8;
  684. itemsListContainer.style.borderBottomRightRadius = 8;
  685. itemsListContainer.style.paddingTop = 8;
  686. itemsListContainer.style.paddingBottom = 8;
  687. itemsListContainer.style.paddingLeft = 8;
  688. itemsListContainer.style.paddingRight = 8;
  689. itemsListContainer.style.marginTop = 5;
  690. itemsListContainer.style.marginBottom = 10;
  691. itemsListContainer.style.marginLeft = 5;
  692. itemsListContainer.style.marginRight = 5;
  693. if (allItems.Count > 0)
  694. {
  695. foreach (var item in allItems)
  696. {
  697. var itemEntry = new Label($"📦 {item}");
  698. itemEntry.AddToClassList("total-item-entry");
  699. itemEntry.style.fontSize = 14;
  700. itemEntry.style.color = Color.white;
  701. itemEntry.style.marginBottom = 2;
  702. itemEntry.style.paddingLeft = 6;
  703. itemEntry.style.paddingRight = 6;
  704. itemEntry.style.paddingTop = 3;
  705. itemEntry.style.paddingBottom = 3;
  706. itemEntry.style.backgroundColor = new Color(0.25f, 0.35f, 0.45f, 0.8f);
  707. itemEntry.style.borderTopLeftRadius = 4;
  708. itemEntry.style.borderTopRightRadius = 4;
  709. itemEntry.style.borderBottomLeftRadius = 4;
  710. itemEntry.style.borderBottomRightRadius = 4;
  711. itemEntry.style.borderTopWidth = 1;
  712. itemEntry.style.borderBottomWidth = 1;
  713. itemEntry.style.borderLeftWidth = 1;
  714. itemEntry.style.borderRightWidth = 1;
  715. itemEntry.style.borderTopColor = new Color(0.7f, 0.7f, 0.9f, 0.6f);
  716. itemEntry.style.borderBottomColor = new Color(0.7f, 0.7f, 0.9f, 0.6f);
  717. itemEntry.style.borderLeftColor = new Color(0.7f, 0.7f, 0.9f, 0.6f);
  718. itemEntry.style.borderRightColor = new Color(0.7f, 0.7f, 0.9f, 0.6f);
  719. itemsListContainer.Add(itemEntry);
  720. }
  721. }
  722. else
  723. {
  724. var noItemsLabel = new Label("No items collected");
  725. noItemsLabel.style.fontSize = 14;
  726. noItemsLabel.style.color = new Color(0.7f, 0.7f, 0.7f, 1f);
  727. noItemsLabel.style.unityTextAlign = TextAnchor.MiddleCenter;
  728. noItemsLabel.style.paddingTop = 20;
  729. itemsListContainer.Add(noItemsLabel);
  730. }
  731. }
  732. // Add informational message about item distribution
  733. if (allItems.Count > 0)
  734. {
  735. var distributionHint = rootElement.Q<Label>("DistributionHint");
  736. if (distributionHint == null)
  737. {
  738. distributionHint = new Label();
  739. distributionHint.name = "DistributionHint";
  740. // Find a good place to add it (after the items section)
  741. var itemsSection = rootElement.Q<VisualElement>("ItemsSection");
  742. if (itemsSection != null)
  743. {
  744. itemsSection.Add(distributionHint);
  745. }
  746. else
  747. {
  748. rootElement.Add(distributionHint);
  749. }
  750. }
  751. if (enablePlayerItemSelection && !takeAllPressed)
  752. {
  753. distributionHint.text = "💡 Click individual items above to choose which character gets them, or use 'Take All' for automatic distribution.";
  754. }
  755. else
  756. {
  757. distributionHint.text = "📋 Items will be distributed automatically among surviving party members.";
  758. }
  759. distributionHint.style.fontSize = 12;
  760. distributionHint.style.color = new Color(0.8f, 0.8f, 0.9f, 0.9f);
  761. distributionHint.style.unityTextAlign = TextAnchor.MiddleCenter;
  762. distributionHint.style.marginTop = 10;
  763. distributionHint.style.marginBottom = 5;
  764. distributionHint.style.paddingLeft = 10;
  765. distributionHint.style.paddingRight = 10;
  766. distributionHint.style.whiteSpace = WhiteSpace.Normal;
  767. }
  768. }
  769. /// <summary>
  770. /// Auto-loot all items and currency with optional player selection
  771. /// </summary>
  772. private void AutoLootAll()
  773. {
  774. int totalGold = 0, totalSilver = 0, totalCopper = 0;
  775. List<string> allItems = new List<string>();
  776. foreach (var enemy in lootableEnemies)
  777. {
  778. if (!enemy.hasBeenLooted)
  779. {
  780. totalGold += enemy.goldReward;
  781. totalSilver += enemy.silverReward;
  782. totalCopper += enemy.copperReward;
  783. allItems.AddRange(enemy.dropItems);
  784. enemy.hasBeenLooted = true;
  785. }
  786. }
  787. // Always distribute currency automatically
  788. DistributeCurrency(totalGold, totalSilver, totalCopper);
  789. // Handle item distribution based on settings
  790. // Show item distribution UI if:
  791. // 1. Player item selection is enabled
  792. // 2. There are items to distribute
  793. // 3. We're NOT in "Take All" mode (which forces auto-distribution)
  794. if (enablePlayerItemSelection && allItems.Count > 0 && !takeAllPressed)
  795. {
  796. ShowItemDistributionUI(allItems);
  797. }
  798. else
  799. {
  800. // Auto-distribute items
  801. DistributeItemsAutomatically(allItems);
  802. if (showDebugLogs)
  803. {
  804. Debug.Log($"💰 Auto-looted: {totalGold}g {totalSilver}s {totalCopper}c and {allItems.Count} items");
  805. }
  806. // End looting phase
  807. FinishLooting();
  808. }
  809. }
  810. /// <summary>
  811. /// Distribute looted rewards among surviving players
  812. /// </summary>
  813. private void DistributeRewards(int gold, int silver, int copper, List<string> items)
  814. {
  815. // Get surviving players
  816. var survivingPlayers = GetSurvivingPlayers();
  817. if (survivingPlayers.Count == 0)
  818. {
  819. Debug.LogWarning("💰 No surviving players to distribute loot to!");
  820. return;
  821. }
  822. // Distribute currency evenly
  823. int goldPerPlayer = gold / survivingPlayers.Count;
  824. int silverPerPlayer = silver / survivingPlayers.Count;
  825. int copperPerPlayer = copper / survivingPlayers.Count;
  826. // Handle remainder
  827. int goldRemainder = gold % survivingPlayers.Count;
  828. int silverRemainder = silver % survivingPlayers.Count;
  829. int copperRemainder = copper % survivingPlayers.Count;
  830. for (int i = 0; i < survivingPlayers.Count; i++)
  831. {
  832. var player = survivingPlayers[i];
  833. Character playerCharacter = player.GetComponent<Character>();
  834. if (playerCharacter == null) continue;
  835. // Get player's bank (assuming it exists)
  836. var bank = playerCharacter.GetComponent<Bank>();
  837. if (bank != null)
  838. {
  839. bank.gold += goldPerPlayer + (i < goldRemainder ? 1 : 0);
  840. bank.silver += silverPerPlayer + (i < silverRemainder ? 1 : 0);
  841. bank.copper += copperPerPlayer + (i < copperRemainder ? 1 : 0);
  842. }
  843. // Distribute items (simple round-robin for now)
  844. var inventory = playerCharacter.GetComponent<Inventory>();
  845. if (inventory != null)
  846. {
  847. for (int j = i; j < items.Count; j += survivingPlayers.Count)
  848. {
  849. // TODO: Create proper Item objects instead of strings
  850. // For now, add to string-based inventory if available
  851. AddItemToPlayer(playerCharacter, items[j]);
  852. }
  853. }
  854. if (showDebugLogs)
  855. {
  856. int finalGold = goldPerPlayer + (i < goldRemainder ? 1 : 0);
  857. int finalSilver = silverPerPlayer + (i < silverRemainder ? 1 : 0);
  858. int finalCopper = copperPerPlayer + (i < copperRemainder ? 1 : 0);
  859. Debug.Log($"💰 {playerCharacter.CharacterName} received: {finalGold}g {finalSilver}s {finalCopper}c");
  860. }
  861. }
  862. }
  863. /// <summary>
  864. /// Distribute currency to players (separated from items for flexibility)
  865. /// </summary>
  866. private void DistributeCurrency(int gold, int silver, int copper)
  867. {
  868. var survivingPlayers = GetSurvivingPlayers();
  869. if (survivingPlayers.Count == 0)
  870. {
  871. Debug.LogWarning("💰 No surviving players to distribute currency to!");
  872. return;
  873. }
  874. // Distribute currency evenly
  875. int goldPerPlayer = gold / survivingPlayers.Count;
  876. int silverPerPlayer = silver / survivingPlayers.Count;
  877. int copperPerPlayer = copper / survivingPlayers.Count;
  878. // Handle remainder
  879. int goldRemainder = gold % survivingPlayers.Count;
  880. int silverRemainder = silver % survivingPlayers.Count;
  881. int copperRemainder = copper % survivingPlayers.Count;
  882. for (int i = 0; i < survivingPlayers.Count; i++)
  883. {
  884. var player = survivingPlayers[i];
  885. Character playerCharacter = player.GetComponent<Character>();
  886. if (playerCharacter == null) continue;
  887. // Get player's bank
  888. var bank = playerCharacter.GetComponent<Bank>();
  889. if (bank != null)
  890. {
  891. bank.gold += goldPerPlayer + (i < goldRemainder ? 1 : 0);
  892. bank.silver += silverPerPlayer + (i < silverRemainder ? 1 : 0);
  893. bank.copper += copperPerPlayer + (i < copperRemainder ? 1 : 0);
  894. if (showDebugLogs)
  895. {
  896. int finalGold = goldPerPlayer + (i < goldRemainder ? 1 : 0);
  897. int finalSilver = silverPerPlayer + (i < silverRemainder ? 1 : 0);
  898. int finalCopper = copperPerPlayer + (i < copperRemainder ? 1 : 0);
  899. Debug.Log($"💰 {playerCharacter.CharacterName} received: {finalGold}g {finalSilver}s {finalCopper}c");
  900. }
  901. }
  902. }
  903. }
  904. /// <summary>
  905. /// Show UI for manually assigning items to players
  906. /// </summary>
  907. private void ShowItemDistributionUI(List<string> items)
  908. {
  909. var survivingPlayers = GetSurvivingPlayers();
  910. if (survivingPlayers.Count == 0)
  911. {
  912. Debug.LogWarning("💰 No surviving players for item distribution!");
  913. FinishLooting();
  914. return;
  915. }
  916. // For now, create a simple distribution UI overlay
  917. var distributionOverlay = new VisualElement();
  918. distributionOverlay.name = "ItemDistributionOverlay";
  919. distributionOverlay.style.position = Position.Absolute;
  920. distributionOverlay.style.top = 0;
  921. distributionOverlay.style.left = 0;
  922. distributionOverlay.style.right = 0;
  923. distributionOverlay.style.bottom = 0;
  924. distributionOverlay.style.backgroundColor = new Color(0, 0, 0, 0.8f);
  925. distributionOverlay.style.justifyContent = Justify.Center;
  926. distributionOverlay.style.alignItems = Align.Center;
  927. // Create distribution panel
  928. var panel = new VisualElement();
  929. panel.style.backgroundColor = new Color(0.15f, 0.15f, 0.25f, 0.95f);
  930. panel.style.borderTopWidth = 2;
  931. panel.style.borderBottomWidth = 2;
  932. panel.style.borderLeftWidth = 2;
  933. panel.style.borderRightWidth = 2;
  934. panel.style.borderTopColor = Color.yellow;
  935. panel.style.borderBottomColor = Color.yellow;
  936. panel.style.borderLeftColor = Color.yellow;
  937. panel.style.borderRightColor = Color.yellow;
  938. panel.style.borderTopLeftRadius = 10;
  939. panel.style.borderTopRightRadius = 10;
  940. panel.style.borderBottomLeftRadius = 10;
  941. panel.style.borderBottomRightRadius = 10;
  942. panel.style.paddingTop = 20;
  943. panel.style.paddingBottom = 20;
  944. panel.style.paddingLeft = 20;
  945. panel.style.paddingRight = 20;
  946. panel.style.width = new Length(90, LengthUnit.Percent);
  947. panel.style.maxWidth = 600;
  948. panel.style.maxHeight = new Length(80, LengthUnit.Percent);
  949. // Title
  950. var title = new Label("Distribute Items to Players");
  951. title.style.fontSize = 24;
  952. title.style.color = Color.yellow;
  953. title.style.unityTextAlign = TextAnchor.MiddleCenter;
  954. title.style.marginBottom = 20;
  955. panel.Add(title);
  956. // Scroll view for items
  957. var scrollView = new ScrollView();
  958. scrollView.style.flexGrow = 1;
  959. scrollView.style.marginBottom = 15;
  960. // Create item distribution entries
  961. foreach (var item in items)
  962. {
  963. var itemRow = CreateItemDistributionRow(item, survivingPlayers);
  964. scrollView.Add(itemRow);
  965. }
  966. panel.Add(scrollView);
  967. // Buttons
  968. var buttonContainer = new VisualElement();
  969. buttonContainer.style.flexDirection = FlexDirection.Row;
  970. buttonContainer.style.justifyContent = Justify.SpaceAround;
  971. var autoDistributeBtn = new Button(() =>
  972. {
  973. DistributeItemsAutomatically(items);
  974. rootElement.Remove(distributionOverlay);
  975. FinishLooting();
  976. });
  977. autoDistributeBtn.text = "Auto Distribute";
  978. autoDistributeBtn.style.fontSize = 14;
  979. var confirmBtn = new Button(() =>
  980. {
  981. DistributeItemsManually(items, survivingPlayers);
  982. rootElement.Remove(distributionOverlay);
  983. FinishLooting();
  984. });
  985. confirmBtn.text = "Confirm Distribution";
  986. confirmBtn.style.fontSize = 14;
  987. buttonContainer.Add(autoDistributeBtn);
  988. buttonContainer.Add(confirmBtn);
  989. panel.Add(buttonContainer);
  990. distributionOverlay.Add(panel);
  991. rootElement.Add(distributionOverlay);
  992. }
  993. /// <summary>
  994. /// Create a row for item distribution with player selection buttons
  995. /// </summary>
  996. private VisualElement CreateItemDistributionRow(string itemName, List<GameObject> players)
  997. {
  998. var row = new VisualElement();
  999. row.style.flexDirection = FlexDirection.Row;
  1000. row.style.alignItems = Align.Center;
  1001. row.style.marginBottom = 10;
  1002. row.style.paddingTop = 8;
  1003. row.style.paddingBottom = 8;
  1004. row.style.paddingLeft = 12;
  1005. row.style.paddingRight = 12;
  1006. row.style.backgroundColor = new Color(0.2f, 0.2f, 0.3f, 0.9f);
  1007. row.style.borderTopLeftRadius = 8;
  1008. row.style.borderTopRightRadius = 8;
  1009. row.style.borderBottomLeftRadius = 8;
  1010. row.style.borderBottomRightRadius = 8;
  1011. row.style.borderTopWidth = 1;
  1012. row.style.borderBottomWidth = 1;
  1013. row.style.borderLeftWidth = 1;
  1014. row.style.borderRightWidth = 1;
  1015. row.style.borderTopColor = new Color(0.5f, 0.5f, 0.6f, 0.8f);
  1016. row.style.borderBottomColor = new Color(0.5f, 0.5f, 0.6f, 0.8f);
  1017. row.style.borderLeftColor = new Color(0.5f, 0.5f, 0.6f, 0.8f);
  1018. row.style.borderRightColor = new Color(0.5f, 0.5f, 0.6f, 0.8f);
  1019. // Item name with better styling
  1020. var itemLabel = new Label($"📦 {itemName}");
  1021. itemLabel.style.fontSize = 15;
  1022. itemLabel.style.color = Color.white;
  1023. itemLabel.style.unityFontStyleAndWeight = FontStyle.Bold;
  1024. itemLabel.style.width = new Length(35, LengthUnit.Percent);
  1025. itemLabel.style.unityTextAlign = TextAnchor.MiddleLeft;
  1026. row.Add(itemLabel);
  1027. // "Assign to:" label
  1028. var assignLabel = new Label("→");
  1029. assignLabel.style.fontSize = 14;
  1030. assignLabel.style.color = Color.yellow;
  1031. assignLabel.style.unityFontStyleAndWeight = FontStyle.Bold;
  1032. assignLabel.style.width = 20;
  1033. assignLabel.style.unityTextAlign = TextAnchor.MiddleCenter;
  1034. row.Add(assignLabel);
  1035. // Player selection buttons
  1036. var buttonContainer = new VisualElement();
  1037. buttonContainer.style.flexDirection = FlexDirection.Row;
  1038. buttonContainer.style.flexGrow = 1;
  1039. buttonContainer.style.justifyContent = Justify.SpaceAround;
  1040. int selectedPlayerIndex = selectedPlayerForItem.ContainsKey(itemName) ? selectedPlayerForItem[itemName] : -1;
  1041. // Store button references for updating
  1042. var playerButtons = new List<Button>();
  1043. for (int i = 0; i < players.Count; i++)
  1044. {
  1045. var player = players[i];
  1046. var character = player.GetComponent<Character>();
  1047. if (character == null) continue;
  1048. int playerIndex = i; // Capture for closure
  1049. var playerBtn = new Button(() =>
  1050. {
  1051. selectedPlayerForItem[itemName] = playerIndex;
  1052. // Update all buttons in this row to show selection
  1053. UpdatePlayerButtonVisuals(playerButtons, playerIndex);
  1054. Debug.Log($"💰 Assigned {itemName} to {character.CharacterName}");
  1055. });
  1056. playerBtn.text = character.CharacterName;
  1057. playerBtn.style.fontSize = 13;
  1058. playerBtn.style.flexGrow = 1;
  1059. playerBtn.style.marginLeft = 3;
  1060. playerBtn.style.marginRight = 3;
  1061. playerBtn.style.paddingTop = 8;
  1062. playerBtn.style.paddingBottom = 8;
  1063. playerBtn.style.paddingLeft = 6;
  1064. playerBtn.style.paddingRight = 6;
  1065. playerBtn.style.borderTopLeftRadius = 5;
  1066. playerBtn.style.borderTopRightRadius = 5;
  1067. playerBtn.style.borderBottomLeftRadius = 5;
  1068. playerBtn.style.borderBottomRightRadius = 5;
  1069. playerBtn.style.borderTopWidth = 2;
  1070. playerBtn.style.borderBottomWidth = 2;
  1071. playerBtn.style.borderLeftWidth = 2;
  1072. playerBtn.style.borderRightWidth = 2;
  1073. // Set initial visual state
  1074. if (i == selectedPlayerIndex)
  1075. {
  1076. // Selected state
  1077. playerBtn.style.backgroundColor = new Color(0.2f, 0.8f, 0.2f, 0.9f);
  1078. playerBtn.style.borderTopColor = Color.green;
  1079. playerBtn.style.borderBottomColor = Color.green;
  1080. playerBtn.style.borderLeftColor = Color.green;
  1081. playerBtn.style.borderRightColor = Color.green;
  1082. playerBtn.style.color = Color.white;
  1083. }
  1084. else
  1085. {
  1086. // Unselected state
  1087. playerBtn.style.backgroundColor = new Color(0.3f, 0.3f, 0.4f, 0.8f);
  1088. playerBtn.style.borderTopColor = new Color(0.6f, 0.6f, 0.7f, 0.8f);
  1089. playerBtn.style.borderBottomColor = new Color(0.6f, 0.6f, 0.7f, 0.8f);
  1090. playerBtn.style.borderLeftColor = new Color(0.6f, 0.6f, 0.7f, 0.8f);
  1091. playerBtn.style.borderRightColor = new Color(0.6f, 0.6f, 0.7f, 0.8f);
  1092. playerBtn.style.color = new Color(0.9f, 0.9f, 0.9f, 0.9f);
  1093. }
  1094. playerButtons.Add(playerBtn);
  1095. buttonContainer.Add(playerBtn);
  1096. }
  1097. row.Add(buttonContainer);
  1098. return row;
  1099. }
  1100. /// <summary>
  1101. /// Update visual state of player selection buttons
  1102. /// </summary>
  1103. private void UpdatePlayerButtonVisuals(List<Button> buttons, int selectedIndex)
  1104. {
  1105. for (int i = 0; i < buttons.Count; i++)
  1106. {
  1107. var button = buttons[i];
  1108. if (i == selectedIndex)
  1109. {
  1110. // Selected state - green highlight
  1111. button.style.backgroundColor = new Color(0.2f, 0.8f, 0.2f, 0.9f);
  1112. button.style.borderTopColor = Color.green;
  1113. button.style.borderBottomColor = Color.green;
  1114. button.style.borderLeftColor = Color.green;
  1115. button.style.borderRightColor = Color.green;
  1116. button.style.color = Color.white;
  1117. }
  1118. else
  1119. {
  1120. // Unselected state - neutral gray
  1121. button.style.backgroundColor = new Color(0.3f, 0.3f, 0.4f, 0.8f);
  1122. button.style.borderTopColor = new Color(0.6f, 0.6f, 0.7f, 0.8f);
  1123. button.style.borderBottomColor = new Color(0.6f, 0.6f, 0.7f, 0.8f);
  1124. button.style.borderLeftColor = new Color(0.6f, 0.6f, 0.7f, 0.8f);
  1125. button.style.borderRightColor = new Color(0.6f, 0.6f, 0.7f, 0.8f);
  1126. button.style.color = new Color(0.9f, 0.9f, 0.9f, 0.9f);
  1127. }
  1128. }
  1129. }
  1130. /// <summary>
  1131. /// Distribute items automatically using round-robin
  1132. /// </summary>
  1133. private void DistributeItemsAutomatically(List<string> items)
  1134. {
  1135. var survivingPlayers = GetSurvivingPlayers();
  1136. if (survivingPlayers.Count == 0) return;
  1137. for (int i = 0; i < items.Count; i++)
  1138. {
  1139. var player = survivingPlayers[i % survivingPlayers.Count];
  1140. var character = player.GetComponent<Character>();
  1141. if (character != null)
  1142. {
  1143. AddItemToPlayer(character, items[i]);
  1144. }
  1145. }
  1146. if (showDebugLogs)
  1147. {
  1148. Debug.Log($"💰 Auto-distributed {items.Count} items among {survivingPlayers.Count} players");
  1149. }
  1150. }
  1151. /// <summary>
  1152. /// Distribute items based on manual player selection
  1153. /// </summary>
  1154. private void DistributeItemsManually(List<string> items, List<GameObject> players)
  1155. {
  1156. foreach (var item in items)
  1157. {
  1158. if (selectedPlayerForItem.ContainsKey(item))
  1159. {
  1160. int playerIndex = selectedPlayerForItem[item];
  1161. if (playerIndex >= 0 && playerIndex < players.Count)
  1162. {
  1163. var character = players[playerIndex].GetComponent<Character>();
  1164. if (character != null)
  1165. {
  1166. AddItemToPlayer(character, item);
  1167. if (showDebugLogs)
  1168. {
  1169. Debug.Log($"💰 Manually distributed {item} to {character.CharacterName}");
  1170. }
  1171. }
  1172. }
  1173. }
  1174. else
  1175. {
  1176. // If no selection was made, give to first player
  1177. var character = players[0].GetComponent<Character>();
  1178. if (character != null)
  1179. {
  1180. AddItemToPlayer(character, item);
  1181. if (showDebugLogs)
  1182. {
  1183. Debug.Log($"💰 {item} given to {character.CharacterName} (no selection made)");
  1184. }
  1185. }
  1186. }
  1187. }
  1188. // Clear selections for next battle
  1189. selectedPlayerForItem.Clear();
  1190. }
  1191. /// <summary>
  1192. /// Add an item to a player's inventory
  1193. /// </summary>
  1194. private void AddItemToPlayer(Character player, string itemName)
  1195. {
  1196. // Try to add to CombatDataTransfer session data
  1197. if (CombatDataTransfer.HasValidSession())
  1198. {
  1199. var session = CombatDataTransfer.GetCurrentSession();
  1200. var playerData = session.playerTeam.Find(p =>
  1201. p.characterName == player.CharacterName ||
  1202. player.CharacterName.StartsWith(p.characterName));
  1203. if (playerData != null)
  1204. {
  1205. if (playerData.miscItems == null)
  1206. playerData.miscItems = new List<string>();
  1207. playerData.miscItems.Add(itemName);
  1208. if (showDebugLogs)
  1209. Debug.Log($"💰 Added {itemName} to {player.CharacterName}'s inventory");
  1210. return;
  1211. }
  1212. }
  1213. // Fallback: log that item would be added
  1214. if (showDebugLogs)
  1215. Debug.Log($"💰 Would add {itemName} to {player.CharacterName} (no inventory system found)");
  1216. }
  1217. /// <summary>
  1218. /// Get list of surviving player characters
  1219. /// </summary>
  1220. private List<GameObject> GetSurvivingPlayers()
  1221. {
  1222. var gameManager = GameManager.Instance;
  1223. if (gameManager == null) return new List<GameObject>();
  1224. return gameManager.playerCharacters
  1225. .Where(p => p != null)
  1226. .Where(p =>
  1227. {
  1228. var character = p.GetComponent<Character>();
  1229. return character != null && !character.IsDead;
  1230. })
  1231. .ToList();
  1232. }
  1233. /// <summary>
  1234. /// Complete the looting phase
  1235. /// </summary>
  1236. private void FinishLooting()
  1237. {
  1238. isLootingActive = false;
  1239. if (rootElement != null)
  1240. rootElement.style.display = DisplayStyle.None;
  1241. OnLootingComplete?.Invoke();
  1242. if (showDebugLogs)
  1243. Debug.Log("💰 Looting phase completed");
  1244. }
  1245. /// <summary>
  1246. /// Skip looting and proceed to battle end
  1247. /// </summary>
  1248. public void SkipLooting()
  1249. {
  1250. if (showDebugLogs)
  1251. Debug.Log("💰 Skipping looting phase");
  1252. FinishLooting();
  1253. }
  1254. /// <summary>
  1255. /// Check if all enemies have been looted
  1256. /// </summary>
  1257. public bool AllEnemiesLooted()
  1258. {
  1259. return lootableEnemies.All(e => e.hasBeenLooted);
  1260. }
  1261. /// <summary>
  1262. /// Get total weight of all available loot
  1263. /// </summary>
  1264. public int GetTotalLootWeight()
  1265. {
  1266. // TODO: Implement when item weight system is added
  1267. return lootableEnemies.Sum(e => e.dropItems.Count); // Placeholder: 1 weight per item
  1268. }
  1269. /// <summary>
  1270. /// Get total value of all available loot in copper
  1271. /// </summary>
  1272. public int GetTotalLootValue()
  1273. {
  1274. int totalValue = 0;
  1275. foreach (var enemy in lootableEnemies)
  1276. {
  1277. totalValue += enemy.goldReward * 100; // 1 gold = 100 copper
  1278. totalValue += enemy.silverReward * 10; // 1 silver = 10 copper
  1279. totalValue += enemy.copperReward;
  1280. // TODO: Add item values when item system is integrated
  1281. }
  1282. return totalValue;
  1283. }
  1284. }