Sprache Deutsch Language English

Script Dokumentation LS 2015 - AICombine (Patch 1.3)

Script Dokumentation Übersicht

scripts/vehicles/specializations/AICombine.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-- AICombine
3-- Specialization for ai combines
4--
5-- @author Stefan Geiger
6-- @date 10/01/09
7--
8-- Copyright (C) GIANTS Software GmbH, Confidential, All Rights Reserved.
9
10AICombine = {};
11source("dataS/scripts/vehicles/specializations/AICombineSetStartedEvent.lua");
12source("dataS/scripts/vehicles/specializations/AISetImplementsMoveDownEvent.lua");
13
14function AICombine.prerequisitesPresent(specializations)
15 return SpecializationUtil.hasSpecialization(Hirable, specializations) and SpecializationUtil.hasSpecialization(Combine, specializations);
16end;
17
18function AICombine:load(xmlFile)
19
20 self.startAIThreshing = SpecializationUtil.callSpecializationsFunction("startAIThreshing");
21 self.stopAIThreshing = SpecializationUtil.callSpecializationsFunction("stopAIThreshing");
22 self.setAIImplementsMoveDown = SpecializationUtil.callSpecializationsFunction("setAIImplementsMoveDown");
23
24 self.addCollisionTrigger = AICombine.addCollisionTrigger;
25 self.removeCollisionTrigger = AICombine.removeCollisionTrigger;
26 self.onTrafficCollisionTrigger = AICombine.onTrafficCollisionTrigger;
27
28 self.canStartAIThreshing = AICombine.canStartAIThreshing;
29 self.getIsAIThreshingAllowed = AICombine.getIsAIThreshingAllowed;
30
31 self.isAIThreshing = false;
32 self.aiTreshingDirectionNode = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.aiTreshingDirectionNode#index"));
33 if self.aiTreshingDirectionNode == nil then
34 self.aiTreshingDirectionNode = self.components[1].node;
35 end;
36
37 self.lookAheadDistance = 10;
38 self.turnTimeout = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.turnTimeout"), 200);
39 self.turnTimeoutLong = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.turnTimeoutLong"), 6000);
40 self.turnTimer = self.turnTimeout;
41 self.turnEndDistance = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.turnEndDistance"), 4);
42
43 self.waitForTurnTimeout = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.waitForTurnTime"), 1500);
44 self.waitForTurnTime = 0;
45
46
47 self.sideWatchDirOffset = -8;
48 self.sideWatchDirSize = 8;
49
50 self.frontAreaSize = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.frontAreaSize#value"), 2);
51
52 self.waitingForTrailerToUnload = false;
53
54 self.waitingForDischarge = false;
55 self.waitForDischargeTime = 0;
56 self.waitForDischargeTimeout = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.waitForDischargeTime"), 10000);
57
58 self.turnStage = 0;
59
60
61 self.aiLeftMarker = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.aiLeftMarker#index"));
62 self.aiRightMarker = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.aiRightMarker#index"));
63 self.aiTrafficCollisionTrigger = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.aiTrafficCollisionTrigger#index"));
64
65 self.aiTurnThreshWidthScale = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.aiTurnThreshWidthScale#value"), 0.9);
66 self.aiTurnThreshWidthMaxDifference = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.aiTurnThreshWidthMaxDifference#value"), 0.6); -- do at most a 0.6m overlap
67
68
69 self.trafficCollisionIgnoreList = {};
70 for k,v in pairs(self.components) do
71 self.trafficCollisionIgnoreList[v.node] = true;
72 end;
73 self.numCollidingVehicles = {};
74
75 self.driveBackTimeout = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.driveBackTimeout"), 1000);
76 self.driveBackTime = 0;
77 self.driveBackAfterDischarge = false;
78
79 self.dtSum = 0;
80
81 self.turnStage1Timeout = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.turnForwardTimeout"), 20000);
82 self.turnStage1AngleCosThreshold = math.cos(math.rad(Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.turnForwardAngleThreshold"), 75)));
83 self.turnStage2Timeout = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.turnBackwardTimeout"), 20000);
84 self.turnStage2AngleCosThreshold = math.cos(math.rad(Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.turnBackwardAngleThreshold"), 15)));
85 self.turnStage4Timeout = 3000;
86
87 self.waitingForWeather = false;
88
89 self.aiRescueTimeout = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.aiRescue#timeout"), 10000);
90 self.aiRescueTimer = self.aiRescueTimeout;
91 self.aiRescueForce = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.aiRescue#force"), 60);
92 self.aiRescueSpeedThreshold = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.aiRescue#speedThreshold"), 0.0001);
93 self.aiRescueNode = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.aiRescue#index"));
94 if self.aiRescueNode == nil then
95 self.aiRescueNode = self.components[1].node;
96 end;
97
98 self.numAttachedTrailers = 0;
99
100 self.showFieldNotOwnedWarningTimer = 0;
101
102 --self.debugDirection = loadI3DFile("data/debugDirection.i3d");
103 --link(self.aiTreshingDirectionNode, self.debugDirection);
104
105 --self.debugPosition = loadI3DFile("data/debugPosition.i3d");
106 --link(self.aiTreshingDirectionNode, self.debugPosition);
107
108end;
109
110function AICombine:delete()
111 self:removeCollisionTrigger(self);
112 self:stopAIThreshing(true);
113end;
114
115function AICombine:readStream(streamId, connection)
116 local isAIThreshing = streamReadBool(streamId);
117 if isAIThreshing then
118 self:startAIThreshing(true);
119 else
120 self:stopAIThreshing(true);
121 end;
122end;
123
124function AICombine:writeStream(streamId, connection)
125 streamWriteBool(streamId, self.isAIThreshing);
126end;
127
128function AICombine:mouseEvent(posX, posY, isDown, isUp, button)
129end;
130
131function AICombine:keyEvent(unicode, sym, modifier, isDown)
132end;
133
134function AICombine:update(dt)
135 --if self:getIsActiveForInput(false) then
136 local activeForInput = true;
137 if g_gui.currentGui ~= nil or g_currentMission.isPlayerFrozen then
138 activeForInput = false;
139 end;
140 if activeForInput and self.isEntered then
141 if InputBinding.hasEvent(InputBinding.TOGGLE_AI) and not g_currentMission.inGameMessage:getIsVisible() then
142 if g_currentMission:getHasPermission("hireAI") then
143 if self.isAIThreshing then
144 self:stopAIThreshing();
145 else
146 if self:canStartAIThreshing() then
147 self:startAIThreshing();
148 end;
149 end;
150 end
151 end;
152 end;
153end;
154
155function AICombine:updateTick(dt)
156 if self.isServer then
157 if self.isAIThreshing then
158 if self.isBroken then
159 self:stopAIThreshing();
160 end;
161
162 self.dtSum = self.dtSum + dt;
163 if self.dtSum > 50 then
164 AICombine.updateAIMovement(self, self.dtSum);
165 self.dtSum = 0;
166 end;
167
168 if self.isAIThreshing then
169 if (self.fillLevel > 0 or self:getCapacity() <= 0) and (self.fillLevel >= self:getCapacity()*0.8 or next(self.overloadingTrailersInRange) ~= nil) then
170 local pipeState = self:getOverloadingTrailerInRangePipeState();
171 if pipeState > 0 then
172 self:setPipeState(pipeState);
173 else
174 self:setPipeState(2);
175 end;
176 if next(self.overloadingTrailersInRange) ~= nil then
177 self.waitForDischargeTime = g_currentMission.time + self.waitForDischargeTimeout;
178 end;
179 if self.fillLevel >= self:getCapacity() and self:getCapacity() > 0 then
180 self.driveBackAfterDischarge = true;
181 self.waitingForDischarge = true;
182 self.waitForDischargeTime = g_currentMission.time + self.waitForDischargeTimeout;
183 elseif next(self.overloadingTrailersInRange) == nil then
184 if self:getIsThreshingAllowed(true) then
185 self:setIsTurnedOn(true);
186 self.waitingForDischarge = false;
187 end;
188 end;
189 else
190 -- no trailer in range and not full
191 if (self.waitingForDischarge and self.fillLevel < self:getCapacity()) or self.waitForDischargeTime <= g_currentMission.time then
192 self.waitingForDischarge = false;
193 if next(self.overloadingTrailersInRange) == nil then
194 -- only close the pipe if no trailer is in range
195 self:setPipeState(1);
196 end;
197 if self:getIsThreshingAllowed(true) then
198 self:setIsTurnedOn(true);
199 end;
200 end;
201 end;
202
203 if self:getCapacity() > 0 and #self.beaconLights > 0 then
204 if self.fillLevel >= 0.8 * self:getCapacity() and self.beaconLightsActive == false then
205 self:setBeaconLightsVisibility(true);
206 elseif self.fillLevel < 0.8 * self:getCapacity() and self.beaconLightsActive == true then
207 self:setBeaconLightsVisibility(false);
208 end;
209 end;
210
211 if self.aiLeftMarker == nil or self.aiRightMarker == nil then
212 if not self:canStartAIThreshing() then
213 self:stopAIThreshing();
214 end;
215 end;
216
217 end
218 else
219 self.dtSum = 0;
220 end;
221 self.showFieldNotOwnedWarningTimer = self.showFieldNotOwnedWarningTimer - dt;
222 end;
223end;
224
225function AICombine:draw()
226 if g_currentMission:getHasPermission("hireAI") then
227 if self.isAIThreshing then
228 g_currentMission:addHelpButtonText(g_i18n:getText("DismissEmployee"), InputBinding.TOGGLE_AI);
229 else
230 if self:canStartAIThreshing() then
231 g_currentMission:addHelpButtonText(g_i18n:getText("HireEmployee"), InputBinding.TOGGLE_AI);
232 end;
233 end;
234 end
235 if self.showFieldNotOwnedWarningTimer > 0 then
236 g_currentMission:showBlinkingWarning(g_i18n:getText("You_dont_own_this_field"));
237 end;
238end;
239
240function AICombine:startAIThreshing(noEventSend)
241 if noEventSend == nil or noEventSend == false then
242 if g_server ~= nil then
243 g_server:broadcastEvent(AICombineSetStartedEvent:new(self, true), nil, nil, self);
244 else
245 g_client:getServerConnection():sendEvent(AICombineSetStartedEvent:new(self, true));
246 end;
247 end;
248
249 self:hire();
250 if not self.isAIThreshing then
251 g_currentMission.missionStats:updateStats("workersHired", 1);
252 self.isAIThreshing = true;
253
254 local hotspotX, _, hotspotZ = getWorldTranslation(self.rootNode);
255 self.mapAIHotspot = g_currentMission.ingameMap:createMapHotspot("mapAIHotspot", "dataS2/menu/hud/hud_pda_spot_helper.png", hotspotX, hotspotZ, nil, nil, false, false, false, self.components[1].node);
256
257 self:setCruiseControlState(Drivable.CRUISECONTROL_STATE_ACTIVE, noEventSend);
258
259 if self.isServer then
260 self.turnTimer = self.turnTimeoutLong;
261 self.turnStage = 0;
262
263 self.mapAIHotspot.enabled = false;
264
265 local dx,_,dz = localDirectionToWorld(self.aiTreshingDirectionNode, 0, 0, 1);
266 if g_currentMission.snapAIDirection then
267 local snapAngle = self:getDirectionSnapAngle();
268 snapAngle = math.max(snapAngle, math.pi/(g_currentMission.terrainDetailAngleMaxValue+1));
269
270 local angleRad = Utils.getYRotationFromDirection(dx, dz)
271 angleRad = math.floor(angleRad / snapAngle + 0.5) * snapAngle;
272
273 self.aiThreshingDirectionX, self.aiThreshingDirectionZ = Utils.getDirectionFromYRotation(angleRad);
274 else
275 local length = Utils.vector2Length(dx,dz);
276 self.aiThreshingDirectionX = dx/length;
277 self.aiThreshingDirectionZ = dz/length;
278 end
279
280 local x,y,z = getWorldTranslation(self.aiTreshingDirectionNode);
281 self.aiThreshingTargetX = x;
282 self.aiThreshingTargetZ = z;
283
284 self:addCollisionTrigger(self);
285 AIVehicleUtil.registerCollisions(self, self);
286 end;
287
288 for _,implement in pairs(self.attachedImplements) do
289 if implement.object ~= nil then
290 if implement.object.attacherJoint.needsLowering and implement.object.aiNeedsLowering then
291 self:setJointMoveDown(implement.jointDescIndex, true, true)
292 end;
293 implement.object:aiTurnOn();
294 end
295 end;
296 if self.threshingStartAnimation ~= nil and self.playAnimation ~= nil then
297 self:playAnimation(self.threshingStartAnimation, self.threshingStartAnimationSpeedScale, nil, true);
298 end
299
300 self.waitingForDischarge = false;
301 self:setIsTurnedOn(true, true);
302
303 --self.checkSpeedLimit = true;
304 self.waitingForWeather = false;
305 end;
306end;
307
308function AICombine:stopAIThreshing(noEventSend)
309 if noEventSend == nil or noEventSend == false then
310 if g_server ~= nil then
311 g_server:broadcastEvent(AICombineSetStartedEvent:new(self, false), nil, nil, self);
312 else
313 g_client:getServerConnection():sendEvent(AICombineSetStartedEvent:new(self, false));
314 end;
315 end;
316 self:dismiss();
317 if self.isAIThreshing then
318 g_currentMission.missionStats:updateStats("workersHired", -1);
319 self.isAIThreshing = false;
320
321 self.allowsThreshing = true;
322
323 --self.checkSpeedLimit = false;
324 self.waitingForWeather = false;
325
326 if self.mapAIHotspot ~= nil then
327 g_currentMission.ingameMap:deleteMapHotspot(self.mapAIHotspot);
328 self.mapAIHotspot = nil;
329 end;
330
331 self:setIsTurnedOn(false, true);
332
333 self:setCruiseControlState(Drivable.CRUISECONTROL_STATE_OFF, noEventSend);
334
335 if self.isServer then
336 WheelsUtil.updateWheelsPhysics(self, 0, self.lastSpeedReal, 0, false, self.requiredDriveMode);
337
338 self:removeCollisionTrigger(self);
339 AIVehicleUtil.unregisterCollisions(self, self);
340 end;
341 for _,implement in pairs(self.attachedImplements) do
342 if implement.object ~= nil then
343 if implement.object.attacherJoint.needsLowering and implement.object.aiNeedsLowering then
344 self:setJointMoveDown(implement.jointDescIndex, false, true)
345 end;
346 implement.object:aiTurnOff();
347 end
348 end;
349
350 self.driveBackPosX = nil;
351
352 if self.beaconLightsActive == true then
353 self:setBeaconLightsVisibility(false);
354 end
355
356 if not self:getIsActive() then
357 self:onLeave();
358 end;
359 end;
360end;
361
362function AICombine:onEnter(isControlling)
363 if self.mapAIHotspot ~= nil then
364 self.mapAIHotspot.enabled = false;
365 end;
366end;
367
368function AICombine:onLeave()
369 if self.mapAIHotspot ~= nil then
370 self.mapAIHotspot.enabled = true;
371 end;
372end;
373
374function AICombine.updateAIMovement(self, dt)
375
376 if not self:getIsAIThreshingAllowed() then
377 self:stopAIThreshing();
378 return;
379 end;
380
381 if not self.isControlled then
382 if g_currentMission.environment.needsLights then
383 self:setLightsVisibility(true);
384 else
385 self:setLightsVisibility(false);
386 end;
387 end;
388
389 local allowedToDrive = true;
390 if self:getCapacity() == 0 then
391 if not self.pipeStateIsUnloading[self.currentPipeState] then
392 allowedToDrive = false;
393 end
394 if not self.isPipeUnloading and (self.lastArea > 0 or self.lastLostFillLevel > 0) then
395 -- there is some fruit to unload, but there is no trailer. Stop and wait for a trailer
396 self.waitingForTrailerToUnload = true;
397 end;
398 else
399 if self.fillLevel >= self:getCapacity() then
400 allowedToDrive = false;
401 end
402 end
403
404 if self.waitingForTrailerToUnload then
405 if self.lastValidFillType ~= Fillable.FILLTYPE_UNKNOWN then
406 local trailer = self:findTrailerToUnload(self.lastValidFillType);
407 if trailer ~= nil then
408 -- there is a trailer to unload. Continue working
409 self.waitingForTrailerToUnload = false;
410 end;
411 else
412 -- we did not cut anything yet. We shouldn't have ended in this state. Just continue working
413 self.waitingForTrailerToUnload = false;
414 end;
415 end;
416
417 if (self.fillLevel >= self:getCapacity() and self:getCapacity() > 0) or self.waitingForTrailerToUnload or self.waitingForDischarge then
418 allowedToDrive = false;
419 if self.driveBackPosX == nil then
420 self.driveBackPosX, self.driveBackPosY, self.driveBackPosZ = getWorldTranslation(self.aiTreshingDirectionNode);
421 end
422 end;
423 for _,v in pairs(self.numCollidingVehicles) do
424 if v > 0 then
425 allowedToDrive = false;
426 break;
427 end;
428 end;
429 if self.turnStage > 0 then
430 if self.waitForTurnTime > g_currentMission.time or (self.pipeIsUnloading and self.turnStage < 3) then
431 allowedToDrive = false;
432 end;
433 end;
434 if not self:getIsThreshingAllowed(true) then
435 if self.turnStage == 0 then
436 allowedToDrive = false;
437 self:setIsTurnedOn(false);
438 self.waitingForWeather = true;
439 if self.driveBackPosX == nil then
440 self.driveBackPosX, self.driveBackPosY, self.driveBackPosZ = getWorldTranslation(self.aiTreshingDirectionNode);
441 end
442 end;
443 else
444 if self.waitingForWeather then
445 self:startThreshing();
446 self.waitingForWeather = false;
447 end;
448 end;
449
450 -- check if cutter is lowered completly
451 if self.driveBackPosX == nil then
452
453 if self.turnStage == 0 or self.turnStage > 2 then
454 for _,implement in pairs(self.attachedImplements) do
455 if implement.object ~= nil then
456 if implement.object.attacherJoint.needsLowering and implement.object.aiNeedsLowering then
457 local jointDesc = self.attacherJoints[implement.jointDescIndex];
458 allowedToDrive = allowedToDrive and (jointDesc.moveAlpha == jointDesc.lowerAlpha);
459 end;
460 end;
461 end;
462 end;
463
464 end
465
466
467 if not allowedToDrive then
468 self.isHirableBlocked = true;
469 --local x,y,z = getWorldTranslation(self.aiTreshingDirectionNode);
470 --local lx, lz = 0, 1; --AIVehicleUtil.getDriveDirection(self.aiTreshingDirectionNode, self.aiThreshingTargetX, y, self.aiThreshingTargetZ);
471 --AIVehicleUtil.driveInDirection(self, dt, 30, 0, 0, 28, false, moveForwards, lx, lz)
472 AIVehicleUtil.driveInDirection(self, dt, 30, 0, 0, 28, false, moveForwards, nil, nil)
473 return;
474 elseif self:getIsTurnedOn() == false then
475 self:setIsTurnedOn(true);
476 end;
477 self.isHirableBlocked = false;
478
479 local maxSpeed,_ = self:getSpeedLimit();
480 maxSpeed = math.min(maxSpeed, self.cruiseControl.speed);
481 if self.turnStage > 0 then
482 --maxSpeed = math.max(8, maxSpeed / 2);
483 end;
484
485 local leftMarker = self.aiLeftMarker;
486 local rightMarker = self.aiRightMarker;
487 local hasFruitPreparer = false;
488 local fruitType = self.lastValidInputFruitType;
489 if self.fruitPreparerFruitType ~= nil and self.fruitPreparerFruitType == fruitType then
490 hasFruitPreparer = true;
491 end
492 for cutter,implement in pairs(self.attachedCutters) do
493 if cutter.aiLeftMarker ~= nil and leftMarker == nil then
494 leftMarker = cutter.aiLeftMarker;
495 end;
496 if cutter.aiRightMarker ~= nil and rightMarker == nil then
497 rightMarker = cutter.aiRightMarker;
498 end;
499 if Cutter.getUseLowSpeedLimit(cutter) then
500 maxSpeed = maxSpeed*0.5;
501 end;
502 end;
503
504 if leftMarker == nil or rightMarker == nil then
505 self:stopAIThreshing();
506 return;
507 end;
508
509 if self.driveBackPosX ~= nil then
510 local x,y,z = getWorldTranslation(self.aiTreshingDirectionNode);
511 local dx, dy, dz = worldToLocal(self.aiTreshingDirectionNode, self.driveBackPosX, self.driveBackPosY, self.driveBackPosZ);
512 local lx, lz = AIVehicleUtil.getDriveDirection(self.aiTreshingDirectionNode, self.aiThreshingTargetX, y, self.aiThreshingTargetZ);
513 AIVehicleUtil.driveInDirection(self, dt, 30, 1, 0.5, 28, true, false, lx, lz, maxSpeed, 1); -- dz > 0, lx, lz, maxSpeed, 1)
514 if dz >= 0 then
515 self.driveBackPosX = nil;
516 else
517 return;
518 end
519 end;
520
521 local hasArea = true;
522 if self.lastArea < 1 then
523 local x,y,z = getWorldTranslation(self.aiTreshingDirectionNode);
524 local dirX, dirZ = self.aiThreshingDirectionX, self.aiThreshingDirectionZ;
525 local lInX, lInY, lInZ = getWorldTranslation(leftMarker);
526 local rInX, rInY, rInZ = getWorldTranslation(rightMarker);
527
528 local heightX = lInX + dirX * self.frontAreaSize;
529 local heightZ = lInZ + dirZ * self.frontAreaSize;
530
531 local area = Utils.getFruitArea(fruitType, lInX, lInZ, rInX, rInZ, heightX, heightZ, hasFruitPreparer);
532 if area < 1 then
533 hasArea = false;
534 end;
535 end;
536 if hasArea then
537 self.turnTimer = self.turnTimeout;
538 else
539 self.turnTimer = self.turnTimer - dt;
540 end;
541
542 local newTargetX, newTargetY, newTargetZ;
543
544 local moveForwards = true;
545 local updateWheels = true;
546
547 if self.turnTimer < 0 or self.turnStage > 0 then
548 if self.turnStage > 0 then
549 local x,y,z = getWorldTranslation(self.aiTreshingDirectionNode);
550 local dirX, dirZ = self.aiThreshingDirectionX, self.aiThreshingDirectionZ;
551 local myDirX, myDirY, myDirZ = localDirectionToWorld(self.aiTreshingDirectionNode, 0, 0, 1);
552
553 newTargetX = self.aiThreshingTargetX;
554 newTargetY = y;
555 newTargetZ = self.aiThreshingTargetZ;
556 if self.turnStage == 1 then
557 self.turnStageTimer = self.turnStageTimer - dt;
558 if self.lastSpeed < self.aiRescueSpeedThreshold then
559 self.aiRescueTimer = self.aiRescueTimer - dt;
560 else
561 self.aiRescueTimer = self.aiRescueTimeout;
562 end;
563 if myDirX*dirX + myDirZ*dirZ > self.turnStage1AngleCosThreshold or self.turnStageTimer < 0 or self.aiRescueTimer < 0 then
564 self.turnStage = 2;
565 moveForwards = false;
566 if self.turnStageTimer < 0 or self.aiRescueTimer < 0 then
567
568 self.aiThreshingTargetBeforeSaveX = self.aiThreshingTargetX;
569 self.aiThreshingTargetBeforeSaveZ = self.aiThreshingTargetZ;
570
571 newTargetX = self.aiThreshingTargetBeforeTurnX;
572 newTargetZ = self.aiThreshingTargetBeforeTurnZ;
573
574 moveForwards = false;
575 self.turnStage = 4;
576 self.turnStageTimer = self.turnStage4Timeout;
577 else
578 self.turnStageTimer = self.turnStage2Timeout;
579 end;
580 self.aiRescueTimer = self.aiRescueTimeout;
581 end;
582 elseif self.turnStage == 2 then
583 self.turnStageTimer = self.turnStageTimer - dt;
584 if self.lastSpeed < self.aiRescueSpeedThreshold then
585 self.aiRescueTimer = self.aiRescueTimer - dt;
586 else
587 self.aiRescueTimer = self.aiRescueTimeout;
588 end;
589 if myDirX*dirX + myDirZ*dirZ > self.turnStage2AngleCosThreshold or self.turnStageTimer < 0 or self.aiRescueTimer < 0 then
590 AICombine.switchToTurnStage3(self);
591 else
592 moveForwards = false;
593 end;
594 elseif self.turnStage == 3 then
595 --[[if Utils.vector2Length(x-newTargetX, z-newTargetZ) < self.turnEndDistance then
596 self.turnTimer = self.turnTimeoutLong;
597 self.turnStage = 0;
598 --print("turning done");
599 end;]]
600 if self.lastSpeed < self.aiRescueSpeedThreshold then
601 self.aiRescueTimer = self.aiRescueTimer - dt;
602 else
603 self.aiRescueTimer = self.aiRescueTimeout;
604 end;
605 local dx, dz = x-newTargetX, z-newTargetZ;
606 local dot = dx*dirX + dz*dirZ;
607 if -dot < self.turnEndDistance then
608 self.turnTimer = self.turnTimeoutLong;
609 self.turnStage = 0;
610 elseif self.aiRescueTimer < 0 then
611 self.aiThreshingTargetBeforeSaveX = self.aiThreshingTargetX;
612 self.aiThreshingTargetBeforeSaveZ = self.aiThreshingTargetZ;
613
614 newTargetX = self.aiThreshingTargetBeforeTurnX;
615 newTargetZ = self.aiThreshingTargetBeforeTurnZ;
616
617 moveForwards = false;
618 self.turnStage = 4;
619 self.turnStageTimer = self.turnStage4Timeout;
620 end;
621 elseif self.turnStage == 4 then
622 self.turnStageTimer = self.turnStageTimer - dt;
623 if self.lastSpeed < self.aiRescueSpeedThreshold then
624 self.aiRescueTimer = self.aiRescueTimer - dt;
625 else
626 self.aiRescueTimer = self.aiRescueTimeout;
627 end;
628 if self.aiRescueTimer < 0 then
629 self.aiRescueTimer = self.aiRescueTimeout;
630 local x,y,z = localDirectionToWorld(self.aiRescueNode, 0, 0, -1);
631 local scale = self.aiRescueForce/Utils.vector2Length(x,z);
632 addForce(self.aiRescueNode, x*scale, 0, z*scale, 0, 0, 0, true);
633 end;
634 if self.turnStageTimer < 0 then
635 self.aiRescueTimer = self.aiRescueTimeout;
636 self.turnStageTimer = self.turnStage1Timeout;
637 self.turnStage = 1;
638
639 newTargetX = self.aiThreshingTargetBeforeSaveX;
640 newTargetZ = self.aiThreshingTargetBeforeSaveZ;
641 else
642 local dirX, dirZ = -dirX, -dirZ;
643 -- just drive along direction
644 local targetX, targetZ = self.aiThreshingTargetX, self.aiThreshingTargetZ;
645 local dx, dz = x-targetX, z-targetZ;
646 local dot = dx*dirX + dz*dirZ;
647
648 local projTargetX = targetX +dirX*dot;
649 local projTargetZ = targetZ +dirZ*dot;
650
651 newTargetX = projTargetX-dirX*self.lookAheadDistance;
652 newTargetZ = projTargetZ-dirZ*self.lookAheadDistance;
653 moveForwards = false;
654 end;
655 end;
656 elseif fruitType == FruitUtil.FRUITTYPE_UNKNOWN then
657 self:stopAIThreshing();
658 return;
659 else
660 -- turn
661
662 local x,y,z = getWorldTranslation(self.aiTreshingDirectionNode);
663 local dirX, dirZ = self.aiThreshingDirectionX, self.aiThreshingDirectionZ;
664 local sideX, sideZ = -dirZ, dirX;
665 local lInX, lInY, lInZ = getWorldTranslation(leftMarker);
666 local rInX, rInY, rInZ = getWorldTranslation(rightMarker);
667
668 local threshWidth = Utils.vector2Length(lInX-rInX, lInZ-rInZ);
669 local turnLeft = true;
670
671 local lWidthX = x - sideX*0.5*threshWidth + dirX * self.sideWatchDirOffset;
672 local lWidthZ = z - sideZ*0.5*threshWidth + dirZ * self.sideWatchDirOffset;
673 local lStartX = lWidthX - sideX*0.7*threshWidth;
674 local lStartZ = lWidthZ - sideZ*0.7*threshWidth;
675 local lHeightX = lStartX + dirX*self.sideWatchDirSize;
676 local lHeightZ = lStartZ + dirZ*self.sideWatchDirSize;
677
678 local rWidthX = x + sideX*0.5*threshWidth + dirX * self.sideWatchDirOffset;
679 local rWidthZ = z + sideZ*0.5*threshWidth + dirZ * self.sideWatchDirOffset;
680 local rStartX = rWidthX + sideX*0.7*threshWidth;
681 local rStartZ = rWidthZ + sideZ*0.7*threshWidth;
682 local rHeightX = rStartX + dirX*self.sideWatchDirSize;
683 local rHeightZ = rStartZ + dirZ*self.sideWatchDirSize;
684
685 local leftFruit = Utils.getFruitArea(fruitType, lStartX, lStartZ, lWidthX, lWidthZ, lHeightX, lHeightZ, hasFruitPreparer);
686 local rightFruit = Utils.getFruitArea(fruitType, rStartX, rStartZ, rWidthX, rWidthZ, rHeightX, rHeightZ, hasFruitPreparer);
687 -- turn to where more fruit is to cut
688 if leftFruit > 0 or rightFruit > 0 then
689 if leftFruit > rightFruit then
690 turnLeft = true;
691 else
692 turnLeft = false;
693 end
694 else
695 self:stopAIThreshing();
696 return;
697 end;
698 local targetX, targetZ = self.aiThreshingTargetX, self.aiThreshingTargetZ;
699 --local dx, dz = x-targetX, z-targetZ;
700 --local dot = dx*dirX + dz*dirZ;
701 --local x, z = targetX + dirX*dot, targetZ + dirZ*dot;
702 --threshWidth = threshWidth*self.aiTurnThreshWidthScale;
703
704
705
706 local markerSideOffset;
707 if turnLeft then
708 markerSideOffset, _, _ = worldToLocal(self.aiTreshingDirectionNode, lInX, lInY, lInZ);
709 else
710 markerSideOffset, _, _ = worldToLocal(self.aiTreshingDirectionNode, rInX, rInY, rInZ);
711 end
712 markerSideOffset = 2*markerSideOffset;
713
714 local areaOverlap = math.min(threshWidth*(1-self.aiTurnThreshWidthScale), self.aiTurnThreshWidthMaxDifference);
715 if markerSideOffset > 0 then
716 markerSideOffset = math.max(markerSideOffset - areaOverlap, 0.01);
717 else
718 markerSideOffset = math.min(markerSideOffset + areaOverlap, -0.01);
719 end
720
721 local x,z = Utils.projectOnLine(x, z, targetX, targetZ, dirX, dirZ)
722 newTargetX = x-sideX*markerSideOffset;
723 newTargetY = y;
724 newTargetZ = z-sideZ*markerSideOffset;
725
726 self.aiThreshingDirectionX = -dirX;
727 self.aiThreshingDirectionZ = -dirZ;
728 self.turnStage = 1;
729 self.aiRescueTimer = self.aiRescueTimeout;
730 self.turnStageTimer = self.turnStage1Timeout;
731
732 self.aiThreshingTargetBeforeTurnX = self.aiThreshingTargetX;
733 self.aiThreshingTargetBeforeTurnZ = self.aiThreshingTargetZ;
734
735 self.waitForTurnTime = g_currentMission.time + math.max(self.waitForTurnTimeout, self.strawToggleTime);
736 self:setAIImplementsMoveDown(false);
737 -- do not thresh while turning
738 self.allowsThreshing = false;
739 updateWheels = false;
740 if turnLeft then
741 --print("turning left ", threshWidth);
742 else
743 --print("turning right ", threshWidth);
744 end;
745 end;
746 else
747 local x,y,z = getWorldTranslation(self.aiTreshingDirectionNode);
748 local dirX, dirZ = self.aiThreshingDirectionX, self.aiThreshingDirectionZ;
749 -- just drive along direction
750 local targetX, targetZ = self.aiThreshingTargetX, self.aiThreshingTargetZ;
751 local dx, dz = x-targetX, z-targetZ;
752 local dot = dx*dirX + dz*dirZ;
753
754 local projTargetX = targetX +dirX*dot;
755 local projTargetZ = targetZ +dirZ*dot;
756
757 --print("old target: "..targetX.." ".. targetZ .. " distOnDir " .. dot.." proj: "..projTargetX.." "..projTargetZ);
758
759 newTargetX = projTargetX+self.aiThreshingDirectionX*self.lookAheadDistance;
760 newTargetY = y;
761 newTargetZ = projTargetZ+self.aiThreshingDirectionZ*self.lookAheadDistance;
762 --print(distOnDir.." target: "..newTargetX.." ".. newTargetZ);
763
764 if not g_currentMission:getIsFieldOwnedAtWorldPos(x,z) then
765 self.showFieldNotOwnedWarningTimer = 1000;
766 --print("Stopping AICombine because field is not owned");
767 self:stopAIThreshing();
768 return;
769 end
770 end;
771
772 if updateWheels then
773 local lx, lz = AIVehicleUtil.getDriveDirection(self.aiTreshingDirectionNode, newTargetX, newTargetY, newTargetZ);
774
775 if self.turnStage == 2 and math.abs(lx) < 0.1 then
776 AICombine.switchToTurnStage3(self);
777 moveForwards = true;
778 end;
779
780 AIVehicleUtil.driveInDirection(self, dt, 30, 0.5, 0.5, 20, true, moveForwards, lx, lz, maxSpeed, 0.9);
781
782 --local maxAngle = 0.785398163; --45°;
783 local maxlx = 0.7071067; --math.sin(maxAngle);
784 local colDirX = lx;
785 local colDirZ = lz;
786
787 if colDirX > maxlx then
788 colDirX = maxlx;
789 colDirZ = 0.7071067; --math.cos(maxAngle);
790 elseif colDirX < -maxlx then
791 colDirX = -maxlx;
792 colDirZ = 0.7071067; --math.cos(maxAngle);
793 end;
794
795 for triggerId,_ in pairs(self.numCollidingVehicles) do
796 AIVehicleUtil.setCollisionDirection(self.aiTreshingDirectionNode, triggerId, colDirX, colDirZ);
797 end;
798 end;
799
800 self.aiThreshingTargetX = newTargetX;
801 self.aiThreshingTargetZ = newTargetZ;
802end;
803
804function AICombine.switchToDirection(self, myDirX, myDirZ)
805 self.aiThreshingDirectionX = myDirX;
806 self.aiThreshingDirectionZ = myDirZ;
807 --print("switch to direction");
808end;
809
810function AICombine:setAIImplementsMoveDown(moveDown)
811 if self.isServer then
812 g_server:broadcastEvent(AISetImplementsMoveDownEvent:new(self, moveDown), nil, nil, self);
813 end;
814 if moveDown then
815 -- same as Foldable.aiLower
816 if self.foldMiddleAnimTime ~= nil and self.foldMiddleAIRaiseDirection ~= 0 then
817 self:setFoldState(-self.foldMiddleAIRaiseDirection, false, true);
818 end
819 else
820 -- same as Foldable.aiRaise
821 if self.foldMiddleAnimTime ~= nil and self.foldMiddleAIRaiseDirection ~= 0 then
822 if self.foldMiddleAIRaiseDirection > 0 then
823 if self.foldAnimTime > self.foldMiddleAnimTime then
824 self:setFoldState(self.foldMiddleAIRaiseDirection, false, true);
825 else
826 self:setFoldState(self.foldMiddleAIRaiseDirection, true, true);
827 end;
828 else
829 if self.foldAnimTime < self.foldMiddleAnimTime then
830 self:setFoldState(self.foldMiddleAIRaiseDirection, false, true);
831 else
832 self:setFoldState(self.foldMiddleAIRaiseDirection, true, true);
833 end;
834 end;
835 end
836 end;
837 for _,implement in pairs(self.attachedImplements) do
838 if implement.object ~= nil then
839 if implement.object.attacherJoint.needsLowering and implement.object.aiNeedsLowering then
840 self:setJointMoveDown(implement.jointDescIndex, moveDown, true);
841 end;
842 if moveDown then
843 implement.object:aiLower();
844 else
845 implement.object:aiRaise();
846 end
847 end
848 end;
849
850 if self.threshingStartAnimation ~= nil and self.playAnimation ~= nil then
851 if moveDown then
852 self:playAnimation(self.threshingStartAnimation, self.threshingStartAnimationSpeedScale, nil, true);
853 else
854 self:playAnimation(self.threshingStartAnimation, -self.threshingStartAnimationSpeedScale, nil, true);
855 end
856 end
857end;
858
859function AICombine:addCollisionTrigger(object)
860 if self.isServer then
861 if object.aiTrafficCollisionTrigger ~= nil then
862 addTrigger(object.aiTrafficCollisionTrigger, "onTrafficCollisionTrigger", self);
863 self.numCollidingVehicles[object.aiTrafficCollisionTrigger] = 0;
864 end
865 if object ~= self then
866 for _,v in pairs(object.components) do
867 self.trafficCollisionIgnoreList[v.node] = true;
868 end
869 end
870 end
871end
872
873function AICombine:removeCollisionTrigger(object)
874 if self.isServer then
875 if object.aiTrafficCollisionTrigger ~= nil then
876 removeTrigger(object.aiTrafficCollisionTrigger);
877 self.numCollidingVehicles[object.aiTrafficCollisionTrigger] = nil;
878 end
879 if object ~= self then
880 for _,v in pairs(object.components) do
881 self.trafficCollisionIgnoreList[v.node] = nil;
882 end
883 end
884 end
885end
886
887
888function AICombine:attachImplement(implement)
889 local object = implement.object;
890 if object.attacherJoint.jointType == Vehicle.JOINTTYPE_CUTTER or object.attacherJoint.jointType == Vehicle.JOINTTYPE_CUTTERHARVESTER then
891 --
892 elseif object.attacherJoint.jointType == Vehicle.JOINTTYPE_TRAILER or object.attacherJoint.jointType == Vehicle.JOINTTYPE_TRAILERLOW then
893 self.numAttachedTrailers = self.numAttachedTrailers+1;
894 end;
895 if self.isServer then
896 self:removeCollisionTrigger(object);
897 AIVehicleUtil.unregisterCollisions(self, self);
898 self:addCollisionTrigger(self);
899 AIVehicleUtil.registerCollisions(self, self);
900 end;
901end;
902
903function AICombine:detachImplement(implementIndex)
904 local object = self.attachedImplements[implementIndex].object;
905 if object ~= nil then
906 if self.aiLeftMarker == object.aiLeftMarker then
907 self.aiLeftMarker = nil;
908 self.aiRightMarker = nil;
909 end;
910 if object.attacherJoint.jointType == Vehicle.JOINTTYPE_CUTTER or object.attacherJoint.jointType == Vehicle.JOINTTYPE_CUTTERHARVESTER then
911 --
912 elseif object.attacherJoint.jointType == Vehicle.JOINTTYPE_TRAILER or object.attacherJoint.jointType == Vehicle.JOINTTYPE_TRAILERLOW then
913 self.numAttachedTrailers = self.numAttachedTrailers-1;
914 end;
915 end
916 if self.isServer then
917 self:removeCollisionTrigger(object);
918 AIVehicleUtil.unregisterCollisions(self, object);
919 end;
920end;
921
922function AICombine:onTrafficCollisionTrigger(triggerId, otherId, onEnter, onLeave, onStay, otherShapeId)
923 if onEnter or onLeave then
924 if g_currentMission.players[otherId] ~= nil then
925 if onEnter then
926 self.numCollidingVehicles[triggerId] = self.numCollidingVehicles[triggerId]+1;
927 elseif onLeave then
928 self.numCollidingVehicles[triggerId] = math.max(self.numCollidingVehicles[triggerId]-1, 0);
929 end;
930 else
931 local vehicle = g_currentMission.nodeToVehicle[otherId];
932 if vehicle ~= nil and self.trafficCollisionIgnoreList[otherId] == nil then
933 if onEnter then
934 self.numCollidingVehicles[triggerId] = self.numCollidingVehicles[triggerId]+1;
935 elseif onLeave then
936 self.numCollidingVehicles[triggerId] = math.max(self.numCollidingVehicles[triggerId]-1, 0);
937 end;
938 end;
939 end;
940 end;
941end;
942
943function AICombine.switchToTurnStage3(self)
944 self.turnStage = 3;
945 self:setAIImplementsMoveDown(true);
946 self.allowsThreshing = true;
947 self.aiRescueTimer = self.aiRescueTimeout;
948end;
949
950function AICombine:canStartAIThreshing()
951 if g_currentMission.disableCombineAI then
952 return false;
953 end;
954 if not self:getIsTurnedOnAllowed(true) then
955 return false;
956 end
957 if self.numAttachedTrailers > 0 then
958 return false;
959 end;
960 if Hirable.numHirablesHired >= g_currentMission.maxNumHirables then
961 return false;
962 end;
963 if self.aiLeftMarker == nil or self.aiRightMarker == nil then
964 for cutter,implement in pairs(self.attachedCutters) do
965 if cutter.aiLeftMarker ~= nil and self.aiLeftMarker == nil then
966 self.aiLeftMarker = cutter.aiLeftMarker;
967 end;
968 if cutter.aiRightMarker ~= nil and self.aiRightMarker == nil then
969 self.aiRightMarker = cutter.aiRightMarker;
970 end;
971 end;
972 if self.aiLeftMarker == nil or self.aiRightMarker == nil then
973 return false;
974 end;
975 end;
976 return true;
977end;
978
979function AICombine:getIsAIThreshingAllowed()
980 if g_currentMission.disableCombineAI then
981 return false;
982 end;
983 if not self:getIsTurnedOnAllowed(true) then
984 return false;
985 end
986 if self.numAttachedTrailers > 0 then
987 return false;
988 end;
989 return true;
990end;
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