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;
}
}
}