--================= NPC Firearm đứng nguyên =================--
--Xử lý OnUpdate các NPC có variable "npcQuestRifleAiming"
--NPC Firearm sẽ đứng yên, bắn, thay đạn, gỡ kẹt đạn
--Target tất cả zombie không phải là NPC của mod này
--===========================================================--

require "vl_quest_npc_server"

VLOCC["NPCFirearmCM"] = VLOCC["NPCFirearmCM"] or {}

assert(VLZB, "Stack trace: Missing dependency: VLZB - vl_npc_firearm_s.lua")
assert(VLIT, "Stack trace: Missing dependency: VLIT - vl_npc_firearm_s.lua")

local GetZombieByOnlineIDOrUID = VLZB.GetZombieByOnlineIDOrUID
local DelayFunction = VLF.DelayFunction
local GetZombieModDataQuest = VLZB.GetZombieModDataQuest
local NpcDoRifleShot = VLZB.NpcDoRifleShot
local GetItem = VLIT.GetItem
local IsCharExistAndAlive = VLZB.IsCharExistAndAlive

--Tính Hit Rate và Time Delay Attack
local function CalculateRateAndDelayAttack(wpnFullType, skillLevel, distance, isAuto)
    local weapon = GetItem(wpnFullType, "HandWeapon")
    if weapon then
        skillLevel = tonumber(skillLevel) or 5
        if skillLevel < 0 then skillLevel = 0 end
        
        local minRange = weapon:getMinRange()
        local maxRange = weapon:getMaxRange()
        
        if not distance then return nil end
        distance = tonumber(distance)
        if distance <= minRange or distance >= maxRange then return nil end
        
        local aimingTime = weapon:getAimingTime()
        local swingtime = weapon:getSwingTime()
        local recoilDelay = weapon:getRecoilDelay()
        
        --tỉ lệ bắn chính xác
        local hitChanceMod = weapon:getAimingPerkHitChanceModifier() * skillLevel
        local hitChance = weapon:getHitChance()
        local finalHitChance = hitChance + hitChanceMod
        
        --thời gian ngắm bắn --skillLevel càng cao thì aimingTime càng thấp
        local minMul, maxMul = 1, 10
        local t = skillLevel / maxMul
        local multiplier = maxMul - (maxMul - minMul) * t  --multiplier tối thiểu là tiệm cận minMul
        local bestTimeToAim = aimingTime * multiplier    --thời gian tối thiểu để ngắm bắn chính xác. skill càng cao, ngắm càng nhanh
        --local bestTimeToAim = (aimingTime * 10) / skillLevel    --thời gian tối thiểu để ngắm bắn chính xác. skill càng cao, ngắm càng nhanh
        local minDelayAttack = swingtime + recoilDelay  --thời gian tối thiểu delay giữa mỗi lần bắn
        local timeToAim, hitRate
        
        --Tính hitRate ảnh hưởng bởi thời gian ngắm và khoảng cách
        --nếu khoảng cách xa thì cần ngắm lâu hơn
        local function calculate(_min, _mid, _max)
            local minBestTimeToAim = bestTimeToAim * _min
            local midBestTimeToAim = bestTimeToAim * _mid   --càng cao trong min-max thì hitRate càng giảm
            local maxBestTimeToAim = bestTimeToAim * _max
            local minRandom = minBestTimeToAim > minDelayAttack and minBestTimeToAim or minDelayAttack
            local maxRandom = maxBestTimeToAim > minRandom and maxBestTimeToAim or minRandom + 15
            timeToAim = ZombRandFloat(minRandom, maxRandom)
            --lấy tạm giá trị timeToAim tỉ lệ cùng hitChance để tính hitRate
            local _timeToAim = timeToAim * (finalHitChance / 100)
            --tính % hitRate. 
            hitRate = math.floor((_timeToAim / midBestTimeToAim) * 100)
        end
        
        if distance > 30 then
            --calculate(1.7, 2.05, 2.4)
            calculate(2.0, 2.8, 3.0)
        elseif distance > 23 and distance <= 30 then
            --calculate(1.0, 1.35, 1.7)
            calculate(1.6, 2.2, 2.4)
        elseif distance > 16 and distance <= 23 then
            --calculate(0.8, 1.2, 1.6)  
            calculate(1.3, 1.7, 1.9) 
        elseif distance > 9 and distance <= 16 then
            --calculate(0.4, 0.7, 1.0)
            calculate(1.0, 1.25, 1.5)
        --nếu khoảng cách gần
        elseif distance <= 9 then
            calculate(0.1, 0.20, 0.35)
        end
        
        --chế độ bắn tự động. Thời gian ngắm GIẢM 5/10, chính xác GIẢM 3/10
        if isAuto == true then  
            local timeToAimAuto = math.floor(timeToAim * 0.5)
            if timeToAimAuto < minDelayAttack then
                timeToAimAuto = minDelayAttack
            end
            timeToAim = math.floor(timeToAimAuto)
            hitRate = math.floor(hitRate * 0.7)

        --chế độ bắn Single. Thời gian tối thiểu giữa các lần bắn là minDelayAttack * 2.5
        else
            timeToAim = math.floor(timeToAim)
            minDelayAttack = math.floor(minDelayAttack * 2.5)
            if timeToAim < minDelayAttack then
                timeToAim = minDelayAttack
            end
        end

        --print("Server---CalculateRateAndDelayAttack= ", hitRate, timeToAim, skillLevel, distance, maxRange, bestTimeToAim)
        return hitRate, timeToAim
    end
