108 | function AIDriveStrategyCollision:generateTriggerPath(vehicle, trigger) |
109 | trigger.positions[1], trigger.positions[2], trigger.positions[3] = getWorldTranslation(trigger.node) |
110 | trigger.positions[2] = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, trigger.positions[1], trigger.positions[2], trigger.positions[3]) + trigger.height * 0.5 |
111 | |
112 | trigger.isValid = self:getCollisionCheckActive() or trigger.hasCollision |
113 | if trigger.isValid then |
114 | if not trigger.hasCollision then |
115 | trigger.curTriggerLength = MathUtil.clamp(self.vehicle:getLastSpeed() * 0.75, math.min(3, trigger.length), trigger.length) |
116 | end |
117 | |
118 | local posIndex = 3 |
119 | local pointToPointDistance = trigger.curTriggerLength / AIDriveStrategyCollision.TRIGGER_SUBDIVISIONS |
120 | |
121 | local remainingDistance = trigger.curTriggerLength |
122 | local remainingPoints = AIDriveStrategyCollision.TRIGGER_SUBDIVISIONS |
123 | |
124 | local px, _, pz = localToWorld(trigger.node, 0, 0, pointToPointDistance) |
125 | trigger.positions[posIndex+1], trigger.positions[posIndex+2], trigger.positions[posIndex+3] = px, trigger.positions[2], pz |
126 | posIndex = posIndex + 3 |
127 | remainingDistance = remainingDistance - pointToPointDistance |
128 | remainingPoints = remainingPoints - 1 |
129 | |
130 | local _, _, zOffset = worldToLocal(self.vehicle:getAIDirectionNode(), trigger.positions[1], trigger.positions[2], trigger.positions[3]) |
131 | if zOffset >= 0 or vehicle == self.vehicle then |
132 | if self.turnStrategy ~= nil then |
133 | if not self.turnStrategy:calculatePathPrediction(trigger.positions, trigger.node, px, trigger.positions[2], pz, posIndex, pointToPointDistance, remainingDistance, remainingPoints) then |
134 | trigger.isValid = false |
135 | end |
136 | else |
137 | local x, z = MathUtil.projectOnLine(px, pz, self.vehicle.aiDriveTarget[1], self.vehicle.aiDriveTarget[2], self.vehicle.aiDriveDirection[1], self.vehicle.aiDriveDirection[2]) |
138 | for i=1, remainingPoints do |
139 | x, z = x + self.vehicle.aiDriveDirection[1] * pointToPointDistance, z + self.vehicle.aiDriveDirection[2] * pointToPointDistance |
140 | trigger.positions[posIndex+1], trigger.positions[posIndex+2], trigger.positions[posIndex+3] = x, trigger.positions[2], z |
141 | posIndex = posIndex + 3 |
142 | end |
143 | end |
144 | else |
145 | local target = self.vehicle:getAIDirectionNode() |
146 | if self.collisionTriggerByVehicle[self.vehicle] ~= nil then |
147 | target = self.collisionTriggerByVehicle[self.vehicle].node |
148 | end |
149 | |
150 | local sx, sy, sz = trigger.positions[1], trigger.positions[2], trigger.positions[3] |
151 | local ex, ey, ez = getWorldTranslation(target) |
152 | local dirX, _, dirZ = MathUtil.vector3Normalize(ex-sx, ey-sy, ez-sz) |
153 | for i=1, remainingPoints do |
154 | trigger.positions[posIndex+1], trigger.positions[posIndex+2], trigger.positions[posIndex+3] = trigger.positions[posIndex-2] + dirX * pointToPointDistance, trigger.positions[2], trigger.positions[posIndex] + dirZ * pointToPointDistance |
155 | posIndex = posIndex + 3 |
156 | end |
157 | end |
158 | end |
159 | end |
179 | function AIDriveStrategyCollision:getDriveData(dt, vX,vY,vZ) |
180 | if not self:getCollisionCheckActive() then |
181 | return nil, nil, nil, nil, nil |
182 | end |
183 | |
184 | for v, trigger in pairs(self.collisionTriggerByVehicle) do |
185 | if trigger.hasCollision then |
186 | local tX, _, tZ = localToWorld(self.vehicle:getAIDirectionNode(), 0,0,1) |
187 | if VehicleDebug.state == VehicleDebug.DEBUG_AI then |
188 | self.vehicle:addAIDebugText(" AIDriveStrategyCollision :: STOP due to collision") |
189 | end |
190 | |
191 | self:setHasCollision(true) |
192 | |
193 | return tX, tZ, true, 0, math.huge |
194 | end |
195 | end |
196 | |
197 | self:setHasCollision(false) |
198 | |
199 | if VehicleDebug.state == VehicleDebug.DEBUG_AI then |
200 | self.vehicle:addAIDebugText(" AIDriveStrategyCollision :: no collision") |
201 | end |
202 | return nil, nil, nil, nil, nil |
203 | end |
236 | function AIDriveStrategyCollision:onVehicleCollisionDistanceCallback(distance, objectId, subShapeIndex, isLast, trigger) |
237 | -- check for mission and strategy existence in case we receive the async callback after one of them has been removed (e.g. leaving game) |
238 | if g_currentMission ~= nil and self.collisionTriggerByVehicle ~= nil then |
239 | if objectId ~= 0 then |
240 | local vehicle = g_currentMission.nodeToObject[objectId] |
241 | if self.collisionTriggerByVehicle[vehicle] == nil then |
242 | if not getHasTrigger(objectId) then |
243 | if vehicle == nil or vehicle.getRootVehicle == nil or vehicle:getRootVehicle() ~= self.rootVehicle then |
244 | trigger.hitCounter = trigger.hitCounter + 1 |
245 | end |
246 | end |
247 | end |
248 | end |
249 | |
250 | if objectId == 0 or isLast then |
251 | self:onVehicleCollisionDistanceCheckFinished(trigger) |
252 | return false |
253 | else |
254 | return true |
255 | end |
256 | end |
257 | end |
35 | function AIDriveStrategyCollision:setAIVehicle(vehicle) |
36 | AIDriveStrategyCollision:superClass().setAIVehicle(self, vehicle) |
37 | |
38 | if self.vehicle.isServer then |
39 | self.collisionTriggerByVehicle = {} |
40 | |
41 | local vehicles = self.vehicle.rootVehicle.childVehicles |
42 | for i=1, #vehicles do |
43 | local subVehicle = vehicles[i] |
44 | if subVehicle.getAICollisionTriggers ~= nil then |
45 | subVehicle:getAICollisionTriggers(self.collisionTriggerByVehicle) |
46 | end |
47 | |
48 | if subVehicle.getAIImplementCollisionTriggers ~= nil then |
49 | subVehicle:getAIImplementCollisionTriggers(self.collisionTriggerByVehicle) |
50 | end |
51 | end |
52 | |
53 | self.rootVehicle = vehicle:getRootVehicle() |
54 | |
55 | local index = 1 |
56 | for v, trigger in pairs(self.collisionTriggerByVehicle) do |
57 | trigger.updateIndex = index |
58 | trigger.hasCollision = false |
59 | trigger.isValid = true |
60 | trigger.hitCounter = 0 |
61 | trigger.curTriggerLength = 5 |
62 | trigger.positions = {} |
63 | for i=1, AIDriveStrategyCollision.TRIGGER_SUBDIVISIONS * 3 do |
64 | table.insert(trigger.positions, 0) |
65 | end |
66 | |
67 | index = index + AIDriveStrategyCollision.UPDATE_INTERVAL |
68 | end |
69 | self.maxUpdateIndex = index |
70 | end |
71 | end |
76 | function AIDriveStrategyCollision:update(dt) |
77 | local currentIndex = g_updateLoopIndex % self.maxUpdateIndex |
78 | for v, trigger in pairs(self.collisionTriggerByVehicle) do |
79 | if trigger.updateIndex == currentIndex then |
80 | self:generateTriggerPath(v, trigger) |
81 | |
82 | if trigger.isValid then |
83 | trigger.hitCounter = 0 |
84 | local dx, dy, dz = localDirectionToWorld(trigger.node, 0, 0, 1) |
85 | getVehicleCollisionDistance(trigger.positions, dx, dy, dz, trigger.width, trigger.height, "onVehicleCollisionDistanceCallback", self, trigger, CollisionMask.TRIGGER_AI_COLLISION, true, false, true) |
86 | end |
87 | end |
88 | end |
89 | |
90 | if VehicleDebug.state == VehicleDebug.DEBUG_AI then |
91 | for v, trigger in pairs(self.collisionTriggerByVehicle) do |
92 | self:generateTriggerPath(v, trigger) |
93 | |
94 | if trigger.isValid then |
95 | for i=1, #trigger.positions - 3, 3 do |
96 | drawDebugLine(trigger.positions[i+0], trigger.positions[i+1] + 2, trigger.positions[i+2], 1, 0, 0, trigger.positions[i+3], trigger.positions[i+4] + 2, trigger.positions[i+5], 0, 1, 0, true) |
97 | end |
98 | |
99 | local dx, dy, dz = localDirectionToWorld(trigger.node, 0, 0, 1) |
100 | debugDrawVehicleCollision(trigger.positions, dx, dy, dz, trigger.width, trigger.height) |
101 | end |
102 | end |
103 | end |
104 | end |