-- =============================================================================
-- PURGATORY MAP SCRIPT - ENHANCED WAVE ESCALATION SYSTEM (v8.3 - FIXED POWER-UP PICKUP)
-- "Descent into Purgatory" - Each wave represents a deeper layer of torment
-- =============================================================================

-- Global player references (set in WorldLoaded)
local Neutral, Monsters, Aliens, Units

-- Wave system state tracking
local waveCount = {}
local eggStatus = {}
local gameStartTime = 0
local currentTormentLevel = 1
local maxTormentLevel = 9

-- Track spawned monsters for cleanup
local MissionOverCheckMonster = {}

-- Store wave systems for later use
local activeSystems = {}

-- Atmospheric state
local atmosphereIntensity = 0
local lastLightningTime = 0

-- Track individual eggs for proper wave disabling
local eggActors = {}

-- Mini objectives system
local activeObjectives = {}
local powerUpSpawned = false
local damageBoostActive = false
local damageBoostEndTime = 0
local lastPowerUpTime = 0
local lastBeaconTime = 0

-- FIXED: Define valid enemy types for power-up targeting
local validEnemyTypes = { "pvice", "imp", "reve", "bspi", "pinky", "manc", "exciter", "mechwarrior", "tripod" }

-- FIXED: Enhanced message debounce system with major event focus
local lastMessageTime = {}
local messageCooldown = 45 -- Increased cooldown for major events only
local messageHistory = {}
local mothershipPhaseActive = false -- FIXED: Track mothership phase

-- =============================================================================
-- DIFFICULTY AND UNIT CONFIGURATION
-- =============================================================================

local DIFFICULTY_CONFIG = {
  easy = {
    baseUnits = { "pvice" },
    waveMultiplier = 0.7,
    spawnDelay = 45,
    maxUnitsPerWave = 3,
    eliteChance = 0.1,
    pviceBonus = 1.0,
    heavyReduction = 1.0
  },
  normal = {
    baseUnits = { "pvice", "bspi" },
    waveMultiplier = 1.0,
    spawnDelay = 35,
    maxUnitsPerWave = 5,
    eliteChance = 0.25,
    pviceBonus = 1.0,
    heavyReduction = 1.0
  },
  hard = {
    baseUnits = { "pvice", "bspi", "reve" },
    waveMultiplier = 1.5,
    spawnDelay = 25,
    maxUnitsPerWave = 8,
    eliteChance = 0.4,
    pviceBonus = 2.2,
    heavyReduction = 0.75
  }
}

-- FIXED: Enhanced Torment Level Progression with proper HSLColor values and standalone messages
local TORMENT_LEVELS = {
  [1] = { 
    name = "Limbo",
    color = HSLColor.Gray,
    units = { "pvice" },
    spawnPattern = "trickle",
    atmosphere = "eerie_calm",
    timeRange = { 0, 2 },
    isMajor = true
  },
  [2] = { 
    name = "Lust", 
    color = HSLColor.Salmon,
    units = { "pvice", "bspi" },
    spawnPattern = "burst",
    atmosphere = "wind_howling",
    timeRange = { 2, 4 },
    isMajor = true
  },
  [3] = { 
    name = "Gluttony",
    color = HSLColor.Orange,
    units = { "pvice", "bspi", "reve" },
    spawnPattern = "coordinated",
    atmosphere = "rumbling",
    timeRange = { 4, 6 },
    isMajor = true
  },
  [4] = { 
    name = "Greed",
    color = HSLColor.Yellow,
    units = { "pvice", "reve", "bspi" },
    spawnPattern = "flanking",
    atmosphere = "metallic_grinding",
    timeRange = { 6, 8 },
    isMajor = true
  },
  [5] = { 
    name = "Wrath",
    color = HSLColor.Fuchsia,
    units = { "pvice", "bspi", "reve", "imp" },
    spawnPattern = "aggressive",
    atmosphere = "fire_crackling",
    timeRange = { 8, 10 },
    isMajor = true
  },
  [6] = { 
    name = "Heresy",
    color = HSLColor.Red,
    units = { "reve", "imp", "bspi", "pinky" },
    spawnPattern = "tactical",
    atmosphere = "demonic_chanting",
    timeRange = { 10, 12 },
    isMajor = true
  },
  [7] = { 
    name = "Violence", 
    color = HSLColor.DarkRed,
    units = { "imp", "pinky", "reve", "bspi" },
    spawnPattern = "assault",
    atmosphere = "war_drums",
    timeRange = { 12, 15 },
    isMajor = true
  },
  [8] = { 
    name = "Fraud",
    color = HSLColor.Purple,
    units = { "pvice", "reve", "imp", "pinky", "bspi" },
    spawnPattern = "deceptive",
    atmosphere = "whispers",
    timeRange = { 15, 18 },
    isMajor = true
  },
  [9] = { 
    name = "Treachery",
    color = HSLColor.Blue,
    units = { "manc", "imp", "pinky", "reve", "bspi", "pvice" },
    spawnPattern = "chaos",
    atmosphere = "screaming_void",
    timeRange = { 18, 999 },
    isMajor = true
  }
}

function DefineUnits(difficulty)
  local config = DIFFICULTY_CONFIG[difficulty] or DIFFICULTY_CONFIG.normal
  return config.baseUnits
end

function GetDifficultyConfig(difficulty)
  return DIFFICULTY_CONFIG[difficulty] or DIFFICULTY_CONFIG.normal
end

-- =============================================================================
-- FIXED: MAJOR EVENTS ONLY MESSAGE SYSTEM
-- =============================================================================

function DisplayMajorMessage(message, color, systemName)
  local currentTime = DateTime.GameTime
  local messageKey = systemName .. "_" .. message
  
  -- Check if this exact message was recently shown
  if lastMessageTime[messageKey] and 
     (currentTime - lastMessageTime[messageKey]) < DateTime.Seconds(messageCooldown) then
    return false
  end
  
  -- Display the message for major events only
  Media.DisplayMessage(message, "The Watcher", color)
  lastMessageTime[messageKey] = currentTime
  
  return true
end

-- =============================================================================
-- UTILITY FUNCTIONS
-- =============================================================================