end
         
--Tính sát thương đầu ra
local function CalculateWeaponDamage(wpnFullType, skillLevel)
    local weapon = GetItem(wpnFullType, "HandWeapon")
    if weapon then
        skillLevel = tonumber(skillLevel) or 5
        local minDmg = weapon:getMinDamage()
        local maxDmg = weapon:getMaxDamage()
        local critMod = weapon:getAimingPerkCritModifier() * skillLevel --tăng thêm cho critChance
        local critDmgMulti = weapon:getCriticalDamageMultiplier()
        local critChance = weapon:getCriticalChance() + critMod
        if critChance > 95 then critChance = 95 end
        
        local finalDmg = ZombRandFloat(minDmg, maxDmg)
        
        --nếu critical hit
        if ZombRand(100) < critChance then
            --local _critDmgMulti = critDmgMulti * (1 + (critMod / 100))
            finalDmg = finalDmg * critDmgMulti
        end
        --print("Server---CalculateWeaponDamage= ", finalDmg, skillLevel)
        return finalDmg
     end   
end

--Server Only --Lấy BumpType để chọn hành động cho NPC
local function GetShootActionBumpType(zNPC, wpnFullType)
    if not zNPC or not wpnFullType then return nil end
    
    local zData = GetZombieModDataQuest(zNPC)
    if not zData.shotCount then zData.shotCount = 0 end
    
    local weapon = getScriptManager():getItem(wpnFullType)
    if weapon and weapon:isItemType(ItemType.WEAPON) and weapon:hasTag(ItemTag.FIREARM) then
        
        --print("Server---GetShootActionBumpType---shotCount= ", zData.shotCount)
        --Jammed --kẹt đạn -> gỡ kẹt
        local wpn = instanceItem(wpnFullType)
        if ZombRand(100) < wpn:getJamGunChance() then
            zData.shotCount = zData.shotCount + 1
            --zNPC:setVariable("vlVarRifleRack", true) --Server set không tác dụng
            return {"vlRifleShootEmpty", "Rack"}  --FIXME: khi làm nhiều vũ khí-> Bumptype khác nhau -> làm riêng function trả về BumbType tùy vào vũ khí
        end
        
        --Reload --Khi bắn hết băng đạn -> shot empty x1-x2 -> reload
        if zData.shotCount >= wpn:getMaxAmmo() then
            zData.shotCount = 0
            --zNPC:setVariable("vlVarRifleReload", true) --Server set không tác dụng
            return {"vlRifleShootEmpty", "Reload"}
        end
        
        --Shoot
        zData.shotCount = zData.shotCount + 1
        return {"vlRifleShoot", ""}
    end
