| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510 |
- using UnityEngine;
- using UnityEngine.UIElements;
- using System.Collections.Generic;
- using System.Linq;
- /// <summary>
- /// Beautiful UI popup for combat event encounters
- /// Shows enemy details and provides options to attack or run away
- /// </summary>
- public class CombatEventPopup : MonoBehaviour
- {
- [Header("UI References")]
- public UIDocument uiDocument;
- public VisualTreeAsset popupTemplate;
- [Header("Popup Settings")]
- public float animationDuration = 0.3f;
- public float backgroundBlurIntensity = 0.5f;
- // Events
- public System.Action<bool> OnCombatDecision; // true = attack, false = run away
- // UI Elements
- private VisualElement popupContainer;
- private VisualElement backgroundOverlay;
- private VisualElement popupPanel;
- private Label eventTitleLabel;
- private Label eventDescriptionLabel;
- private VisualElement enemyListContainer;
- private Button attackButton;
- private Button runAwayButton;
- private VisualElement popupContent;
- // Current combat data
- private BattleEventData currentBattleData;
- private TravelEventContext currentContext;
- private bool isPopupActive = false;
- void Awake()
- {
- // Find UI Document if not assigned
- if (uiDocument == null)
- uiDocument = GetComponent<UIDocument>();
- if (uiDocument == null)
- {
- Debug.LogError("CombatEventPopup: No UIDocument found! Please assign one.");
- return;
- }
- SetupUI();
- }
- void SetupUI()
- {
- var root = uiDocument.rootVisualElement;
- // Create main popup container
- popupContainer = new VisualElement();
- popupContainer.name = "combat-event-popup";
- popupContainer.AddToClassList("combat-popup-container");
- popupContainer.style.display = DisplayStyle.None;
- popupContainer.style.position = Position.Absolute;
- popupContainer.style.top = 0;
- popupContainer.style.left = 0;
- popupContainer.style.right = 0;
- popupContainer.style.bottom = 0;
- popupContainer.style.justifyContent = Justify.Center;
- popupContainer.style.alignItems = Align.Center;
- // Background overlay
- backgroundOverlay = new VisualElement();
- backgroundOverlay.name = "background-overlay";
- backgroundOverlay.AddToClassList("combat-popup-overlay");
- backgroundOverlay.style.position = Position.Absolute;
- backgroundOverlay.style.top = 0;
- backgroundOverlay.style.left = 0;
- backgroundOverlay.style.right = 0;
- backgroundOverlay.style.bottom = 0;
- backgroundOverlay.style.backgroundColor = new Color(0, 0, 0, 0.7f);
- // Main popup panel
- popupPanel = new VisualElement();
- popupPanel.name = "popup-panel";
- popupPanel.AddToClassList("combat-popup-panel");
- popupPanel.style.backgroundColor = new Color(0.15f, 0.15f, 0.2f, 0.95f);
- popupPanel.style.borderLeftColor = new Color(0.8f, 0.2f, 0.2f, 1f);
- popupPanel.style.borderRightColor = new Color(0.8f, 0.2f, 0.2f, 1f);
- popupPanel.style.borderTopColor = new Color(0.8f, 0.2f, 0.2f, 1f);
- popupPanel.style.borderBottomColor = new Color(0.8f, 0.2f, 0.2f, 1f);
- popupPanel.style.borderLeftWidth = 3;
- popupPanel.style.borderRightWidth = 3;
- popupPanel.style.borderTopWidth = 3;
- popupPanel.style.borderBottomWidth = 3;
- popupPanel.style.borderTopLeftRadius = 10;
- popupPanel.style.borderTopRightRadius = 10;
- popupPanel.style.borderBottomLeftRadius = 10;
- popupPanel.style.borderBottomRightRadius = 10;
- popupPanel.style.minWidth = 400;
- popupPanel.style.maxWidth = 600;
- popupPanel.style.paddingTop = 20;
- popupPanel.style.paddingBottom = 20;
- popupPanel.style.paddingLeft = 20;
- popupPanel.style.paddingRight = 20;
- CreatePopupContent();
- popupContainer.Add(backgroundOverlay);
- popupContainer.Add(popupPanel);
- root.Add(popupContainer);
- // Setup event handlers
- backgroundOverlay.RegisterCallback<ClickEvent>(OnBackgroundClick);
- attackButton.clicked += OnAttackClicked;
- runAwayButton.clicked += OnRunAwayClicked;
- }
- void CreatePopupContent()
- {
- // Title
- eventTitleLabel = new Label("⚔️ COMBAT ENCOUNTER!");
- eventTitleLabel.AddToClassList("combat-title");
- eventTitleLabel.style.fontSize = 24;
- eventTitleLabel.style.color = new Color(1f, 0.3f, 0.3f, 1f);
- eventTitleLabel.style.unityFontStyleAndWeight = FontStyle.Bold;
- eventTitleLabel.style.unityTextAlign = TextAnchor.MiddleCenter;
- eventTitleLabel.style.marginBottom = 15;
- popupPanel.Add(eventTitleLabel);
- // Description
- eventDescriptionLabel = new Label("Your party encounters hostile enemies!");
- eventDescriptionLabel.AddToClassList("combat-description");
- eventDescriptionLabel.style.fontSize = 16;
- eventDescriptionLabel.style.color = Color.white;
- eventDescriptionLabel.style.unityTextAlign = TextAnchor.MiddleCenter;
- eventDescriptionLabel.style.whiteSpace = WhiteSpace.Normal;
- eventDescriptionLabel.style.marginBottom = 20;
- popupPanel.Add(eventDescriptionLabel);
- // Enemy list container
- var enemySection = new VisualElement();
- enemySection.AddToClassList("enemy-section");
- enemySection.style.marginBottom = 25;
- var enemyHeader = new Label("🛡️ ENEMIES");
- enemyHeader.AddToClassList("enemy-header");
- enemyHeader.style.fontSize = 18;
- enemyHeader.style.color = new Color(1f, 0.8f, 0.2f, 1f);
- enemyHeader.style.unityFontStyleAndWeight = FontStyle.Bold;
- enemyHeader.style.unityTextAlign = TextAnchor.MiddleCenter;
- enemyHeader.style.marginBottom = 10;
- enemySection.Add(enemyHeader);
- enemyListContainer = new VisualElement();
- enemyListContainer.AddToClassList("enemy-list");
- enemyListContainer.style.backgroundColor = new Color(0.1f, 0.1f, 0.15f, 0.8f);
- enemyListContainer.style.borderTopLeftRadius = 5;
- enemyListContainer.style.borderTopRightRadius = 5;
- enemyListContainer.style.borderBottomLeftRadius = 5;
- enemyListContainer.style.borderBottomRightRadius = 5;
- enemyListContainer.style.paddingTop = 10;
- enemyListContainer.style.paddingBottom = 10;
- enemyListContainer.style.paddingLeft = 15;
- enemyListContainer.style.paddingRight = 15;
- enemySection.Add(enemyListContainer);
- popupPanel.Add(enemySection);
- // Button container
- var buttonContainer = new VisualElement();
- buttonContainer.AddToClassList("button-container");
- buttonContainer.style.flexDirection = FlexDirection.Row;
- buttonContainer.style.justifyContent = Justify.SpaceAround;
- buttonContainer.style.marginTop = 20;
- // Run Away button
- runAwayButton = new Button();
- runAwayButton.text = "🏃 RUN AWAY";
- runAwayButton.AddToClassList("run-button");
- runAwayButton.style.fontSize = 16;
- runAwayButton.style.backgroundColor = new Color(0.6f, 0.6f, 0.1f, 1f);
- runAwayButton.style.color = Color.white;
- runAwayButton.style.borderTopLeftRadius = 8;
- runAwayButton.style.borderTopRightRadius = 8;
- runAwayButton.style.borderBottomLeftRadius = 8;
- runAwayButton.style.borderBottomRightRadius = 8;
- runAwayButton.style.paddingTop = 12;
- runAwayButton.style.paddingBottom = 12;
- runAwayButton.style.paddingLeft = 20;
- runAwayButton.style.paddingRight = 20;
- runAwayButton.style.minWidth = 120;
- // Attack button
- attackButton = new Button();
- attackButton.text = "⚔️ ATTACK!";
- attackButton.AddToClassList("attack-button");
- attackButton.style.fontSize = 16;
- attackButton.style.backgroundColor = new Color(0.8f, 0.2f, 0.2f, 1f);
- attackButton.style.color = Color.white;
- attackButton.style.borderTopLeftRadius = 8;
- attackButton.style.borderTopRightRadius = 8;
- attackButton.style.borderBottomLeftRadius = 8;
- attackButton.style.borderBottomRightRadius = 8;
- attackButton.style.paddingTop = 12;
- attackButton.style.paddingBottom = 12;
- attackButton.style.paddingLeft = 20;
- attackButton.style.paddingRight = 20;
- attackButton.style.minWidth = 120;
- buttonContainer.Add(runAwayButton);
- buttonContainer.Add(attackButton);
- popupPanel.Add(buttonContainer);
- // Add hover effects
- SetupButtonHoverEffects();
- }
- void SetupButtonHoverEffects()
- {
- // Attack button hover effects
- attackButton.RegisterCallback<MouseEnterEvent>(evt =>
- {
- attackButton.style.backgroundColor = new Color(1f, 0.3f, 0.3f, 1f);
- attackButton.style.scale = new Vector2(1.05f, 1.05f);
- });
- attackButton.RegisterCallback<MouseLeaveEvent>(evt =>
- {
- attackButton.style.backgroundColor = new Color(0.8f, 0.2f, 0.2f, 1f);
- attackButton.style.scale = new Vector2(1f, 1f);
- });
- // Run away button hover effects
- runAwayButton.RegisterCallback<MouseEnterEvent>(evt =>
- {
- runAwayButton.style.backgroundColor = new Color(0.7f, 0.7f, 0.2f, 1f);
- runAwayButton.style.scale = new Vector2(1.05f, 1.05f);
- });
- runAwayButton.RegisterCallback<MouseLeaveEvent>(evt =>
- {
- runAwayButton.style.backgroundColor = new Color(0.6f, 0.6f, 0.1f, 1f);
- runAwayButton.style.scale = new Vector2(1f, 1f);
- });
- }
- /// <summary>
- /// Show the combat popup with enemy details
- /// </summary>
- public void ShowCombatEncounter(BattleEventData battleData, TravelEventContext context, string eventDescription)
- {
- if (isPopupActive || battleData == null) return;
- currentBattleData = battleData;
- currentContext = context;
- isPopupActive = true;
- // Update content
- eventDescriptionLabel.text = eventDescription;
- UpdateEnemyList(battleData);
- // Show popup with animation
- popupContainer.style.display = DisplayStyle.Flex;
- AnimatePopupIn();
- Debug.Log($"🎭 Combat popup shown: {battleData.enemyCount} enemies");
- }
- void UpdateEnemyList(BattleEventData battleData)
- {
- enemyListContainer.Clear();
- if (battleData.enemyCharacterData != null)
- {
- // Group enemies by type and count
- var enemyGroups = new Dictionary<string, int>();
- for (int i = 0; i < battleData.enemyCount; i++)
- {
- string enemyName = battleData.enemyCharacterData.enemyName;
- if (enemyGroups.ContainsKey(enemyName))
- enemyGroups[enemyName]++;
- else
- enemyGroups[enemyName] = 1;
- }
- // Create enemy display elements
- foreach (var group in enemyGroups)
- {
- var enemyElement = CreateEnemyElement(group.Key, group.Value, battleData.enemyCharacterData);
- enemyListContainer.Add(enemyElement);
- }
- }
- else
- {
- // Fallback for legacy string-based enemies
- var enemyElement = CreateFallbackEnemyElement(battleData.enemyType, battleData.enemyCount);
- enemyListContainer.Add(enemyElement);
- }
- }
- VisualElement CreateEnemyElement(string enemyName, int count, EnemyCharacterData enemyData)
- {
- var enemyContainer = new VisualElement();
- enemyContainer.style.flexDirection = FlexDirection.Row;
- enemyContainer.style.justifyContent = Justify.SpaceBetween;
- enemyContainer.style.alignItems = Align.Center;
- enemyContainer.style.marginBottom = 8;
- enemyContainer.style.paddingTop = 8;
- enemyContainer.style.paddingBottom = 8;
- enemyContainer.style.paddingLeft = 10;
- enemyContainer.style.paddingRight = 10;
- enemyContainer.style.backgroundColor = new Color(0.2f, 0.2f, 0.25f, 0.6f);
- enemyContainer.style.borderTopLeftRadius = 4;
- enemyContainer.style.borderTopRightRadius = 4;
- enemyContainer.style.borderBottomLeftRadius = 4;
- enemyContainer.style.borderBottomRightRadius = 4;
- // Enemy info section
- var enemyInfo = new VisualElement();
- enemyInfo.style.flexDirection = FlexDirection.Column;
- enemyInfo.style.flexGrow = 1;
- // Enemy name and count
- var nameLabel = new Label($"{count}x {enemyName}");
- nameLabel.style.fontSize = 16;
- nameLabel.style.color = new Color(1f, 0.9f, 0.7f, 1f);
- nameLabel.style.unityFontStyleAndWeight = FontStyle.Bold;
- enemyInfo.Add(nameLabel);
- // Enemy stats
- var statsLabel = new Label($"HP: {enemyData.maxHealth} | AC: {enemyData.armorClass} | Threat: {enemyData.threatLevel}");
- statsLabel.style.fontSize = 12;
- statsLabel.style.color = new Color(0.8f, 0.8f, 0.8f, 1f);
- enemyInfo.Add(statsLabel);
- // Weapon info
- if (enemyData.preferredWeapon != null)
- {
- var weaponLabel = new Label($"⚔️ {enemyData.preferredWeapon.itemName}");
- weaponLabel.style.fontSize = 11;
- weaponLabel.style.color = new Color(0.7f, 0.7f, 1f, 1f);
- enemyInfo.Add(weaponLabel);
- }
- enemyContainer.Add(enemyInfo);
- // Threat level indicator
- var threatIndicator = new VisualElement();
- threatIndicator.style.width = 60;
- threatIndicator.style.height = 20;
- threatIndicator.style.borderTopLeftRadius = 10;
- threatIndicator.style.borderTopRightRadius = 10;
- threatIndicator.style.borderBottomLeftRadius = 10;
- threatIndicator.style.borderBottomRightRadius = 10;
- threatIndicator.style.justifyContent = Justify.Center;
- threatIndicator.style.alignItems = Align.Center;
- // Color based on threat level
- Color threatColor = enemyData.threatLevel switch
- {
- <= 2 => new Color(0.3f, 0.8f, 0.3f, 1f), // Green - Easy
- <= 4 => new Color(0.8f, 0.8f, 0.3f, 1f), // Yellow - Medium
- <= 6 => new Color(0.9f, 0.5f, 0.2f, 1f), // Orange - Hard
- _ => new Color(0.9f, 0.2f, 0.2f, 1f) // Red - Very Hard
- };
- threatIndicator.style.backgroundColor = threatColor;
- var threatLabel = new Label($"T{enemyData.threatLevel}");
- threatLabel.style.fontSize = 11;
- threatLabel.style.color = Color.white;
- threatLabel.style.unityFontStyleAndWeight = FontStyle.Bold;
- threatIndicator.Add(threatLabel);
- enemyContainer.Add(threatIndicator);
- return enemyContainer;
- }
- VisualElement CreateFallbackEnemyElement(string enemyType, int count)
- {
- var enemyContainer = new VisualElement();
- enemyContainer.style.flexDirection = FlexDirection.Row;
- enemyContainer.style.justifyContent = Justify.SpaceBetween;
- enemyContainer.style.alignItems = Align.Center;
- enemyContainer.style.marginBottom = 8;
- enemyContainer.style.paddingTop = 8;
- enemyContainer.style.paddingBottom = 8;
- enemyContainer.style.paddingLeft = 10;
- enemyContainer.style.paddingRight = 10;
- enemyContainer.style.backgroundColor = new Color(0.2f, 0.2f, 0.25f, 0.6f);
- enemyContainer.style.borderTopLeftRadius = 4;
- enemyContainer.style.borderTopRightRadius = 4;
- enemyContainer.style.borderBottomLeftRadius = 4;
- enemyContainer.style.borderBottomRightRadius = 4;
- var nameLabel = new Label($"{count}x {enemyType}");
- nameLabel.style.fontSize = 16;
- nameLabel.style.color = new Color(1f, 0.9f, 0.7f, 1f);
- nameLabel.style.unityFontStyleAndWeight = FontStyle.Bold;
- var unknownLabel = new Label("Unknown Threat");
- unknownLabel.style.fontSize = 12;
- unknownLabel.style.color = new Color(0.6f, 0.6f, 0.6f, 1f);
- enemyContainer.Add(nameLabel);
- enemyContainer.Add(unknownLabel);
- return enemyContainer;
- }
- void AnimatePopupIn()
- {
- // Simple scale animation
- popupPanel.style.scale = new Vector2(0.7f, 0.7f);
- popupPanel.style.opacity = 0f;
- // Use coroutine for smooth animation
- StartCoroutine(AnimateScale(0.7f, 1f, 0f, 1f, animationDuration));
- }
- void AnimatePopupOut(System.Action onComplete = null)
- {
- StartCoroutine(AnimateScale(1f, 0.7f, 1f, 0f, animationDuration, () =>
- {
- popupContainer.style.display = DisplayStyle.None;
- onComplete?.Invoke();
- }));
- }
- System.Collections.IEnumerator AnimateScale(float fromScale, float toScale, float fromOpacity, float toOpacity, float duration, System.Action onComplete = null)
- {
- float elapsed = 0f;
- while (elapsed < duration)
- {
- elapsed += Time.deltaTime;
- float t = elapsed / duration;
- // Smooth easing
- t = t * t * (3f - 2f * t); // Smoothstep
- float currentScale = Mathf.Lerp(fromScale, toScale, t);
- float currentOpacity = Mathf.Lerp(fromOpacity, toOpacity, t);
- popupPanel.style.scale = new Vector2(currentScale, currentScale);
- popupPanel.style.opacity = currentOpacity;
- yield return null;
- }
- popupPanel.style.scale = new Vector2(toScale, toScale);
- popupPanel.style.opacity = toOpacity;
- onComplete?.Invoke();
- }
- void OnBackgroundClick(ClickEvent evt)
- {
- // Optionally close popup when clicking background
- // For now, we'll require explicit choice
- }
- void OnAttackClicked()
- {
- if (!isPopupActive) return;
- Debug.Log("⚔️ Player chose to ATTACK!");
- HidePopup(() =>
- {
- OnCombatDecision?.Invoke(true); // true = attack
- });
- }
- void OnRunAwayClicked()
- {
- if (!isPopupActive) return;
- Debug.Log("🏃 Player chose to RUN AWAY!");
- HidePopup(() =>
- {
- OnCombatDecision?.Invoke(false); // false = run away
- });
- }
- void HidePopup(System.Action onComplete = null)
- {
- if (!isPopupActive) return;
- isPopupActive = false;
- AnimatePopupOut(onComplete);
- }
- /// <summary>
- /// Check if the popup is currently active
- /// </summary>
- public bool IsActive => isPopupActive;
- /// <summary>
- /// Force close the popup (for cleanup)
- /// </summary>
- public void ForceClose()
- {
- if (isPopupActive)
- {
- isPopupActive = false;
- popupContainer.style.display = DisplayStyle.None;
- }
- }
- }
|