function GetFlankPosition(centerPos)
  local offsets = {
    CPos.New(-8, -8), CPos.New(8, -8), CPos.New(-8, 8), CPos.New(8, 8),
    CPos.New(-12, 0), CPos.New(12, 0), CPos.New(0, -12), CPos.New(0, 12)
  }
  local offset = Utils.Random(offsets)
  return CPos.New(centerPos.X + offset.X, centerPos.Y + offset.Y)
end

function GetPatrolPosition(centerPos)
  local radius = Utils.RandomInteger(8, 20)
  local angle = Utils.RandomInteger(0, 360)
  local offsetX = math.floor(radius * math.cos(math.rad(angle)))
  local offsetY = math.floor(radius * math.sin(math.rad(angle)))
  return CPos.New(centerPos.X + offsetX, centerPos.Y + offsetY)
end

function GetScatterPosition(centerPos)
  local offsets = {
    CPos.New(-25, -20), CPos.New(25, -20), CPos.New(-20, 25), CPos.New(20, 25),
    CPos.New(-30, 0), CPos.New(30, 0), CPos.New(0, -30), CPos.New(0, 30),
    CPos.New(-18, -18), CPos.New(18, 18), CPos.New(-22, 15), CPos.New(22, -15)
  }
  local offset = Utils.Random(offsets)
  return CPos.New(centerPos.X + offset.X, centerPos.Y + offset.Y)
end

-- =============================================================================
-- ATMOSPHERIC EFFECTS (DEFINED EARLY)
-- =============================================================================

function TriggerAtmosphericEffect(atmosphereType)
  atmosphereIntensity = math.min(10, atmosphereIntensity + 1)
  
  if atmosphereType == "eerie_calm" then
    Media.PlaySound("ambloop1.aud")
  elseif atmosphereType == "wind_howling" then
    Media.PlaySound("wind1.aud")
  elseif atmosphereType == "rumbling" then
    Media.PlaySound("rumble1.aud")
  elseif atmosphereType == "fire_crackling" then
    Media.PlaySound("firebl3.aud")
  elseif atmosphereType == "demonic_chanting" then
    Media.PlaySound("tone1.aud")
  elseif atmosphereType == "war_drums" then
    Media.PlaySound("turrut1.aud")
  elseif atmosphereType == "screaming_void" then
    Media.PlaySound("scream1.aud")
  end
end

function TriggerMajorAtmosphericShift(torment)
  if currentTormentLevel >= 5 then
    Media.PlaySound("kaboom1.aud")
  end
  
  for i = 1, 5 do
    Trigger.AfterDelay(DateTime.Seconds(i * 2), function()
      Lighting.Flash("LightningStrike", 8)
      Media.PlaySound("thunder" .. Utils.RandomInteger(1, 6) .. ".aud")
    end)
  end
end

-- =============================================================================
-- ENHANCED WAVE SYSTEM (DEFINED EARLY)
-- =============================================================================

local WaveSystem = {}

function WaveSystem.Create(name, entryPoints, spawnPoints, difficulty)
  local config = GetDifficultyConfig(difficulty)
  
  waveCount[name] = 0
  eggStatus[name] = false

  local system = {
    name = name,
    entryPoints = entryPoints,
    spawnPoints = spawnPoints,
    difficulty = difficulty,
    config = config,
    active = false,
    nextWaveTime = DateTime.Seconds(config.spawnDelay)
  }

  return system
end

function WaveSystem.Start(system)
  if not system or system.active or mothershipPhaseActive then -- FIXED: Check mothership phase
    return
  end
  
  system.active = true
  
  -- Start the first wave after initial delay
  Trigger.AfterDelay(system.nextWaveTime, function()
    WaveSystem.SendWave(system)
  end)
end

function WaveSystem.UpdateTormentLevel(elapsedMinutes)
  local newLevel = currentTormentLevel
  
  for level, torment in pairs(TORMENT_LEVELS) do
    if elapsedMinutes >= torment.timeRange[1] and elapsedMinutes < torment.timeRange[2] then
      newLevel = level
      break
    end
  end
  
  if newLevel > currentTormentLevel then
    currentTormentLevel = newLevel
    local torment = TORMENT_LEVELS[currentTormentLevel]
    
    -- FIXED: Only show messages for major level transitions with standalone messages
    if torment.isMajor then
      Media.DisplayMessage("Descending to " .. torment.name .. "...", "The Watcher", torment.color)
      
      -- FIXED: Standalone atmospheric messages with independent timing
      Trigger.AfterDelay(DateTime.Seconds(3), function()
        if torment.name == "Limbo" then
          Media.DisplayMessage("The lost souls stir in the depths...", "The Watcher", torment.color)
        elseif torment.name == "Lust" then
          Media.DisplayMessage("Restless spirits emerge from the void...", "The Watcher", torment.color)
        elseif torment.name == "Gluttony" then
          Media.DisplayMessage("The gluttonous demons hunger for flesh...", "The Watcher", torment.color)
        elseif torment.name == "Greed" then
          Media.DisplayMessage("Greedy horrors guard their cursed domain...", "The Watcher", torment.color)        
        elseif torment.name == "Wrath" then
          Media.DisplayMessage("The wrathful demons bring destruction!", "The Watcher", torment.color)
        elseif torment.name == "Heresy" then
          Media.DisplayMessage("Heretical abominations rise from the depths...", "The Watcher", torment.color)
        elseif torment.name == "Violence" then
          Media.DisplayMessage("The violent legions march to war!", "The Watcher", torment.color)
         elseif torment.name == "Fraud" then
          Media.DisplayMessage("Fraudulent nightmares twist reality...", "The Watcher", torment.color)
        elseif torment.name == "Treachery" then
          Media.DisplayMessage("The frozen depths of treachery await!", "The Watcher", torment.color)
        end
      end)
      
      TriggerMajorAtmosphericShift(torment)
    end
  end
end

function WaveSystem.CalculateWaveIntensity(system, elapsedMinutes)
  local baseIntensity = 0.3
  local timeMultiplier = math.min(3.0, 1.0 + (elapsedMinutes / 10))
  local tormentMultiplier = 1.0 + (currentTormentLevel - 1) * 0.12
  local difficultyMultiplier = system.config.waveMultiplier
  
  return baseIntensity * timeMultiplier * tormentMultiplier * difficultyMultiplier
end

