--[[
!!!THIS SCRIPT CONTAINS OVERRIDDEN VERSIONS OF THE FUNCTIONS IN BWO!!!
SHOULD THE OG DEV MAKE A MAJOR CHANGE THESE FUNCTIONS WILL EITHER CAUSE ERRORS OR BE OUTDATED IN LIGHT OF UPDATED FUNCTIONS
--]]
local debug = true
--imported locals from BWOEvents
local arrivalSound = function(x, y, sound)
    local player = getSpecificPlayer(0)
    local arrivalSoundX
    local arrivalSoundY
    if x < player:getX() then 
        arrivalSoundX = player:getX() - 30
    else
        arrivalSoundX = player:getX() + 30
    end

    if y < player:getY() then 
        arrivalSoundY = player:getY() - 30
    else
        arrivalSoundY = player:getY() + 30
    end

    local emitter = getWorld():getFreeEmitter(arrivalSoundX, arrivalSoundY, 0)
    emitter:setVolumeAll(0.5)
    emitter:playSound(sound)
end

local isInCircle = function(x, y, cx, cy, r)
    local d2 = (x - cx) ^ 2 + (y - cy) ^ 2
    return d2 <= r ^ 2
end

local findVehicleSpot2 = function(sx, sy)
    local player = getSpecificPlayer(0)
    local px = player:getX()
    local py = player:getY()
    local rmin = 20
    local rmax = 65
    local cell = getCell()
    for x=sx-rmax, sx+rmax, 5 do
        for y=sy-rmax, sy+rmax, 5 do
            if isInCircle(x, y, sx, sy, rmax) then
                if BanditUtils.HasZoneType(x, y, 0, "Nav") then
                    local dist = BanditUtils.DistToManhattan(x, y, px, py)
                    if dist > rmin then
                        local square = getCell():getGridSquare(x, y, 0)
                        if square then
                            local gt = BanditUtils.GetGroundType(square)
                            if gt == "street" then
                                local allFree = true
                                for dx=x-4, x+4 do
                                    for dy=y-4, y+4 do
                                        local dsquare = getCell():getGridSquare(dx, dy, 0)
                                        if dsquare then
                                            if not square:isFree(false) or square:getVehicleContainer() then
                                                allFree = false
                                            end
                                        end
                                    end
                                end
                                if allFree then
                                    return x, y
                                end
                            end
                        end
                    end
                end
            end
        end
    end
end

local spawnVehicle = function(x, y, vtype)
    local cell = getCell()
    local square = getCell():getGridSquare(x, y, 0)
    if not square then return end

    local vehicle = addVehicleDebug(vtype, IsoDirections.S, nil, square)
    if not vehicle then return end

    for i = 0, vehicle:getPartCount() - 1 do
        local container = vehicle:getPartByIndex(i):getItemContainer()
        if container then
            container:removeAllItems()
        end
    end

    vehicle:repair()
    vehicle:setTrunkLocked(true)
    for i=0, vehicle:getMaxPassengers() - 1 do 
        local part = vehicle:getPassengerDoor(i)
        if part then 
            local door = part:getDoor()
            if door then
                door:setLocked(true)
            end
        end
    end

    local key = vehicle:createVehicleKey()

    vehicle:getModData().BWO = {}
    vehicle:getModData().BWO.wasRepaired = true
    vehicle:repair()
    vehicle:setColor(0, 0, 0)
    vehicle:setGeneralPartCondition(80, 100)
    -- vehicle:putKeyInIgnition(key)
    -- vehicle:tryStartEngine(true)
    -- vehicle:engineDoStartingSuccess()
    -- vehicle:engineDoRunning()
    vehicle:setHeadlightsOn(false)
    vehicle:setLightbarLightsMode(3)

    return key
end

--imported locals from ZAHit

