-- LUA Script - precede every function and global member with lowercase name of script -- globals for AI enemies ai_newdest_time = {} ai_cover_on = {} animationstate={} --AI Arrays ai_list_place=1 AI_List={} --Complete List of all AI character AI_Chosen_Enemy={} -- List of AI characters who are chosen from other AI characters as target EntityDist = {} -- Distance to taget ai_soldier_target={} -- a string which shows if an AI or the player is the target ai_soldier_target_list_position={} -- Safes the position of the target in the AI_List array for deleting it if it is destroyed --Angles arrays AI_Rotation_Angle={} -- The angle the AI rotates --Angles for calculating SINUS side_opposite_an_angle={} side_adjacent_an_angle={} Hypotenuse={} -- init when level first runs function darkai_soldier_init(e) CharacterControlLimbo(e) StartTimer(e) ai_soldier_state[e] = "patrol"; ai_soldier_pathindex[e] = -1; ai_start_x[e] = nil ai_starting_heath[e] = nil ai_ran_to_cover[e] = 0 ai_old_health[e] = 0 ai_returning_home[e] = 0 ai_newdest_time[e] = -1 ai_cover_on[e] = 0 ai_alerted_spoken[e] = 0 SetCharacterSoundSet(e) AI_List[ai_list_place]=e ai_list_place=ai_list_place+1 EntityDist[e] = 0 end function darkai_soldier_main(e) -- Handle health if ai_starting_heath[e] == nil then ai_starting_heath[e] = g_Entity[e]['health'] end -- Entity Object Number EntObjNo = g_Entity[e]['obj']; -- Choose enemy out of AI List or Player -- Choose nearest enemy as target ChooseEnemy(e) -- if enemy die, set target to nil, so it search for a new enemy if AI_Chosen_Enemy[e] ~= nil then if g_Entity[AI_Chosen_Enemy[e]]['health']<1 and AI_List[ai_soldier_target_list_position[e]]~=nil then AI_Chosen_Enemy[e]=nil AI_List[ai_soldier_target_list_position[e]]=nil ai_soldier_target[e]="No" SetAnimationFrames(900,999) LoopAnimation(e) end end -- Detect player/Entity distance (note: possible optimization here) PlayerDist = GetPlayerDistance(e) EntityDist[e] = math.floor(calcEntityDistance(e,AI_Chosen_Enemy[e])) --Rotate to entity calRotationAngleToEntity(e,AI_Chosen_Enemy[e]) if AI_Rotation_Angle[e] ~=nil and ai_soldier_target[e]=="AI" then SetRotation(e,0,AI_Rotation_Angle[e],0) end --example for animation -- if ai_soldier_state[e] ~= "stoodnormal" then -- CharacterControlLimbo(e) -- SetAnimationFrames(100,205) -- LoopAnimation(e) -- ModulateSpeed(e,1.0) -- SetAnimationSpeed(e,1.0) -- ai_soldier_state[e] = "stoodnormal" -- end -- FireWeapon(e) --commented cause it does not work if player is not in sight -- --Debug Code(use this just if the AI is not fighting, otherwise it can create crashes) -- Text(1,7,1,"AI Quantity: " ..ai_list_place-1) -- -- from which AI character you want to see the stats -- local ai_character_number = 1 -- if ai_character_number <= AI_List[#AI_List] and ai_list_place > 0 then -- if g_Entity[AI_List[ai_character_number]] ~=nil and AI_Chosen_Enemy[AI_List[ai_character_number]]~=nil and AI_Rotation_Angle[AI_List[ai_character_number]]~=nil then -- if side_opposite_an_angle[AI_List[ai_character_number]]~=nil and Hypotenuse[AI_List[ai_character_number]]~= nil then -- Text(1,10,1,"AI 1: "..AI_List[ai_character_number]) -- Text(1,13,1,"AI 1 Enemy: "..AI_Chosen_Enemy[AI_List[ai_character_number]]) -- Text(1,16,1,"AI 1 Angle: "..AI_Rotation_Angle[AI_List[ai_character_number]]) -- Text(1,19,1,"AI 1 Gegen: "..side_opposite_an_angle[AI_List[ai_character_number]]) -- Text(1,22,1,"AI 1 Hypo: "..Hypotenuse[AI_List[ai_character_number]]) -- end -- end -- end -- AI attack AI -- Early Stage, no animation and Actions added at the moment if ai_soldier_target[e]=="AI" then if ai_soldier_state[e] ~= "attackposition" then CharacterControlLimbo(e) SetAnimationFrames(100,205) LoopAnimation(e) ModulateSpeed(e,1.0) SetAnimationSpeed(e,1.0) StartTimer(e) ai_soldier_state[e] = "attackposition" end if GetTimer(e) > 250 then --Make the target a random damage between 1 and 6 SetEntityHealth(AI_Chosen_Enemy[e],g_Entity[AI_Chosen_Enemy[e]]['health']-math.random(1,6)) StartTimer(e) end end -- AI attack Player -- Use Standart AI(So no addition here at the moment) if ai_soldier_target[e]=="Player" then if ai_soldier_state[e] == "patrol" then ai_patrole(e) end if ai_soldier_state[e] == "alerted" then ai_alerted(e) end if ai_soldier_state[e] == "combat" then ai_combat(e) end end end -- End of main -- Start of AI VS AI functions -- Choose an enemy out of the AI List -- A lot optimization possible function ChooseEnemy(e) if AI_Chosen_Enemy[e] == nil then for i=1, #AI_List do if AI_List[i]~=e and AI_List[i]~=nil and calcEntityDistance(e,AI_Chosen_Enemy[e])>calcEntityDistance(e,AI_List[i]) then if GetPlayerDistance(e) > calcEntityDistance(e,AI_List[i]) then AI_Chosen_Enemy[e]=AI_List[i] ai_soldier_target[e]="AI" ai_soldier_target_list_position[e]=i else ai_soldier_target[e]="Player" RotateToPlayer(e) end end end end end --Angle calculation between two entities, so you can rotate them with setrotation(e,x,y,z) function calRotationAngleToEntity(e1,e2) if AI_Chosen_Enemy[e1] ~= nil then side_opposite_an_angle[e1]=g_Entity[e1]['x']-g_Entity[e2]['x'] side_adjacent_an_angle[e1]=g_Entity[e1]['z']-g_Entity[e2]['z'] Hypotenuse[e1]=calcEntityDistance(e1,e2) AI_Rotation_Angle[e1]=math.floor(math.deg(math.asin(math.abs(side_opposite_an_angle[e1])/Hypotenuse[e1]))+0.5) if side_opposite_an_angle[e1] > 0 then if side_adjacent_an_angle[e1] > 0 then AI_Rotation_Angle[e1]=AI_Rotation_Angle[e1]+180 elseif side_adjacent_an_angle[e1] < 0 then AI_Rotation_Angle[e1]=-AI_Rotation_Angle[e1] else AI_Rotation_Angle[e1]=270 end elseif side_opposite_an_angle[e1] < 0 then if side_adjacent_an_angle[e1] > 0 then AI_Rotation_Angle[e1]=180-AI_Rotation_Angle[e1] elseif side_adjacent_an_angle[e1] < 0 then AI_Rotation_Angle[e1]=AI_Rotation_Angle[e1] else AI_Rotation_Angle[e1]=90 end else if side_adjacent_an_angle[e1] > 0 then AI_Rotation_Angle[e1]=180 elseif side_adjacent_an_angle[e1] < 0 then AI_Rotation_Angle[e1]=0 else AI_Rotation_Angle[e1]=0 end end end end -- Calculate the distance between two entities function calcEntityDistance(e1,e2) if g_Entity[e1] ~=nil and g_Entity[e2] ~=nil then local tEntityDX = (g_Entity[e1]['x'] - g_Entity[e2]['x']) local tEntityDY = (g_Entity[e1]['y'] - g_Entity[e2]['y']) local tEntityDZ = (g_Entity[e1]['z'] - g_Entity[e2]['z']) return math.sqrt(tEntityDX*tEntityDX+tEntityDY*tEntityDY+tEntityDZ*tEntityDZ) else return 1000000000000 end end -- Start of AI Vs Player functions function ai_combat(e) -- Look at player LookAtPlayer(e) -- If we are the soldier with aggro then update our position so if we run near to others, we can alert them to the fight if ai_aggro_entity ~= null then if ai_aggro_entity == e then ai_aggro_x = g_Entity[e]['x'] ai_aggro_z = g_Entity[e]['z'] end end -- Kick off combat by choosing a destination to head for around the player if ai_combat_mode[e] == 0 then tSpeed = math.random(100,130) / 100.0 ModulateSpeed(e,tSpeed) angle = math.random(360) ai_dest_x[e] = g_PlayerPosX + (math.sin(angle) * 200) ai_dest_z[e] = g_PlayerPosZ - (math.cos(angle) * 200) -- Check if we can get to the new position without going around something if ( AICouldSee ( EntObjNo , ai_dest_x[e] , g_Entity[e]['y'] , ai_dest_z[e] ) == 0 ) then ai_dest_x[e] = g_PlayerPosX; ai_dest_z[e] = g_PlayerPosZ; end if PlayerDist < AI_CLOSEST_TO_PLAYER then ai_dest_x[e] = g_Entity[e]['x'] ai_dest_z[e] = g_Entity[e]['z'] end StartTimer(e) CharacterControlArmed(e) ai_combat_mode[e] = 1 ai_cover_on[e] = AIEntityMoveToCover(EntObjNo,g_PlayerPosX,g_PlayerPosZ) if ai_cover_on[e] == 0 then AIEntityGoToPosition(EntObjNo,ai_dest_x[e],ai_dest_z[e]) ai_combat_state_delay[e] = math.random(250,500) else ai_combat_state_delay[e] = math.random(1000,2000) end -- Target the player, wait a short time then run in to attack elseif ai_combat_mode[e] == 1 then if GetTimer(e) > ai_combat_state_delay[e] then ai_combat_state_delay[e] = math.random(1000,2500) StartTimer(e) ai_combat_mode[e] = 2 end if g_Entity[e]['plrvisible'] == 1 then FireWeapon(e) end -- Run to destination for a small amount of time elseif ai_combat_mode[e] == 2 then if g_Entity[e]['plrvisible'] == 1 then FireWeapon(e) end if (GetTimer(e) > ai_combat_state_delay[e]) or PlayerDist <= AI_CLOSEST_TO_PLAYER then ai_combat_mode[e] = 3 end -- Target the player elseif ai_combat_mode[e] == 3 then CharacterControlArmed(e) ai_combat_mode[e] = 4 if ai_cover_on[e] == 1 then ai_combat_state_delay[e] = math.random(500,1500) else ai_combat_state_delay[e] = math.random(250,500) end StartTimer(e) -- Shoot the player if we can, if not head off to find him elseif ai_combat_mode[e] == 4 then if g_Entity[e]['plrvisible'] == 1 then FireWeapon(e) end if ai_cover_on[e] == 1 then CharacterControlDucked(e) end -- Once our shooting time has ended, lets go back to mode 0 and start over -- If we cannot see the player, wait a short time before hunting him down if GetTimer(e) > ai_combat_state_delay[e] then CharacterControlStand(e) if g_Entity[e]['plrvisible'] == 0 then ai_combat_mode[e] = 7 ai_cover_on[e] = 0 ai_combat_state_delay[e] = math.random(1000,2000) StartTimer(e) AIEntityGoToPosition(EntObjNo,g_PlayerPosX,g_PlayerPosZ) else ai_combat_mode[e] = 0 end end ai_old_health[e] = g_Entity[e]['health'] -- In cover, we wait a while, shoot if we can see the player, before venturing out again elseif ai_combat_mode[e] == 5 then CharacterControlArmed(e) if g_Entity[e]['plrvisible'] == 1 then FireWeapon(e) end if GetTimer(e) > ai_combat_old_time[e] then ModulateSpeed(e,1.0) --AIEntityStop(EntObjNo) ai_combat_mode[e] = 0 end -- Out of the range of the player, heading back to start point for a while, once there going back to Patrol mode elseif ai_combat_mode[e] == 6 then ai_returning_home[e] = 1 SetCharacterToWalk(e) CharacterControlUnarmed(e) AIEntityGoToPosition(EntObjNo,ai_start_x[e],ai_start_z[e]) ai_soldier_state[e] = "patrol" ai_combat_state_delay[e] = 3000 StartTimer(e) tSpeed = math.random(100,130) / 100.0 ModulateSpeed(e,tSpeed) return -- Unable to see the player to shoot, so heading after him elseif ai_combat_mode[e] == 7 then if g_Entity[e]['plrvisible'] == 1 then FireWeapon(e) end AIEntityGoToPosition(EntObjNo,g_PlayerPosX,g_PlayerPosZ) if ( g_Entity[e]['plrvisible'] == 1 or PlayerDist <= AI_CLOSEST_TO_PLAYER ) and GetTimer(e) > ai_combat_state_delay[e] then ai_combat_mode[e] = 0 end elseif ai_combat_mode[e] == 8 then ai_combat_mode[e] = 9 AIEntityStop(EntObjNo) elseif ai_combat_mode[e] == 9 then ai_combat_mode[e] = 10 -- Pick left or right strafe if ( math.random(0,1) == 0 ) then SetCharacterToStrafeLeft(e) else SetCharacterToStrafeRight(e) end ai_combat_old_time[e] = os.clock() elseif ai_combat_mode[e] == 10 then -- We can see the player, lets shoot him! if g_Entity[e]['plrvisible'] == 1 then FireWeapon(e) end if os.clock() - ai_combat_old_time[e] > ai_combat_state_delay[e] then ai_combat_state_delay[e] = math.random(1,3) ai_combat_mode[e] = 4 ai_old_health[e] = g_Entity[e]['health'] end elseif ai_combat_mode[e] == 11 then if g_Entity[e]['plrvisible'] == 1 then if os.clock() - ai_combat_old_time[e] > ai_combat_state_delay[e] then CharacterControlStand(e) ai_combat_old_time[e] = os.clock() ai_combat_state_delay[e] = 1 ai_combat_mode[e] = 12 end end if ai_combat_mode[e] == 12 then if g_Entity[e]['plrvisible'] == 1 then FireWeapon(e) end if os.clock() - ai_combat_old_time[e] > ai_combat_state_delay[e] then ai_combat_mode[e] = 4 end end end -- If health is below 30, run and hide, then head to mode 5 if g_Entity[e]['health'] < 30 and ai_combat_mode[e] ~= 4 and ai_ran_to_cover[e] == 0 then ModulateSpeed(e,1.3) ai_ran_to_cover[e] = 1 AISetEntityControl(EntObjNo,AI_MANUAL); a=AIEntityMoveToCover(EntObjNo,g_PlayerPosX,g_PlayerPosZ); ai_combat_old_time[e] = 3000 StartTimer(e) ai_combat_mode[e] = 5 end -- When player out of range, return to patrol if PlayerDist > AIGetEntityViewRange(EntObjNo)*1.5 then ai_combat_mode[e] = 6 end end function ai_alerted(e) LookAtPlayer(e) -- alert taken out if g_Entity[e]['plrvisible'] == 1 then ai_soldier_state[e] = "combat"; PlayCombatMusic(8000,500) ai_combat_mode[e] = 0 -- Add this enemy as a point of aggro, alerting anyone near to join the fight ai_aggro_entity = e ai_aggro_x = g_Entity[e]['x'] ai_aggro_z = g_Entity[e]['z'] AISetEntityControl(EntObjNo,AI_MANUAL) AIEntityStop(EntObjNo) CharacterControlArmed(e) SetCharacterToRun(e) else if ai_alerted_mode[e] == 0 then tSpeed = math.random(100,130) / 100.0 ModulateSpeed(e,tSpeed) angle = math.random(360) ai_dest_x[e] = g_PlayerPosX + (math.sin(angle) * 150) ai_dest_z[e] = g_PlayerPosZ + (math.cos(angle) * 150) AIEntityGoToPosition(EntObjNo,ai_dest_x[e],ai_dest_z[e]) ai_alerted_state_delay[e] = math.random(0,1) ai_alerted_old_time[e] = os.clock() CharacterControlArmed(e) ai_alerted_mode[e] = 1 if ai_alerted_spoken[e] == 0 then PlayCharacterSound(e,"onAlert") ai_alerted_spoken[e] = 1 end -- Target the player, run in to attack elseif ai_alerted_mode[e] == 1 then LookAtPlayer(e) if os.clock() - ai_alerted_old_time[e] > ai_alerted_state_delay[e] then ai_alerted_state_delay[e] = 2 ai_alerted_mode[e] = 2 AIEntityGoToPosition(EntObjNo,ai_dest_x[e],ai_dest_z[e]) end elseif ai_alerted_mode[e] == 2 then if os.clock() - ai_alerted_old_time[e] > ai_alerted_state_delay[e] then AIEntityStop(EntObjNo) ai_alerted_mode[e] = 0 ai_soldier_state[e] = "patrol"; ai_soldier_AggroCheck(e) end end if g_Entity[e]['plrvisible'] == 1 then ai_soldier_state[e] = "combat"; PlayCombatMusic(8000,500) ai_combat_mode[e] = 0 -- Add this enemy as a point of aggro, alerting anyone near to join the fight ai_aggro_entity = e ai_aggro_x = g_Entity[e]['x'] ai_aggro_z = g_Entity[e]['z'] AISetEntityControl(EntObjNo,AI_MANUAL) AIEntityStop(EntObjNo) CharacterControlArmed(e) SetCharacterToRun(e) end end end function ai_patrole(e) -- Try and find a close path to patrol, just check once for it if ai_soldier_pathindex[e] == -1 then ai_soldier_pathindex[e] = -2 CharacterControlArmed(e) -- find initial waypoint path to follow PathIndex = -1; pClosest = 99999; for pa = 1, AIGetTotalPaths(), 1 do for po = 1 , AIGetPathCountPoints(pa), 1 do pDX = g_Entity[e]['x'] - AIPathGetPointX(pa,po); pDZ = g_Entity[e]['z'] - AIPathGetPointZ(pa,po); pDist = math.sqrt(math.abs(pDX*pDX)+math.abs(pDZ*pDZ)); if pDist < pClosest and pDist < 200 then pClosest = pDist; PathIndex = pa; end end -- po end -- pa -- follow found path if PathIndex > -1 then ai_soldier_pathindex[e] = PathIndex; ai_path_point_index[e] = 2 ModulateSpeed(e,1.0) SetCharacterToWalk(e) ai_path_point_direction[e] = 1 ai_path_point_max[e] = AIGetPathCountPoints(ai_soldier_pathindex[e]) end end -- Force character as unarmed CharacterControlUnarmed(e) -- If set to head home, lets go there if ai_returning_home[e] == 1 then tDistX = g_Entity[e]['x'] - ai_start_x[e]; tDistZ = g_Entity[e]['z'] - ai_start_z[e]; DistToStart = math.sqrt(math.abs(tDistX*tDistX)+math.abs(tDistZ*tDistZ)) if DistToStart < 200 or GetTimer(e) > ai_combat_state_delay[e] then ai_soldier_pathindex[e] = -1 SetCharacterToWalk(e) CharacterControlUnarmed(e) AIEntityStop(EntObjNo); ModulateSpeed(e,1.0) ai_returning_home[e] = 0 ai_soldier_state[e] = "alerted" ai_alerted_mode[e] = 0 else AIEntityGoToPosition(EntObjNo,ai_start_x[e],ai_start_z[e]) end -- If we have a path then lets patrol it elseif ai_soldier_pathindex[e] > -1 then ai_patrol_x[e] = AIPathGetPointX(ai_soldier_pathindex[e],ai_path_point_index[e]) ai_patrol_z[e] = AIPathGetPointZ(ai_soldier_pathindex[e],ai_path_point_index[e]) AIEntityGoToPosition(EntObjNo,ai_patrol_x[e],ai_patrol_z[e]) tDistX = g_Entity[e]['x'] - ai_patrol_x[e] tDistZ = g_Entity[e]['z'] - ai_patrol_z[e] DistFromPath = math.sqrt(math.abs(tDistX*tDistX)+math.abs(tDistZ*tDistZ)) if DistFromPath < 50 then if ai_path_point_direction[e] == 1 then ai_path_point_index[e] = ai_path_point_index[e] + 1 if ( ai_path_point_index[e] > ai_path_point_max[e] ) then ai_path_point_index[e] = ai_path_point_max[e] -1 ai_path_point_direction[e] = 0 end else ai_path_point_index[e] = ai_path_point_index[e] - 1 if ( ai_path_point_index[e] < 1 ) then ai_path_point_index[e] = 2 ai_path_point_direction[e] = 1 end end end else CharacterControlFidget(e) end ai_soldier_AggroCheck(e) ai_soldier_AlertAggroCheck(e) -- Alerted, perhaps player is near or gunshots can be heard? if PlayerDist < AIGetEntityViewRange(EntObjNo) / 3 or AIGetEntityHeardSound(EntObjNo) == 1 then ai_soldier_state[e] = "alerted" ai_alerted_mode[e] = 0 ai_alert_x = g_Entity[e]['x'] ai_alert_z = g_Entity[e]['z'] ai_alert_counter = 10 ai_alert_entity = e end end function ai_soldier_AggroCheck(e) -- If there player is in range and we can see him, or we have been shot, move into combat mode if PlayerDist < AIGetEntityViewRange(EntObjNo) then if g_Entity[e]['plrvisible'] == 1 or g_Entity[e]['health'] < ai_starting_heath[e] then ai_soldier_state[e] = "combat"; PlayCombatMusic(8000,500) ai_combat_mode[e] = 0 PlayCharacterSound(e,"onAggro") -- Add this enemy as a point of aggro, alerting anyone near to join the fight ai_aggro_entity = e ai_aggro_x = g_Entity[e]['x'] ai_aggro_z = g_Entity[e]['z'] ai_alert_x = g_Entity[e]['x'] ai_alert_z = g_Entity[e]['z'] AISetEntityControl(EntObjNo,AI_MANUAL) AIEntityStop(EntObjNo); SetCharacterToRun(e) else -- We cant see the player, but perhaps we are near someone that can if ai_aggro_entity ~= nil then tDistX = g_Entity[e]['x'] - ai_aggro_x tDistZ = g_Entity[e]['z'] - ai_aggro_z tDist = math.sqrt(math.abs(tDistX*tDistX)+math.abs(tDistZ*tDistZ)) if tDist < ai_aggro_range then ai_soldier_state[e] = "combat"; PlayCombatMusic(8000,500) ai_combat_mode[e] = 0 AISetEntityControl(EntObjNo,AI_MANUAL) AIEntityStop(EntObjNo); SetCharacterToRun(e) end end end end end function ai_soldier_AlertAggroCheck(e) -- If there is an alert close by if e == ai_alert_entity then ai_alert_counter = ai_alert_counter - 1 if ai_alert_counter <= 0 then ai_alert_x = -10000 ai_alert_z = -10000 ai_alert_entity = 0 end end pDX = g_Entity[e]['x'] - ai_alert_x pDZ = g_Entity[e]['z'] - ai_alert_z; pDist = math.sqrt(math.abs(pDX*pDX)+math.abs(pDZ*pDZ)); if pDist < 800 then ai_soldier_state[e] = "alerted" ai_alerted_mode[e] = 0 end end function darkai_soldier_exit(e) AI_List[e]=nil PlayCharacterSound(e,"onDeath") end