function WaveSystem.SelectUnitByTime(availableUnits, elapsedMinutes, config)
  local weights = {}
  
  for _, unit in ipairs(availableUnits) do
    if unit == "pvice" then
      weights[unit] = 100 * config.pviceBonus
      if elapsedMinutes < 4 then
        weights[unit] = weights[unit] * 2.5
      end
    elseif unit == "bspi" then
      weights[unit] = 80
    elseif unit == "reve" then
      weights[unit] = 70
      if elapsedMinutes >= 4 and elapsedMinutes < 12 then
        weights[unit] = weights[unit] * 1.5
      end
    elseif unit == "imp" then
      weights[unit] = 30 * config.heavyReduction
      if elapsedMinutes >= 8 and elapsedMinutes < 15 then
        weights[unit] = weights[unit] * 2
      elseif elapsedMinutes < 8 then
        weights[unit] = weights[unit] * 0.2
      end
    elseif unit == "pinky" then
      weights[unit] = 20 * config.heavyReduction
      if elapsedMinutes >= 11 and elapsedMinutes < 16 then
        weights[unit] = weights[unit] * 2.5
      elseif elapsedMinutes < 10 then
        weights[unit] = 3
      end
    elseif unit == "manc" then
      weights[unit] = 10 * config.heavyReduction
      if elapsedMinutes < 18 then
        weights[unit] = 2
      end
    else
      weights[unit] = 50
    end
  end
  
  local totalWeight = 0
  for _, weight in pairs(weights) do
    totalWeight = totalWeight + weight
  end
  
  local random = Utils.RandomInteger(1, totalWeight)
  local currentWeight = 0
  
  for unit, weight in pairs(weights) do
    currentWeight = currentWeight + weight
    if random <= currentWeight then
      return unit
    end
  end
  
  return Utils.Random(availableUnits)
end

function WaveSystem.GenerateEnhancedWaveComposition(system, torment, intensity, elapsedMinutes)
  local composition = {}
  local config = system.config
  local maxUnits = math.floor(config.maxUnitsPerWave * intensity)
  maxUnits = math.max(1, math.min(12, maxUnits))
  
  for i = 1, maxUnits do
    local unitType = WaveSystem.SelectUnitByTime(torment.units, elapsedMinutes, config)
    
    if Utils.RandomInteger(1, 100) <= (config.eliteChance * 100) then
      unitType = unitType
    end
    
    table.insert(composition, unitType)
  end
  
  return composition
end

function WaveSystem.SendWave(system)
  -- FIXED: Check mothership phase to disable demon spawning
  if not system or eggStatus[system.name] or not system.active or mothershipPhaseActive then 
    return 
  end
  
  -- Additional check for egg actor existence
  if eggActors[system.name] and eggActors[system.name].IsDead then
    eggStatus[system.name] = true
    system.active = false
    return
  end

  waveCount[system.name] = waveCount[system.name] + 1
  local elapsedMinutes = (DateTime.GameTime - gameStartTime) / DateTime.Minutes(1)
  
  -- Update torment level based on time ranges
  WaveSystem.UpdateTormentLevel(elapsedMinutes)
  
  -- Get current torment configuration
  local torment = TORMENT_LEVELS[currentTormentLevel]
  if not torment then
    return
  end
  
  -- Calculate wave intensity
  local waveIntensity = WaveSystem.CalculateWaveIntensity(system, elapsedMinutes)
  
  -- Generate enhanced wave composition
  local waveComposition = WaveSystem.GenerateEnhancedWaveComposition(system, torment, waveIntensity, elapsedMinutes)
  
  -- FIXED: Only display messages for major torment levels and less frequently (removed message logic)
  -- Messages now handled independently in UpdateTormentLevel

  -- Execute wave spawn with behavior pattern
  WaveSystem.SpawnWave(system, waveComposition, torment.spawnPattern)
  
  -- Schedule next wave
  WaveSystem.ScheduleNextWave(system, torment)
end