local function Hit(attacker, item, victim)
    -- Clone the attacker to create a temporary IsoPlayer
    local tempAttacker = BanditUtils.CloneIsoPlayer(attacker)

    -- Calculate distance between attacker and victim
    local dist = BanditUtils.DistTo(victim:getX(), victim:getY(), tempAttacker:getX(), tempAttacker:getY())
    local range = item:getMaxRange()
    if dist < range + 0.2 then
        BanditPlayer.WakeEveryone()

        local hitSound
        local veh = victim:getVehicle()
        
        if veh then
            hitSound = "HitVehicleWindowWithWeapon"
        else
            local chainsaw = tempAttacker:isPrimaryEquipped("AuthenticZClothing.Chainsaw")
            if chainsaw then
                hitSound = "BloodSplatter"
            else
                hitSound = item:getZombieHitSound()
            end
            if victim:isSprinting() or victim:isRunning() and ZombRand(5) == 1 then
                victim:clearVariable("BumpFallType")
                victim:setBumpType("stagger")
                victim:setBumpFall(true)
                victim:setBumpFallType("pushedBehind")
            else
                victim:setHitFromBehind(attacker:isBehind(victim))

                if instanceof(victim, "IsoZombie") then
                    victim:setHitAngle(attacker:getForwardDirection())
                    victim:setPlayerAttackPosition(victim:testDotSide(attacker))
                end

                if instanceof(victim, "IsoPlayer") then
                    FDUtils.DynamicMeleeHit(victim,item)
                    --local meleeItem = instanceItem("Base.Pencil")
                    --victim:Hit(meleeItem, getCell():getFakeZombieForHit(), 0.01, false, 0.01, false)
                    --[[local bodyDamage = victim:getBodyDamage()
                    if bodyDamage then
                        local health = bodyDamage:getOverallBodyHealth()
                        health = health - 5
                        bodyDamage:setOverallBodyHealth(health)
                    end]]
                else
                    victim:Hit(item, tempAttacker, 0.5, false, 1, false)
                end
                victim:setAttackedBy(attacker)
                --[[
                local bodyDamage = victim:getBodyDamage()
                if bodyDamage then
                    local health = bodyDamage:getOverallBodyHealth()
                    health = health + 12
                    if health > 100 then health = 100 end
                    bodyDamage:setOverallBodyHealth(health)
                end]]
            end
            victim:addBlood(0.6)
            
            local splatNo = item:getSplatNumber()
            for i=0, splatNo do
                victim:splatBlood(3, 0.3)
            end
            victim:splatBloodFloorBig()
            victim:playBloodSplatterSound()
            if instanceof(victim, "IsoPlayer") then
                victim:playerVoiceSound("PainFromFallHigh")
            end

            if victim:getHealth() <= 0 then 
                victim:Kill(getCell():getFakeZombieForHit(), true) 
            end
        end
        victim:playSound(hitSound)
        -- addSound(getPlayer(), victim:getX(), victim:getY(), victim:getZ(), 4, 50)
    end

    -- Clean up the temporary player after use
    tempAttacker:removeFromWorld()
    tempAttacker = nil
end

--imported locals

--Replace ZAHit.lua
local function HitOnWorkingReplace(bandit, task)
    bandit:faceLocation(task.x, task.y)
    local bumpType = bandit:getBumpType()

    if bumpType ~= task.anim then return false end
    
    if not task.hit and task.time <= 50 then

        local asn = bandit:getActionStateName()
        -- print ("HIT AS:" .. asn)
        if asn == "getup" or asn == "getup-fromonback" or asn == "getup-fromonfront" or asn == "getup-fromsitting"
                 or asn =="staggerback" or asn == "staggerback-knockeddown" then return false end

        task.hit = true
        Bandit.UpdateTask(bandit, task)

        local item = instanceItem(task.weapon)
        local enemy = BanditZombie.GetInstanceById(task.eid)
        if enemy then 
            local brainBandit = BanditBrain.Get(bandit)
            local brainEnemy = BanditBrain.Get(enemy)
            if not brainEnemy or not brainEnemy.clan or brainBandit.clan ~= brainEnemy.clan or (brainBandit.hostile and not brainEnemy.hostile) then 
                Hit (bandit, item, enemy)
                if task.weapon ~= "AuthenticZClothing.Chainsaw" then return false end
            end
        end

        if Bandit.IsHostile(bandit) then
            local player = BanditPlayer.GetPlayerById(task.eid)
            if player then
                local eid = BanditUtils.GetCharacterID(player)
                if player:isAlive() and eid == task.eid then
                    Hit (bandit, item, player)
                    if task.weapon ~= "AuthenticZClothing.Chainsaw" then return false end
                end
            end
        end

        return false

    end
    
    return false
end

