My ThreatOnPath code using a pie-segment <) to find if an enemy poses a thread rather than a band =
-- Arkhan 03.2006: Work around for Relics buggy HasThreatOnPath() function-- VidiVici 10.2007: Changed to check a cone and potential cover from friendly unitfunction CpuManager:HasThreatOnPath(startPos, targetPos, iRange) dbAssert(startPos ~= nil) dbAssert(targetPos ~= nil) dbAssert(iRange ~= nil) -- Check if high speed AI setting is activated if (CpuManager.AISettings.bHighSpeedAI == true) then return false end -- Update object table if (g_iGMT > self.m_iLastTableUpdate + 5) then self:UpdateObjectTable() self.m_iLastTableUpdate = g_iGMT end -- polar coordinates of target local rTarget = self:DistanceXZ(startPos, targetPos) local tTarget = self:AngleXZ(startPos, targetPos) -- if rTarget > iRange then look for danger in cone if (rTarget > iRange) then -- define dangerCone angle local angle = math.atan2( iRange, rTarget ) -- dangerCone bounderies local rRad = rTarget + iRange local mint = tTarget - angle local maxt = tTarget + angle -- Check for threat local threatDist local threatAngle for iLoop1 in self.m_aThreatPositions do -- Get threat position threatPos = self.m_aThreatPositions[iLoop1] -- polar coordinates of threat threatDist = self:DistanceXZ(startPos, threatPos) threatAngle = self:AngleXZ(startPos, threatPos) -- check if threat is in dangerCone if (threatDist < rRad and threatAngle > mint and threatAngle < maxt ) then print("DANGER @ ["..threatPos.x..", "..threatPos.z.."]".. " Form ["..startPos.x..", ".. startPos.z .."]".. " to [".. targetPos.x ..", ".. targetPos.z .."]".." ("..iRange..")") -- check if threat cen be countered if (not self:FriendInRange(threatPos, iRange)) then return true end end end else -- rTarget <= iRange check threat proximity to target for iLoop1 in self.m_aThreatPositions do -- Get threat position threatPos = self.m_aThreatPositions[iLoop1] -- distance of threat to target threatDist = self:DistanceXZ(targetPos, threatPos) -- check if threat is in dangerCone if ( threatDist < iRange ) then print("DANGER @ ["..threatPos.x..", "..threatPos.z.."] around [".. targetPos.x ..", ".. targetPos.z .."]") if (not self:FriendInRange(threatPos, iRange)) then return true end end end end return falseend-- VidiVici 10.2007: computes the distance between 2 points-- WARNING! no nil check for positions use with carefunction CpuManager:DistanceXZ(pos1, pos2) local tmp = Vector3f (0,0,0) tmp.x = pos2.x - pos1.x tmp.z = pos2.z - pos1.z return math.sqrt(tmp.x*tmp.x + tmp.z*tmp.z )end-- VidiVici 10.2007: computes the angle between 2 points-- WARNING! no nil check for positions use with carefunction CpuManager:AngleXZ(pos1, pos2) return math.atan2( pos2.x - pos1.x , pos2.z - pos1.z )end-- VidiVici 10.2007: checks if a friendly unit is located around a postion-- WARNING! no nil check for position use with carefunction CpuManager:FriendInRange(pos, iRange) -- Update object table if (g_iGMT > self.m_iLastTableUpdate + 5) then self:UpdateObjectTable() self.m_iLastTableUpdate = g_iGMT end for fLoop in self.m_aFriendlyPositions do -- get friendly postion fPos = self.m_aFriendlyPositions[fLoop] -- if friendly can cover there is no threat if ( self:DistanceXZ(pos, fPos) < iRange ) then print("Friendly @ ["..fPos.x..", "..fPos.z.."]") return true end endend-- Arkhan 06.2006: Inits object tables to increase performance -- VidiVici 07.2007: Added FriendlyPositions tablefunction CpuManager:UpdateObjectTable() -- Update tables self.m_aEnemyUnits = {} self.m_aAlliedUnits = {} self.m_aMyUnits = {} self.m_aEnemyTurrets = {} self.m_aAlliedTurrets = {} self.m_aMyTurrets = {} self.m_aThreatPositions = {} self.m_aFriendlyPositions = {} for oPlayer in cpu_manager.stats:GetPlayerStats() do -- Check if player is still alive if (not oPlayer:IsPlayerDead()) then -- Check if player is an enemy local bEnemy = self.player_stats:IsEnemy(oPlayer) -- Check if player is an ally local bAlly = (not bEnemy and oPlayer:GetPlayerID() ~= self.player_id) -- Compute all player units for oSquad in oPlayer:GetSquads() do -- Check if unit is valid if (oSquad:IsValid() and not oSquad:IsEngineer()) then -- Check squad strength local iSquadStrength = cpu_manager:GetUnitStrength(oSquad) if (iSquadStrength > 0) then -- Add unit to table if (bEnemy) then table.insert(self.m_aEnemyUnits, { Vector3f(oSquad:GetPosition()), iSquadStrength }) if (iSquadStrength > 100) then table.insert(self.m_aThreatPositions, Vector3f(oSquad:GetPosition())) end elseif (bAlly) then table.insert(self.m_aAlliedUnits, { Vector3f(oSquad:GetPosition()), iSquadStrength }) if (iSquadStrength > 100) then table.insert(self.m_aFriendlyPositions, Vector3f(oSquad:GetPosition())) end else table.insert(self.m_aMyUnits, { Vector3f(oSquad:GetPosition()), iSquadStrength }) if (iSquadStrength > 100) then table.insert(self.m_aFriendlyPositions, Vector3f(oSquad:GetPosition())) end end end end end -- Compute all player turrets local sRace = oPlayer:GetPlayerRaceName() local iPostThreat = self:GetTurretPower(sRace) for oBuilding in oPlayer:GetBases() do -- Check for valid building if (oBuilding:IsValid() and oBuilding:HasGuns()) then -- Check for posts if (oBuilding:IsListeningPost()) then -- Add post to table if (bEnemy) then table.insert(self.m_aEnemyTurrets, { Vector3f(oBuilding:GetPosition()), iPostThreat }) table.insert(self.m_aThreatPositions, Vector3f(oBuilding:GetPosition())) elseif (bAlly) then table.insert(self.m_aAlliedTurrets, { Vector3f(oBuilding:GetPosition()), iPostThreat }) table.insert(self.m_aFriendlyPositions, Vector3f(oBuilding:GetPosition())) else table.insert(self.m_aMyTurrets, { Vector3f(oBuilding:GetPosition()), iPostThreat }) table.insert(self.m_aFriendlyPositions, Vector3f(oBuilding:GetPosition())) end else -- Get building name and threat local sBuildingName = oBuilding:GetBaseName() local sBuildingType, iThreat = self.m_oRaceLoader:GetBuildingType(sRace, sBuildingName) if (iThreat > 0) then -- Add turret to table if (bEnemy) then table.insert(self.m_aEnemyTurrets, { Vector3f(oBuilding:GetPosition()), iThreat }) table.insert(self.m_aThreatPositions, Vector3f(oBuilding:GetPosition())) elseif (bAlly) then table.insert(self.m_aAlliedTurrets, { Vector3f(oBuilding:GetPosition()), iThreat }) table.insert(self.m_aFriendlyPositions, Vector3f(oBuilding:GetPosition())) else table.insert(self.m_aMyTurrets, { Vector3f(oBuilding:GetPosition()), iThreat }) table.insert(self.m_aFriendlyPositions, Vector3f(oBuilding:GetPosition())) end end end end end end endend
What do you think of it?
On another note I need some help to fully test it. What map is prone to misdirect the builders?
The next steps will be to
2) Locate impassable terrain and adapt the DangerCone to have bends in it...
The builders seem to wait a long time b4 moving to go build LP's can't they move in a bit faster to avoid having to span big distances?
Is there command to make the AI ping on the minimap? It would be a lot easier to see what points/units are involved,
and I have added the friendly unit check.
ThreatMap
More complex systems to try and find the safest path to a point will be quite heavy imo as I think it will require:
1) Having a threatMap mapping potential threat in each point.
2) Defining impassable zones by combining impassable terrain and all zones above a certain threshold.
3) Performing an heuristic search similar to A-star to find the best path.
4) Reducing the path to a couple of target points that have to be followed and updated as battle progresses.
Edited by VidiVici, 02 October 2007 - 02:31 PM.