function WaveSystem.SpawnWave(system, composition, spawnPattern)
  if not composition or #composition == 0 or mothershipPhaseActive then -- FIXED: Check mothership phase
    return
  end

  -- Use MonsterWaypoints for spawn entry, Waypoints for targeting
  local entryPoint = Utils.Random(system.entryPoints).Location
  local targetPoint = Utils.Random(system.spawnPoints).Location
  
  if spawnPattern == "trickle" then
    for i, unitType in ipairs(composition) do
      Trigger.AfterDelay(DateTime.Seconds(i * 3), function()
        if not eggStatus[system.name] and not (eggActors[system.name] and eggActors[system.name].IsDead) and not mothershipPhaseActive then
          SendUnits(entryPoint, { unitType }, targetPoint, "hunt", system.name)
        end
      end)
    end
  elseif spawnPattern == "burst" then
    if not eggStatus[system.name] and not (eggActors[system.name] and eggActors[system.name].IsDead) and not mothershipPhaseActive then
      SendUnits(entryPoint, composition, targetPoint, "aggressive", system.name)
    end
  elseif spawnPattern == "flanking" then
    local midPoint = math.ceil(#composition / 2)
    local group1 = {}
    local group2 = {}
    
    for i = 1, midPoint do
      table.insert(group1, composition[i])
    end
    for i = midPoint + 1, #composition do
      table.insert(group2, composition[i])
    end
    
    if not eggStatus[system.name] and not (eggActors[system.name] and eggActors[system.name].IsDead) and not mothershipPhaseActive then
      SendUnits(entryPoint, group1, targetPoint, "flanking", system.name)
      Trigger.AfterDelay(DateTime.Seconds(8), function()
        if not eggStatus[system.name] and not (eggActors[system.name] and eggActors[system.name].IsDead) and not mothershipPhaseActive then
          local secondEntry = Utils.Random(system.entryPoints).Location
          SendUnits(secondEntry, group2, targetPoint, "flanking", system.name)
        end
      end)
    end
  elseif spawnPattern == "coordinated" then
    local groupSize = math.min(3, math.ceil(#composition / 2))
    for i = 1, #composition, groupSize do
      local group = {}
      for j = i, math.min(i + groupSize - 1, #composition) do
        table.insert(group, composition[j])
      end
      
      Trigger.AfterDelay(DateTime.Seconds((i - 1) * 4), function()
        if not eggStatus[system.name] and not (eggActors[system.name] and eggActors[system.name].IsDead) and not mothershipPhaseActive then
          SendUnits(entryPoint, group, targetPoint, "coordinated", system.name)
        end
      end)
    end
  elseif spawnPattern == "chaos" then
    for i, unitType in ipairs(composition) do
      local delay = Utils.RandomInteger(1, 20)
      local randomEntry = Utils.Random(system.entryPoints).Location
      local randomTarget = Utils.Random(system.spawnPoints).Location
      
      Trigger.AfterDelay(DateTime.Seconds(delay), function()
        if not eggStatus[system.name] and not (eggActors[system.name] and eggActors[system.name].IsDead) and not mothershipPhaseActive then
          SendUnits(randomEntry, { unitType }, randomTarget, "aggressive", system.name)
        end
      end)
    end
  else
    if not eggStatus[system.name] and not (eggActors[system.name] and eggActors[system.name].IsDead) and not mothershipPhaseActive then
      SendUnits(entryPoint, composition, targetPoint, "hunt", system.name)
    end
  end
end

function WaveSystem.ScheduleNextWave(system, torment)
  if not system.active or mothershipPhaseActive then -- FIXED: Check mothership phase
    return
  end

  local baseDelay = system.config.spawnDelay
  local intensityModifier = math.max(0.65, 1.0 - (currentTormentLevel / 18))
  local nextDelay = DateTime.Seconds(baseDelay * intensityModifier)
  
  system.nextWaveTime = nextDelay

  Trigger.AfterDelay(nextDelay, function()
    if system.active and not eggStatus[system.name] and not (eggActors[system.name] and eggActors[system.name].IsDead) and not mothershipPhaseActive then
      WaveSystem.SendWave(system)
    end
  end)
end

function WaveSystem.Stop(systemName)
  eggStatus[systemName] = true
  for _, system in ipairs(activeSystems) do
    if system.name == systemName then
      system.active = false
      break
    end
  end
end

function WaveSystem.StopAll()
  for name, _ in pairs(eggStatus) do
    eggStatus[name] = true
  end
  
  Utils.Do(activeSystems, function(system)
    system.active = false
  end)
end

-- =============================================================================
-- FIXED SPAWN LOGIC - CORRECTED SPAWN POINTS AND TARGETING
-- =============================================================================

SendUnits = function(entryCell, unitTypes, targetCell, behavior, eggName)
  behavior = behavior or "hunt"
  
  -- FIXED: Check mothership phase to prevent demon spawning
  if mothershipPhaseActive then
    return
  end
  
  -- FIXED: Check if the associated egg is still alive before spawning
  if eggName and eggStatus[eggName] then
    return -- Don't spawn if egg is destroyed
  end
  
  -- FIXED: Check if egg actor still exists
  if eggName and eggActors[eggName] and eggActors[eggName].IsDead then
    return -- Don't spawn if egg is dead
  end
  
  Reinforcements.Reinforce(Monsters, unitTypes, { entryCell }, 40, function(a)
    table.insert(MissionOverCheckMonster, a)
    
    -- Check if unit has AttackMove property before using it
    if not a.HasProperty("AttackMove") then
      Trigger.OnIdle(a, function(a)
        if not a.IsDead then
          a.Move(targetCell)
        end
      end)
      return
    end

    -- Enhanced behavior with anti-clustering and improved hunt logic
    if behavior == "flanking" then
      local flankPos = GetFlankPosition(targetCell)
      a.Move(flankPos)
      Trigger.OnIdle(a, function(a)
        if not a.IsDead then
          a.AttackMove(targetCell)
          Trigger.AfterDelay(DateTime.Seconds(8), function()
            if not a.IsDead then
              a.Hunt()
              Trigger.AfterDelay(DateTime.Seconds(6), function()
                if not a.IsDead then
                  local scatterPos = GetScatterPosition(targetCell)
                  a.Move(scatterPos)
                  Trigger.OnIdle(a, function(a)
                    if not a.IsDead then
                      a.Hunt()
                    end
                  end)
                end
              end)
            end
          end)
        end
      end)
    elseif behavior == "coordinated" then
      local delay = Utils.RandomInteger(3, 12)
      Trigger.AfterDelay(DateTime.Seconds(delay), function()
        if not a.IsDead then
          a.AttackMove(targetCell)
          Trigger.OnIdle(a, function(a)
            if not a.IsDead then
              a.Hunt()
              Trigger.AfterDelay(DateTime.Seconds(10), function()
                if not a.IsDead then
                  local patrolPos = GetPatrolPosition(targetCell)
                  a.Move(patrolPos)
                  Trigger.OnIdle(a, function(a)
                    if not a.IsDead then
                      a.Hunt()
                    end
                  end)
                end
              end)
            end
          end)
        end
      end)
    elseif behavior == "aggressive" then
      a.AttackMove(targetCell)
      Trigger.OnIdle(a, function(a)
        if not a.IsDead then
          a.Hunt()
          Trigger.AfterDelay(DateTime.Seconds(6), function()
            if not a.IsDead then
              local patrolPos = GetPatrolPosition(targetCell)
              a.Move(patrolPos)
              Trigger.OnIdle(a, function(a)
                if not a.IsDead then
                  a.Hunt()
                  Trigger.AfterDelay(DateTime.Seconds(8), function()
                    if not a.IsDead then
                      local scatterPos = GetScatterPosition(targetCell)
                      a.Move(scatterPos)
                    end
                  end)
                end
              end)
            end
          end)
        end
      end)
    else
      -- Default enhanced hunt behavior
      a.AttackMove(targetCell)
      Trigger.OnIdle(a, function(a)
        if not a.IsDead then
          a.Hunt()
          Trigger.AfterDelay(DateTime.Seconds(12), function()
            if not a.IsDead then
              local patrolPos = GetPatrolPosition(targetCell)
              a.Move(patrolPos)
              Trigger.OnIdle(a, function(a)
                if not a.IsDead then
                  a.Hunt()
                  Trigger.AfterDelay(DateTime.Seconds(10), function()
                    if not a.IsDead then
                      local scatterPos = GetScatterPosition(targetCell)
                      a.Move(scatterPos)
                      Trigger.OnIdle(a, function(a)
                        if not a.IsDead then
                          a.Hunt()
                        end
                      end)
                    end
                  end)
                end
              end)
            end
          end)
        end
      end)
    end
  end)
end

-- =============================================================================
-- ENHANCED OBJECTIVES AND POWER-UP SYSTEM - FIXED PLAYER-ONLY PICKUP
-- =============================================================================

local Objectives = {}

-- Helper function to check if a unit belongs to a valid player
function IsPlayerUnit(unit)
  if not unit or unit.IsDead then
    return false
  end
  
  -- Check if the unit belongs to a player (not Neutral, Monsters, or Aliens)
  local owner = unit.Owner
  if not owner then
    return false
  end
  
  -- Exclude enemy factions
  if owner == Neutral or owner == Monsters or owner == Aliens then
    return false
  end
  
  -- Additional check: ensure the owner is a valid combat player
  if owner.IsNonCombatant then
    return false
  end
  
  return true
end

-- FIXED: Enhanced power-up spawning with player-only pickup
function Objectives.SpawnPowerUp()
  local elapsedMinutes = (DateTime.GameTime - gameStartTime) / DateTime.Minutes(1)
  local timeSinceLastPowerUp = elapsedMinutes - lastPowerUpTime
  
  -- Spawn power-up every 5 minutes starting from minute 5
  if elapsedMinutes >= 5 and timeSinceLastPowerUp >= 5 then
    lastPowerUpTime = elapsedMinutes
    
    local spawnPoints = { Waypoint.Location, Waypoint1.Location, Waypoint2.Location, Waypoint3.Location, Waypoint4.Location, Waypoint5.Location, Waypoint6.Location, Waypoint7.Location }
    local powerUpLocation = Utils.Random(spawnPoints)
    
    local powerUp = Actor.Create("weaponscrate", true, { Location = powerUpLocation, Owner = Neutral })
    
    Media.DisplayMessage("POWER-UP: Divine Annihilation available!", "The Watcher", HSLColor.Gold)
    
    -- FIXED: Player-only collection trigger with enhanced validation
    local function checkCollection()
      if powerUp.IsDead then
        return
      end
      
      local nearbyUnits = Map.ActorsInCircle(powerUp.CenterPosition, WDist.FromCells(2))
      for _, unit in ipairs(nearbyUnits) do
        -- FIXED: Use proper player validation function
        if IsPlayerUnit(unit) then
          powerUp.Destroy()
          Objectives.ActivateNukeEffect()
          Media.DisplayMessage("Divine power flows through " .. unit.Owner.Name .. "'s forces!", "The Watcher", HSLColor.Gold)
          return
        end
      end
      
      Trigger.AfterDelay(DateTime.Seconds(1), checkCollection)
    end
    
    Trigger.AfterDelay(DateTime.Seconds(2), checkCollection)
    
    -- Power-up expires after 2 minutes
    Trigger.AfterDelay(DateTime.Minutes(2), function()
      if powerUp and not powerUp.IsDead then
        powerUp.Destroy()
        Media.DisplayMessage("POWER-UP: Divine power fades away...", "The Watcher", HSLColor.Gold)
      end
    end)
  end
end

-- FIXED: Power-up behavior to only target valid enemy types
function Objectives.ActivateNukeEffect()
  Media.DisplayMessage("DIVINE ANNIHILATION ACTIVATED! All demons vanquished!", "The Watcher", HSLColor.Gold)
  -- Media.PlaySound("chrono.aud")
  
  -- Visual effects sequence with enhanced lightning
  for i = 1, 8 do
    Trigger.AfterDelay(DateTime.Seconds(i * 0.5), function()
      Lighting.Flash("LightningStrike", 8)
      Media.PlaySound("kaboom1.aud")
    end)
  end
  
  -- FIXED: Only eliminate valid enemy unit types after visual buildup
  Trigger.AfterDelay(DateTime.Seconds(4), function()
    local validEnemies = {}
    
    -- Collect only valid enemy types from both Monster and Alien factions
    Utils.Do(Map.ActorsInBox(Map.TopLeft, Map.BottomRight, function(actor)
      if not actor.IsDead and (actor.Owner == Monsters or actor.Owner == Aliens) then
        -- Check if actor type is in our valid enemy types list
        for _, validType in ipairs(validEnemyTypes) do
          if actor.Type == validType then
            return true
          end
        end
      end
      return false
    end), function(enemy)
      table.insert(validEnemies, enemy)
    end)
    
    -- Kill all valid enemies using the Kill function
    Utils.Do(validEnemies, function(enemy)
      if not enemy.IsDead and enemy.HasProperty("Health") then
        enemy.Kill() -- Using Kill function as requested
      end
    end)
    
    -- Clear tracking arrays of killed units
    local newMissionOverCheck = {}
    Utils.Do(MissionOverCheckMonster, function(unit)
      if not unit.IsDead then
        table.insert(newMissionOverCheck, unit)
      end
    end)
    MissionOverCheckMonster = newMissionOverCheck
    
    Media.DisplayMessage("Divine wrath has cleansed " .. #validEnemies .. " demonic entities!", "The Watcher", HSLColor.Gold)
    
    -- Additional flash effect for dramatic finish
    Trigger.AfterDelay(DateTime.Seconds(1), function()
      Lighting.Flash("LightningStrike", 15)
      Media.PlaySound("thunder1.aud")
    end)
  end)
end

-- FIXED: Enhanced beacon spawning with player-only activation
function Objectives.CreateBeaconObjective()
  local elapsedMinutes = (DateTime.GameTime - gameStartTime) / DateTime.Minutes(1)
  local timeSinceLastBeacon = elapsedMinutes - lastBeaconTime
  
  -- Spawn beacon every 5 minutes starting from minute 8 (offset from power-ups)
  if elapsedMinutes >= 8 and timeSinceLastBeacon >= 5 then
    lastBeaconTime = elapsedMinutes
    local Waypoints = { Waypoint.Location, Waypoint1.Location, Waypoint2.Location, Waypoint3.Location, Waypoint4.Location, Waypoint5.Location, Waypoint6.Location, Waypoint7.Location }
    local beaconLocation = Utils.Random(Waypoints)
    -- local beaconLocation = Waypoint4.Location
    local beacon = Actor.Create("flare", true, { Location = beaconLocation, Owner = Neutral })
    
    Media.DisplayMessage("OBJECTIVE: Heavy Reinforcement Signal Detected!", "The Watcher", HSLColor.Gold)
    
    -- FIXED: Player-only beacon activation with enhanced validation
    local function checkBeaconProximity()
      if beacon.IsDead then
        return
      end
      
      local nearbyUnits = Map.ActorsInCircle(beacon.CenterPosition, WDist.FromCells(2))
      for _, unit in ipairs(nearbyUnits) do
        -- FIXED: Use proper player validation function
        if IsPlayerUnit(unit) then
          beacon.Destroy()
          Objectives.ActivateHeavyReinforcements()
          Media.DisplayMessage("Reinforcement beacon activated by " .. unit.Owner.Name .. "!", "The Watcher", HSLColor.Gold)
          return
        end
      end
      
      Trigger.AfterDelay(DateTime.Seconds(2), checkBeaconProximity)
    end
    
    Trigger.AfterDelay(DateTime.Seconds(2), checkBeaconProximity)
    
    -- Beacon expires after 3 minutes
    Trigger.AfterDelay(DateTime.Minutes(3), function()
      if beacon and not beacon.IsDead then
        beacon.Destroy()
        Media.DisplayMessage("OBJECTIVE: Heavy reinforcement signal expired!", "The Watcher", HSLColor.Gold)
      end
    end)
  end
end

function Objectives.ActivateHeavyReinforcements()
  Media.DisplayMessage("OBJECTIVE: HEAVY REINFORCEMENTS INCOMING!", "The Watcher", HSLColor.Gold)
  Media.PlaySound("reinfor1.aud")
  
  -- Enhanced visual effects sequence
  for i = 1, 5 do
    Trigger.AfterDelay(DateTime.Seconds(i * 2), function()
      Lighting.Flash("LightningStrike", 5)
      Media.PlaySound("thunder" .. Utils.RandomInteger(1, 6) .. ".aud")
    end)
  end
  
  -- Deploy reinforcements after buildup
  Trigger.AfterDelay(DateTime.Seconds(12), function()
    Objectives.DeployMammothTanks()
  end)
end

function Objectives.DeployMammothTanks()
  local reinforcementData = {
    { player = "Multi0", spawnWaypoint = "SPMulti0Waypoint", targetWaypoint = "SPMulti0" },
    { player = "Multi1", spawnWaypoint = "SPMulti1Waypoint", targetWaypoint = "SPMulti1" },
    { player = "Multi2", spawnWaypoint = "SPMulti2Waypoint", targetWaypoint = "SPMulti2" },
    { player = "Multi3", spawnWaypoint = "SPMulti3Waypoint", targetWaypoint = "SPMulti3" }
  }
  
  local totalDeployed = 0
  
  for _, data in ipairs(reinforcementData) do
    local player = Player.GetPlayer(data.player)
    if player and not player.IsNonCombatant then
      local spawnWaypoint = Map.NamedActor(data.spawnWaypoint)
      local targetWaypoint = Map.NamedActor(data.targetWaypoint)
      
      if spawnWaypoint and targetWaypoint then
        -- Deploy 3 Mammoth Tanks per active player with staggered timing
        for i = 1, 3 do
          Trigger.AfterDelay(DateTime.Seconds(i * 3), function()
            Reinforcements.Reinforce(player, { "4tnk" }, { spawnWaypoint.Location }, 30, function(tank)
              tank.Move(targetWaypoint.Location)
              
              -- Enhanced tank behavior
              Trigger.OnIdle(tank, function(tank)
                if not tank.IsDead then
                  tank.Hunt()
                  
                  -- Patrol behavior for better map coverage
                  Trigger.AfterDelay(DateTime.Seconds(15), function()
                    if not tank.IsDead then
                      local patrolPos = GetPatrolPosition(targetWaypoint.Location)
                      tank.Move(patrolPos)
                      Trigger.OnIdle(tank, function(tank)
                        if not tank.IsDead then
                          tank.Hunt()
                        end
                      end)
                    end
                  end)
                end
              end)
            end)
          end)
        end
        
        totalDeployed = totalDeployed + 3
        -- Media.DisplayMessage("Mammoth Tank squadron deployed for " .. player.Name, "The Watcher", HSLColor.Green)
      end
    end
  end
  
  -- Summary message
  Trigger.AfterDelay(DateTime.Seconds(10), function()
    Media.DisplayMessage("Heavy reinforcement complete: " .. totalDeployed .. " Mammoth Tanks deployed!", "The Watcher", HSLColor.Gold)
  end)
end

-- =============================================================================
-- BOSS ENCOUNTERS AND MOTHERSHIP SYSTEM
-- =============================================================================

local Boss = {}

function Boss.DeployMothership()
  -- FIXED: Set mothership phase active to disable demon spawning
  mothershipPhaseActive = true
  
  Media.DisplayMessage("WARNING: The Gates of the Final Circle Open!", "The Watcher", HSLColor.Red)
  Media.PlaySound("mothershipdeployed.aud")
  
  -- Stop all demon wave systems
  WaveSystem.StopAll()
  
  -- Trigger atmospheric effects directly
  TriggerMajorAtmosphericShift(TORMENT_LEVELS[9])

  Trigger.AfterDelay(DateTime.Seconds(7), function()
    Boss.CreateMothershipSequence()
  end)
end

function Boss.CreateMothershipSequence()
  Media.DisplayMessage("The Frozen Lake of Treachery reveals its guardian...", "The Watcher", HSLColor.Blue)
  Media.PlaySound("Chrono.aud")

  -- Dramatic buildup with enhanced lightning
  for i = 1, 5 do
    Trigger.AfterDelay(DateTime.Seconds(i * 1.5), function()
      Lighting.Flash("LightningStrike", 8)
      Media.PlaySound("thunder" .. Utils.RandomInteger(1, 6) .. ".aud")
    end)
  end

  Trigger.AfterDelay(DateTime.Seconds(8), function()
    Boss.SpawnFinalMothership()
  end)
end

function Boss.SpawnFinalMothership()
  -- Spawn the mothership safely
  local mothershipDestroy = Actor.Create("mothership", true, { 
    Location = MotherShipWaypoint.Location, 
    Owner = Aliens 
  })
  
  local mothership = Actor.Create("mothership", true, { 
    Location = MotherShipWaypoint.Location, 
    Owner = Aliens 
  })
  mothershipDestroy.Kill()
  Media.DisplayMessage("LUCIFER'S VESSEL HAS ARRIVED", "The Watcher", HSLColor.Blue)

  Trigger.AfterDelay(DateTime.Seconds(5), function()
    -- Environmental damage effect (not affecting players)
    Boss.TriggerEnvironmentalDamage()
    
    -- Start immediate alien wave spawning
    Boss.SpawnAlienHeavyWave()
    
    Trigger.OnKilled(mothership, function()
      MothershipDestroyed()
    end)
    
    -- Continuous alien wave spawning from mothership
    local function mothershipWaveTimer()
      if not mothership.IsDead then
        Boss.SpawnAlienHeavyWave()
        Trigger.AfterDelay(DateTime.Seconds(30), mothershipWaveTimer)
      end
    end
    
    Trigger.AfterDelay(DateTime.Seconds(20), mothershipWaveTimer)
  end)
end

function Boss.SpawnAlienHeavyWave()
  local difficulty = Map.LobbyOption("difficulty")
  local config = GetDifficultyConfig(difficulty)
  
  -- Enhanced alien composition with heavy units (balanced)
  local alienUnits = {}
  local elapsedMinutes = (DateTime.GameTime - gameStartTime) / DateTime.Minutes(1)
  
  -- Base units
  -- for i = 1, 3 do
  --   table.insert(alienUnits, "pvice")
  --   table.insert(alienUnits, "bspi")
  -- end
   if Utils.RandomInteger(1, 100) <= 20 then -- 20% chance
    table.insert(alienUnits, "exciter")
  end
  -- Add moderate amount of heavy units
  table.insert(alienUnits, "exciter")
  table.insert(alienUnits, "mechwarrior")
  
  -- Add boss unit occasionally (balanced)
  if Utils.RandomInteger(1, 100) <= 20 then -- 20% chance
    table.insert(alienUnits, "tripod")
    Media.DisplayMessage("Alien Tripod Detected!", "The Watcher", HSLColor.Cyan)

  end
  
  -- Additional units based on difficulty
  if difficulty == "hard" then
    table.insert(alienUnits, "exciter")
  end
  
  -- Spawn from mothership location targeting player areas
  local entryPoint = MotherShipWaypoint.Location
  local targetPoints = { Waypoint.Location, Waypoint1.Location, Waypoint2.Location, Waypoint3.Location }
  local targetPoint = Utils.Random(targetPoints)
  
  Reinforcements.Reinforce(Aliens, alienUnits, { entryPoint }, 40, function(a)
    table.insert(MissionOverCheckMonster, a)
    
    if a.HasProperty("AttackMove") then
      a.AttackMove(targetPoint)
      Trigger.OnIdle(a, function(a)
        if not a.IsDead then
          a.Hunt()
        end
      end)
    else
      a.Move(targetPoint)
    end
  end)
  
  Media.DisplayMessage("Alien heavy forces deployed: " .. #alienUnits .. " units", "The Watcher", HSLColor.Cyan)
end

function Boss.TriggerEnvironmentalDamage()
  Media.DisplayMessage("The mothership's arrival scorches the earth!", "The Watcher", HSLColor.Orange)
  
  -- Damage only environmental targets (trees, neutral structures)
  local environmentalTargets = Map.ActorsInCircle(MotherShipWaypoint.CenterPosition, WDist.FromCells(15), function(actor)
    return actor.HasProperty("Health") and (actor.Owner == Neutral and actor.Type ~= "flare" and actor.Type ~= "weaponscrate")
  end)
  
  Utils.Do(environmentalTargets, function(target)
    if target.HasProperty("InflictDamage") then
      target.InflictDamage(target, target.Health * 0.75, "environmental")
    end
  end)
  
  -- Enhanced visual effects for environmental damage
  for i = 1, 12 do
    Trigger.AfterDelay(DateTime.Seconds(i * 0.4), function()
      Lighting.Flash("LightningStrike", 4)
      -- if i % 3 == 0 then
      --   Media.PlaySound("kaboom1.aud")
      -- end
    end)
  end
end

-- =============================================================================
-- GAME FLOW MANAGEMENT
-- =============================================================================

local GameFlow = {}

function GameOver()
  if MissionOverCheckMonster then
    Utils.Do(MissionOverCheckMonster, function(a)
      if not a.IsDead and a.Owner == Monsters then
        a.Destroy()
      end
    end)
  end
end

function GameFlow.HandleEggDestruction()
  GameOver()
  WaveSystem.StopAll()

  local difficulty = Map.LobbyOption("difficulty")
  if difficulty ~= "easy" then
    Boss.DeployMothership()
  else
    Media.DisplayMessage("The eggs are destroyed! Easy victory achieved!", "The Watcher", HSLColor.Green)
    MothershipDestroyed()
  end
end

function GameFlow.SetupEggDestructionTriggers()
  eggActors = { 
    ["Egg"] = Egg,
    ["Egg1"] = Egg1, 
    ["Egg2"] = Egg2,
    ["Egg3"] = Egg3,
    ["Egg4"] = Egg4,
    ["Egg5"] = Egg5,
    ["Egg6"] = Egg6,
    ["Egg7"] = Egg7
  }
  
  -- Individual egg destruction triggers
  for systemName, eggActor in pairs(eggActors) do
    if eggActor and not eggActor.IsDead then
      Trigger.OnKilled(eggActor, function()
        WaveSystem.Stop(systemName)
      end)
    end
  end
  
  -- Global egg destruction trigger
  local eggActorsList = {}
  for _, eggActor in pairs(eggActors) do
    table.insert(eggActorsList, eggActor)
  end
  
  Trigger.OnAllRemovedFromWorld(eggActorsList, GameFlow.HandleEggDestruction)
end

function GameFlow.InitializeWaveSystems()
  local difficulty = Map.LobbyOption("difficulty")
  
  GameFlow.CreateWaypoints()

  -- Correct spawn point mapping
  local eggSystems = {
    { name = "Egg", entries = { MonsterWaypoint4 }, spawns = { Waypoint, Waypoint1 } },
    { name = "Egg1", entries = { MonsterWaypoint1 }, spawns = { Waypoint2, Waypoint3 } },
    { name = "Egg2", entries = { MonsterWaypoint6 }, spawns = { Waypoint4, Waypoint5 } },
    { name = "Egg3", entries = { MonsterWaypoint3 }, spawns = { Waypoint6, Waypoint7 } },
    { name = "Egg4", entries = { MonsterWaypoint }, spawns = { Waypoint1, Waypoint2 } },
    { name = "Egg5", entries = { MonsterWaypoint5 }, spawns = { Waypoint3, Waypoint4 } },
    { name = "Egg6", entries = { MonsterWaypoint2 }, spawns = { Waypoint5, Waypoint6 } },
    { name = "Egg7", entries = { MonsterWaypoint7 }, spawns = { Waypoint7, Waypoint } }
  }
  
  for _, eggData in ipairs(eggSystems) do
    if eggData.entries and eggData.spawns then
      local system = WaveSystem.Create(eggData.name, eggData.entries, eggData.spawns, difficulty)
      if system then
        table.insert(activeSystems, system)
      end
    end
  end
end

function GameFlow.CreateWaypoints()
  -- Waypoints are already defined in the map, no need to recreate them
end

function GameFlow.StartPeriodicChecks()
  local function periodicCheck()
    local elapsedMinutes = (DateTime.GameTime - gameStartTime) / DateTime.Minutes(1)
    
    Objectives.SpawnPowerUp()
    Objectives.CreateBeaconObjective()
    
    Trigger.AfterDelay(DateTime.Seconds(30), periodicCheck)
  end
  
  Trigger.AfterDelay(DateTime.Seconds(30), periodicCheck)
end

-- =============================================================================
-- LEGACY FUNCTIONS FOR COMPATIBILITY
-- =============================================================================

function AlienSendWave()
  -- Redirect to new enhanced system
  Boss.SpawnAlienHeavyWave()
end

-- FIXED: Enhanced MothershipDestroyed function with safe player validation
function MothershipDestroyed()
  Media.DisplayMessage("VICTORY! The gates of purgatory are sealed!", "The Watcher", HSLColor.Green)
  Media.PlaySound("missionaccomplished.aud")
  
  -- Victory celebration effects
  for i = 1, 8 do
    Trigger.AfterDelay(DateTime.Seconds(i * 2), function()
      Lighting.Flash("LightningStrike", 10)
      Media.PlaySound("thunder" .. Utils.RandomInteger(1, 6) .. ".aud")
    end)
  end
  
  GameOver()
  
  -- FIXED: Safe objective completion with validation
  Trigger.AfterDelay(DateTime.Seconds(15), function()
    Utils.Do(Units, function(player)
      -- FIXED: Validate player has required properties before accessing
      if player and player.IsLocalPlayer and player.HasProperty and player.HasProperty("GetPrimaryObjectives") then
        local objectives = player.GetPrimaryObjectives()
        if objectives and #objectives > 0 then
          player.MarkCompletedObjective(objectives[1])
        end
      elseif player and player.IsLocalPlayer then
        -- Fallback: Create a generic victory condition if GetPrimaryObjectives doesn't exist
        Media.DisplayMessage("Victory achieved for " .. (player.Name or "Player"), "The Watcher", HSLColor.Green)
      end
    end)
  end)
end

-- =============================================================================
-- ENHANCED ATMOSPHERIC EFFECTS
-- =============================================================================

local Environment = {}

function Environment.StartLightningLoop()
  local function lightningCheck()
    local currentTime = DateTime.GameTime
    local timeSinceLastLightning = currentTime - lastLightningTime
    
    local baseChance = 300
    local tormentModifier = currentTormentLevel * 50
    local lightningChance = math.max(50, baseChance - tormentModifier)
    
    if Utils.RandomInteger(1, lightningChance) <= 10 then
      local flashDelay = Utils.RandomInteger(1, 15)
      
      Lighting.Flash("LightningStrike", flashDelay)
      
      Trigger.AfterDelay(flashDelay, function()
        local thunderSound = "thunder" .. Utils.RandomInteger(1, 6) .. ".aud"
        Media.PlaySound(thunderSound)
      end)
      
      lastLightningTime = currentTime
    end

    local nextCheck = math.max(15, 45 - (currentTormentLevel * 3))
    Trigger.AfterDelay(DateTime.Seconds(nextCheck), lightningCheck)
  end

  Trigger.AfterDelay(DateTime.Seconds(30), lightningCheck)
end

function Environment.StartEggGlowEffects()
  local function eggGlowCheck()
    for _, system in ipairs(activeSystems) do
      if system.active and not eggStatus[system.name] then
        local timeToNextWave = system.nextWaveTime
        if timeToNextWave and timeToNextWave <= DateTime.Seconds(10) then
          Lighting.Flash("LightningStrike", 3)
          Media.PlaySound("tone1.aud")
          
          Trigger.AfterDelay(DateTime.Seconds(5), function()
            if system.active and not eggStatus[system.name] then
              Lighting.Flash("LightningStrike", 2)
            end
          end)
        end
      end
    end
    
    Trigger.AfterDelay(DateTime.Seconds(5), eggGlowCheck)
  end
  
  Trigger.AfterDelay(DateTime.Seconds(30), eggGlowCheck)
end

-- =============================================================================
-- MAIN INITIALIZATION
-- =============================================================================

function WorldLoaded()
  -- Initialize player references
  Neutral = Player.GetPlayer("Neutral")
  Monsters = Player.GetPlayer("Monsters")  
  Aliens = Player.GetPlayer("Aliens")
  Units = Player.GetPlayers(function(p) return not p.IsNonCombatant end)
  
  gameStartTime = DateTime.GameTime
  
  local difficulty = Map.LobbyOption("difficulty")
  local difficultyName = "Medium"
  if difficulty == "easy" then
    difficultyName = "Easy"
  elseif difficulty == "hard" then
    difficultyName = "Hard"
  end
  
  Media.DisplayMessage("Welcome to Purgatory! Difficulty: " .. difficultyName, "The Watcher", HSLColor.White)
  Media.DisplayMessage("Survive escalating waves of demonic horrors.", "The Watcher", HSLColor.Gray)
  
  Trigger.AfterDelay(DateTime.Seconds(3), function()
    Media.DisplayMessage("Destroy all eggs to seal the gates of purgatory!", "The Watcher", HSLColor.Gold)
  end)
  
  -- Initialize in correct order
  GameFlow.SetupEggDestructionTriggers()
  GameFlow.InitializeWaveSystems()
  
  -- Start wave systems after delay
  Trigger.AfterDelay(DateTime.Seconds(10), function()
    Utils.Do(activeSystems, function(system)
      WaveSystem.Start(system)
    end)
  end)
  
  Environment.StartLightningLoop()
  Environment.StartEggGlowEffects()
  GameFlow.StartPeriodicChecks()
  
  Media.DisplayMessage("The descent begins... May fortune favor the brave!", "The Watcher", HSLColor.Salmon)
end