--replace BWOEvents.lua
local function CallSWATReplace(params)
    if not BWOPopControl.SWAT.On then return end
    if BWOPopControl.SWAT.Cooldown > 0 then return end

    --FD CHANGE
    if(debug) then FDUtils.Log("Edited Swat Called") end
    if(not FDUtils.RandPer(BeyondOneWeek.swatRoll)) then 
        if(debug) then FDUtils.Log("roll failed") end
        BWOPopControl.SWAT.Cooldown = ZombRandFloat(tonumber(BeyondOneWeek.minimumQuickCooldown),tonumber(BeyondOneWeek.maximumQuickCooldown)) --on a failed roll it will extend the time by 5 minutes
        return 
    end
    if(debug) then FDUtils.Log("roll passed") end
    --FD CHANGE

    local player = getSpecificPlayer(0)
    local x, y = findVehicleSpot2(params.x, params.y)
    if not x or not y then return end

    local vehicleCount = player:getCell():getVehicles():size()
    if vehicleCount < 10 then
        spawnVehicle (x, y, "Base.PickUpVanLightsPolice")
        arrivalSound(x, y, "ZSPoliceCar1")

        local vparams = {}
        vparams.lightbar = true
        BWOScheduler.Add("VehiclesUpdate", vparams, 500)
    end
    
    local function AddBanditToTable() 
        config = {}
        config.clanId = 0
        config.hasRifleChance = 75
        config.hasPistolChance = 100
        config.rifleMagCount = ZombRand(1, 4)
        config.pistolMagCount = ZombRand(1, 3)

        local event = {}
        event.hostile = params.hostile
        event.occured = false
        event.program = {}
        event.program.name = "Bandit"
        event.program.stage = "Prepare"
        event.x = x + 6
        event.y = y + 6
        event.bandits = {}
                
        local bandit = BanditCreator.MakeFromWave(config)
        bandit.outfit = "ZSPoliceSpecialOps"
        bandit.accuracyBoost = FDUtils.RandFloat(0.8,1.2) --FD CHANGE accuracy boost slash detriment
        bandit.femaleChance = 0
        bandit.weapons.melee = "Base.Nightstick"
    end
    FDUtils.Recur(AddBanditToTable,ZombRand(2, 6)) --FD CHANGE make police more varied in number
            
    sendClientCommand(getSpecificPlayer(0), 'Commands', 'SpawnGroup', event)

    BWOPopControl.SWAT.Cooldown = SandboxVars.BanditsWeekOne.SWATCooldown -- 120
end

local function CallHazmatsReplace(params)
    
    if not BWOPopControl.Hazmats.On then return end
    if BWOPopControl.Hazmats.Cooldown > 0 then return end

    --FD CHANGE
    if(debug) then FDUtils.Log("Edited Swat Called") end
    if(not FDUtils.RandPer(BeyondOneWeek.hazmatRoll)) then 
        if(debug) then FDUtils.Log("roll failed") end
        BWOPopControl.Hazmats.Cooldown = ZombRandFloat(tonumber(BeyondOneWeek.minimumQuickCooldown),tonumber(BeyondOneWeek.maximumQuickCooldown)) --on a failed roll it will extend the time by 5 minutes
        return 
    end
    if(debug) then FDUtils.Log("roll passed") end
    --FD CHANGE

    local player = getSpecificPlayer(0)
    local x, y = findVehicleSpot2(params.x, params.y)
    if not x or not y then return end

    local vehicleCount = player:getCell():getVehicles():size()
    if vehicleCount < 10 then
        spawnVehicle (x, y, "Base.VanAmbulance")
        arrivalSound(x, y, "ZSPoliceCar1")

        local vparams = {}
        vparams.lightbar = true
        BWOScheduler.Add("VehiclesUpdate", vparams, 500)
    end
    local function AddBanditToTable() 
        config = {}
        config.clanId = 0
        config.hasRifleChance = 1
        config.hasPistolChance = 100
        config.rifleMagCount = 0
        config.pistolMagCount = ZombRand(1, 3)

        local event = {}
        event.hostile = false
        event.occured = false
        event.program = {}
        event.program.name = "Medic"
        event.program.stage = "Prepare"
        event.x = x + 6
        event.y = y + 6
        event.bandits = {}
                
        local bandit = BanditCreator.MakeFromWave(config)
        bandit.outfit = "HazardSuit"
        bandit.accuracyBoost = FDUtils.RandFloat(0.3,1.1) --these are scientists lol they're gonna be god awful shots
        bandit.weapons.melee = "Base.Scalpel"
        table.insert(event.bandits, bandit)
    end
    FDUtils.Recur(AddBanditToTable,ZombRand(1, 3)) --FD CHANGE make police more varied in number
            
    sendClientCommand(getSpecificPlayer(0), 'Commands', 'SpawnGroup', event)

    BWOPopControl.Hazmats.Cooldown = SandboxVars.BanditsWeekOne.HazmatCooldown -- 50
