using UnityEngine; using UnityEngine.UIElements; using System.Collections.Generic; using System.Linq; /// /// Beautiful UI popup for combat event encounters /// Shows enemy details and provides options to attack or run away /// 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 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(); 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(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(evt => { attackButton.style.backgroundColor = new Color(1f, 0.3f, 0.3f, 1f); attackButton.style.scale = new Vector2(1.05f, 1.05f); }); attackButton.RegisterCallback(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(evt => { runAwayButton.style.backgroundColor = new Color(0.7f, 0.7f, 0.2f, 1f); runAwayButton.style.scale = new Vector2(1.05f, 1.05f); }); runAwayButton.RegisterCallback(evt => { runAwayButton.style.backgroundColor = new Color(0.6f, 0.6f, 0.1f, 1f); runAwayButton.style.scale = new Vector2(1f, 1f); }); } /// /// Show the combat popup with enemy details /// 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(); 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); } /// /// Check if the popup is currently active /// public bool IsActive => isPopupActive; /// /// Force close the popup (for cleanup) /// public void ForceClose() { if (isPopupActive) { isPopupActive = false; popupContainer.style.display = DisplayStyle.None; } } }