Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- penDrone : CCombatDronePuppetEntity
- -- penFoe : CPuppetEntity
- local Math = import("Content/Shared/Scripts/Math.lua")
- local bDebug = false
- if bDebug then
- local srZero = CreateScriptedRenderer(worldGlobals.worldInfo, Math.qvIdentity)
- end
- local afShootChances = {0.25, 0.5, 0.75, 1}
- local aiNumberOfShots = {2, 2, 3, 5}
- local aiNumberOfShots = {3, 3, 3, 3}
- local tblBehavior = { DestinationWaitMin = 0.25, DestinationWaitMax = 0.5, PreShootWaitMin = 0.3, PreShootWaitMax = 0.6, PostDismountDelay = 1.0}
- local tblSoldierParams = {}
- tblSoldierParams.fShootingDelayMin = 3
- tblSoldierParams.fShootingDelayMax = 3
- tblSoldierParams.fShootingCount = 6
- -- character behavior hints
- local ID_KAMIKAZE = "Carry kamikaze"
- local ID_FLY_IN_FRONT = "Fly in front of ride"
- -- function called from SED so that script can provide list of behavior hints for user to choose using popup menu
- function globals.GetStringList()
- return ID_FLY_IN_FRONT, ID_KAMIKAZE
- end
- -- stop if executing from GUI
- if worldGlobals==nil then return end
- local worldInfo = worldGlobals.worldInfo --worldInfo : CWorldInfoEntity
- local BEH_NORMAL = 0
- local BEH_FLY_IN_FRONT = 1
- local kamikazeSpawnerName = "Kamikaze_Drone"
- --local spwKamikaze = worldInfo:GetEntityByName(kamikazeSpawnerName) -- spwKamikaze : CSpawnerEntity
- local spwKamikaze = worldInfo:GetAllEntitiesOfClassAndName("CSpawnerEntity", kamikazeSpawnerName)
- --[[print(#spwKamikaze)
- for i=1,#spwKamikaze,1 do
- local vPlace = spwKamikaze[i]:GetPos()
- print("("..vPlace.x..","..vPlace.y..","..vPlace.z..")")
- end]]--
- if (#spwKamikaze == 0) then
- conErrorF("Combat drone found unable to spawn kamikaze! Please resolve this by creating kamikaze spawner named \"" .. kamikazeSpawnerName .. "\" on your level.\n")
- spwKamikaze = nil
- else
- spwKamikaze = spwKamikaze[1]
- spwKamikaze:SetTotalCount(100000)
- end
- --[[if spwKamikaze ~= nil and spwKamikaze:GetClassName() == "CSpawnerEntity" then
- local totalCount = spwKamikaze:GetTotalCount()
- if totalCount < 10000 then
- conWarningF("Drone's kamikaze spawner total count is set to " .. totalCount .. ". It is recommended to set it to something above 10000 in order prevent undesired errors.\n")
- end
- end]]--
- local function CanBehave(penDrone)
- if penDrone == nil or IsDeleted(penDrone) then
- return false
- end
- while globals.worldInfo:IsCutSceneActive() do
- Wait(Delay(0.5))
- if IsDeleted(penDrone) then
- return false
- end
- end
- if not penDrone:IsAlive() then
- return false
- end
- return true
- end
- local function CanRiderBehave(penRider, penDrone)
- if penDrone == nil or IsDeleted(penDrone) then
- return false
- end
- if penRider == nil or IsDeleted(penRider) then
- return false
- end
- while globals.worldInfo:IsCutSceneActive() do
- Wait(Delay(0.5))
- if IsDeleted(penDrone) or IsDeleted(penRider) then
- return false
- end
- end
- if not penDrone:IsAlive() or not penRider:IsAlive() then
- return false
- end
- return penDrone:IsRiddenBy(penRider)
- end
- local function ShouldShoot(penDrone, fShootAngle)
- local penFoe = penDrone:GetFoe()
- if penFoe==nil then
- return false
- end
- local vFoeToMe = mthSafeNormalizeV3f(penDrone:GetPlacement():GetVect()-penFoe:GetPlacement():GetVect())
- vFoeToMe.y = 0
- vFoeToMe = mthSafeNormalizeV3f(vFoeToMe)
- local vFoeLookDirXZ = penFoe:GetLookDir(true)
- local fDot = mthDotV3f(vFoeLookDirXZ, vFoeToMe)
- local fAngle = mthRadToDeg(mthACosF(fDot))
- -- if we see foe
- if fAngle<fShootAngle then
- -- we can shoot
- return true
- end
- -- if we have already skipped several attacks
- penDrone.ctSkippedAttacks = penDrone.ctSkippedAttacks+1
- if penDrone.ctSkippedAttacks>4 then
- -- attack
- penDrone.ctSkippedAttacks = 0
- return true
- end
- return false
- end
- function worldGlobals.CombatDroneBehavior(penDrone)
- penDrone.BehaviorHint = penDrone:GetBehaviorHint()
- -- handler for kamikaze spawning functionality
- RunAsync(function()
- RunHandled(function() while not IsDeleted(penDrone) do Wait(Times(20, CustomEvent("OnStep"))) end end,
- OnEvery(CustomEvent("DroneSpawnKamikaze")), function(DroneSpawnKamikaze)
- if penDrone.BehaviorHint ~= ID_KAMIKAZE then return end
- local Denemy = DroneSpawnKamikaze:GetEventThrower()
- if Denemy ~= penDrone then return end
- if spwKamikaze == nil then
- conErrorF("Combat drone found unable to spawn kamikaze! Please resolve this by creating kamikaze spawner named \"" .. kamikazeSpawnerName .. "\" on your level.")
- return
- elseif spwKamikaze:GetClassName() ~= "CSpawnerEntity" then
- conErrorF("Found an entity named \"" .. kamikazeSpawnerName .. "\" which is not CSpawnerEntity. Perhaps you named a puppet, but not a spawner? (expand Spawner in puppet properties, there is a 'Name' field too)\n")
- return
- elseif spwKamikaze:GetRemainToSpawnCount() <= 0 then
- conErrorF("Combat drone's kamikaze spawner has run out of total spawn count! Combat drones cannot spawn kamikaze anymore.\nPlease, increase total count of combat drone's kamikaze spawner.\n")
- return
- end
- local Kamikazepuppet = spwKamikaze:SpawnOne() -- Kamikazepuppet : CLeggedCharacterEntity
- Denemy:MountPuppetToDrone(Kamikazepuppet, "Kamikaze")
- while not IsDeleted(Denemy) do
- local foe = Denemy:GetFoe()
- if foe ~= nil then
- if worldInfo:GetDistance(foe, Denemy) < 35 then
- --Denemy:PlayAnim("ThrowKamikaze")
- Denemy:DismountRider()
- if not IsDeleted(Kamikazepuppet) then
- Kamikazepuppet:SetPlacement(Denemy:GetAttachmentAbsolutePlacement("Kamikaze"))
- end
- break
- end
- end
- Wait(CustomEvent("OnStep"))
- end
- if not IsDeleted(Denemy) and Denemy:IsAlive() and not IsDeleted(Kamikazepuppet) and Kamikazepuppet:IsAlive() then
- local q4plc2 = Denemy:GetAttachmentAbsolutePlacement("Kamikaze")
- local foe = Denemy:GetFoe()
- if not IsDeleted(foe) then
- local foepls = foe:GetPlacement()
- local v3fdir = mthNormalize(mthVector3f(foepls.vx - q4plc2.vx, foepls.vy - q4plc2.vy, foepls.vz - q4plc2.vz))
- v3fdir.x = v3fdir.x * 25
- v3fdir.z = v3fdir.z * 25
- Kamikazepuppet:SetLinearVelocity(v3fdir)
- Kamikazepuppet:ForceFoe(foe)
- end
- end
- end
- )
- end) -- 1st async end
- Wait(Delay(0.1))
- if penDrone:IsMovedUsingSpecialMover() then
- Wait(CustomEvent(penDrone, "StoppedUsingSpecialMover"))
- end
- local strBehaviorHint = penDrone:GetBehaviorHint()
- if strBehaviorHint == ID_FLY_IN_FRONT then
- penDrone.iBehavior = BEH_FLY_IN_FRONT
- end
- -- disable mover
- penDrone:DisableAutoMove()
- -- counter of flights without shooting
- local iShootCounter = 1
- penDrone.ctSkippedAttacks = 0
- local strDifficulty = worldGlobals.worldInfo:GetGameDifficulty()
- local fDifficultyBias = 0
- strDifficulty="Normal"
- if strDifficulty=="Tourist" then
- fDifficultyBias = -1
- elseif strDifficulty=="Easy" then
- fDifficultyBias = -0.5
- elseif strDifficulty=="Normal" then
- fDifficultyBias = 0
- elseif strDifficulty=="Hard" then
- fDifficultyBias = 0.5
- elseif strDifficulty=="Serious" then
- fDifficultyBias = 1
- elseif strDifficulty=="Mental" then
- fDifficultyBias = 1.5
- end
- -- control rider's AI
- RunAsync(function()
- -- while the drone is alive
- while CanBehave(penDrone) do
- -- fetch the rider
- local penRider = penDrone:GetRiderPuppet()
- while penRider == nil and CanBehave(penDrone) do
- penRider = penDrone:GetRiderPuppet()
- Wait(Delay(1))
- end
- -- if a rider is found, get it's attack
- if penRider ~= nil then
- local idAttack = penDrone:GetRiderAttack()
- -- while rider can behave and is riding the drone
- while CanRiderBehave(penRider, penDrone) do
- -- freeze behaviour if requested
- if penDrone:ShouldFreezeAutoActing() then
- while CanRiderBehave(penRider, penDrone) and penDrone:ShouldFreezeAutoActing() do
- Wait(Delay(0.5))
- end
- end
- -- break if can not behave any more
- if not CanRiderBehave(penRider, penDrone) then
- break
- end
- -- wait between shootings
- local fDelay = mthLerpF(tblSoldierParams.fShootingDelayMin, tblSoldierParams.fShootingDelayMax, mthRndF())
- Wait(Delay(fDelay))
- -- break if can not behave any more
- if not CanRiderBehave(penRider, penDrone) then
- break
- end
- -- shoot at foe
- if penRider:GetFoe() and penRider:CanSeeEntity(penRider:GetFoe()) then
- local fShots = tblSoldierParams.fShootingCount
- Wait(penRider:AttackShoot(idAttack, fShots, false))
- end
- end
- end
- end
- end)
- while CanBehave(penDrone) do
- -- while automatic acting should be frozen or if there is a flight request
- while penDrone:ShouldFreezeAutoActing() or penDrone:IsFlightRequested() do
- if not CanBehave(penDrone) then
- break
- end
- -- if there is a flight requested
- if penDrone:IsFlightRequested() then
- local qvRequestedPlacement = penDrone:GetRequestedPlacement()
- Wait(penDrone:FlyToGivenDestination(qvRequestedPlacement, true, true, true))
- if not CanBehave(penDrone) then
- break
- end
- -- if dismount is requested
- if penDrone:IsDismountRequested() then
- penDrone:DismountRider()
- local fDismountDelay = tblBehavior.PostDismountDelay
- Wait(Delay(fDismountDelay))
- end
- end
- -- if freezing, skip some time
- if penDrone:ShouldFreezeAutoActing() then
- Wait(Delay(0.5))
- end
- end
- -- select delay due to difficulty factor
- local fDelay = 0.25
- fDelay = mthClampDnF(fDelay-fDifficultyBias,0.1)
- Wait(Delay(fDelay))
- if not CanBehave(penDrone) then
- break
- end
- local fDestinationWait = mthLerpF( tblBehavior["DestinationWaitMin"], tblBehavior["DestinationWaitMax"], mthRndF())
- local fPreShootWait = mthLerpF( tblBehavior["PreShootWaitMin"], tblBehavior["PreShootWaitMax"], mthRndF())
- local fShootChance = afShootChances[iShootCounter]
- local fShootAngle = 30
- local ctDrones = worldGlobals.worldInfo:GetEnemyCount("Drone_Combat", penFoe)
- if ctDrones<3 then
- fDestinationWait = fDestinationWait/4
- fPreShootWait = fPreShootWait/4
- fShootChance = fShootChance+0.5
- fShootAngle = fShootAngle+30
- elseif ctDrones<6 then
- fDestinationWait = fDestinationWait/2
- fPreShootWait = fPreShootWait/2
- fShootChance = fShootChance+0.25
- fShootAngle = fShootAngle+10
- end
- local bShooting = false
- local penFoe = penDrone:GetFoe()
- if penFoe then
- bShooting = mthRndF() <= fShootChance
- end
- local bAutoFly = true
- if penDrone.iBehavior == BEH_FLY_IN_FRONT then
- local penFoe = penDrone:GetFoe()
- if penFoe~=nil and penFoe:GetRide()~=nil then
- local penRide = penFoe:GetRide()
- local vXZVelocity = penRide:GetLinearVelocity()
- local qvRide = penRide:GetPlacement()
- local fH = mthLerpF(mthDegToRad(-70), mthDegToRad(70), mthRndF())
- local fP = mthLerpF(mthDegToRad(3), mthDegToRad(20), mthRndF())
- local fDistance = mthLerpF(20, 60, mthRndF())
- local qDirRel = mthEulerToQuaternion(mthVector3f(fH,fP,0))
- local qDirAbs = mthMulQ4f(qvRide:GetQuat(), qDirRel)
- local vDirAbs = mthQuaternionToDirection(qDirAbs)
- local vPos = qvRide:GetVect()+vDirAbs*fDistance+vXZVelocity*5
- local qvRequestedPlacement = penDrone:GetPlacement()
- qvRequestedPlacement:SetVect(vPos)
- bAutoFly = false
- if bDebug then
- srZero:AddPoint(vPos, 1)
- end
- Wait(penDrone:FlyToGivenDestination(qvRequestedPlacement, false, true, true))
- end
- end
- if bAutoFly then
- Wait(penDrone:FlyToNewDestination(bShooting))
- end
- if not CanBehave(penDrone) then
- break
- end
- if penDrone:GetFoe() and bShooting then
- if fDestinationWait>0 then
- Wait(Delay(fPreShootWait))
- end
- if CanBehave(penDrone) and ShouldShoot(penDrone, fShootAngle) then
- if penDrone:CanSeeEntity(penDrone:GetFoe()) then
- penDrone.ctSkippedAttacks = 0
- Wait(penDrone:AttackShoot("Shoot_Laser", aiNumberOfShots[iShootCounter], false))
- end
- end
- iShootCounter = 0
- else
- if fDestinationWait>0 then
- Wait(Delay(fDestinationWait))
- end
- end
- if iShootCounter < 4 then
- iShootCounter = iShootCounter + 1
- end
- end
- end
Advertisement
Add Comment
Please, Sign In to add comment