end

local function CallCopsReplace(params)
    if not BWOPopControl.Police.On then return end
    if BWOPopControl.Police.Cooldown > 0 then return end

    --FD CHANGE
    if(debug) then FDUtils.Log("Edited cop Called") end
    if(not FDUtils.RandPer(BeyondOneWeek.policeRoll)) then 
        if(debug) then FDUtils.Log("roll failed") end
        BWOPopControl.Police.Cooldown = ZombRandFloat(tonumber(BeyondOneWeek.minimumQuickCooldown),tonumber(BeyondOneWeek.maximumQuickCooldown)) --on a failed roll it will extend the time by 5 minutes
        return 
    end
    if(debug) then FDUtils.Log("roll passed") end
    --FD CHANGE

    local player = getSpecificPlayer(0)
    local x, y = findVehicleSpot2(params.x, params.y)
    if not x or not y then return end

    local vehicleCount = player:getCell():getVehicles():size()
    if vehicleCount < 10 then
        spawnVehicle (x, y, "Base.PickUpVanLightsPolice")
        arrivalSound (x, y, "ZSPoliceCar1")

        local vparams = {}
        vparams.lightbar = true
        BWOScheduler.Add("VehiclesUpdate", vparams, 500)
    end
    
    local function AddBanditToTable() 
        config = {}
        config.clanId = 0
        config.hasRifleChance = 1
        config.hasPistolChance = 75 --FD CHANGE to 75 to provide more varied combat
        config.rifleMagCount = 0
        config.pistolMagCount = ZombRand(1, 3) --FD CHANGE varied mag count to provide more varied combat

        local event = {}
        event.hostile = params.hostile
        event.occured = false
        event.program = {}
        event.program.name = "Police"
        event.program.stage = "Prepare"
        event.x = x + 6
        event.y = y + 6
        event.bandits = {}
                
        local bandit = BanditCreator.MakeFromWave(config)
        bandit.outfit = "Police"
        bandit.accuracyBoost = FDUtils.RandFloat(0.6,1) --FD CHANGE accuracy boost slash detriment
        bandit.weapons.melee = "Base.Nightstick"

        table.insert(event.bandits, bandit)
    end

    FDUtils.Recur(AddBanditToTable,ZombRand(1, 2)) --FD CHANGE make police more varied in number
            
    sendClientCommand(getSpecificPlayer(0), 'Commands', 'SpawnGroup', event)


    if SandboxVars.Bandits.General_ArrivalIcon then
        local icon = "media/ui/sheriff.png"
        local color
        if event.hostile then
            color = {r=1, g=0, b=0} -- red
            BWOPopControl.Police.Cooldown = SandboxVars.BanditsWeekOne.PoliceCooldown -- 30
        else
            color = {r=0, g=1, b=0} -- green
            BWOPopControl.Police.Cooldown = ZombRandFloat(tonumber(BeyondOneWeek.minimumQuickCooldown),tonumber(BeyondOneWeek.maximumQuickCooldown)) -- 30 reduce the wait greatly if its non-hostile (so you cant easily abuse this)
        end
        BanditEventMarkerHandler.setOrUpdate(getRandomUUID(), icon, 10, params.x, params.y, color)
    end
end

Events.OnGameBoot.Add(function()
    if(BeyondOneWeek.variedRaids) then
        BWOEvents.CallCops = CallCopsReplace
        BWOEvents.CallSWAT = CallSWATReplace
        BWOEvents.CallHazmats = CallHazmatsReplace
    end
    if(BeyondOneWeek.meleeRebalance) then
        ZombieActions.Hit.onWorking = HitOnWorkingReplace
    end
end)