Copyright (c) 2008-2015 GIANTS Software GmbH, Confidential, All Rights Reserved.
This document is to be published solely by ls-mods.de
1 | -- |
2 | -- AITractor |
3 | -- Specialization for ai tractors |
4 | -- |
5 | -- @author Stefan Geiger |
6 | -- @date 10/01/09 |
7 | -- |
8 | -- Copyright (C) GIANTS Software GmbH, Confidential, All Rights Reserved. |
9 | |
10 | AITractor = {} |
11 | source("dataS/scripts/vehicles/specializations/AITractorSetStartedEvent.lua"); |
12 | source("dataS/scripts/vehicles/specializations/AISetImplementsMoveDownEvent.lua"); |
13 | source("dataS/scripts/vehicles/specializations/AITractorRotateLeftEvent.lua"); |
14 | source("dataS/scripts/vehicles/specializations/AITractorRotateRightEvent.lua"); |
15 | |
16 | |
17 | function AITractor.prerequisitesPresent(specializations) |
18 | return SpecializationUtil.hasSpecialization(Hirable, specializations) and SpecializationUtil.hasSpecialization(Drivable, specializations); |
19 | end; |
20 | |
21 | function AITractor:load(xmlFile) |
22 | |
23 | self.startAITractor = SpecializationUtil.callSpecializationsFunction("startAITractor"); |
24 | self.stopAITractor = SpecializationUtil.callSpecializationsFunction("stopAITractor"); |
25 | self.setAIImplementsMoveDown = SpecializationUtil.callSpecializationsFunction("setAIImplementsMoveDown"); |
26 | |
27 | self.addCollisionTrigger = AITractor.addCollisionTrigger; |
28 | self.removeCollisionTrigger = AITractor.removeCollisionTrigger; |
29 | self.onTrafficCollisionTrigger = AITractor.onTrafficCollisionTrigger; |
30 | |
31 | self.canStartAITractor = AITractor.canStartAITractor; |
32 | self.getIsAITractorAllowed = AITractor.getIsAITractorAllowed; |
33 | |
34 | self.isAITractorActivated = false; |
35 | self.aiTractorDirectionNode = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.aiTractorDirectionNode#index")); |
36 | if self.aiTractorDirectionNode == nil then |
37 | self.aiTractorDirectionNode = self.components[1].node; |
38 | end; |
39 | |
40 | self.aiTractorLookAheadDistance = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.aiTractorLookAheadDistance"), 10); |
41 | self.turnTimeout = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.turnTimeout"), 800); |
42 | self.turnTimeoutLong = self.turnTimeout*10; |
43 | self.turnTimer = self.turnTimeoutLong; |
44 | --self.frontMarkerDistance = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.frontMarkerDistance"), 1.5); |
45 | self.frontMarkerDistanceScale = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.frontMarkerDistanceScale"), 0.6); -- 1.1); |
46 | self.lastFrontMarkerDistance = 0; |
47 | self.turnTargetMoveBack = 7; |
48 | self.turnEndDistance = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.turnEndDistance"), 4); |
49 | self.turnEndBackDistance = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.turnEndBackDistance"), 0.6)+self.turnTargetMoveBack; ---self.frontMarkerDistance; |
50 | |
51 | self.aiToolExtraTargetMoveBack = 0; |
52 | |
53 | self.aiTractorTurnRadius = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.aiTractorTurnRadius"), 5); |
54 | self.aiTurnNoBackward = false; |
55 | |
56 | |
57 | self.waitForTurnTimeout = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.waitForTurnTime"), 1600); |
58 | self.waitForTurnTime = 0; |
59 | |
60 | |
61 | self.turnStage2Timeout = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.turnForwardTimeout"), 20000); |
62 | self.turnStage3Timeout = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.turnBackwardTimeout"), 20000); |
63 | self.turnStage6Timeout = 3000; |
64 | |
65 | |
66 | self.sideWatchDirOffset = -8; |
67 | self.sideWatchDirSize = 7; |
68 | |
69 | self.turnStage = 0; |
70 | |
71 | |
72 | |
73 | self.aiTrafficCollisionTrigger = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.aiTrafficCollisionTrigger#index")); |
74 | |
75 | self.aiTurnWidthScale = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.aiTurnWidthScale#value"), 0.9); |
76 | self.aiTurnWidthMaxDifference = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.aiTurnWidthMaxDifference#value"), 0.5); -- do at most a 0.5m overlap |
77 | |
78 | |
79 | self.trafficCollisionIgnoreList = {}; |
80 | for k,v in pairs(self.components) do |
81 | self.trafficCollisionIgnoreList[v.node] = true; |
82 | end; |
83 | self.numCollidingVehicles = {}; |
84 | |
85 | self.aiToolsDirty = true; |
86 | |
87 | self.dtSum = 0; |
88 | |
89 | self.showFieldNotOwnedWarningTimer = 0; |
90 | |
91 | self.lastArea = 0; |
92 | |
93 | --self.debugDirection = loadI3DFile("data/debugDirection.i3d"); |
94 | --link(self.aiTractorDirectionNode, self.debugDirection); |
95 | --self.debugPosition = loadI3DFile("data/debugPosition.i3d"); |
96 | --link(getRootNode(), self.debugPosition); |
97 | |
98 | end; |
99 | |
100 | function AITractor:delete() |
101 | self:removeCollisionTrigger(self); |
102 | self:stopAITractor(true); |
103 | end; |
104 | |
105 | function AITractor:readStream(streamId, connection) |
106 | local isAITractorActivated = streamReadBool(streamId); |
107 | if isAITractorActivated then |
108 | self:startAITractor(true); |
109 | else |
110 | self:stopAITractor(true); |
111 | end; |
112 | end; |
113 | |
114 | function AITractor:writeStream(streamId, connection) |
115 | streamWriteBool(streamId, self.isAITractorActivated); |
116 | end; |
117 | |
118 | function AITractor:mouseEvent(posX, posY, isDown, isUp, button) |
119 | end; |
120 | |
121 | function AITractor:keyEvent(unicode, sym, modifier, isDown) |
122 | end; |
123 | |
124 | function AITractor:update(dt) |
125 | --if self:getIsActiveForInput(false) then |
126 | local activeForInput = true; |
127 | if g_gui.currentGui ~= nil or g_currentMission.isPlayerFrozen then |
128 | activeForInput = false; |
129 | end; |
130 | if activeForInput and self.isEntered then |
131 | if InputBinding.hasEvent(InputBinding.TOGGLE_AI) and not g_currentMission.inGameMessage:getIsVisible() then |
132 | if g_currentMission:getHasPermission("hireAI") then |
133 | if self.isAITractorActivated then |
134 | self:stopAITractor(); |
135 | else |
136 | if self:canStartAITractor() then |
137 | self:startAITractor(); |
138 | end; |
139 | end; |
140 | end |
141 | end; |
142 | end; |
143 | |
144 | if self.aiToolsDirty then |
145 | AITractor.updateToolsInfo(self); |
146 | end; |
147 | |
148 | end; |
149 | |
150 | function AITractor:updateTick(dt) |
151 | if self.isServer then |
152 | if self.isAITractorActivated then |
153 | |
154 | if self.isBroken then |
155 | self:stopAITractor(); |
156 | end; |
157 | |
158 | self.dtSum = self.dtSum + dt; |
159 | if self.dtSum > 50 then |
160 | AITractor.updateAIMovement(self, self.dtSum); |
161 | self.dtSum = 0; |
162 | end; |
163 | |
164 | -- check if tool has been sold during work |
165 | if self.aiCurrentLeftMarker == nil or self.aiCurrentRightMarker == nil or self.aiCurrentBackMarker == nil then |
166 | AITractor.updateToolsInfo(self); |
167 | if self.aiCurrentLeftMarker == nil or self.aiCurrentRightMarker == nil or self.aiCurrentBackMarker == nil then |
168 | self:stopAITractor(); |
169 | return; |
170 | end; |
171 | end; |
172 | |
173 | else |
174 | self.dtSum = 0; |
175 | end; |
176 | self.showFieldNotOwnedWarningTimer = self.showFieldNotOwnedWarningTimer - dt; |
177 | end; |
178 | |
179 | end; |
180 | |
181 | function AITractor:draw() |
182 | if g_currentMission:getHasPermission("hireAI") then |
183 | if self.isAITractorActivated then |
184 | g_currentMission:addHelpButtonText(g_i18n:getText("DismissEmployee"), InputBinding.TOGGLE_AI); |
185 | else |
186 | if self:canStartAITractor() then |
187 | g_currentMission:addHelpButtonText(g_i18n:getText("HireEmployee"), InputBinding.TOGGLE_AI); |
188 | end; |
189 | end; |
190 | end |
191 | if self.showFieldNotOwnedWarningTimer > 0 then |
192 | g_currentMission:showBlinkingWarning(g_i18n:getText("You_dont_own_this_field")); |
193 | end; |
194 | end; |
195 | |
196 | function AITractor:startAITractor(noEventSend) |
197 | if noEventSend == nil or noEventSend == false then |
198 | if g_server ~= nil then |
199 | g_server:broadcastEvent(AITractorSetStartedEvent:new(self, true), nil, nil, self); |
200 | else |
201 | g_client:getServerConnection():sendEvent(AITractorSetStartedEvent:new(self, true)); |
202 | end; |
203 | end; |
204 | self:hire(); |
205 | if not self.isAITractorActivated then |
206 | g_currentMission.missionStats:updateStats("workersHired", 1); |
207 | self.isAITractorActivated = true; |
208 | |
209 | local hotspotX, _, hotspotZ = getWorldTranslation(self.rootNode); |
210 | self.mapAIHotspot = g_currentMission.ingameMap:createMapHotspot("mapAIHotspot", "dataS2/menu/hud/hud_pda_spot_helper.png", hotspotX, hotspotZ, nil, nil, false, false, false, self.rootNode); |
211 | |
212 | self:setCruiseControlState(Drivable.CRUISECONTROL_STATE_ACTIVE); |
213 | |
214 | if self.isServer then |
215 | self.turnTimer = self.turnTimeoutLong; |
216 | self.turnStage = 0; |
217 | |
218 | self.mapAIHotspot.enabled = false; |
219 | |
220 | local dx,_,dz = localDirectionToWorld(self.aiTractorDirectionNode, 0, 0, 1); |
221 | |
222 | if g_currentMission.snapAIDirection then |
223 | local snapAngle = self:getDirectionSnapAngle(); |
224 | snapAngle = math.max(snapAngle, math.pi/(g_currentMission.terrainDetailAngleMaxValue+1)); |
225 | |
226 | local angleRad = Utils.getYRotationFromDirection(dx, dz) |
227 | angleRad = math.floor(angleRad / snapAngle + 0.5) * snapAngle; |
228 | |
229 | self.aiTractorDirectionX, self.aiTractorDirectionZ = Utils.getDirectionFromYRotation(angleRad); |
230 | else |
231 | local length = Utils.vector2Length(dx,dz); |
232 | self.aiTractorDirectionX = dx/length; |
233 | self.aiTractorDirectionZ = dz/length; |
234 | end |
235 | |
236 | |
237 | local x,y,z = getWorldTranslation(self.aiTractorDirectionNode); |
238 | self.aiTractorTargetX = x; |
239 | self.aiTractorTargetZ = z; |
240 | |
241 | self.aiTractorTurnLeft = nil; |
242 | |
243 | self.hasSeenValidArea = false; |
244 | |
245 | --AITractor.addCollisionTrigger(self, self); |
246 | end; |
247 | |
248 | AITractor.updateToolsInfo(self); |
249 | |
250 | --self:setAIImplementsMoveDown(true); |
251 | for _,implement in pairs(self.attachedImplements) do |
252 | if implement.object ~= nil then |
253 | implement.object:aiTurnOn(); |
254 | end; |
255 | end; |
256 | |
257 | if self.isServer then |
258 | self:addCollisionTrigger(self); |
259 | AIVehicleUtil.registerCollisions(self, self); |
260 | end; |
261 | |
262 | --self.checkSpeedLimit = true; |
263 | |
264 | end; |
265 | end; |
266 | |
267 | function AITractor:stopAITractor(noEventSend) |
268 | if noEventSend == nil or noEventSend == false then |
269 | if g_server ~= nil then |
270 | g_server:broadcastEvent(AITractorSetStartedEvent:new(self, false)); |
271 | else |
272 | g_client:getServerConnection():sendEvent(AITractorSetStartedEvent:new(self, false)); |
273 | end; |
274 | end; |
275 | self:dismiss(); |
276 | if self.isAITractorActivated then |
277 | g_currentMission.missionStats:updateStats("workersHired", -1); |
278 | |
279 | self:setCruiseControlState(Drivable.CRUISECONTROL_STATE_OFF, true); |
280 | |
281 | if self.isServer then |
282 | WheelsUtil.updateWheelsPhysics(self, 0, self.lastSpeedReal, 0, false, self.requiredDriveMode); |
283 | |
284 | self:removeCollisionTrigger(self); |
285 | AIVehicleUtil.unregisterCollisions(self, self); |
286 | end; |
287 | self.isAITractorActivated = false; |
288 | |
289 | if self.mapAIHotspot ~= nil then |
290 | g_currentMission.ingameMap:deleteMapHotspot(self.mapAIHotspot); |
291 | self.mapAIHotspot = nil; |
292 | end; |
293 | |
294 | --self.checkSpeedLimit = false; |
295 | |
296 | self.aiTractorTurnLeft = nil; |
297 | |
298 | self:setAIImplementsMoveDown(false, true); |
299 | --[[ |
300 | for _,implement in pairs(self.attachedImplements) do |
301 | if implement.object ~= nil then |
302 | implement.object:aiTurnOff(); |
303 | end |
304 | end; |
305 | ]]-- |
306 | |
307 | if not self:getIsActive() then |
308 | self:onLeave(); |
309 | end; |
310 | |
311 | end; |
312 | end; |
313 | |
314 | function AITractor:onEnter(isControlling) |
315 | if self.mapAIHotspot ~= nil then |
316 | self.mapAIHotspot.enabled = false; |
317 | end; |
318 | end; |
319 | |
320 | function AITractor:onLeave() |
321 | if self.mapAIHotspot ~= nil then |
322 | self.mapAIHotspot.enabled = true; |
323 | end; |
324 | end; |
325 | |
326 | function AITractor.updateAIMovement(self, dt) |
327 | |
328 | if not self.isControlled then |
329 | if g_currentMission.environment.needsLights then |
330 | self:setLightsVisibility(true); |
331 | else |
332 | self:setLightsVisibility(false); |
333 | end; |
334 | end; |
335 | |
336 | local allowedToDrive = true; |
337 | for _,v in pairs(self.numCollidingVehicles) do |
338 | if v > 0 then |
339 | allowedToDrive = false; |
340 | break; |
341 | end; |
342 | end; |
343 | if self.turnStage > 0 then |
344 | if self.waitForTurnTime > g_currentMission.time then |
345 | allowedToDrive = false; |
346 | end; |
347 | end; |
348 | if not allowedToDrive then |
349 | self.isHirableBlocked = true; |
350 | --local x,y,z = getWorldTranslation(self.aiTractorDirectionNode); |
351 | --local lx, lz = 0, 1; --AIVehicleUtil.getDriveDirection(self.aiTractorDirectionNode, self.aiTractorTargetX, y, self.aiTractorTargetZ); |
352 | --AIVehicleUtil.driveInDirection(self, dt, 30, 0, 0, 28, false, moveForwards, lx, lz) |
353 | AIVehicleUtil.driveInDirection(self, dt, 30, 0, 0, 28, false, moveForwards, nil, nil) |
354 | return; |
355 | end; |
356 | self.isHirableBlocked = false; |
357 | |
358 | local maxSpeed,_ = self:getSpeedLimit(); |
359 | maxSpeed = math.min(maxSpeed, self.cruiseControl.speed); |
360 | if self.turnStage > 0 then |
361 | maxSpeed = math.max(8, maxSpeed / 2); |
362 | end; |
363 | |
364 | if not self:getIsAITractorAllowed() then |
365 | self:stopAITractor(); |
366 | return; |
367 | end; |
368 | |
369 | |
370 | -- Seeding: |
371 | -- Required: Cultivated, Ploughed |
372 | -- Direct Planting: |
373 | -- Required: Seeded, Cultivated, Ploughed without Fruit of current type |
374 | -- Forage Wagon: |
375 | -- Required: Windrow of current type |
376 | -- Spray: |
377 | -- Required: Seeded, Cultivated, Ploughed without Sprayed |
378 | -- Mower: |
379 | -- Required: Fruit of type grass |
380 | |
381 | local leftMarker = self.aiCurrentLeftMarker; |
382 | local rightMarker = self.aiCurrentRightMarker; |
383 | local backMarker = self.aiCurrentBackMarker; |
384 | local groundInfoObject = self.aiCurrentGroundInfoObject; |
385 | |
386 | local terrainDetailRequiredMask = 0; |
387 | if groundInfoObject.aiTerrainDetailChannel1 >= 0 then |
388 | terrainDetailRequiredMask = bitOR(terrainDetailRequiredMask, 2^groundInfoObject.aiTerrainDetailChannel1); |
389 | if groundInfoObject.aiTerrainDetailChannel2 >= 0 then |
390 | terrainDetailRequiredMask = bitOR(terrainDetailRequiredMask, 2^groundInfoObject.aiTerrainDetailChannel2); |
391 | if groundInfoObject.aiTerrainDetailChannel3 >= 0 then |
392 | terrainDetailRequiredMask = bitOR(terrainDetailRequiredMask, 2^groundInfoObject.aiTerrainDetailChannel3); |
393 | if groundInfoObject.aiTerrainDetailChannel4 >= 0 then |
394 | terrainDetailRequiredMask = bitOR(terrainDetailRequiredMask, 2^groundInfoObject.aiTerrainDetailChannel4); |
395 | end |
396 | end |
397 | end |
398 | end |
399 | |
400 | local terrainDetailProhibitedMask = groundInfoObject.aiTerrainDetailProhibitedMask; |
401 | local requiredFruitType = groundInfoObject.aiRequiredFruitType; |
402 | local requiredMinGrowthState = groundInfoObject.aiRequiredMinGrowthState; |
403 | local requiredMaxGrowthState = groundInfoObject.aiRequiredMaxGrowthState; |
404 | local prohibitedFruitType = groundInfoObject.aiProhibitedFruitType; |
405 | local prohibitedMinGrowthState = groundInfoObject.aiProhibitedMinGrowthState; |
406 | local prohibitedMaxGrowthState = groundInfoObject.aiProhibitedMaxGrowthState; |
407 | |
408 | |
409 | local newTargetX, newTargetY, newTargetZ; |
410 | |
411 | local moveForwards = true; |
412 | local updateWheels = true; |
413 | |
414 | self.turnTimer = self.turnTimer - dt; |
415 | |
416 | self.lastArea = 0; |
417 | |
418 | if self.turnTimer < 0 or self.turnStage > 0 then |
419 | if self.turnStage > 1 then |
420 | local x,y,z = getWorldTranslation(self.aiTractorDirectionNode); |
421 | local dirX, dirZ = self.aiTractorDirectionX, self.aiTractorDirectionZ; |
422 | local myDirX, myDirY, myDirZ = localDirectionToWorld(self.aiTractorDirectionNode, 0, 0, 1); |
423 | |
424 | newTargetX = self.aiTractorTargetX; |
425 | newTargetY = y; |
426 | newTargetZ = self.aiTractorTargetZ; |
427 | if self.turnStage == 2 then |
428 | self.turnStageTimer = self.turnStageTimer - dt; |
429 | if myDirX*dirX + myDirZ*dirZ > 0.2 or self.turnStageTimer < 0 then |
430 | if self.aiTurnNoBackward then |
431 | self.turnStage = 4; |
432 | else |
433 | self.turnStage = 3; |
434 | moveForwards = false; |
435 | end; |
436 | if self.turnStageTimer < 0 then |
437 | |
438 | self.aiTractorTargetBeforeSaveX = self.aiTractorTargetX; |
439 | self.aiTractorTargetBeforeSaveZ = self.aiTractorTargetZ; |
440 | |
441 | newTargetX = self.aiTractorTargetBeforeTurnX; |
442 | newTargetZ = self.aiTractorTargetBeforeTurnZ; |
443 | |
444 | moveForwards = false; |
445 | self.turnStage = 6; |
446 | self.turnStageTimer = self.turnStage6Timeout; |
447 | else |
448 | self.turnStageTimer = self.turnStage3Timeout; |
449 | end; |
450 | end; |
451 | elseif self.turnStage == 3 then |
452 | self.turnStageTimer = self.turnStageTimer - dt; |
453 | if myDirX*dirX + myDirZ*dirZ > 0.95 or self.turnStageTimer < 0 then |
454 | self.turnStage = 4; |
455 | else |
456 | moveForwards = false; |
457 | end; |
458 | elseif self.turnStage == 4 then |
459 | local dx, dz = x-newTargetX, z-newTargetZ; |
460 | local dot = dx*dirX + dz*dirZ; |
461 | if -dot < self.turnEndDistance then |
462 | newTargetX = self.aiTractorTargetX + dirX*(self.turnTargetMoveBack + self.aiToolExtraTargetMoveBack); |
463 | newTargetY = y; |
464 | newTargetZ = self.aiTractorTargetZ + dirZ*(self.turnTargetMoveBack + self.aiToolExtraTargetMoveBack); |
465 | self.turnStage = 5; |
466 | --print("turning done"); |
467 | end; |
468 | elseif self.turnStage == 5 then |
469 | local backX, backY, backZ = getWorldTranslation(backMarker); |
470 | local dx, dz = backX-newTargetX, backZ-newTargetZ; |
471 | local dot = dx*dirX + dz*dirZ; |
472 | if -dot < self.turnEndBackDistance+self.aiToolExtraTargetMoveBack then |
473 | self.turnTimer = self.turnTimeoutLong; |
474 | self.turnStage = 0; |
475 | self:setAIImplementsMoveDown(true); |
476 | self.waitForTurnTime = g_currentMission.time + self.waitForTurnTimeout; |
477 | AITractor.updateInvertLeftRight(self); |
478 | leftMarker = self.aiCurrentLeftMarker; |
479 | rightMarker = self.aiCurrentRightMarker; |
480 | --print("turning done"); |
481 | end; |
482 | elseif self.turnStage == 6 then |
483 | self.turnStageTimer = self.turnStageTimer - dt; |
484 | if self.turnStageTimer < 0 then |
485 | self.turnStageTimer = self.turnStage2Timeout; |
486 | self.turnStage = 2; |
487 | |
488 | newTargetX = self.aiTractorTargetBeforeSaveX; |
489 | newTargetZ = self.aiTractorTargetBeforeSaveZ; |
490 | else |
491 | local x,y,z = getWorldTranslation(self.aiTractorDirectionNode); |
492 | local dirX, dirZ = -self.aiTractorDirectionX, -self.aiTractorDirectionZ; |
493 | -- just drive along direction |
494 | local targetX, targetZ = self.aiTractorTargetX, self.aiTractorTargetZ; |
495 | local dx, dz = x-targetX, z-targetZ; |
496 | local dot = dx*dirX + dz*dirZ; |
497 | |
498 | local projTargetX = targetX +dirX*dot; |
499 | local projTargetZ = targetZ +dirZ*dot; |
500 | |
501 | newTargetX = projTargetX-dirX*self.aiTractorLookAheadDistance; |
502 | newTargetZ = projTargetZ-dirZ*self.aiTractorLookAheadDistance; |
503 | moveForwards = false; |
504 | end; |
505 | end; |
506 | elseif self.turnStage == 1 then |
507 | -- turn |
508 | AITractor.updateInvertLeftRight(self); |
509 | leftMarker = self.aiCurrentLeftMarker; |
510 | rightMarker = self.aiCurrentRightMarker; |
511 | |
512 | local x,y,z = getWorldTranslation(self.aiTractorDirectionNode); |
513 | local dirX, dirZ = self.aiTractorDirectionX, self.aiTractorDirectionZ; |
514 | local sideX, sideZ = -dirZ, dirX; |
515 | local lX, lY, lZ = getWorldTranslation(leftMarker); |
516 | local rX, rY, rZ = getWorldTranslation(rightMarker); |
517 | |
518 | local markerWidth = Utils.vector2Length(lX-rX, lZ-rZ); |
519 | |
520 | local lWidthX = lX + dirX * self.sideWatchDirOffset; |
521 | local lWidthZ = lZ + dirZ * self.sideWatchDirOffset; |
522 | local lStartX = lWidthX - sideX*0.7*markerWidth; |
523 | local lStartZ = lWidthZ - sideZ*0.7*markerWidth; |
524 | local lHeightX = lStartX + dirX*self.sideWatchDirSize; |
525 | local lHeightZ = lStartZ + dirZ*self.sideWatchDirSize; |
526 | |
527 | local rWidthX = rX + dirX * self.sideWatchDirOffset; |
528 | local rWidthZ = rZ + dirZ * self.sideWatchDirOffset; |
529 | local rStartX = rWidthX + sideX*0.7*markerWidth; |
530 | local rStartZ = rWidthZ + sideZ*0.7*markerWidth; |
531 | local rHeightX = rStartX + dirX*self.sideWatchDirSize; |
532 | local rHeightZ = rStartZ + dirZ*self.sideWatchDirSize; |
533 | |
534 | local leftArea, leftAreaTotal = AITractor.getAIArea(self, lStartX, lStartZ, lWidthX, lWidthZ, lHeightX, lHeightZ, terrainDetailRequiredMask, terrainDetailProhibitedMask, requiredFruitType, requiredMinGrowthState, requiredMaxGrowthState, prohibitedFruitType, prohibitedMinGrowthState, prohibitedMaxGrowthState) |
535 | local rightArea, rightAreaTotal = AITractor.getAIArea(self, rStartX, rStartZ, rWidthX, rWidthZ, rHeightX, rHeightZ, terrainDetailRequiredMask, terrainDetailProhibitedMask, requiredFruitType, requiredMinGrowthState, requiredMaxGrowthState, prohibitedFruitType, prohibitedMinGrowthState, prohibitedMaxGrowthState) |
536 | |
537 | -- turn to where ground/fruit is to be changed |
538 | |
539 | local leftOk = (leftArea > 0 and leftArea > 0.15*leftAreaTotal); |
540 | local rightOk = (rightArea > 0 and rightArea > 0.15*rightAreaTotal); |
541 | |
542 | if self.aiTractorTurnLeft == nil then |
543 | if leftOk or rightOk then |
544 | if leftArea > rightArea then |
545 | self.aiTractorTurnLeft = true; |
546 | else |
547 | self.aiTractorTurnLeft = false; |
548 | end |
549 | else |
550 | self:stopAITractor(); |
551 | return; |
552 | end; |
553 | else |
554 | self.aiTractorTurnLeft = not self.aiTractorTurnLeft; |
555 | if (self.aiTractorTurnLeft and not leftOk) or (not self.aiTractorTurnLeft and not rightOk) then |
556 | self:stopAITractor(); |
557 | return; |
558 | end |
559 | end |
560 | |
561 | local targetX, targetZ = self.aiTractorTargetX, self.aiTractorTargetZ; |
562 | --[[local x = (lX+rX)/2; |
563 | local z = (lZ+rZ)/2; |
564 | local markerSideOffset, lY, lZ = worldToLocal(self.aiTractorDirectionNode, x, (lY+rY)/2, z); |
565 | markerSideOffset = math.abs(markerSideOffset); |
566 | local dx, dz = x-targetX, z-targetZ; |
567 | local dot = dx*dirX + dz*dirZ; |
568 | local x, z = targetX + dirX*dot, targetZ + dirZ*dot;]] |
569 | markerWidth = math.max(markerWidth*self.aiTurnWidthScale, markerWidth-self.aiTurnWidthMaxDifference); -- - markerSideOffset; |
570 | |
571 | local invertsMarker = AITractor.invertsMarkerOnTurn(self, self.aiTractorTurnLeft); |
572 | if not invertsMarker then |
573 | -- if not inverting, we need to adjust the markerWidth |
574 | local mx = (lX+rX)*0.5; |
575 | local mz = (lZ+rZ)*0.5; |
576 | local markerSideOffset, _, _ = worldToLocal(self.aiTractorDirectionNode, mx, (lY+rY)*0.5, mz); |
577 | --markerSideOffset = math.abs(markerSideOffset); |
578 | markerWidth = markerWidth + markerSideOffset; |
579 | --local dx, dz = x-targetX, z-targetZ; |
580 | --local dot = dx*dirX + dz*dirZ; |
581 | --local x, z = targetX + dirX*dot, targetZ + dirZ*dot;]] |
582 | end; |
583 | |
584 | |
585 | --local backX, backY, backZ = getWorldTranslation(backMarker); |
586 | local projTargetLX, projTargetLZ = Utils.projectOnLine(lX, lZ, targetX, targetZ, dirX, dirZ) |
587 | local projTargetRX, projTargetRZ = Utils.projectOnLine(rX, rZ, targetX, targetZ, dirX, dirZ) |
588 | |
589 | x = (projTargetLX+projTargetRX)*0.5; |
590 | z = (projTargetLZ+projTargetRZ)*0.5; |
591 | |
592 | local _, _, localZ = worldToLocal(self.aiTractorDirectionNode, x, (lY+rY)*0.5, z); |
593 | self.aiToolExtraTargetMoveBack = math.max(-localZ, 0); |
594 | |
595 | |
596 | if self.aiTractorTurnLeft then |
597 | newTargetX = x-sideX*markerWidth; |
598 | newTargetY = y; |
599 | newTargetZ = z-sideZ*markerWidth; |
600 | AITractor.aiRotateLeft(self, true); |
601 | else |
602 | newTargetX = x+sideX*markerWidth; |
603 | newTargetY = y; |
604 | newTargetZ = z+sideZ*markerWidth; |
605 | AITractor.aiRotateRight(self, true); |
606 | end; |
607 | local aiForceTurnNoBackward = false; |
608 | for _,implement in pairs(self.attachedImplements) do |
609 | if implement.object.aiForceTurnNoBackward then |
610 | aiForceTurnNoBackward = true; |
611 | break; |
612 | end; |
613 | end; |
614 | |
615 | self.aiTurnNoBackward = (markerWidth >= 2*self.aiTractorTurnRadius) or aiForceTurnNoBackward; |
616 | |
617 | self.aiTractorTargetBeforeTurnX = self.aiTractorTargetX; |
618 | self.aiTractorTargetBeforeTurnZ = self.aiTractorTargetZ; |
619 | |
620 | self.aiTractorDirectionX = -dirX; |
621 | self.aiTractorDirectionZ = -dirZ; |
622 | |
623 | self.turnStage = 2; |
624 | self.turnStageTimer = self.turnStage2Timeout; |
625 | |
626 | if self.aiTractorTurnLeft then |
627 | --print("turning left ", markerWidth); |
628 | else |
629 | --print("turning right ", markerWidth); |
630 | end; |
631 | else |
632 | self.turnStage = 1; |
633 | |
634 | self.waitForTurnTime = g_currentMission.time + self.waitForTurnTimeout; |
635 | self:setAIImplementsMoveDown(false); |
636 | updateWheels = false; |
637 | self.hasSeenValidArea = false; |
638 | end; |
639 | else |
640 | local dirX, dirZ = self.aiTractorDirectionX, self.aiTractorDirectionZ; |
641 | local lX, lY, lZ = getWorldTranslation(leftMarker); |
642 | local rX, rY, rZ = getWorldTranslation(rightMarker); |
643 | self.lastFrontMarkerDistance = self.lastSpeed*self.turnTimeout; |
644 | local scaledDistance = self.lastFrontMarkerDistance*self.frontMarkerDistanceScale |
645 | lX = lX + dirX*scaledDistance; |
646 | lZ = lZ + dirZ*scaledDistance; |
647 | |
648 | rX = rX + dirX*scaledDistance; |
649 | rZ = rZ + dirZ*scaledDistance; |
650 | |
651 | local heightX = lX + dirX*2; |
652 | local heightZ = lZ + dirZ*2; |
653 | |
654 | local area = AITractor.getAIArea(self, lX, lZ, rX, rZ, heightX, heightZ, terrainDetailRequiredMask, terrainDetailProhibitedMask, requiredFruitType, requiredMinGrowthState, requiredMaxGrowthState, prohibitedFruitType, prohibitedMinGrowthState, prohibitedMaxGrowthState); |
655 | |
656 | self.lastArea = area; |
657 | |
658 | local fieldIsOwned = g_currentMission:getIsFieldOwnedAtWorldPos((lX+rX)/2, (lZ+rZ)/2); |
659 | |
660 | if area >= 1 and fieldIsOwned then |
661 | self.turnTimer = math.max(self.turnTimer, self.turnTimeout); |
662 | self.hasSeenValidArea = true; |
663 | elseif area == 0 and self.hasSeenValidArea then |
664 | self.turnTimer = math.min(self.turnTimer, self.turnTimeout); |
665 | end; |
666 | |
667 | if not fieldIsOwned then |
668 | self:stopAITractor(); |
669 | return; |
670 | end; |
671 | |
672 | local x,y,z = getWorldTranslation(self.aiTractorDirectionNode); |
673 | local dirX, dirZ = self.aiTractorDirectionX, self.aiTractorDirectionZ; |
674 | -- just drive along direction |
675 | local targetX, targetZ = self.aiTractorTargetX, self.aiTractorTargetZ; |
676 | local dx, dz = x-targetX, z-targetZ; |
677 | local dot = dx*dirX + dz*dirZ; |
678 | |
679 | local projTargetX = targetX +dirX*dot; |
680 | local projTargetZ = targetZ +dirZ*dot; |
681 | |
682 | --print("old target: "..targetX.." ".. targetZ .. " distOnDir " .. dot.." proj: "..projTargetX.." "..projTargetZ); |
683 | |
684 | newTargetX = projTargetX+self.aiTractorDirectionX*self.aiTractorLookAheadDistance; |
685 | newTargetY = y; |
686 | newTargetZ = projTargetZ+self.aiTractorDirectionZ*self.aiTractorLookAheadDistance; |
687 | --print(distOnDir.." target: "..newTargetX.." ".. newTargetZ); |
688 | |
689 | --check if tool is folded/unfolded |
690 | for _,implement in pairs(self.attachedImplements) do |
691 | if implement.object ~= nil then |
692 | --if implement.object.turnOnFoldDirection ~= nil and implement.object.turnOnFoldDirection ~= 0 then |
693 | if implement.object.getIsInWorkPosition ~= nil then |
694 | updateWheels = updateWheels and implement.object:getIsInWorkPosition(); |
695 | end; |
696 | end; |
697 | end; |
698 | if updateWheels then |
699 | self:setAIImplementsMoveDown(true); |
700 | for _,implement in pairs(self.attachedImplements) do |
701 | if implement.object ~= nil then |
702 | if implement.object.attacherJoint.needsLowering and implement.object.aiNeedsLowering then |
703 | local jointDesc = self.attacherJoints[implement.jointDescIndex]; |
704 | if jointDesc.moveAlpha ~= jointDesc.lowerAlpha then |
705 | updateWheels = false; |
706 | self:setJointMoveDown(implement.jointDescIndex, true, true); |
707 | else |
708 | updateWheels = updateWheels and true; |
709 | end; |
710 | end; |
711 | end; |
712 | end; |
713 | end; |
714 | |
715 | if updateWheels then |
716 | for _,implement in pairs(self.attachedImplements) do |
717 | if implement.object ~= nil and implement.object.getIsTurnedOn ~= nil and not implement.object:getIsTurnedOn() and implement.object:getIsTurnedOnAllowed(true) then |
718 | implement.object:aiTurnOn(); |
719 | updateWheels = updateWheels and implement.object:getIsTurnedOn(); |
720 | end; |
721 | end; |
722 | end; |
723 | |
724 | if not updateWheels then |
725 | local lx, lz = AIVehicleUtil.getDriveDirection(self.aiTractorDirectionNode, newTargetX, newTargetY, newTargetZ); |
726 | AIVehicleUtil.driveInDirection(self, dt, 25, 1.0, 0.5, 20, false, moveForwards, lx, lz, 0, 1.0); |
727 | self.turnTimer = math.max(self.turnTimer, self.turnTimeout); |
728 | end; |
729 | |
730 | end; |
731 | |
732 | if updateWheels then |
733 | local lx, lz = AIVehicleUtil.getDriveDirection(self.aiTractorDirectionNode, newTargetX, newTargetY, newTargetZ); |
734 | |
735 | if self.turnStage == 3 and math.abs(lx) < 0.1 then |
736 | self.turnStage = 4; |
737 | moveForwards = true; |
738 | end; |
739 | |
740 | AIVehicleUtil.driveInDirection(self, dt, 25, 1.0, 0.5, 20, true, moveForwards, lx, lz, maxSpeed, 1.0); --0.9); |
741 | |
742 | --local maxAngle = 0.785398163; --45°; |
743 | local maxlx = 0.7071067; --math.sin(maxAngle); |
744 | local colDirX = lx; |
745 | local colDirZ = lz; |
746 | |
747 | if colDirX > maxlx then |
748 | colDirX = maxlx; |
749 | colDirZ = 0.7071067; --math.cos(maxAngle); |
750 | elseif colDirX < -maxlx then |
751 | colDirX = -maxlx; |
752 | colDirZ = 0.7071067; --math.cos(maxAngle); |
753 | end; |
754 | |
755 | for triggerId,_ in pairs(self.numCollidingVehicles) do |
756 | AIVehicleUtil.setCollisionDirection(self.aiTractorDirectionNode, triggerId, colDirX, colDirZ); |
757 | end; |
758 | end; |
759 | |
760 | if newTargetX ~= nil and newTargetZ ~= nil then |
761 | self.aiTractorTargetX = newTargetX; |
762 | self.aiTractorTargetZ = newTargetZ; |
763 | end; |
764 | end; |
765 | |
766 | function AITractor.switchToDirection(self, myDirX, myDirZ) |
767 | self.aiTractorDirectionX = myDirX; |
768 | self.aiTractorDirectionZ = myDirZ; |
769 | --print("switch to direction"); |
770 | end; |
771 | |
772 | function AITractor:addCollisionTrigger(object) |
773 | if self.isServer then |
774 | if object.aiTrafficCollisionTrigger ~= nil then |
775 | addTrigger(object.aiTrafficCollisionTrigger, "onTrafficCollisionTrigger", self); |
776 | self.numCollidingVehicles[object.aiTrafficCollisionTrigger] = 0; |
777 | end; |
778 | if object ~= self then |
779 | for k,v in pairs(object.components) do |
780 | self.trafficCollisionIgnoreList[v.node] = true; |
781 | end; |
782 | end |
783 | end |
784 | end; |
785 | |
786 | function AITractor:removeCollisionTrigger(object) |
787 | if self.isServer then |
788 | if object.aiTrafficCollisionTrigger ~= nil then |
789 | removeTrigger(object.aiTrafficCollisionTrigger); |
790 | self.numCollidingVehicles[object.aiTrafficCollisionTrigger] = nil; |
791 | end; |
792 | if object ~= self then |
793 | for k,v in pairs(object.components) do |
794 | self.trafficCollisionIgnoreList[v.node] = nil; |
795 | end; |
796 | end |
797 | end |
798 | end; |
799 | |
800 | |
801 | function AITractor:attachImplement(implement) |
802 | local object = implement.object; |
803 | if self.isAITractorActivated then |
804 | object:aiTurnOn(); |
805 | end; |
806 | self.aiToolsDirty = true; |
807 | if self.isServer then |
808 | self:removeCollisionTrigger(self); |
809 | AIVehicleUtil.unregisterCollisions(self, self); |
810 | self:addCollisionTrigger(self); |
811 | AIVehicleUtil.registerCollisions(self, self); |
812 | end; |
813 | end; |
814 | |
815 | function AITractor:detachImplement(implementIndex) |
816 | local object = self.attachedImplements[implementIndex].object; |
817 | if object ~= nil then |
818 | if self.isAITractorActivated then |
819 | object:aiTurnOff(); |
820 | end; |
821 | if self.aiCurrentLeftMarker == object.aiLeftMarker then |
822 | self.aiCurrentLeftMarker = nil; |
823 | self.aiCurrentRightMarker = nil; |
824 | self.aiCurrentBackMarker = nil; |
825 | end; |
826 | end |
827 | if self.isServer then |
828 | self:removeCollisionTrigger(object); |
829 | AIVehicleUtil.unregisterCollisions(self, object); |
830 | end; |
831 | self.aiToolsDirty = true; |
832 | end; |
833 | |
834 | function AITractor:onTrafficCollisionTrigger(triggerId, otherId, onEnter, onLeave, onStay, otherShapeId) |
835 | if onEnter or onLeave then |
836 | if g_currentMission.players[otherId] ~= nil then |
837 | if onEnter then |
838 | self.numCollidingVehicles[triggerId] = self.numCollidingVehicles[triggerId]+1; |
839 | elseif onLeave then |
840 | self.numCollidingVehicles[triggerId] = math.max(self.numCollidingVehicles[triggerId]-1, 0); |
841 | end; |
842 | else |
843 | local vehicle = g_currentMission.nodeToVehicle[otherId]; |
844 | if vehicle ~= nil and self.trafficCollisionIgnoreList[otherId] == nil then |
845 | if onEnter then |
846 | self.numCollidingVehicles[triggerId] = self.numCollidingVehicles[triggerId]+1; |
847 | elseif onLeave then |
848 | self.numCollidingVehicles[triggerId] = math.max(self.numCollidingVehicles[triggerId]-1, 0); |
849 | end; |
850 | end; |
851 | end; |
852 | end; |
853 | end; |
854 | |
855 | function AITractor.updateToolsInfo(self) |
856 | local leftMarker = nil; --self.aiLeftMarker; |
857 | local rightMarker = nil; --self.aiRightMarker; |
858 | local backMarker = nil; --self.aiBackMarker; |
859 | local groundInfoObject = nil; |
860 | if self.aiTerrainDetailChannel1 ~= nil and self.aiRequiredFruitType ~= nil and (self.aiTerrainDetailChannel1 >= 0 or self.aiRequiredFruitType ~= FruitUtil.FRUITTYPE_UNKNOWN) then |
861 | groundInfoObject = self; |
862 | end |
863 | local allImplementsAvailable = true; |
864 | for _,implement in pairs(self.attachedImplements) do |
865 | local object = implement.object; |
866 | if object ~= nil then |
867 | if object.aiLeftMarker ~= nil and leftMarker == nil then |
868 | leftMarker = object.aiLeftMarker; |
869 | end; |
870 | if object.aiRightMarker ~= nil and rightMarker == nil then |
871 | rightMarker = object.aiRightMarker; |
872 | end; |
873 | if object.aiBackMarker ~= nil and backMarker == nil then |
874 | backMarker = object.aiBackMarker; |
875 | end; |
876 | if groundInfoObject == nil and (object.aiTerrainDetailChannel1 >= 0 or object.aiRequiredFruitType ~= FruitUtil.FRUITTYPE_UNKNOWN) then |
877 | groundInfoObject = object; |
878 | end; |
879 | else |
880 | allImplementsAvailable = false; |
881 | end |
882 | end; |
883 | self.aiCurrentLeftMarker = leftMarker; |
884 | self.aiCurrentRightMarker = rightMarker; |
885 | self.aiCurrentBackMarker = backMarker; |
886 | self.aiCurrentGroundInfoObject = groundInfoObject; |
887 | AITractor.updateInvertLeftRight(self); |
888 | |
889 | -- only markes as non-dirty if all tools were available |
890 | if allImplementsAvailable then |
891 | self.aiToolsDirty = false; |
892 | end |
893 | end; |
894 | |
895 | function AITractor.updateInvertLeftRight(self) |
896 | if self.aiCurrentLeftMarker ~= nil and self.aiCurrentRightMarker ~= nil then |
897 | local lX, lY, lZ = worldToLocal(self.aiTractorDirectionNode, getWorldTranslation(self.aiCurrentLeftMarker)); |
898 | local rX, rY, rZ = worldToLocal(self.aiTractorDirectionNode, getWorldTranslation(self.aiCurrentRightMarker)); |
899 | if rX > lX then |
900 | self.aiCurrentLeftMarker, self.aiCurrentRightMarker = self.aiCurrentRightMarker, self.aiCurrentLeftMarker; |
901 | end; |
902 | end; |
903 | end; |
904 | |
905 | function AITractor.invertsMarkerOnTurn(self, turnLeft) |
906 | local res = false; |
907 | for _,implement in pairs(self.attachedImplements) do |
908 | if implement.object ~= nil then |
909 | for _, spec in pairs(implement.object.specializations) do |
910 | if spec.aiInvertsMarkerOnTurn ~= nil then |
911 | res = res or spec.aiInvertsMarkerOnTurn(implement.object, turnLeft); |
912 | end; |
913 | end; |
914 | end |
915 | end; |
916 | return res; |
917 | end; |
918 | |
919 | function AITractor:setAIImplementsMoveDown(moveDown, noEventSend) |
920 | if self.isServer and self.moveAIImplementsDown ~= moveDown and (noEventSend == nil or noEventSend == false) then |
921 | g_server:broadcastEvent(AISetImplementsMoveDownEvent:new(self, moveDown), nil, nil, self); |
922 | self.moveAIImplementsDown = moveDown; |
923 | end; |
924 | for _,implement in pairs(self.attachedImplements) do |
925 | if implement.object ~= nil then |
926 | if implement.object.attacherJoint.needsLowering and implement.object.aiNeedsLowering then |
927 | self:setJointMoveDown(implement.jointDescIndex, moveDown, true); |
928 | end; |
929 | if moveDown then |
930 | implement.object:aiLower(); |
931 | else |
932 | implement.object:aiRaise(); |
933 | end |
934 | end |
935 | end; |
936 | end; |
937 | |
938 | function AITractor.aiRotateRight(self, force) |
939 | if self.isServer then |
940 | g_server:broadcastEvent(AITractorRotateRightEvent:new(self), nil, nil, self); |
941 | end; |
942 | for _,implement in pairs(self.attachedImplements) do |
943 | if implement.object ~= nil then |
944 | implement.object:aiRotateRight(force); |
945 | end |
946 | end; |
947 | end; |
948 | |
949 | function AITractor.aiRotateLeft(self, force) |
950 | if self.isServer then |
951 | g_server:broadcastEvent(AITractorRotateLeftEvent:new(self), nil, nil, self); |
952 | end; |
953 | for _,implement in pairs(self.attachedImplements) do |
954 | if implement.object ~= nil then |
955 | implement.object:aiRotateLeft(force); |
956 | end |
957 | end; |
958 | end; |
959 | |
960 | function AITractor:canStartAITractor() |
961 | if g_currentMission.disableTractorAI then |
962 | return false; |
963 | end; |
964 | if self.aiCurrentLeftMarker == nil or self.aiCurrentRightMarker == nil or self.aiCurrentBackMarker == nil or self.aiCurrentGroundInfoObject == nil then |
965 | return false; |
966 | end; |
967 | if Hirable.numHirablesHired >= g_currentMission.maxNumHirables then |
968 | return false; |
969 | end; |
970 | |
971 | local hasGrimmerTrailer = false; |
972 | local hasGrimmerFruitPreparer = false; |
973 | |
974 | for _,implement in pairs(self.attachedImplements) do |
975 | if implement.object ~= nil then |
976 | if implement.object.setIsTurnedOn ~= nil and implement.object.powerConsumer ~= nil then |
977 | if not PowerConsumer.getIsTurnedOnAllowed(implement.object, nil, true) then |
978 | return false; |
979 | end; |
980 | end; |
981 | |
982 | if implement.object.configFileName == "data/vehicles/tools/grimme/grimmeFT300.xml" or implement.object.configFileName == "data/vehicles/tools/grimme/grimmeKS75-4.xml" then |
983 | hasGrimmerFruitPreparer = true; |
984 | end; |
985 | if implement.object.configFileName == "data/vehicles/tools/grimme/grimmeRootster604.xml" or implement.object.configFileName == "data/vehicles/tools/grimme/grimmeSE260.xml" then |
986 | hasGrimmerTrailer = true; |
987 | end; |
988 | end; |
989 | end |
990 | |
991 | -- invalid combos |
992 | if hasGrimmerFruitPreparer and hasGrimmerTrailer then |
993 | return false; |
994 | end; |
995 | |
996 | return true; |
997 | end; |
998 | |
999 | function AITractor:getIsAITractorAllowed() |
1000 | if g_currentMission.disableTractorAI then |
1001 | return false; |
1002 | end; |
1003 | if self.aiCurrentLeftMarker == nil or self.aiCurrentRightMarker == nil or self.aiCurrentBackMarker == nil or self.aiCurrentGroundInfoObject == nil then |
1004 | return false; |
1005 | end; |
1006 | return true; |
1007 | end; |
1008 | |
1009 | function AITractor.getAIArea(self, startWorldX, startWorldZ, widthWorldX, widthWorldZ, heightWorldX, heightWorldZ, terrainDetailRequiredMask, terrainDetailProhibitedMask, requiredFruitType, requiredMinGrowthState, requiredMaxGrowthState, prohibitedFruitType, prohibitedMinGrowthState, prohibitedMaxGrowthState) |
1010 | local area = 0; |
1011 | local totalArea = 1; |
1012 | if terrainDetailRequiredMask > 0 then |
1013 | local detailId = g_currentMission.terrainDetailId; |
1014 | local x,z, widthX,widthZ, heightX,heightZ = Utils.getXZWidthAndHeight(detailId, startWorldX, startWorldZ, widthWorldX, widthWorldZ, heightWorldX, heightWorldZ); |
1015 | setDensityCompareParams(detailId, "greater", 0, 0, terrainDetailRequiredMask, terrainDetailProhibitedMask); |
1016 | _,area,totalArea = getDensityParallelogram(detailId, x, z, widthX, widthZ, heightX, heightZ, g_currentMission.terrainDetailAIFirstChannel, g_currentMission.terrainDetailAINumChannels); |
1017 | if prohibitedFruitType ~= FruitUtil.FRUITTYPE_UNKNOWN then |
1018 | local ids = g_currentMission.fruits[prohibitedFruitType]; |
1019 | if ids ~= nil and ids.id ~= 0 then |
1020 | setDensityMaskParams(detailId, "between", prohibitedMinGrowthState+1, prohibitedMaxGrowthState+1); -- only fruit outside the given range is allowed |
1021 | local _,prohibitedArea = getDensityMaskedParallelogram(detailId, x, z, widthX, widthZ, heightX, heightZ, g_currentMission.terrainDetailAIFirstChannel, g_currentMission.terrainDetailAINumChannels, ids.id, 0, g_currentMission.numFruitStateChannels); |
1022 | setDensityMaskParams(detailId, "greater", 0); |
1023 | area = area - prohibitedArea; |
1024 | end |
1025 | end |
1026 | setDensityCompareParams(detailId, "greater", -1); |
1027 | |
1028 | elseif requiredFruitType ~= FruitUtil.FRUITTYPE_UNKNOWN then |
1029 | local ids = g_currentMission.fruits[requiredFruitType]; |
1030 | if ids ~= nil and ids.id ~= 0 then |
1031 | local x,z, widthX,widthZ, heightX,heightZ = Utils.getXZWidthAndHeight(ids.id, startWorldX, startWorldZ, widthWorldX, widthWorldZ, heightWorldX, heightWorldZ); |
1032 | setDensityCompareParams(ids.id, "between", requiredMinGrowthState+1, requiredMaxGrowthState+1); |
1033 | if terrainDetailProhibitedMask ~= 0 then |
1034 | local detailId = g_currentMission.terrainDetailId; |
1035 | setDensityMaskParams(ids.id, "greater", 0, 0, 0, terrainDetailProhibitedMask); |
1036 | _,area,totalArea = getDensityMaskedParallelogram(ids.id, x, z, widthX, widthZ, heightX, heightZ, 0, g_currentMission.numFruitStateChannels, detailId, g_currentMission.terrainDetailAIFirstChannel, g_currentMission.terrainDetailAINumChannels); |
1037 | setDensityMaskParams(ids.id, "greater", 0); |
1038 | else |
1039 | _,area,totalArea = getDensityParallelogram(ids.id, x, z, widthX, widthZ, heightX, heightZ, 0, g_currentMission.numFruitStateChannels); |
1040 | end |
1041 | setDensityCompareParams(ids.id, "greater", -1); |
1042 | end |
1043 | end |
1044 | return area,totalArea; |
1045 | end
|
Copyright (c) 2008-2015 GIANTS Software GmbH, Confidential, All Rights Reserved.
This document is to be published solely by ls-mods.de