--- COMBATCORE Common Module -- anim0 : Idle -- anim1 : Move -- anim2 : Punch/Kick/Bite -- anim3 : Hurt -- sound0 : Start moving A -- sound1 : Start strike -- sound2 : Start moving B -- sound3 : Get hurt A -- sound4 : Get hurt B module_agro = require "scriptbank\\ai\\module_agro" module_cameraoverride = require "scriptbank\\ai\\module_cameraoverride" local RayCast = IntersectAll local module_combatcore = {} local function getPlayerPos2() return g_PlayerPosX, g_PlayerPosZ end local function getPlayerPos3() return g_PlayerPosX, g_PlayerPosY, g_PlayerPosZ end local function ai_CanSeePlayer( e ) local px, py, pz = getPlayerPos3() local Ent = g_Entity[e] return RayCast( px, py + 50, pz, Ent.x, Ent.y + 50, Ent.z, 0 ) == Ent.obj end function module_combatcore.init( e, startstate, coverstate ) local Ent = g_Entity[e] CharacterControlManual(e) AISetEntityControl(Ent.obj, AI_MANUAL) ai_bot_oldhealth[e] = Ent.health ai_bot_coverindex[e] = coverstate ai_bot_state[e] = startstate ai_bot_substate[e] = 0 ai_bot_targetx[e] = nil ai_bot_pointtime[e] = -1 ai_bot_patroltime[e] = 0 ai_bot_targetx[e] = Ent.x ai_bot_targety[e] = Ent.y ai_bot_targetz[e] = Ent.z end function module_combatcore.findcover(e, x, y, z) local CoverIndex = -1 local pClosest = math.huge for ca = 1, AIGetTotalCover() do local pDX, pDZ = x - AICoverGetPointX(ca), z - AICoverGetPointZ(ca) pDist = pDX*pDX + pDZ*pDZ if pDist < pClosest and pDist < 1000 * 1000 then pClosest = pDist CoverIndex = ca end end if CoverIndex > -1 then local px, pz = getPlayerPos2() local tdx, tdz = x - px, z - pz local tbotdd = tdx*tdx + tdz*tdz tdx = AICoverGetPointX(CoverIndex) - px tdz = AICoverGetPointZ(CoverIndex) - pz local tcoverdd = tdx*tdx + tdz*tdz if tbotdd < tcoverdd then CoverIndex = -1 end end return CoverIndex end function module_combatcore.atcover(e) local Ent = g_Entity[e] local CoverIndex = ai_bot_coverindex[e] if CoverIndex ~= -1 then tdx = AICoverGetPointX(CoverIndex) - Ent.x tdz = AICoverGetPointZ(CoverIndex) - Ent.z local tcoverdd = tdx*tdx + tdz*tdz if tcoverdd < 50 * 50 then return true end end return false end function module_combatcore.detectplayer(e, AIObjNo, PlayerDist, CanFire, detectstate) local Ent = g_Entity[e] if Ent.activated == 2 or (PlayerDist < AIGetEntityViewRange(AIObjNo) and ( (Ent.plrvisible == 1 and ai_CanSeePlayer( e )) or ai_bot_coverindex[e] == -2)) then if Ent.activated == 2 then ai_bot_targetx[e], ai_bot_targety[e], ai_bot_targetz[e] = getPlayerPos3() ai_bot_substate[e] = 0 SetActivated(e, 0) else if ai_bot_targetx[e] == nil then if ai_bot_coverindex[e] ~= -2 then ai_bot_coverindex[e] = module_combatcore.findcover(e, Ent.x, Ent.y, Ent.z) if ai_bot_coverindex[e] > 0 then ai_bot_targetx[e] = AICoverGetPointX(ai_bot_coverindex[e]) ai_bot_targety[e] = g_PlayerPosY ai_bot_targetz[e] = AICoverGetPointZ(ai_bot_coverindex[e]) end end if ai_bot_coverindex[e] < 0 then ai_bot_coverindex[e] = -2 end end if ai_bot_coverindex[e] == -2 and PlayerDist < AIGetEntityViewRange(AIObjNo) then ai_bot_targetx[e], ai_bot_targety[e], ai_bot_targetz[e] = getPlayerPos3() end end if ai_bot_targetx[e] ~= nil then if ai_bot_substate[e] == 0 then AIEntityGoToPosition(AIObjNo, ai_bot_targetx[e], ai_bot_targety[e], ai_bot_targetz[e]) if AIGetEntityIsMoving(AIObjNo) == 1 then ai_bot_state[e] = detectstate PlaySound(e, 0 + (math.random(0,1)*2)) else if Ent.plrvisible == 1 and ai_CanSeePlayer( e ) then RotateToPlayer(e) if CanFire then module_combatcore.fireweapon(e) end ai_bot_targetx[e], ai_bot_targety[e], ai_bot_targetz[e] = getPlayerPos3() AIEntityGoToPosition(AIObjNo, ai_bot_targetx[e], ai_bot_targety[e], ai_bot_targetz[e]) end end end end end end function module_combatcore.idle(e, AIObjNo, PlayerDist, CanFire, detectstate) CharacterControlManual(e) AISetEntityControl(AIObjNo, AI_MANUAL) if ai_bot_state[e] == ai_state_startidle then ai_bot_state[e] = ai_state_idle SetAnimation(0) LoopAnimation(e) SetAnimationSpeedModulation(e, 1.0) end if ai_bot_state[e] == ai_state_idle then module_combatcore.detectplayer(e, AIObjNo, PlayerDist, CanFire, detectstate) module_combatcore.donotmove(e) end end function module_combatcore.patrol(e, AIObjNo, PlayerDist, MoveType, CanFire, detectstate, stopstate) local Ent = g_Entity[e] if ai_bot_pointtime[e] == -1 then ai_bot_pointtime[e] = 0 StartTimer(e) end if ai_bot_state[e] == ai_state_findpatrolpath and ai_bot_pointtime[e] == 0 then local PathIndex = -1 local PointIndex = -1 local pClosest = math.huge for pa = 1, AIGetTotalPaths() do for po = 1, AIGetPathCountPoints(pa) do local pDX = Ent.x - AIPathGetPointX(pa, po) local pDY = Ent.y - AIPathGetPointY(pa, po) local pDZ = Ent.z - AIPathGetPointZ(pa, po) local pDist = pDX*pDX + pDY*pDY + pDZ*pDZ if pDist < pClosest and pDist < 200*200 then pClosest = pDist PathIndex = pa PointIndex = po end end -- po end -- pa ai_bot_pathindex[e] = PathIndex if PathIndex > -1 then ai_bot_state[e] = ai_state_startpatrol ai_bot_pointdirection[e] = 1 ai_bot_pointindex[e] = PointIndex ai_bot_pointmax[e] = AIGetPathCountPoints(PathIndex) else ai_bot_state[e] = ai_state_startidle end end if ai_bot_state[e] == ai_state_startpatrol then if ai_bot_pathindex[e] ~= -1 then ai_bot_state[e] = ai_state_patrol ai_bot_pointtime[e] = g_Time + 100 SetAnimationSpeedModulation(e, 0.0) StartTimer(e) else ai_bot_state[e] = ai_state_idle SetAnimation(0) LoopAnimation(e) SetAnimationSpeedModulation(e, 1.0) end end if ai_bot_state[e] == ai_state_patrol then local patrolx = AIPathGetPointX(ai_bot_pathindex[e], ai_bot_pointindex[e]) local patroly = AIPathGetPointY(ai_bot_pathindex[e], ai_bot_pointindex[e]) local patrolz = AIPathGetPointZ(ai_bot_pathindex[e], ai_bot_pointindex[e]) module_combatcore.moveandavoid(e, AIObjNo, PlayerDist, MoveType, patrolx, patroly, patrolz, stopstate) if AIGetEntityIsMoving(AIObjNo) == 1 then if GetAnimationSpeedModulation(e) == 0.0 then SetAnimation(1) LoopAnimation(e) SetAnimationSpeedModulation(e,0.1) else tWalkDelta = GetMovementDelta(e) * 2.0 if tWalkDelta > 0.9 then tWalkDelta = 0.9 end SetAnimationSpeedModulation(e, 0.1 + tWalkDelta) end end local tDistX = Ent.x - patrolx local tDistZ = Ent.z - patrolz local VertDist = math.abs(Ent.y - patroly) local DistFromPath = tDistX*tDistX + tDistZ*tDistZ if DistFromPath < 25*25 and VertDist < 95 and g_Time > ai_bot_pointtime[e] then ai_bot_pointtime[e] = g_Time + 100 StartTimer(e) if ai_bot_pointdirection[e] == 1 then ai_bot_pointindex[e] = ai_bot_pointindex[e] + 1 if ai_bot_pointindex[e] > ai_bot_pointmax[e] then ai_bot_pointindex[e] = ai_bot_pointmax[e] -1 ai_bot_pointdirection[e] = 0 end else ai_bot_pointindex[e] = ai_bot_pointindex[e] - 1 if ai_bot_pointindex[e] < 1 then ai_bot_pointindex[e] = 2 ai_bot_pointdirection[e] = 1 end end end module_combatcore.detectplayer(e, AIObjNo, PlayerDist, CanFire, detectstate) else SetAnimationSpeedModulation(e, 1.0) end end function module_combatcore.hunt(e, AIObjNo, PlayerDist, MoveType, CanFire, stopstate) local Ent = g_Entity[e] if ai_bot_state[e] == ai_state_startmove then ai_bot_state[e] = ai_state_move SetAnimation(1) LoopAnimation(e) SetAnimationSpeedModulation(e,1.0) StartTimer(e) end if ai_bot_state[e] == ai_state_move then if GetTimer(e) > 500 then if ai_bot_coverindex[e] > 0 then if module_combatcore.atcover(e) then ai_bot_coverindex[e] = -2 end end if ai_bot_coverindex[e] < 0 then if Ent.plrvisible == 1 and ai_CanSeePlayer( e ) and CanFire then ai_bot_state[e] = ai_state_startfireonspot end end if ai_bot_substate[e] == 0 then StartTimer(e) end end if ai_bot_state[e] ~= ai_state_startfireonspot then if ai_bot_coverindex[e] == -2 then if Ent.plrvisible == 1 and ai_CanSeePlayer( e ) then ai_bot_targetx[e], ai_bot_targety[e], ai_bot_targetz[e] = getPlayerPos3() end end if ai_bot_coverindex[e] > 0 then ai_bot_targetx[e] = AICoverGetPointX(ai_bot_coverindex[e]) ai_bot_targety[e] = g_PlayerPosY ai_bot_targetz[e] = AICoverGetPointZ(ai_bot_coverindex[e]) end if ai_bot_targetx[e] ~= nil then module_combatcore.moveandavoid(e, AIObjNo, PlayerDist, MoveType, ai_bot_targetx[e], ai_bot_targety[e], ai_bot_targetz[e], stopstate) if CanFire then module_combatcore.fireweapon(e) end end end end if Ent.plrvisible == 1 and ai_CanSeePlayer( e ) then ai_bot_hunttime[e] = Timer() else if ai_bot_hunttime[e] ~= nil then if ai_bot_hunttime[e] > 0 and Timer() > ai_bot_hunttime[e]+500 then ai_bot_state[e] = ai_state_startmove ai_bot_coverindex[e] = -1 ai_bot_hunttime[e] = 0 SetActivated(e,0) end end end end function module_combatcore.homein(e, AIObjNo, PlayerDist, MoveType, CanFire, stopstate) if ai_bot_state[e] == ai_state_startmove then ai_bot_state[e] = ai_state_move SetAnimation(1) LoopAnimation(e) SetAnimationSpeedModulation(e, 1.0) end if ai_bot_state[e] == ai_state_move then -- scan if another bot is crowding player local tbeingcrowded = false local px, py, pz = getPlayerPos3() for k, v in pairs(g_Entity) do if k ~= e and v.health > 0 then local pDX, pDY, pDZ = v.x - px, v.z - py, v.z - pz if pDX*pDX + pDY*pDY + pDZ*pDZ < 100*100 then tbeingcrowded = true end end end if not tbeingcrowded then ai_bot_targetx[e] = px ai_bot_targety[e] = py ai_bot_targetz[e] = pz else ai_bot_targetx[e] = px + (math.sin(e)*50.0) ai_bot_targety[e] = py ai_bot_targetz[e] = pz + (math.cos(e)*50.0) end module_combatcore.moveandavoid(e, AIObjNo, PlayerDist, MoveType, ai_bot_targetx[e], ai_bot_targety[e], ai_bot_targetz[e], stopstate) end end function module_combatcore.moveandavoid(e, AIObjNo, PlayerDist, MoveType, x, y, z, stopstate) local Ent = g_Entity[e] if ai_bot_substate[e] == 0 then if PlayerDist < 100 then local tDistX, tdistZ = x - Ent.x, z - Ent.z local tDA = math.atan2(tDistX, tDistZ) x = x + (math.sin(tDA) * 50) z = z + (math.cos(tDA) * 50) end AIEntityGoToPosition(AIObjNo, x, y, z) SetRotation(e, 0, AIGetEntityAngleY(AIObjNo), 0) if AIGetEntityIsMoving(AIObjNo) == 1 then if MoveType == ai_movetype_useanim then MoveWithAnimation(e, 1) else MoveForward(e, AIGetEntitySpeed(AIObjNo)) end else MoveForward(e, 0.0) if GetTimer(e) > 250 then ai_bot_state[e] = stopstate end end AISetEntityPosition(AIObjNo, GetEntityPositionX(e), GetEntityPositionY(e), GetEntityPositionZ(e)) if Ent.avoid == 2 or (Ent.avoid == 1 and stopstate ~= ai_state_startpatrol) then ai_bot_substate[e] = math.random(1,2) local tAvoidAngle = AIGetEntityAngleY(AIObjNo) if ai_bot_substate[e] == 1 then tAvoidAngle = tAvoidAngle - 95 else tAvoidAngle = tAvoidAngle + 95 end tAvoidAngle = (tAvoidAngle / 360.0) * 6.28 local tAvoidX = GetEntityPositionX(e) + (math.sin(tAvoidAngle) * 30) local tAvoidZ = GetEntityPositionZ(e) + (math.cos(tAvoidAngle) * 30) AIEntityGoToPosition(AIObjNo, tAvoidX, GetEntityPositionY(e), tAvoidZ) StartTimer(e) end end if ai_bot_substate[e] > 0 then SetRotation(e, 0, AIGetEntityAngleY(AIObjNo), 0) if AIGetEntityIsMoving(AIObjNo) == 1 then if MoveType == ai_movetype_useanim then MoveWithAnimation(e,1) else MoveForward(e,AIGetEntitySpeed(AIObjNo)) end else MoveForward(e, 0.0) end AISetEntityPosition(AIObjNo, GetEntityPositionX(e), GetEntityPositionY(e), GetEntityPositionZ(e)) if GetTimer(e) > 1000 then ai_bot_substate[e] = 0 end end end function module_combatcore.donotmove(e) MoveForward(e,0) ai_bot_substate[e] = 0 end function module_combatcore.sensepunch(e,AIObjNo,PlayerDist,combattype) local Ent = g_Entity[e] if ai_bot_state[e] == ai_state_move or ai_bot_state[e] == ai_state_fireonspot then if PlayerDist < 70 and g_PlayerPosY > Ent.y and g_PlayerPosY < Ent.y + 70 then if combattype ~= ai_combattype_freezermelee or (combattype == ai_combattype_freezermelee and module_cameraoverride.hasowner() == 0) then -- must also check for line of sight (i.e not through walls) (first and third person) local thaslineofsight = 0 local px, py, pz = getPlayerPos3() local tthitvalue = IntersectAll(Ent.x, Ent.y+50, Ent.z, px, py+50, pz, AIObjNo) local ttpersonobj = 0 if GetGamePlayerControlThirdpersonEnabled() == 1 then local ttpersone = GetGamePlayerControlThirdpersonCharactere() if ttpersone > 0 then ttpersonobj = GetEntityElementObj(ttpersone) if GetObjectExist(ttpersonobj) == 0 then ttpersonobj = 0 end end end if tthitvalue == 0 or tthitvalue == ttpersonobj then thaslineofsight = 1 end if thaslineofsight == 1 then ai_bot_state[e] = ai_state_punch SetAnimation(2) PlayAnimation(e) SetAnimationSpeedModulation(e,1.0) PlaySound(e,1) RotateToPlayer(e) if combattype == ai_combattype_freezermelee then module_cameraoverride.beingattackedby(e,60.0) end end else ai_bot_state[e] = ai_state_recoverstart end end end end function module_combatcore.hurt(e,responsestate) local Ent = g_Entity[e] if ai_bot_state[e] == ai_state_idle or ai_bot_state[e] == ai_state_patrol or ai_bot_state[e] == ai_state_move or ai_bot_state[e] == ai_state_fireonspot or ai_bot_state[e] == ai_state_recover or ai_bot_state[e] == ai_state_punch then if Ent.health < ai_bot_oldhealth[e] then ai_bot_state[e] = ai_state_hurt ai_bot_oldhealth[e] = Ent.health ai_bot_angryhurt[e] = 1 if ai_bot_coverindex[e] == -1 then ai_bot_coverindex[e] = -2 end SetAnimationSpeed(e,1.5) SetAnimation(3) PlayAnimation(e) SetAnimationSpeedModulation(e,1.0) PlaySound(e,math.random(3,4)) module_agro.alertallwithinradius(Ent.x, Ent.y, Ent.z, 250.0) RotateToPlayer(e) end end if ai_bot_state[e] == ai_state_hurt then local tFrame = GetAnimationFrame(e) local tStart = GetEntityAnimationFinish(e,3) if tFrame >= tStart then ai_bot_state[e] = responsestate end end end function module_combatcore.headshot(e) local Ent = g_Entity[e] if string.find(string.lower(Ent.limbhit), "head") ~= nil then SetEntityHealth(e,0) ResetLimbHit(e) end end function module_combatcore.soundawareness(e,AIObjNo) if AIGetEntityHeardSound(AIObjNo) == 1 then if ai_bot_state[e] == ai_state_idle then RotateToPlayer(e) end if ai_bot_state[e] == ai_state_patrol then ai_bot_state[e] = ai_state_startidle RotateToPlayer(e) end end end function module_combatcore.punch(e,PlayerDist,combattype,afterstate) local Ent = g_Entity[e] if ai_bot_state[e] == ai_state_punch then local wemoved = 0 if combattype == ai_combattype_freezermelee then if module_cameraoverride.manageattackcycle(e) == 1 then MoveWithAnimation(e,1) wemoved = 1 end end local tFrame = GetAnimationFrame(e) local tStart = GetEntityAnimationStart(e,2) local tFinish = GetEntityAnimationFinish(e,2) if PlayerDist < 90 then if combattype == ai_combattype_freezermelee then if tFrame >= tStart+10 and tFrame <= tFinish-30 then if math.random(1,7) == 1 then HurtPlayer(e,math.random(1,2)) end end else if combattype == ai_combattype_bashmelee then if tFrame >= tStart+25 and tFrame <= tStart+35 then HurtPlayer(e, math.random(2,3)) ForcePlayer ( Ent.angley, 2.0 ) end else if tFrame >= tStart+10 and tFrame <= tStart+15 then HurtPlayer(e,math.random(2,3)) ForcePlayer ( Ent.angley, 3.0 ) end end end end if combattype == ai_combattype_freezermelee then if tFrame >= tFinish-30 then module_cameraoverride.finishbeingattacked(e) end end if tFrame >= tFinish then if combattype == ai_combattype_freezermelee or combattype == ai_combattype_bashmelee then ai_bot_state[e] = ai_state_recoverstart else ai_bot_state[e] = afterstate end end if wemoved == 0 then module_combatcore.donotmove(e) end end end function module_combatcore.recover(e,resumestate) if ai_bot_state[e] == ai_state_recoverstart then ai_bot_state[e] = ai_state_recover StopAnimation(e) SetAnimation(0) SetAnimationFrame(e,GetEntityAnimationStart(e,0)) PlayAnimation(e) SetAnimationSpeedModulation(e,1.0) StartTimer(e) end if ai_bot_state[e] == ai_state_recover then module_combatcore.donotmove(e) if GetTimer(e) > 100 then ai_bot_state[e] = resumestate end end end function module_combatcore.fireonspot(e,AIObjNo) local Ent = g_Entity[e] if ai_bot_state[e] == ai_state_startfireonspot then ai_bot_state[e] = ai_state_fireonspot StopAnimation(e) SetAnimation(0) LoopAnimation(e) SetAnimationSpeedModulation(e,1.0) StartTimer(e) end if ai_bot_state[e] == ai_state_fireonspot then if Ent.plrvisible == 0 or not ai_CanSeePlayer( e ) and GetTimer(e) > 500 then ai_bot_state[e] = ai_state_startidle else RotateToPlayer(e) module_combatcore.fireweapon(e) ai_bot_targetx[e], ai_bot_targety[e], ai_bot_targetz[e] = getPlayerPos3() AIEntityGoToPosition(AIObjNo,ai_bot_targetx[e],ai_bot_targety[e],ai_bot_targetz[e]) end module_combatcore.donotmove(e) end end function module_combatcore.fireweapon(e) if ai_bot_state[e] ~= ai_state_startreload and ai_bot_state[e] ~= ai_state_reload then if GetAmmoClip(e) > 0 then FireWeapon(e) else if GetEntityAnimationFinish(e,4) > 0 then ai_bot_state[e] = ai_state_startreload end end end end function module_combatcore.reloadweapon(e) if ai_bot_state[e] == ai_state_startreload then ai_bot_state[e] = ai_state_reload SetAnimation(4) PlayAnimation(e) SetAnimationSpeedModulation(e,1.0) StartTimer(e) else if ai_bot_state[e] == ai_state_reload then if GetTimer(e) > 500 then local tFrame = GetAnimationFrame(e) local tFinish = GetEntityAnimationFinish(e,4) if tFrame >= tFinish or tFinish == 0 then ai_bot_state[e] = ai_state_startidle SetAmmoClip(e,GetAmmoClipMax(e)) end end module_combatcore.donotmove(e) end end end return module_combatcore