-- -- Combine -- This is the specialization for combines -- -- @author Stefan Geiger -- @date 30/11/08 -- -- Copyright (C) GIANTS Software GmbH, Confidential, All Rights Reserved. -- -- Edited for Lexion 770 by Defender -- Edited by Nubsi - Spezial thanks to HeadshotXXL for Help by Pipelight CombineSetWorkModeEvent = {}; CombineSetWorkModeEvent_mt = Class(CombineSetWorkModeEvent, Event); InitEventClass(CombineSetWorkModeEvent, "CombineSetWorkModeEvent"); function CombineSetWorkModeEvent:emptyNew() local self = Event:new(CombineSetWorkModeEvent_mt); self.className="CombineSetWorkModeEvent"; return self; end; function CombineSetWorkModeEvent:new(object, enabled, turbo) local self = CombineSetWorkModeEvent:emptyNew() self.object = object; self.enabled = enabled; self.turbo = turbo; return self; end; function CombineSetWorkModeEvent:readStream(streamId, connection) local id = streamReadInt32(streamId); self.enabled = streamReadBool(streamId); self.turbo = streamReadBool(streamId); self.object = networkGetObject(id); self:run(connection); end; function CombineSetWorkModeEvent:writeStream(streamId, connection) streamWriteInt32(streamId, networkGetObjectId(self.object)); streamWriteBool(streamId, self.enabled); streamWriteBool(streamId, self.turbo); end; function CombineSetWorkModeEvent:run(connection) self.object:setWorkMode(self.enabled, true, self.turbo); if not connection:getIsServer() then g_server:broadcastEvent(CombineSetWorkModeEvent:new(self.object, self.enabled, self.turbo), nil, connection, self.object); end; end; CombineSetCrusherEvent = {}; CombineSetCrusherEvent_mt = Class(CombineSetCrusherEvent, Event); InitEventClass(CombineSetCrusherEvent, "CombineSetCrusherEvent"); function CombineSetCrusherEvent:emptyNew() local self = Event:new(CombineSetCrusherEvent_mt); self.className="CombineSetCrusherEvent"; return self; end; function CombineSetCrusherEvent:new(object, enabled) local self = CombineSetCrusherEvent:emptyNew() self.object = object; self.enabled = enabled; return self; end; function CombineSetCrusherEvent:readStream(streamId, connection) local id = streamReadInt32(streamId); self.enabled = streamReadBool(streamId); self.object = networkGetObject(id); self:run(connection); end; function CombineSetCrusherEvent:writeStream(streamId, connection) streamWriteInt32(streamId, networkGetObjectId(self.object)); streamWriteBool(streamId, self.enabled); end; function CombineSetCrusherEvent:run(connection) self.object:setCrusher(self.enabled, true); if not connection:getIsServer() then g_server:broadcastEvent(CombineSetCrusherEvent:new(self.object, self.enabled), nil, connection, self.object); end; end; Lexion770SetChopperEnableEvent = {}; Lexion770SetChopperEnableEvent_mt = Class(Lexion770SetChopperEnableEvent, Event); InitEventClass(Lexion770SetChopperEnableEvent, "Lexion770SetChopperEnableEvent"); function Lexion770SetChopperEnableEvent:emptyNew() local self = Event:new(Lexion770SetChopperEnableEvent_mt); self.className="Lexion770SetChopperEnableEvent"; return self; end; function Lexion770SetChopperEnableEvent:new(object, enabled, fruitType) local self = Lexion770SetChopperEnableEvent:emptyNew() self.object = object; self.enabled = enabled; self.fruitType = fruitType; return self; end; function Lexion770SetChopperEnableEvent:readStream(streamId, connection) self.object = networkGetObject(streamReadInt32(streamId)); self.enabled = streamReadBool(streamId); self.fruitType = streamReadUIntN(streamId, FruitUtil.sendNumBits); self:run(connection); end; function Lexion770SetChopperEnableEvent:writeStream(streamId, connection) streamWriteInt32(streamId, networkGetObjectId(self.object)); streamWriteBool(streamId, self.enabled); streamWriteUIntN(streamId, self.fruitType, FruitUtil.sendNumBits); end; function Lexion770SetChopperEnableEvent:run(connection) Lexion770SetChopperEnableEvent.execute(self.object, self.enabled, self.fruitType); end; function Lexion770SetChopperEnableEvent.execute(object, enabled, fruitType) if object.currentChopperParticleSystem ~= nil then Utils.setEmittingState(object.currentChopperParticleSystem, false); end; object.currentChopperParticleSystem = object.chopperParticleSystems[fruitType]; if object.currentChopperParticleSystem == nil then object.currentChopperParticleSystem = object.defaultChopperParticleSystem; end; if object.currentGrainTankParticleSystem ~= nil then Utils.setEmittingState(object.currentGrainTankParticleSystem, false); end; object.currentGrainTankParticleSystem = object.grainTankParticleSystems[fruitType]; if object.currentGrainTankParticleSystem == nil then object.currentGrainTankParticleSystem = object.defaultGrainTankParticleSystem; end; if enabled then Utils.setEmittingState(object.currentChopperParticleSystem, true); Utils.setEmittingState(object.currentGrainTankParticleSystem, true); else Utils.setEmittingState(object.currentChopperParticleSystem, false); Utils.setEmittingState(object.currentGrainTankParticleSystem, false); end; end; Lexion770SetStrawEnableEvent = {}; Lexion770SetStrawEnableEvent_mt = Class(Lexion770SetStrawEnableEvent, Event); InitEventClass(Lexion770SetStrawEnableEvent, "Lexion770SetStrawEnableEvent"); function Lexion770SetStrawEnableEvent:emptyNew() local self = Event:new(Lexion770SetStrawEnableEvent_mt); self.className="Lexion770SetStrawEnableEvent"; return self; end; function Lexion770SetStrawEnableEvent:new(object, enabled, fruitType) local self = Lexion770SetStrawEnableEvent:emptyNew() self.object = object; self.enabled = enabled; self.fruitType = fruitType; return self; end; function Lexion770SetStrawEnableEvent:readStream(streamId, connection) self.object = networkGetObject(streamReadInt32(streamId)); self.enabled = streamReadBool(streamId); self.fruitType = streamReadUIntN(streamId, FruitUtil.sendNumBits); self:run(connection); end; function Lexion770SetStrawEnableEvent:writeStream(streamId, connection) streamWriteInt32(streamId, networkGetObjectId(self.object)); streamWriteBool(streamId, self.enabled); streamWriteUIntN(streamId, self.fruitType, FruitUtil.sendNumBits); end; function Lexion770SetStrawEnableEvent:run(connection) Lexion770SetStrawEnableEvent.execute(self.object, self.enabled, self.fruitType); end; function Lexion770SetStrawEnableEvent.execute(object, enabled, fruitType) if object.currentStrawParticleSystem ~= nil then Utils.setEmittingState(object.currentStrawParticleSystem, false); end; object.currentStrawParticleSystem = object.strawParticleSystems[fruitType]; if object.currentStrawParticleSystem == nil then object.currentStrawParticleSystem = object.defaultStrawParticleSystem; end; if object.currentGrainTankParticleSystem ~= nil then Utils.setEmittingState(object.currentGrainTankParticleSystem, false); end; object.currentGrainTankParticleSystem = object.grainTankParticleSystems[fruitType]; if object.currentGrainTankParticleSystem == nil then object.currentGrainTankParticleSystem = object.defaultGrainTankParticleSystem; end; if enabled then Utils.setEmittingState(object.currentStrawParticleSystem, true); Utils.setEmittingState(object.currentGrainTankParticleSystem, true); else Utils.setEmittingState(object.currentStrawParticleSystem, false); Utils.setEmittingState(object.currentGrainTankParticleSystem, false); end; end; Lexion770 = {}; function Lexion770.prerequisitesPresent(specializations) return SpecializationUtil.hasSpecialization(Steerable, specializations); end; function Lexion770:load(xmlFile) self.allowGrainTankFruitType = Lexion770.allowGrainTankFruitType; self.emptyGrainTankIfLowFillLevel = Lexion770.emptyGrainTankIfLowFillLevel; self.setGrainTankFillLevel = SpecializationUtil.callSpecializationsFunction("setGrainTankFillLevel"); self.workModeStart = SpecializationUtil.callSpecializationsFunction("workModeStart"); self.workModeEnd = SpecializationUtil.callSpecializationsFunction("workModeEnd"); self.setWorkMode = SpecializationUtil.callSpecializationsFunction("setWorkMode"); self.setCrusher = SpecializationUtil.callSpecializationsFunction("setCrusher"); self.crusherStart = SpecializationUtil.callSpecializationsFunction("crusherStart"); self.crusherEnd = SpecializationUtil.callSpecializationsFunction("crusherEnd"); self.startThreshing = SpecializationUtil.callSpecializationsFunction("startThreshing"); self.stopThreshing = SpecializationUtil.callSpecializationsFunction("stopThreshing"); self.setIsThreshing = SpecializationUtil.callSpecializationsFunction("setIsThreshing"); self.setPipeOpening = SpecializationUtil.callSpecializationsFunction("setPipeOpening"); self.setPipeState = SpecializationUtil.callSpecializationsFunction("setPipeState"); self.getFruitTypeAndFillLevelToUnload = Lexion770.getFruitTypeAndFillLevelToUnload; self.findAutoAimTrailerToUnload = Lexion770.findAutoAimTrailerToUnload; self.findTrailerToUnload = Lexion770.findTrailerToUnload; self.findTrailerRaycastCallback = Lexion770.findTrailerRaycastCallback; self.getIshreshingAllowed = Lexion770.getIshreshingAllowed; if self.isClient then local threshingStartSound = getXMLString(xmlFile, "vehicle.threshingStartSound#file"); if threshingStartSound ~= nil and threshingStartSound ~= "" then threshingStartSound = Utils.getFilename(threshingStartSound, self.baseDirectory); self.threshingStartSound = createSample("threshingStartSound"); loadSample(self.threshingStartSound, threshingStartSound, false); self.threshingStartSoundPitchOffset = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.threshingStartSound#pitchOffset"), 1); self.threshingStartSoundPitchScale = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.threshingStartSound#pitchScale"), 0); self.threshingStartSoundPitchMax = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.threshingStartSound#pitchMax"), 2.0); end; self.threshingSoundActive = false; local threshingSound = getXMLString(xmlFile, "vehicle.threshingSound#file"); if threshingSound ~= nil and threshingSound ~= "" then threshingSound = Utils.getFilename(threshingSound, self.baseDirectory); self.threshingSound = createSample("threshingSound"); loadSample(self.threshingSound, threshingSound, false); self.threshingSoundPitchOffset = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.threshingSound#pitchOffset"), 1); self.threshingSoundPitchScale = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.threshingSound#pitchScale"), 0); self.threshingSoundPitchMax = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.threshingSound#pitchMax"), 2.0); end; local threshingStopSound = getXMLString(xmlFile, "vehicle.threshingStopSound#file"); if threshingStopSound ~= nil and threshingStopSound ~= "" then threshingStopSound = Utils.getFilename(threshingStopSound, self.baseDirectory); self.threshingStopSound = createSample("threshingStopSound"); loadSample(self.threshingStopSound, threshingStopSound, false); self.threshingStopSoundPitchOffset = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.threshingStopSound#pitchOffset"), 1); self.threshingStopSoundPitchScale = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.threshingStopSound#pitchScale"), 0); self.threshingStopSoundPitchMax = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.threshingStopSound#pitchMax"), 2.0); end; local pipeSound = getXMLString(xmlFile, "vehicle.pipeSound#file"); if pipeSound ~= nil and pipeSound ~= "" then pipeSound = Utils.getFilename(pipeSound, self.baseDirectory); self.pipeSound = createSample("pipeSound"); loadSample(self.pipeSound, pipeSound, false); self.pipeSoundPitchOffset = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.pipeSound#pitchOffset"), 1); self.pipeSoundPitchScale = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.pipeSound#pitchScale"), 0); self.pipeSoundPitchMax = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.pipeSound#pitchMax"), 2.0); end; end; self.chopperBlind = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.chopperBlind#index")); self.pipeParticleSystems = {}; self.grainTankParticleSystems = {}; self.pipeNodes = {}; self.numPipeStates = Utils.getNoNil(getXMLInt(xmlFile, "vehicle.pipe#numStates"), 0); self.currentPipeState = 1; self.targetPipeState = 1; self.pipeStateIsUnloading = {}; self.pipeStateIsAutoAiming = {}; local unloadingPipeStates = Utils.getVectorNFromString(getXMLString(xmlFile, "vehicle.pipe#unloadingStates")); if unloadingPipeStates ~= nil then for i=1, table.getn(unloadingPipeStates) do if unloadingPipeStates[i] ~= nil then self.pipeStateIsUnloading[unloadingPipeStates[i] ] = true; end; end; end; local autoAimPipeStates = Utils.getVectorNFromString(getXMLString(xmlFile, "vehicle.pipe#autoAimStates")); if autoAimPipeStates ~= nil then for i=1, table.getn(autoAimPipeStates) do if autoAimPipeStates[i] ~= nil then self.pipeStateIsAutoAiming[autoAimPipeStates[i] ] = true; end; end; end; local i = 0; while true do local key = string.format("vehicle.pipe.node(%d)", i); if not hasXMLProperty(xmlFile, key) then break; end; local node = Utils.indexToObject(self.components, getXMLString(xmlFile, key.."#index")); if node ~= nil then local entry = {}; entry.node = node; entry.autoAimXRotation = Utils.getNoNil(getXMLBool(xmlFile, key.."#autoAimXRotation"), false); entry.autoAimYRotation = Utils.getNoNil(getXMLBool(xmlFile, key.."#autoAimYRotation"), false); entry.autoAimInvertZ = Utils.getNoNil(getXMLBool(xmlFile, key.."#autoAimInvertZ"), false); entry.states = {}; for state=1,self.numPipeStates do local stateKey = key..string.format(".state%d", state); entry.states[state] = {}; local x,y,z = Utils.getVectorFromString(getXMLString(xmlFile, stateKey.."#translation")); if x == nil or y == nil or z == nil then x,y,z = getTranslation(node); end; entry.states[state].translation = {x,y,z}; local x,y,z = Utils.getVectorFromString(getXMLString(xmlFile, stateKey.."#rotation")); if x == nil or y == nil or z == nil then x,y,z = getRotation(node); else x,y,z = math.rad(x),math.rad(y),math.rad(z); end; entry.states[state].rotation = {x,y,z}; end; local x,y,z = Utils.getVectorFromString(getXMLString(xmlFile, key.."#translationSpeeds")); if x ~= nil and y ~= nil and z ~= nil then x,y,z = x*0.001,y*0.001,z*0.001; if x ~= 0 or y ~= 0 or z ~= 0 then entry.translationSpeeds = {x,y,z}; end; end; local x,y,z = Utils.getVectorFromString(getXMLString(xmlFile, key.."#rotationSpeeds")); if x ~= nil and y ~= nil and z ~= nil then x,y,z = math.rad(x)*0.001,math.rad(y)*0.001,math.rad(z)*0.001; if x ~= 0 or y ~= 0 or z ~= 0 then entry.rotationSpeeds = {x,y,z}; end; end; local x,y,z = getTranslation(node); entry.curTranslation = {x,y,z}; local x,y,z = getRotation(node); entry.curRotation = {x,y,z}; table.insert(self.pipeNodes, entry); end; i = i + 1; end; if table.getn(self.pipeNodes) == 0 then -- use the old format local node = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.pipe#index")); if node ~= nil then self.numPipeStates = 2; local entry = {}; entry.node = node; entry.states = {}; entry.states[1] = {}; entry.states[2] = {}; local x,y,z = getRotation(node); entry.states[1].rotation = {0,0,z}; entry.states[2].rotation = {math.rad(10),math.rad(-90),z}; entry.rotationSpeeds = {0.00006, 0.0006, 0}; local x,y,z = getRotation(node); entry.curRotation = {x,y,z}; table.insert(self.pipeNodes, entry); self.pipeStateIsUnloading[2] = true; end; end; local pipeFlapLid = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.pipeFlapLid#index")); if pipeFlapLid ~= nil then if self.numPipeStates ~= 2 then print("Error: pipeFlapLid is only support with 2 pipe states in '"..self.configFileName.."'."); else local entry = {}; entry.node = pipeFlapLid; entry.states = {}; entry.states[1] = {}; entry.states[2] = {}; entry.states[1].rotation = {0,0,0}; entry.states[2].rotation = {0,math.rad(-90),0}; entry.rotationSpeeds = {0, 0.0006, 0}; local x,y,z = getRotation(pipeFlapLid); entry.curRotation = {x,y,z}; table.insert(self.pipeNodes, entry); end; end; if table.getn(self.pipeNodes) > 0 then self.pipeRaycastNode = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.pipe#raycastNodeIndex")); -- load the pipe particle system for each fruit type local i = 0; while true do local key = string.format("vehicle.pipeParticleSystems.pipeParticleSystem(%d)", i); local t = getXMLString(xmlFile, key .. "#type"); if t == nil then break; end; local desc = FruitUtil.fruitTypes[t]; if desc ~= nil then local currentPS = {}; local particleNode = Utils.loadParticleSystem(xmlFile, currentPS, key, self.components, false, "$data/vehicles/particleSystems/wheatParticleSystem.i3d", self.baseDirectory, self.pipeNodes[1].node); self.pipeParticleSystems[desc.index] = currentPS; if self.defaultPipeParticleSystem == nil then self.defaultPipeParticleSystem = currentPS; end; if self.pipeRaycastNode == nil then self.pipeRaycastNode = particleNode; end; end; i = i + 1; end; if self.pipeRaycastNode == nil then self.pipeRaycastNode = self.components[1].node; end; end; self.pipeRaycastDistance = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.pipe#raycastDistance"), 7); self.convertedFruits = {}; local i = 0; while true do local key = string.format("vehicle.convertedFruits.convertedFruit(%d)", i); if not hasXMLProperty(xmlFile, key) then break; end; local inputType = getXMLString(xmlFile, key .. "#input"); local outputType = getXMLString(xmlFile, key .. "#output"); if inputType ~= nil and outputType ~= nil then local inputDesc = FruitUtil.fruitTypes[inputType]; local outputDesc = FruitUtil.fruitTypes[outputType]; if inputDesc ~= nil and outputDesc ~= nil then self.convertedFruits[inputDesc.index] = outputDesc.index; end; end; i = i + 1; end; self.allowsThreshing = true; self.pipeLight = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.pipeLight#index")); self.rotorFan = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.rotorFan#index")); self.pipeScroll = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.pipeScroll#index")); self.pipeScrollPSRoot = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.grainTankParticleSystems#node")); if self.pipeScrollPSRoot ~= nil then local i = 0; while true do local key = string.format("vehicle.grainTankParticleSystems.grainTankParticleSystem(%d)", i); local t = getXMLString(xmlFile, key .. "#type"); if t == nil then break; end; local desc = FruitUtil.fruitTypes[t]; if desc ~= nil then local currentPS = {}; local particleNode = Utils.loadParticleSystem(xmlFile, currentPS, key, self.components, false, "Particel/LexionKorntankGetreide.i3d", self.baseDirectory, self.pipeScrollPSRoot); self.grainTankParticleSystems[desc.index] = currentPS; if self.grainTankParticleSystems == nil then self.grainTankParticleSystems = currentPS; end; end; i = i + 1; end; end; self.grainTankCapacity = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.grainTankCapacity"), 200); self.grainTankUnloadingCapacity = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.grainTankUnloadingCapacity"), 10); self.grainTankCrowded = false; self.allowThreshingDuringRain = Utils.getNoNil(getXMLBool(xmlFile, "vehicle.allowThreshingDuringRain"), false); self.wiper = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.wiper#index")); self.strawSheet = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.strawSheet#index")); self.chopper = {}; self.chopper.node = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.chopperPart1#transportNode")); self.chopper.zylinderNode = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.chopperPart1#transportZylRot")); self.chopper.zylinderPunch = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.chopperPart1#transportZylTrans")); self.chopper.translationPunch = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.chopperPart1#fixPointTrans")); self.chopper.rotationPunch = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.chopperPart1#fixPointRot")); local ax1, ay1, az1 = getWorldTranslation(self.chopper.zylinderPunch); local bx1, by1, bz1 = getWorldTranslation(self.chopper.translationPunch); self.chopper.punchDistance = Utils.vector3Length(ax1-bx1, ay1-by1, az1-bz1); self.chopper.strawNode = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.chopperPart2#strawNode")); self.chopper.strawNode2 = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.chopperPart2#strawNode2")); self.chopper.zylinderNode2 = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.chopperPart2#strawZylRot")); self.chopper.zylinderPunch2 = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.chopperPart2#strawZylTrans")); self.chopper.translationPunch2 = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.chopperPart2#fixPointTrans")); self.chopper.rotationPunch2 = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.chopperPart2#fixPointRot")); local ax2, ay2, az2 = getWorldTranslation(self.chopper.zylinderPunch2); local bx2, by2, bz2 = getWorldTranslation(self.chopper.translationPunch2); self.chopper.punchDistance2 = Utils.vector3Length(ax2-bx2, ay2-by2, az2-bz2); self.chopper.drumStrawLeft = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.chopperPart3#drumStrawLeft")); self.chopper.drumStrawRight = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.chopperPart3#drumStrawRight")); self.chopper.sheetLeftSmall = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.chopperPart3#sheetLeftSmall")); self.chopper.sheetLeftBig = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.chopperPart3#sheetLeftBig")); self.chopper.sheetRightSmall = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.chopperPart3#sheetRightSmall")); self.chopper.sheetRightBig = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.chopperPart3#sheetRightBig")); local numLights = Utils.getNoNil(getXMLInt(xmlFile, "vehicle.workLights#count"), 0); self.workLights = {}; self.realLights = {}; for i=1, numLights do local lightnamei = string.format("vehicle.workLights.light%d", i); local node = Utils.indexToObject(self.components, getXMLString(xmlFile, lightnamei .. "#index")); local node1 = Utils.indexToObject(self.components, getXMLString(xmlFile, lightnamei .. "#realLight")); if node ~= nil then setVisibility(node, false); table.insert(self.workLights, node); if node1~=nil then table.insert(self.realLights, node1) end; end; end; self.workLightCoronas = {}; local i = 0; while true do local key = string.format("vehicle.workLightCoronas.lightCorona(%d)", i); if not hasXMLProperty(xmlFile, key) then break; end; local node = Utils.indexToObject(self.components, getXMLString(xmlFile, key .. "#index")); if node ~= nil then setVisibility(node, false); table.insert(self.workLightCoronas, node); end; i = i + 1; end; self.workLightCones = {}; local i = 0; while true do local key = string.format("vehicle.workLightCones.lightCone(%d)", i); if not hasXMLProperty(xmlFile, key) then break; end; local node = Utils.indexToObject(self.components, getXMLString(xmlFile, key .. "#index")); if node ~= nil then setVisibility(node, false); table.insert(self.workLightCones, node); end; i = i + 1; end; if self.isClient then local tankAnimRootNode = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.tankAnim#rootNode")); self.tankAnimCharSet = 0; if tankAnimRootNode ~= nil and tankAnimRootNode ~= 0 then self.tankAnimCharSet = getAnimCharacterSet(tankAnimRootNode); if self.tankAnimCharSet ~= 0 then local clip = getAnimClipIndex(self.tankAnimCharSet, getXMLString(xmlFile, "vehicle.tankAnim#animationClip")); assignAnimTrackClip(self.tankAnimCharSet, 0, clip); setAnimTrackLoopState(self.tankAnimCharSet, 0, false); self.tankAnimSpeedScale = 1; self.tankAnimDuration = getAnimClipDuration(self.tankAnimCharSet, clip); end; end; -- grain planes self.grainTankPlanes = {}; local i = 0; while true do local key = string.format("vehicle.grainTankPlanes.grainTankPlane(%d)", i); if not hasXMLProperty(xmlFile, key) then break; end; local grainTankPlane = {}; grainTankPlane.nodes = {}; local fruitType = getXMLString(xmlFile, key.."#type"); if fruitType ~= nil then local nodeI = 0; while true do local nodeKey = key..string.format(".node(%d)", nodeI); if not hasXMLProperty(xmlFile, nodeKey) then break; end; local node = Utils.indexToObject(self.components, getXMLString(xmlFile, nodeKey.."#index")); if node ~= nil then local defaultX, defaultY, defaultZ = getTranslation(node); local defaultRX, defaultRY, defaultRZ = getRotation(node); setVisibility(node, false); local animCurve = AnimCurve:new(linearInterpolatorTransRotScale); local keyI = 0; while true do local animKey = nodeKey..string.format(".key(%d)", keyI); local keyTime = getXMLFloat(xmlFile, animKey.."#time"); local x,y,z = Utils.getVectorFromString(getXMLString(xmlFile, animKey.."#translation")); if y == nil then y = getXMLFloat(xmlFile, animKey.."#y"); end; local rx,ry,rz = Utils.getVectorFromString(getXMLString(xmlFile, animKey.."#rotation")); local sx,sy,sz = Utils.getVectorFromString(getXMLString(xmlFile, animKey.."#scale")); if keyTime == nil then break; end; local x = Utils.getNoNil(x, defaultX); local y = Utils.getNoNil(y, defaultY); local z = Utils.getNoNil(z, defaultZ); local rx = Utils.getNoNil(rx, defaultRX); local ry = Utils.getNoNil(ry, defaultRY); local rz = Utils.getNoNil(rz, defaultRZ); local sx = Utils.getNoNil(sx, 1); local sy = Utils.getNoNil(sy, 1); local sz = Utils.getNoNil(sz, 1); animCurve:addKeyframe({x=x, y=y, z=z, rx=rx, ry=ry, rz=rz, sx=sx, sy=sy, sz=sz, time = keyTime}); keyI = keyI +1; end; if keyI == 0 then local minY, maxY = Utils.getVectorFromString(getXMLString(xmlFile, nodeKey.."#minMaxY")); local minY = Utils.getNoNil(minY, defaultY); local maxY = Utils.getNoNil(maxY, defaultY); animCurve:addKeyframe({x=defaultX, y=minY, z=defaultZ, rx=defaultRX, ry=defaultRY, rz=defaultRZ, sx=1, sy=1, sz=1, time = 0}); animCurve:addKeyframe({x=defaultX, y=maxY, z=defaultZ, rx=defaultRX, ry=defaultRY, rz=defaultRZ, sx=1, sy=1, sz=1, time = 1}); end; table.insert(grainTankPlane.nodes, {node=node, animCurve = animCurve}); end; nodeI = nodeI +1; end; if table.getn(grainTankPlane.nodes) > 0 then if self.defaultGrainTankPlane == nil then self.defaultGrainTankPlane = grainTankPlane; end; self.grainTankPlanes[fruitType] = grainTankPlane; end; end; i = i +1; end; if self.defaultGrainTankPlane==nil then self.grainTankPlanes = nil; end; if self.grainTankPlanes == nil then if hasXMLProperty(xmlFile, "vehicle.grainTankPlane.node") then print("Warning: '"..self.configFileName.. "' uses old grainTankPlane format, which is not supported anymore."); end; end; -- chopper particle system self.chopperParticleSystems = {}; local i = 0; while true do local key = string.format("vehicle.chopperParticleSystems.chopperParticleSystem(%d)", i); local t = getXMLString(xmlFile, key .. "#type"); if t == nil then break; end; local desc = FruitUtil.fruitTypes[t]; if desc ~= nil then local currentPS = {}; local particleNode = Utils.loadParticleSystem(xmlFile, currentPS, key, self.components, false, "$data/vehicles/particleSystems/threshingChopperParticleSystem.i3d", self.baseDirectory); self.chopperParticleSystems[desc.index] = currentPS; if self.defaultChopperParticleSystem == nil then self.defaultChopperParticleSystem = currentPS; end; end; i = i + 1; end; self.chopperToggleTime = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.chopperParticleSystems#toggleTime"), 2500); self.chopperEnableTime = nil; self.chopperDisableTime = nil; -- start particle system self.strawParticleSystems = {}; local i = 0; while true do local key = string.format("vehicle.strawParticleSystems.strawParticleSystem(%d)", i); local t = getXMLString(xmlFile, key .. "#type"); if t == nil then break; end; local desc = FruitUtil.fruitTypes[t]; if desc ~= nil then local currentPS = {}; local particleNode = Utils.loadParticleSystem(xmlFile, currentPS, key, self.components, false, "$data/vehicles/particleSystems/threshingStrawParticleSystem.i3d", self.baseDirectory); self.strawParticleSystems[desc.index] = currentPS; if self.defaultStrawParticleSystem == nil then self.defaultStrawParticleSystem = currentPS; end; end; i = i + 1; end; end; self.strawToggleTime = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.strawParticleSystems#toggleTime"), 2500); self.strawEnableTime = nil; self.strawDisableTime = nil; self.strawEmitState = false; self.autoBeacon=false; self.combineSize = Utils.getNoNil(getXMLInt(xmlFile, "vehicle.combineSize"), 1); local numStrawAreas = Utils.getNoNil(getXMLInt(xmlFile, "vehicle.strawAreas#count"), 0); self.strawAreas = {} for i=1, numStrawAreas do local area = {}; local areanamei = string.format("vehicle.strawAreas.strawArea%d", i); area.start = Utils.indexToObject(self.components, getXMLString(xmlFile, areanamei .. "#startIndex")); area.width = Utils.indexToObject(self.components, getXMLString(xmlFile, areanamei .. "#widthIndex")); area.height = Utils.indexToObject(self.components, getXMLString(xmlFile, areanamei .. "#heightIndex")); table.insert(self.strawAreas, area); end; self.workMode = false; self.pipelight = false; self.crusherEnabled = false; self.isThreshing = false; self.chopperActivated = false; self.defaultChopperState = false; self.hasStraw = false; --self.pipeOpening = false; --self.pipeOpen = false; --self.pipeClose = true; self.pipeIsUnloading = false; self.pipeParticleActivated = false; self.pipeParticleDeactivateTime = 0; self.moveStrawSmall = -0.07; self.moveStrawBig = -0.07; self.maxSpeed = 0; --[[if self.isServer then self.sentPipeOpening = self.pipeOpening; end;]] self.threshingScale = 1; self.grainTankFruitTypes = {}; self.grainTankFruitTypes[FruitUtil.FRUITTYPE_UNKNOWN] = true; local fruitTypes = getXMLString(xmlFile, "vehicle.grainTankFruitTypes#fruitTypes"); if fruitTypes ~= nil then local types = Utils.splitString(" ", fruitTypes); for k,v in pairs(types) do local desc = FruitUtil.fruitTypes[v]; if desc ~= nil then self.grainTankFruitTypes[desc.index] = true; end; end; end; self.currentGrainTankFruitType = FruitUtil.FRUITTYPE_UNKNOWN; self.grainTankFillLevel = 0; self.grainTankTempFillLevel = 0; self.grainTankTempFruitType = FruitUtil.FRUITTYPE_UNKNOWN; self.minThreshold = 0.05; self.speedDisplayScale = 1.0; self.drawFillLevel = true; self.attachedCutters = {}; self.numAttachedCutters = 0; self.lastArea = 0; self.lastFruitType = FruitUtil.FRUITTYPE_UNKNOWN; self.lastValidFruitType = FruitUtil.FRUITTYPE_UNKNOWN; self.lastOutputFruitType = FruitUtil.FRUITTYPE_UNKNOWN; self.lastValidOutputFruitType = FruitUtil.FRUITTYPE_UNKNOWN; self.combineDirtyFlag = self:getNextDirtyFlag(); self:setGrainTankFillLevel(0.0, FruitUtil.FRUITTYPE_UNKNOWN); end; function Lexion770:delete() for k,v in pairs(self.pipeParticleSystems) do Utils.deleteParticleSystem(v); end; for k,v in pairs(self.grainTankParticleSystems) do Utils.deleteParticleSystem(v); end; for k,v in pairs(self.chopperParticleSystems) do Utils.deleteParticleSystem(v); end; for k,v in pairs(self.strawParticleSystems) do Utils.deleteParticleSystem(v); end; if self.threshingStartSound ~= nil then delete(self.threshingStartSound); end; if self.threshingSoundActive then delete(self.threshingSound); self.threshingSoundActive = false; end; if self.threshingStopSound ~= nil then delete(self.threshingStopSound); end; end; function Lexion770:readStream(streamId, connection) local fillLevel = streamReadFloat32(streamId); local fillType = streamReadUIntN(streamId, FruitUtil.sendNumBits); self.pipeParticleActived = streamReadBool(streamId); self.pipeIsUnloading = streamReadBool(streamId); local pipeState = streamReadUIntN(streamId, 3); local workMode = streamReadBool(streamId); local crusher = streamReadBool(streamId); local isThreshing = streamReadBool(streamId); self:setGrainTankFillLevel(fillLevel, fillType); self:setPipeState(pipeState, true); self:setWorkMode(workMode, true, true); self:setCrusher(crusher, true); self:setIsThreshing(isThreshing, true); local chopperPSenabled = streamReadBool(streamId); local chopperPSFruitType streamReadUIntN(streamId, FruitUtil.sendNumBits); local strawPSenabled = streamReadBool(streamId); local strawPSFruitType streamReadUIntN(streamId, FruitUtil.sendNumBits); Lexion770SetChopperEnableEvent.execute(self, chopperPSenabled, chopperPSFruitType); Lexion770SetStrawEnableEvent.execute(self, strawPSenabled, strawPSFruitType); self.lastValidFruitType = streamReadUIntN(streamId, FruitUtil.sendNumBits); self.lastValidOutputFruitType = self.lastValidFruitType; if self.convertedFruits[self.lastValidFruitType] ~= nil then self.lastValidOutputFruitType = self.convertedFruits[self.lastValidFruitType]; end; if self.lastValidFruitType ~= FruitUtil.FRUITTYPE_UNKNOWN then local fruitDesc = FruitUtil.fruitIndexToDesc[self.lastValidFruitType]; self.hasStraw = fruitDesc.hasStraw; if fruitDesc.hasStraw then self.chopperActivated = self.crusherEnabled; else self.chopperActivated = true; end; else self.chopperActivated = self.crusherEnabled; end; end; function Lexion770:writeStream(streamId, connection) streamWriteFloat32(streamId, self.grainTankFillLevel); streamWriteUIntN(streamId, self.currentGrainTankFruitType, FruitUtil.sendNumBits); streamWriteBool(streamId, self.pipeParticleActived); streamWriteBool(streamId, self.pipeIsUnloading); streamWriteUIntN(streamId, self.targetPipeState, 3); streamWriteBool(streamId, self.workMode); streamWriteBool(streamId, self.crusherEnabled); streamWriteBool(streamId, self.isThreshing); streamWriteBool(streamId, self.chopperPSenabled); streamWriteUIntN(streamId, self.chopperPSFruitType, FruitUtil.sendNumBits); streamWriteBool(streamId, self.strawPSenabled); streamWriteUIntN(streamId, self.strawPSFruitType, FruitUtil.sendNumBits); streamWriteUIntN(streamId, self.lastValidFruitType, FruitUtil.sendNumBits); end; function Lexion770:readUpdateStream(streamId, timestamp, connection) if connection:getIsServer() then if streamReadBool(streamId) then local fillLevel = streamReadFloat32(streamId); local fillType = streamReadUIntN(streamId, FruitUtil.sendNumBits); self:setGrainTankFillLevel(fillLevel, fillType); self.lastValidFruitType = streamReadUIntN(streamId, FruitUtil.sendNumBits); self.lastValidOutputFruitType = self.lastValidFruitType; if self.convertedFruits[self.lastValidFruitType] ~= nil then self.lastValidOutputFruitType = self.convertedFruits[self.lastValidFruitType]; end; if self.lastValidFruitType ~= FruitUtil.FRUITTYPE_UNKNOWN then local fruitDesc = FruitUtil.fruitIndexToDesc[self.lastValidFruitType]; self.hasStraw = fruitDesc.hasStraw; if fruitDesc.hasStraw then self.chopperActivated = self.crusherEnabled; else self.chopperActivated = true; end; else self.chopperActivated = self.crusherEnabled; end; end; end; end; function Lexion770:writeUpdateStream(streamId, connection, dirtyMask) if not connection:getIsServer() then if streamWriteBool(streamId, bitAND(dirtyMask, self.combineDirtyFlag) ~= 0) then streamWriteFloat32(streamId, self.grainTankFillLevel); streamWriteUIntN(streamId, self.currentGrainTankFruitType, FruitUtil.sendNumBits); streamWriteUIntN(streamId, self.lastValidFruitType, FruitUtil.sendNumBits); end; end; end; function Lexion770:loadFromAttributesAndNodes(xmlFile, key, resetVehicles) local fillLevel = getXMLFloat(xmlFile, key.."#grainTankFillLevel"); local fruitType = getXMLString(xmlFile, key.."#grainTankFruitType"); if fillLevel ~= nil and fruitType ~= nil then local fruitTypeDesc = FruitUtil.fruitTypes[fruitType]; if fruitTypeDesc ~= nil then self:setGrainTankFillLevel(fillLevel, fruitTypeDesc.index); end; end; return BaseMission.VEHICLE_LOAD_OK; end; function Lexion770:getSaveAttributesAndNodes(nodeIdent) local fruitType = "unknown"; if self.currentGrainTankFruitType ~= FruitUtil.FRUITTYPE_UNKNOWN then fruitType = FruitUtil.fruitIndexToDesc[self.currentGrainTankFruitType].name; end; local attributes = 'grainTankFillLevel="'..self.grainTankFillLevel..'" grainTankFruitType="'..fruitType..'"'; return attributes, nil; end; function Lexion770:mouseEvent(posX, posY, isDown, isUp, button) end; function Lexion770:keyEvent(unicode, sym, modifier, isDown) end; function Lexion770:update(dt) if self.isServer then if not self.workMode and self.grainTankFillLevel >= 3600 then self:setWorkMode(true, false, true); end; if (self.grainTankFillLevel >= self.grainTankCapacity*0.8) and not self.autoBeacon then self:setBeaconLightsVisibility(true); self.autoBeacon=true; end; if (self.grainTankFillLevel < self.grainTankCapacity*0.8) and self.autoBeacon then self:setBeaconLightsVisibility(false); self.autoBeacon = false; end; end; if not self:getIsActive() and self.autoBeacon then if self.beaconLightsActive then for _, beaconLight in pairs(self.beaconLights) do rotate(beaconLight.node, 0, beaconLight.speed*dt, 0); end; end; end; if self:getIsActive() then Cylindered.updateMovingPart(self, self.movingParts(1)); if self.isClient then if self.isThreshing and self:getIsActiveForSound() then if not self.threshingSoundActive and self.threshingSound ~= nil and self.playThreshingSoundTime <= self.time then playSample(self.threshingSound, 0, 1, 0); self.threshingSoundActive = true; end; end; end; if self.isClient and self:getIsActiveForInput() then if not self.isThreshing and self.grainTankFillLevel < self.grainTankCapacity or self.grainTankCapacity <= 3600 then if InputBinding.hasEvent(InputBinding.IMPLEMENT_EXTRA3) then self:setWorkMode(not self.workMode); end; end; if self.workMode then if InputBinding.hasEvent(InputBinding.ACTIVATE_THRESHING) then self:setIsThreshing(not self.isThreshing); end; if (self.hasStraw) or ((self.currentGrainTankFruitType~=FruitUtil.FRUITTYPE_UNKNOWN) and (FruitUtil.fruitIndexToDesc[self.currentGrainTankFruitType].hasStraw)) or (self.currentGrainTankFruitType==FruitUtil.FRUITTYPE_UNKNOWN) then if InputBinding.hasEvent(InputBinding.ACTIVATE_CRUSHER) then self:setCrusher(not self.crusherEnabled); end; end; end; if InputBinding.hasEvent(InputBinding.EMPTY_GRAIN) then local nextState = self.targetPipeState+1; if nextState > self.numPipeStates then nextState = 1; end; self:setPipeState(nextState); end; end; if self.isServer then if self.grainTankFillLevel >= self.grainTankCapacity and self.grainTankCapacity > 0 then self:setIsThreshing(false); end; if self.pipeLight ~= nil then local pipeLightActive = (self.lightsActive or not self.worklightActive) and self.targetPipeState == 2; setVisibility(self.pipeLight, pipeLightActive); end; end; if self.isThreshing and self.rotorFan ~= nil then rotate(self.rotorFan, dt*0.005, 0, 0); end; -- if self.isThreshing and self.chopperActivated then if self.workMode and self.crusherEnabled then rotate(self.chopper.drumStrawLeft, 0, -dt*0.75, 0); rotate(self.chopper.drumStrawRight, 0, dt*0.75, 0); local yMaxBig = Utils.degToRad(55); local yMinBig = Utils.degToRad(-55); local yMaxSmall = Utils.degToRad(32.5); local yMinSmall = Utils.degToRad(-32.5); local xsheetLeftSmall, ysheetLeftSmall, zsheetLeftSmall = getRotation(self.chopper.sheetLeftSmall); local xsheetLeftBig, ysheetLeftBig, zsheetLeftBig = getRotation(self.chopper.sheetLeftBig); local xsheetRightSmall, ysheetRightSmall, zsheetRightSmall = getRotation(self.chopper.sheetRightSmall); local xsheetRightBig, ysheetRightBig, zsheetRightBig = getRotation(self.chopper.sheetRightBig); --special Thanks to Sven777b if (ysheetLeftBig > yMaxBig or ysheetLeftBig < yMinBig) then self.moveStrawBig = self.moveStrawBig*-1; end; if (ysheetLeftSmall > yMaxSmall or ysheetLeftSmall < yMinSmall) then self.moveStrawSmall = self.moveStrawSmall*-1; end; ysheetLeftSmall = ysheetLeftSmall + self.moveStrawSmall*0.827; ysheetLeftBig = ysheetLeftBig + self.moveStrawBig*1.4; ysheetRightSmall = ysheetRightSmall + self.moveStrawSmall*0.827; ysheetRightBig = ysheetRightBig + self.moveStrawBig*1.4; setRotation(self.chopper.sheetLeftSmall, 0, ysheetLeftSmall, 0); setRotation(self.chopper.sheetLeftBig, 0, ysheetLeftBig, 0); setRotation(self.chopper.sheetRightSmall, 0, ysheetRightSmall, 0); setRotation(self.chopper.sheetRightBig, 0, ysheetRightBig, 0); end; if self.isThreshing and self.pipeScroll ~= nil then rotate(self.pipeScroll, -dt*0.0075, 0, 0); end; if g_currentMission.environment.lastRainScale <= 0.02 and g_currentMission.environment.timeSinceLastRain > 20 then setRotation(self.wiper, 0, 0, 0); else rotate(self.wiper, 0, 0, dt*0.005); end; if self.chopper ~= nil then local ax1, ay1, az1 = getWorldTranslation(self.chopper.zylinderNode); local bx1, by1, bz1 = getWorldTranslation(self.chopper.rotationPunch); local x1, y1, z1 = worldDirectionToLocal(getParent(self.chopper.zylinderNode), bx1-ax1, by1-ay1, bz1-az1); setDirection(self.chopper.zylinderNode, 0, y1*-1, z1*-1, 0, 0, 1); if self.chopper.zylinderPunch ~= nil then local distance1 = Utils.vector3Length(ax1-bx1, ay1-by1, az1-bz1); setTranslation(self.chopper.zylinderPunch, 0, 0, (distance1-self.chopper.punchDistance)*-1); end; local ax2, ay2, az2 = getWorldTranslation(self.chopper.zylinderNode2); local bx2, by2, bz2 = getWorldTranslation(self.chopper.rotationPunch2); local x3, y3, z3 = worldDirectionToLocal(getParent(self.chopper.zylinderNode2), bx2-ax2, by2-ay2, bz2-az2); setDirection(self.chopper.zylinderNode2, x3, y3, z3, 0, 0, 1); if self.chopper.zylinderPunch2 ~= nil then local distance2 = Utils.vector3Length(ax2-bx2, ay2-by2, az2-bz2); setTranslation(self.chopper.zylinderPunch2, 0, (distance2-self.chopper.punchDistance2)*-1, 0); end; local xstraw, ystraw, zstraw = getRotation(self.chopper.strawNode); --29|0 local x1straw, y1straw, z1straw = getRotation(self.chopper.strawNode2); --29|0|0 local x2straw, y2straw, z2straw = getRotation(self.chopper.node); --29|0|0|0 local xBlech, yBlech, zBlech = getRotation(self.strawSheet); local moveSpeed = 0.0005; local nodeMin = -80*3.1456/180.0; local node2Min = 80*3.1456/180.0; local xRotMax = 41*3.1456/180.0; local xRotMax2 = 47*3.1456/180.0; local xRotMin2 = 0*3.1456/180.0; local xRotMin2straw = -10*3.1456/180.0; if not self.workMode then xstraw = xstraw+dt*moveSpeed; if xstraw > xRotMin2 then xstraw = xRotMin2; --0 end; x2straw = x2straw-dt*moveSpeed; if x2straw < xRotMin2 then x2straw = xRotMin2; --0 end; if xstraw >= xRotMin2 then xBlech = xBlech+dt*moveSpeed*1.25; if xBlech > xRotMax2 then xBlech = xRotMax2; end; x1straw = x1straw+dt*moveSpeed; if x1straw > xRotMax then x1straw = xRotMax; --41 end; end; else -- if self.chopperActivated then --self.strawActive if self.crusherEnabled then xBlech = xBlech-dt*moveSpeed*1.21; if xBlech < xRotMin2straw then xBlech = xRotMin2straw; end; x1straw = x1straw-dt*moveSpeed; if x1straw < xRotMin2 then x1straw = xRotMin2 end; if x1straw <= xRotMin2 then x2straw = x2straw+dt*moveSpeed; if x2straw > node2Min then x2straw = node2Min; end; xstraw = xstraw-dt*moveSpeed; if xstraw < nodeMin then xstraw = nodeMin; end; end; else xBlech = xBlech-dt*moveSpeed*1.21; if xBlech < xRotMin2 then xBlech = xRotMin2; end; x1straw = x1straw-dt*moveSpeed; if x1straw < xRotMin2 then x1straw = xRotMin2 end; x2straw = x2straw-dt*moveSpeed; if x2straw < xRotMin2 then x2straw = xRotMin2; end; xstraw = xstraw+dt*moveSpeed; if xstraw > xRotMin2 then xstraw = xRotMin2; end; end; end; setRotation(self.chopper.strawNode, xstraw, 0, 0); setRotation(self.chopper.node, x2straw, 0, 0); setRotation(self.chopper.strawNode2, x1straw, 0, 0); setRotation(self.strawSheet, xBlech, 0, 0); end; local chopperBlindRotationSpeed = 0.001; local minRotX = -83*3.1415/180.0; if self.chopperBlind ~= nil then local x,y,z = getRotation(self.chopperBlind); if self.chopperActivated then x = x-dt*chopperBlindRotationSpeed; if x < minRotX then x = minRotX; end; else x = x+dt*chopperBlindRotationSpeed; if x > 0.0 then x = 0.0; end; end; setRotation(self.chopperBlind, x, y, z); end; local doAutoAiming = self.pipeStateIsAutoAiming[self.currentPipeState]; local targetTrailer = nil; if doAutoAiming then targetTrailer = self:findAutoAimTrailerToUnload(self.lastValidOutputFruitType); if targetTrailer == nil then doAutoAiming = false; end; end; if (self.currentPipeState ~= self.targetPipeState or doAutoAiming) and self.targetPipeState <= self.numPipeStates then local autoAimX, autoAimY, autoAimZ; if doAutoAiming then autoAimX, autoAimY, autoAimZ = getWorldTranslation(targetTrailer.fillAutoAimTargetNode); end; local moved = false; for i=1, table.getn(self.pipeNodes) do local nodeMoved = false; local pipeNode = self.pipeNodes[i]; local state = pipeNode.states[self.targetPipeState]; if pipeNode.translationSpeeds ~= nil then for i=1, 3 do if pipeNode.curTranslation[i] ~= state.translation[i] then nodeMoved = true; if pipeNode.curTranslation[i] < state.translation[i] then pipeNode.curTranslation[i] = math.min(pipeNode.curTranslation[i] + dt*pipeNode.translationSpeeds[i], state.translation[i]); else pipeNode.curTranslation[i] = math.max(pipeNode.curTranslation[i] - dt*pipeNode.translationSpeeds[i], state.translation[i]); end; end; end; setTranslation(pipeNode.node, pipeNode.curTranslation[1],pipeNode.curTranslation[2],pipeNode.curTranslation[3]) end; if pipeNode.rotationSpeeds ~= nil then for i=1, 3 do local targetRotation = state.rotation[i]; if doAutoAiming then if pipeNode.autoAimXRotation and i == 1 then local x,y,z = getWorldTranslation(pipeNode.node); local x,y,z = worldDirectionToLocal(getParent(pipeNode.node), autoAimX-x, autoAimY-y, autoAimZ-z); targetRotation = -math.atan2(y,z); if pipeNode.autoAimInvertZ then targetRotation = targetRotation+math.pi; end; targetRotation = Utils.normalizeRotationForShortestPath(targetRotation, pipeNode.curRotation[i]); elseif pipeNode.autoAimYRotation and i == 2 then local x,y,z = getWorldTranslation(pipeNode.node); local x,y,z = worldDirectionToLocal(getParent(pipeNode.node), autoAimX-x, autoAimY-y, autoAimZ-z); targetRotation = math.atan2(x,z); if pipeNode.autoAimInvertZ then targetRotation = targetRotation+math.pi; end; targetRotation = Utils.normalizeRotationForShortestPath(targetRotation, pipeNode.curRotation[i]); end; end; if pipeNode.curRotation[i] ~= targetRotation then nodeMoved = true; if pipeNode.curRotation[i] < targetRotation then pipeNode.curRotation[i] = math.min(pipeNode.curRotation[i] + dt*pipeNode.rotationSpeeds[i], targetRotation); else pipeNode.curRotation[i] = math.max(pipeNode.curRotation[i] - dt*pipeNode.rotationSpeeds[i], targetRotation); end; end; end; setRotation(pipeNode.node, pipeNode.curRotation[1],pipeNode.curRotation[2],pipeNode.curRotation[3]) end; moved = moved or nodeMoved; if nodeMoved and self.setMovingToolDirty ~= nil then self:setMovingToolDirty(pipeNode.node); end; end; if not moved then self.currentPipeState = self.targetPipeState; end; end; if self.isClient then if self.motor ~= nil then if self.motor.speedLevel == 1 then self.speedDisplayScale = 0.7; elseif self.motor.speedLevel == 2 or self.motor.speedLevel == 4 then self.speedDisplayScale = 0.75; else self.speedDisplayScale = 1.0; end; end; if self.currentPipeState ~= self.targetPipeState then if self.pipeSound ~= nil and not self.pipeSoundEnabled then if self:getIsActiveForSound() then setSamplePitch(self.pipeSound, self.pipeSoundPitchOffset); playSample(self.pipeSound, 0, 1, 0); self.pipeSoundEnabled = true; end; end; else if self.pipeSound ~= nil and self.pipeSoundEnabled then stopSample(self.pipeSound); self.pipeSoundEnabled = false; end; end; end; end; end; function Lexion770:updateTick(dt) if self:getIsActive() then for _, part in pairs(self.movingParts) do part.isDirty = true; end; end; if self.isServer then if self.lastFruitType ~= FruitUtil.FRUITTYPE_UNKNOWN then self.lastValidFruitType = self.lastFruitType; end; if self.lastOutputFruitType ~= FruitUtil.FRUITTYPE_UNKNOWN then self.lastValidOutputFruitType = self.lastOutputFruitType; end; self.lastArea = 0; self.lastFruitType = FruitUtil.FRUITTYPE_UNKNOWN; self.lastOutputFruitType = FruitUtil.FRUITTYPE_UNKNOWN; self.grainTankTempFillLevel = 0; local disableChopperEmit = true; local disableStrawEmit = true; if self.isThreshing then local lastArea = 0; local fruitType = FruitUtil.FRUITTYPE_UNKNOWN; for cutter,implement in pairs(self.attachedCutters) do if cutter.reelStarted then if cutter.lastArea > 0 then for cutter1,implement in pairs(self.attachedCutters) do cutter1:setFruitType(cutter.currentFruitType); end; self.currentGrainTankFruitType = cutter.currentFruitType; fruitType = cutter.currentFruitType; lastArea = lastArea + cutter.lastArea; end; end; end; self.lastArea = lastArea; self.lastFruitType = fruitType; local outputFruitType = fruitType; if self.convertedFruits[fruitType] ~= nil then outputFruitType = self.convertedFruits[fruitType]; end; self.lastOutputFruitType = outputFruitType; if self.lastArea > 0 then local fruitDesc = FruitUtil.fruitIndexToDesc[fruitType]; self.hasStraw = fruitDesc.hasStraw; if fruitDesc.hasStraw then self.chopperActivated = self.crusherEnabled; else self.chopperActivated = true; end; if self.chopperActivated then if self.chopperEnableTime == nil then self.chopperEnableTime = self.time + self.chopperToggleTime; else self.chopperDisableTime = nil; end; self.chopperPSFruitType = fruitType; disableChopperEmit = false; else if self.strawEnableTime == nil then self.strawEnableTime = self.time + self.strawToggleTime; else self.strawDisableTime = nil; end; self.strawPSFruitType = fruitType; disableStrawEmit = false; end; -- 8000/1200 = 6.66 liter/meter -- 8000/1200 / 6 = 1.111 liter/m^2 -- 8000/1200 / 6 / 2^2 = 0.277777 liter / density pixel (density is 4096^2, on a area of 2048m^2 local pixelToSqm = g_currentMission:getFruitPixelsToSqm() / g_currentMission.maxFruitValue; -- 4096px are mapped to 2048m local literPerSqm = 1; if (fruitType ~= FruitUtil.FRUITTYPE_UNKNOWN) then literPerSqm = FruitUtil.fruitIndexToDesc[fruitType].literPerSqm; -- * (1 + 0.5 * (3 - g_currentMission.missionStats.difficulty)); if (outputFruitType ~= fruitType and outputFruitType ~= FruitUtil.FRUITTYPE_UNKNOWN) then literPerSqm = literPerSqm * FruitUtil.fruitIndexToDesc[outputFruitType].literPerSqm; end; end; --local literPerPixel = 8000/1200 / 6 / (2*2); --literPerPixel = literPerPixel*1.5; local sqm = self.lastArea*pixelToSqm; local deltaFillLevel = sqm*literPerSqm*self.threshingScale; if self.grainTankCapacity > 0 then local newFillLevel = self.grainTankFillLevel+deltaFillLevel; self:setGrainTankFillLevel(newFillLevel, outputFruitType); else self.grainTankTempFillLevel = deltaFillLevel; self.grainTankTempFruitType = outputFruitType; end; end; end; if disableChopperEmit and self.chopperDisableTime == nil then self.chopperDisableTime = self.time + self.chopperToggleTime; end; if disableStrawEmit and self.strawDisableTime == nil then self.strawDisableTime = self.time + self.strawToggleTime; end; if self.chopperEnableTime ~= nil and self.chopperEnableTime <= self.time then self.chopperPSenabled = true; self.chopperEnableTime = nil; end; if self.strawEnableTime ~= nil and self.strawEnableTime <= self.time then self.strawPSenabled = true; self.strawEnableTime = nil; self.strawEmitState = true; end; if self.strawEmitState then local cuttingAreasSend = {}; for k, strawArea in pairs(self.strawAreas) do local x,y,z = getWorldTranslation(strawArea.start); local x1,y1,z1 = getWorldTranslation(strawArea.width); local x2,y2,z2 = getWorldTranslation(strawArea.height); local old, total = Utils.getFruitWindrowArea(self.strawPSFruitType, x, z, x1, z1, x2, z2); local value = 1+math.floor(old / total + 0.7); -- round, biased to the bigger value value = math.min(value, g_currentMission.maxWindrowValue); --Utils.updateFruitWindrowArea(self.strawPSFruitType, x, z, x1, z1, x2, z2, value, true); table.insert(cuttingAreasSend, {x,z,x1,z1,x2,z2,value}); end; if (table.getn(cuttingAreasSend) > 0) then CombineAreaEvent.runLocally(cuttingAreasSend, self.strawPSFruitType); g_server:broadcastEvent(CombineAreaEvent:new(cuttingAreasSend, self.strawPSFruitType)); end; end; self.pipeIsUnloading = false; self.pipeParticleActivated = false; if self.pipeStateIsUnloading[self.currentPipeState] then local fruitType, fillLevel, useGrainTank = self:getFruitTypeAndFillLevelToUnload(); if fillLevel > 0 then self.pipeParticleActivated = true; self.pipeIsUnloading = true; -- test if we should drain the grain tank local trailer = self:findTrailerToUnload(fruitType); if trailer == nil then self.pipeIsUnloading = false; else trailer:resetFillLevelIfNeeded(FruitUtil.fruitTypeToFillType[fruitType]); local deltaLevel = fillLevel; if useGrainTank then deltaLevel = self.grainTankUnloadingCapacity*dt/1000.0; end; deltaLevel = math.min(deltaLevel, trailer.capacity - trailer.fillLevel); fillLevel = fillLevel-deltaLevel; if fillLevel <= 0.0 then deltaLevel = deltaLevel+fillLevel; fillLevel = 0.0; self.pipeIsUnloading = false; elseif deltaLevel == 0 then self.pipeIsUnloading = false; end; if useGrainTank then self:setGrainTankFillLevel(fillLevel, fruitType); end; trailer:setFillLevel(trailer.fillLevel+deltaLevel, FruitUtil.fruitTypeToFillType[fruitType]); end; if not self.pipeIsUnloading and useGrainTank then self.pipeParticleActivated = false; end; end; end; if self.grainTankFillLevel ~= self.sentGrainTankFillLevel or self.currentGrainTankFruitType ~= self.sentGrainTankFruitType or self.lastValidFruitType ~= self.sentLastValidFruitType then --g_server:broadcastEvent(CombineFillEvent:new(self, self.grainTankFillLevel, self.currentGrainTankFruitType), nil, nil, self); self:raiseDirtyFlags(self.combineDirtyFlag); self.sentGrainTankFillLevel = self.grainTankFillLevel; self.sentGrainTankFruitType = self.currentGrainTankFruitType; self.sentLastValidFruitType = self.lastValidFruitType; end; if self.pipeParticleActivated ~= self.sentPipeParticleActivated or self.pipeIsUnloading ~= self.sentPipeIsUnloading then g_server:broadcastEvent(CombinePipeParticleActivatedEvent:new(self, self.pipeParticleActivated, self.pipeIsUnloading), nil, nil, self); self.sentPipeParticleActivated = self.pipeParticleActivated; self.sentPipeIsUnloading = self.pipeIsUnloading; end; if self.chopperPSenabled~= self.sentChopperPSenabled or (self.chopperPSenabled and self.chopperPSFruitType ~= self.sentChopperPSFruitType) then g_server:broadcastEvent(Lexion770SetChopperEnableEvent:new(self, self.chopperPSenabled, self.chopperPSFruitType), true, nil, self); self.sentChopperPSFruitType = self.chopperPSFruitType; self.sentChopperPSenabled = self.chopperPSenabled; end if self.strawPSenabled ~= self.sentStrawPSenabled or (self.strawPSenabled and self.strawPSFruitType ~= self.sentStrawPSFruitType) then g_server:broadcastEvent(Lexion770SetStrawEnableEvent:new(self, self.strawPSenabled, self.strawPSFruitType), true, nil, self); self.sentStrawPSFruitType = self.strawPSFruitType; self.sentStrawPSenabled = self.strawPSenabled; end; if self.chopperDisableTime ~= nil and self.chopperDisableTime <= self.time then self.chopperPSenabled = false; self.chopperDisableTime = nil; end; if self.strawDisableTime ~= nil and self.strawDisableTime <= self.time then self.strawPSenabled = false; self.strawDisableTime = nil; self.strawEmitState = false; end; end; if self.pipeParticleActivated then self.pipeParticleDeactivateTime = self.time + 100; local currentPipeParticleSystem = self.pipeParticleSystems[self.currentGrainTankFruitType]; if currentPipeParticleSystem == nil then currentPipeParticleSystem = self.defaultPipeParticleSystem; end; if currentPipeParticleSystem ~= self.currentPipeParticleSystem then if self.currentPipeParticleSystem ~= nil then Utils.setEmittingState(self.currentPipeParticleSystem, false); end; end; self.currentPipeParticleSystem = currentPipeParticleSystem; Utils.setEmittingState(self.currentPipeParticleSystem, true); else if self.pipeParticleDeactivateTime <= self.time and self.currentPipeParticleSystem ~= nil then Utils.setEmittingState(self.currentPipeParticleSystem, false); self.currentPipeParticleSystem = nil; end; end; end; function Lexion770:draw() if self.isClient then local percent = 0; if self.grainTankCapacity > 0 then percent = self.grainTankFillLevel/self.grainTankCapacity*100; if self.currentPipeState == 2 and not self.pipeParticleActivated and self.grainTankFillLevel > 0 then g_currentMission:addExtraPrintText(g_i18n:getText("Move_the_pipe_over_a_trailer")); elseif self.grainTankFillLevel == self.grainTankCapacity then g_currentMission:addExtraPrintText(g_i18n:getText("Dump_corn_to_continue_threshing")); end; end; if self.drawFillLevel then self:drawGrainLevel(self.grainTankFillLevel, self.grainTankCapacity, 95); else self:drawGrainLevel(0,0, 101); end; if self.numAttachedCutters > 0 then if self.workMode then if self.isThreshing then g_currentMission:addHelpButtonText(g_i18n:getText("Turn_off_cutter"), InputBinding.ACTIVATE_THRESHING); else if self.grainTankFillLevel <= 3600 then g_currentMission:addHelpButtonText(g_i18n:getText("Transport_mode"), InputBinding.IMPLEMENT_EXTRA3); else g_currentMission:addExtraPrintText(g_i18n:getText("Empty_bunker")); end; g_currentMission:addHelpButtonText(g_i18n:getText("Turn_on_cutter"), InputBinding.ACTIVATE_THRESHING); end; if (self.hasStraw) or ((self.currentGrainTankFruitType~=FruitUtil.FRUITTYPE_UNKNOWN) and (FruitUtil.fruitIndexToDesc[self.currentGrainTankFruitType].hasStraw)) or (self.currentGrainTankFruitType==FruitUtil.FRUITTYPE_UNKNOWN) then if self.crusherEnabled then g_currentMission:addHelpButtonText(g_i18n:getText("Turn_off_crusher"), InputBinding.ACTIVATE_CRUSHER); else g_currentMission:addHelpButtonText(g_i18n:getText("Turn_on_crusher"), InputBinding.ACTIVATE_CRUSHER); end; end; else g_currentMission:addHelpButtonText(g_i18n:getText("Work_mode"), InputBinding.IMPLEMENT_EXTRA3); end; end; if self.numPipeStates == 2 then if self.targetPipeState == 2 then g_currentMission:addHelpButtonText(g_i18n:getText("Pipe_in"), InputBinding.EMPTY_GRAIN); else if percent > 80 then g_currentMission:addHelpButtonText(g_i18n:getText("Dump_corn"), InputBinding.EMPTY_GRAIN); end; end; end; if self.currentGrainTankFruitType ~= FruitUtil.FRUITTYPE_UNKNOWN then g_currentMission:setFruitOverlayFruitType(self.currentGrainTankFruitType); end; local printRainWarning = false; local printSpeedLevelWarning = false; local speedLevelStr; local speedLevelKeyStr; local speedLevel = 10; for _, implement in pairs(self.attachedCutters) do local cutter = implement.object; printRainWarning = printRainWarning or cutter.printRainWarning; if math.abs(cutter.speedViolationTimer - cutter.speedViolationMaxTime) > 2 then printSpeedLevelWarning = true; if Cutter.getUseLowSpeedLimit(cutter) then if speedLevel > 1 then speedLevel = 1; speedLevelStr = "1"; speedLevelKeyStr = InputBinding.getKeyNamesOfDigitalAction(InputBinding.SPEED_LEVEL1) end; else if speedLevel > 2 then speedLevel = 2; speedLevelStr = "2"; speedLevelKeyStr = InputBinding.getKeyNamesOfDigitalAction(InputBinding.SPEED_LEVEL2) end; end; end; end; if printRainWarning then g_currentMission:addWarning(g_i18n:getText("Dont_do_threshing_during_rain_or_hail"), 0.018, 0.033); end; if printSpeedLevelWarning then g_currentMission:addWarning(g_i18n:getText("Dont_drive_to_fast") .. "\n" .. string.format(g_i18n:getText("Cruise_control_levelN"), speedLevelStr, speedLevelKeyStr), 0.07+0.022, 0.019+0.029); end; end; end; function Lexion770:onEnter(isControlling) end; function Lexion770:onLeave() if self.deactivateOnLeave then Lexion770.onDeactivate(self); else Lexion770.onDeactivateSounds(self) end; self:setBeaconLightsVisibility(self.autoBeacon, true); end; function Lexion770:onDeactivate() self:stopThreshing(); for k,v in pairs(self.grainTankParticleSystems) do Utils.setEmittingState(v, false); end; for k,v in pairs(self.chopperParticleSystems) do Utils.setEmittingState(v, false); end; for k,v in pairs(self.strawParticleSystems) do Utils.setEmittingState(v, false); end; self.chopperEnableTime = nil; self.chopperDisableTime = nil; self.strawEnableTime = nil; self.strawDisableTime = nil; self.strawEmitState = false; Lexion770.onDeactivateSounds(self) end; function Lexion770:onDeactivateSounds() if self.pipeSound ~= nil and self.pipeSoundEnabled then stopSample(self.pipeSound); self.pipeSoundEnabled = false; end; if self.threshingSoundActive then stopSample(self.threshingSound); self.threshingSoundActive = false; end; end; function Lexion770:attachImplement(implement) local object = implement.object; if object.attacherJoint.jointType == Vehicle.JOINTTYPE_CUTTER then self.attachedCutters[object] = implement; self.numAttachedCutters = self.numAttachedCutters+1; -- cutter assumes the thresher's loaded fruit type object:setFruitType(self.currentGrainTankFruitType); for a,o in pairs(self.attacherJoints) do if o.jointType == Vehicle.JOINTTYPE_CUTTER then if self.defaultRotMax == nil then self.defaultRotMax = o.maxRot[1]; end; local uarot = getUserAttribute(object.rootNode, "rotation"); if uarot ~= nil then o.maxRot[1] = Utils.degToRad(uarot); else o.maxRot[1] = self.defaultRotMax; end; end; end; end; end; function Lexion770:detachImplement(implementIndex) local object = self.attachedImplements[implementIndex].object; if object.attacherJoint.jointType == Vehicle.JOINTTYPE_CUTTER then self.numAttachedCutters = self.numAttachedCutters-1; if self.numAttachedCutters == 0 then self:stopThreshing(); self:setCrusher(false); end; self.attachedCutters[object] = nil; end; end; function Lexion770:allowGrainTankFruitType(fruitType) local allowed = false; if self.grainTankFruitTypes[fruitType] then -- is fruit type accepted by combine? if self.currentGrainTankFruitType ~= FruitUtil.FRUITTYPE_UNKNOWN then -- is combine currently not empty? if self.currentGrainTankFruitType ~= fruitType then -- is there another fill type in the tank? if self.grainTankCapacity == 0 or self.grainTankFillLevel / self.grainTankCapacity <= self.minThreshold then allowed = true; -- fill level is low enough to be overridden end; else allowed = true; -- fill type is the same as the combine's current fill type end; else allowed = true; -- combine is empty --> FruitUtil.FRUITTYPE_UNKNOWN end; end; return allowed; end; function Lexion770:emptyGrainTankIfLowFillLevel() if self.grainTankCapacity == 0 or self.grainTankFillLevel / self.grainTankCapacity <= self.minThreshold then self.grainTankFillLevel = 0; -- empty the combine --return true; end; --return false; end; function Lexion770:setGrainTankFillLevel(fillLevel, fruitType) if not self:allowGrainTankFruitType(fruitType) then return; end; --self:emptyGrainTankIfLowFillLevel(); self.grainTankFillLevel = Utils.clamp(fillLevel, 0, self.grainTankCapacity); self.currentGrainTankFruitType = fruitType; if self.isClient then if self.currentGrainTankPlane ~= nil then for _, node in ipairs(self.currentGrainTankPlane.nodes) do setVisibility(node.node, false); end; self.currentGrainTankPlane = nil; end; if self.grainTankPlanes ~= nil and self.defaultGrainTankPlane ~= nil and fruitType ~= FruitUtil.FRUITTYPE_UNKNOWN then local fruitTypeName = FruitUtil.fruitIndexToDesc[fruitType].name; local grainPlane = self.grainTankPlanes[fruitTypeName]; if grainPlane == nil then grainPlane = self.defaultGrainTankPlane; end; local t = self.grainTankFillLevel/self.grainTankCapacity for _, node in ipairs(grainPlane.nodes) do local x,y,z, rx,ry,rz, sx,sy,sz = node.animCurve:get(t); setTranslation(node.node, x, y, z); setRotation(node.node, rx, ry, rz); setScale(node.node, sx, sy, sz); setVisibility(node.node, self.grainTankFillLevel > 0); end; self.currentGrainTankPlane = grainPlane; end; end; if self.grainTankFillLevel <= 0 then for cutter,implement in pairs(self.attachedCutters) do cutter:resetFruitType(); end; self.currentGrainTankFruitType = FruitUtil.FRUITTYPE_UNKNOWN; else for cutter,implement in pairs(self.attachedCutters) do cutter:setFruitType(self.currentGrainTankFruitType); end; end; end; function Lexion770:startThreshing() if not self.isThreshing then if self.numAttachedCutters > 0 then self.chopperActivated = self.crusherEnabled; self.isThreshing = true; for cutter,implement in pairs(self.attachedCutters) do self:setJointMoveDown(implement.jointDescIndex, true, true); cutter:setReelSpeedScale(1); --0.003); cutter:onStartReel(); end; if self.isClient then local threshingSoundOffset = 0; if self.threshingStartSound ~= nil then if self:getIsActiveForSound() then setSamplePitch(self.threshingStartSound, self.threshingStartSoundPitchOffset); playSample(self.threshingStartSound, 1, 1, 0); end; threshingSoundOffset = getSampleDuration(self.threshingStartSound); end; self.playThreshingSoundTime = self.time+threshingSoundOffset; end; for cutter,implement in pairs(self.attachedCutters) do if cutter.laserPilot then cutter:laserPilotStart(); end; end; end; end; end; function Lexion770:stopThreshing() if self.isThreshing then if self.isClient then if self.threshingSound ~= nil then stopSample(self.threshingSound); end; if self.threshingStopSound ~= nil and self.threshingSoundActive and self:getIsActiveForSound() then setSamplePitch(self.threshingStopSound, self.threshingStopSoundPitchOffset); playSample(self.threshingStopSound, 1, 1, 0); self.threshingSoundActive = false; end; end; for cutter,implement in pairs(self.attachedCutters) do if cutter.laserPilot then cutter:laserPilotEnd(); end; end; self.chopperActivated = false; self.isThreshing = false; for cutter,implement in pairs(self.attachedCutters) do self:setJointMoveDown(implement.jointDescIndex, false, true); cutter:onStopReel(); end; end; end; function Lexion770:workModeStart(turbo) if not self.workMode then if (self.numAttachedCutters > 0) or turbo then self.workMode = true; if self.tankAnimCharSet ~= nil and self.tankAnimCharSet ~= 0 then if turbo then setAnimTrackTime(self.tankAnimCharSet, 0, self.tankAnimDuration); else if getAnimTrackTime(self.tankAnimCharSet, 0) < 0.0 then setAnimTrackTime(self.tankAnimCharSet, 0, 0.0); end; end; setAnimTrackSpeedScale(self.tankAnimCharSet, 0, self.tankAnimSpeedScale); enableAnimTrack(self.tankAnimCharSet, 0); end; for _, light in pairs(self.realLights) do setLightRange(light, 250); end; for _, light in pairs(self.workLights) do setVisibility(light, true); end; for _, light in pairs(self.workLightCoronas) do setVisibility(light, true); end; for _, light in pairs(self.workLightCones) do setVisibility(light, true); end; end; end; end; function Lexion770:workModeEnd() if self.workMode and self.grainTankFillLevel <= 3600 then if self.isThreshing then self:setIsThreshing(false); end; self.workMode = false; if self.tankAnimCharSet ~= nil and self.tankAnimCharSet ~= 0 then if getAnimTrackTime(self.tankAnimCharSet, 0) > self.tankAnimDuration then setAnimTrackTime(self.tankAnimCharSet, 0, self.tankAnimDuration); end; setAnimTrackSpeedScale(self.tankAnimCharSet, 0, -self.tankAnimSpeedScale); enableAnimTrack(self.tankAnimCharSet, 0); end; for _, light in pairs(self.realLights) do setLightRange(light, 80); end; for _, light in pairs(self.workLights) do setVisibility(light, false); end; for _, light in pairs(self.workLightCoronas) do setVisibility(light, false); end; for _, light in pairs(self.workLightCones) do setVisibility(light, false); end; end; end; function Lexion770:crusherStart() if self.workMode and not self.crusherEnabled then self.crusherEnabled=true; if self.isThreshing and self.hasStraw then self.chopperActivated = self.crusherEnabled; if self.lastArea>0 then self.chopperEnableTime = self.time; self.chopperDisableTime = nil; self.strawDisableTime = self.time; self.strawEnableTime = nil; end; end; end; end; function Lexion770:crusherEnd() if self.workMode and self.crusherEnabled then self.crusherEnabled=false; if self.isThreshing and self.hasStraw then self.chopperActivated = self.crusherEnabled; if self.lastArea>0 then self.strawEnableTime = self.time; self.strawDisableTime = nil; self.chopperDisableTime = self.time; self.chopperEnableTime = nil; end; end; end; end; function Lexion770:setPipeOpening(pipeOpening, noEventSend) if pipeOpening then self:setPipeState(2, noEventSend); else self:setPipeState(1, noEventSend); end; end; function Lexion770:setPipeState(pipeState, noEventSend) if self.targetPipeState ~= pipeState then if noEventSend == nil or noEventSend == false then if g_server ~= nil then g_server:broadcastEvent(CombineSetPipeStateEvent:new(self, pipeState)); else g_client:getServerConnection():sendEvent(CombineSetPipeStateEvent:new(self, pipeState), nil, nil, self); end; end; self.targetPipeState = pipeState; self.currentPipeState = 0; end; end; function Lexion770:setIsThreshing(isThreshing, noEventSend) if isThreshing ~= self.isThreshing then if noEventSend == nil or noEventSend == false then if g_server ~= nil then g_server:broadcastEvent(CombineSetThreshingEnabledEvent:new(self, isThreshing), nil, nil, self); else g_client:getServerConnection():sendEvent(CombineSetThreshingEnabledEvent:new(self, isThreshing)); end; end; if isThreshing then self:startThreshing(); else self:stopThreshing(); end; end; end; function Lexion770:setWorkMode(workMode, noEventSend, turbo) if workMode ~= self.workMode then if turbo == nil then turbo = false; end; if noEventSend == nil or noEventSend == false then if g_server ~= nil then g_server:broadcastEvent(CombineSetWorkModeEvent:new(self, workMode, turbo), nil, nil, self); else g_client:getServerConnection():sendEvent(CombineSetWorkModeEvent:new(self, workMode, turbo)); end; end; if workMode then self:workModeStart(turbo); else self:workModeEnd(); end; end; end; function Lexion770:setCrusher(enabled, noEventSend) if enabled ~= self.crusherEnabled then if noEventSend == nil or noEventSend == false then if g_server ~= nil then g_server:broadcastEvent(CombineSetCrusherEvent:new(self, enabled), nil, nil, self); else g_client:getServerConnection():sendEvent(CombineSetCrusherEvent:new(self, enabled)); end; end; if enabled then self:crusherStart(); else self:crusherEnd(); end; end; end; function Lexion770:getIshreshingAllowed(earlyWarning) if self.allowThreshingDuringRain then return true; end; if earlyWarning ~= nil and earlyWarning == true then if g_currentMission.environment.lastRainScale <= 0.02 and g_currentMission.environment.timeSinceLastRain > 20 then return true; end; else if g_currentMission.environment.lastRainScale <= 0.1 and g_currentMission.environment.timeSinceLastRain > 20 then return true; end; end; return false; end; function Lexion770:getFruitTypeAndFillLevelToUnload() local fillLevel = self.grainTankFillLevel; local fruitType = self.currentGrainTankFruitType; local useGrainTank = self.grainTankCapacity > 0; if not useGrainTank then fillLevel = self.grainTankTempFillLevel; fruitType = self.grainTankTempFruitType; end; return fruitType, fillLevel, useGrainTank; end; function Lexion770:findAutoAimTrailerToUnload(fruitType) local trailer = nil; local smallestTrailerId = nil; if self.trailersInRange ~= nil then for trailerInRange, pipeStage in pairs(self.trailersInRange) do if trailerInRange:allowFillType(FruitUtil.fruitTypeToFillType[fruitType]) and trailerInRange.allowFillFromAir and trailerInRange.fillLevel < trailerInRange.capacity then local id = networkGetObjectId(trailerInRange); -- always take the trailer with the smalles network id. This is deterministic and is the same on the client if trailer == nil or id < smallestTrailerId then trailer = trailerInRange; smallestTrailerId = id; end; end; end; end; return trailer; end; function Lexion770:findTrailerToUnload(fruitType) local x,y,z = getWorldTranslation(self.pipeRaycastNode); local dx,dy,dz = localDirectionToWorld(self.pipeRaycastNode, 0,-1,0); self.trailerFound = 0; raycastAll(x, y, z, dx,dy,dz, "findTrailerRaycastCallback", self.pipeRaycastDistance, self); local trailer = g_currentMission.nodeToVehicle[self.trailerFound]; if trailer == nil or not trailer:allowFillType(FruitUtil.fruitTypeToFillType[fruitType]) or not trailer.allowFillFromAir or trailer.fillLevel >= trailer.capacity then return nil; end; return trailer; end; function Lexion770:findTrailerRaycastCallback(transformId, x, y, z, distance) local vehicle = g_currentMission.nodeToVehicle[transformId]; if vehicle ~= nil then if vehicle.exactFillRootNode == transformId then self.trailerFound = transformId; return false; end; end; return true; end;