end


--================= ONCLIENTCOMMAND =================--
VLOCC["NPCFirearmCM"]["RequestNPCShootZombie"] = function(player, args)  --{qStep = qStep, npcID = zID, targetID = targetID, wpnFullType = wpnFullType, dist = dist}
	local npcID, targetID = args.npcID, args.targetID
	local qStep, wpnFullType = args.qStep, args.wpnFullType
	local dist = args.dist
	if not VLF.var.requestedNSZ[npcID] then --Mỗi NPC chỉ chạy 1 lần, chạy xong thì 60 tick sau mới nhận yêu cầu tiếp
		VLF.var.requestedNSZ[npcID] = true --Không nhận thêm yêu cầu khi đang chạy
		
		local function ResetFlags(npcID, zNPC)
			if not isServer() then  --SP
				local zData = GetZombieModDataQuest(zNPC)
				zData.requestedNPCShootZ = nil
			end
			
			VLF.var.requestedNSZ[npcID] = nil
		end
		
		--local damage = CalculateWeaponDamage(wpnFullType, nil)
		--local hitRate, delay = CalculateRateAndDelayAttack(wpnFullType, nil, dist, true)
		--local isHit = hitRate and ZombRand(100) < hitRate and true or false
		local zNPC = GetZombieByOnlineIDOrUID(npcID)
		local target = GetZombieByOnlineIDOrUID(targetID)
		
		--if zNPC and target and not zNPC:isDead() and not target:isDead() and target:getHealth() > 0 then
		if IsCharExistAndAlive(zNPC) and IsCharExistAndAlive(target) then
			if zNPC:CanSee(target) then
				--yêu cầu các Clients: NPC quay mặt về mục tiêu được chọn và KHÔNG tìm mục tiêu khác nữa
				if isServer() then
					sendServerCommand("NPCFirearmCM_C", "NPCFocusOnTarget_C", {npcID = npcID, targetID = targetID})
				end
				
				local hitRate, delay = CalculateRateAndDelayAttack(wpnFullType, nil, dist, true)
				
				DelayFunction(function()
					local isHit = hitRate and ZombRand(100) < hitRate and true or false
					local damage = CalculateWeaponDamage(wpnFullType, nil)
					local bumpTypeTab = GetShootActionBumpType(zNPC, wpnFullType)
					--SP hoặc Server
					NpcDoRifleShot(zNPC, target, wpnFullType, bumpTypeTab[1], bumpTypeTab[2], delay, damage, isHit)
					--Client
					sendServerCommand("NPCFirearmCM_C", "RequestNPCShootZombie_C", {damage = damage, npcID = npcID, targetID = targetID, wpnFullType = wpnFullType, bumpType = bumpTypeTab[1], bumpTypeCase = bumpTypeTab[2], delay = delay, isHit = isHit})    --Multiplayer
					--print("Server---NPCShootZombie= ", isHit, math.floor(damage*100)/100, bumpTypeTab[1], bumpTypeTab[2], math.floor(target:getX()), math.floor(target:getY()), math.floor(target:getZ()))
					
					VLF.var.requestedNSZ[npcID] = nil
				end, delay)
			else
				--reset Flags SP + Server
				ResetFlags(npcID, zNPC)
				--reset Flag cho clients
				sendServerCommand("NPCFirearmCM_C", "ResetFlags_C", {npcID = npcID, zDataKey = "requestedNPCShootZ"})    --Multiplayer
				
			end
		else
			ResetFlags(npcID, zNPC)
		end
	end
end






