Sprache Deutsch Language English

Script Dokumentation LS 2015 - AITractor (Patch 1.3)

Script Dokumentation Übersicht

scripts/vehicles/specializations/AITractor.lua

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
10AITractor = {}
11source("dataS/scripts/vehicles/specializations/AITractorSetStartedEvent.lua");
12source("dataS/scripts/vehicles/specializations/AISetImplementsMoveDownEvent.lua");
13source("dataS/scripts/vehicles/specializations/AITractorRotateLeftEvent.lua");
14source("dataS/scripts/vehicles/specializations/AITractorRotateRightEvent.lua");
15
16
17function AITractor.prerequisitesPresent(specializations)
18 return SpecializationUtil.hasSpecialization(Hirable, specializations) and SpecializationUtil.hasSpecialization(Drivable, specializations);
19end;
20
21function 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
98end;
99
100function AITractor:delete()
101 self:removeCollisionTrigger(self);
102 self:stopAITractor(true);
103end;
104
105function 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;
112end;
113
114function AITractor:writeStream(streamId, connection)
115 streamWriteBool(streamId, self.isAITractorActivated);
116end;
117
118function AITractor:mouseEvent(posX, posY, isDown, isUp, button)
119end;
120
121function AITractor:keyEvent(unicode, sym, modifier, isDown)
122end;
123
124function 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
148end;
149
150function 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
179end;
180
181function 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;
194end;
195
196function 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;
265end;
266
267function 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;
312end;
313
314function AITractor:onEnter(isControlling)
315 if self.mapAIHotspot ~= nil then
316 self.mapAIHotspot.enabled = false;
317 end;
318end;
319
320function AITractor:onLeave()
321 if self.mapAIHotspot ~= nil then
322 self.mapAIHotspot.enabled = true;
323 end;
324end;
325
326function 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;
764end;
765
766function AITractor.switchToDirection(self, myDirX, myDirZ)
767 self.aiTractorDirectionX = myDirX;
768 self.aiTractorDirectionZ = myDirZ;
769 --print("switch to direction");
770end;
771
772function 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
784end;
785
786function 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
798end;
799
800
801function 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;
813end;
814
815function 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;
832end;
833
834function 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;
853end;
854
855function 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
893end;
894
895function 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;
903end;
904
905function 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;
917end;
918
919function 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;
936end;
937
938function 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;
947end;
948
949function 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;
958end;
959
960function 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;
997end;
998
999function 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;
1007end;
1008
1009function 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;
1045end
Copyright (c) 2008-2015 GIANTS Software GmbH, Confidential, All Rights Reserved.
This document is to be published solely by ls-mods.de
Script Dokumentation Übersicht