-- -- Baler -- Class for all Balers -- -- @author Stefan Geiger -- @date 10/09/08 -- -- Copyright (C) GIANTS Software GmbH, Confidential, All Rights Reserved. --source("dataS/scripts/vehicles/specializations/SetTurnedOnEvent.lua"); --source("dataS/scripts/vehicles/specializations/BalerSetIsUnloadingBaleEvent.lua"); --source("dataS/scripts/vehicles/specializations/BalerSetBaleTimeEvent.lua"); --source("dataS/scripts/vehicles/specializations/BalerCreateBaleEvent.lua"); --source("dataS/scripts/vehicles/specializations/BalerAreaEvent.lua"); ClaasVariant = {}; ClaasVariant.UNLOADING_CLOSED = 1; ClaasVariant.UNLOADING_OPENING = 2; ClaasVariant.UNLOADING_OPEN = 3; ClaasVariant.UNLOADING_CLOSING = 4; function ClaasVariant.prerequisitesPresent(specializations) return SpecializationUtil.hasSpecialization(Fillable, specializations); end; function ClaasVariant:load(xmlFile) self.setIsTurnedOn = SpecializationUtil.callSpecializationsFunction("setIsTurnedOn"); self.getTimeFromLevel = ClaasVariant.getTimeFromLevel; self.moveBales = SpecializationUtil.callSpecializationsFunction("moveBales"); self.moveBale = SpecializationUtil.callSpecializationsFunction("moveBale"); self.allowFillType = ClaasVariant.allowFillType; self.allowPickingUp = ClaasVariant.allowPickingUp; self.setIsUnloadingBale = ClaasVariant.setIsUnloadingBale; self.fillScale = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.fillScale#value"), 1); local firstBaleMarker = getXMLFloat(xmlFile, "vehicle.baleAnimation#firstBaleMarker"); if firstBaleMarker ~= nil then local baleAnimCurve = AnimCurve:new(linearInterpolatorN); local keyI = 0; while true do local key = string.format("vehicle.baleAnimation.key(%d)", keyI); local t = getXMLFloat(xmlFile, key.."#time"); local x,y,z = Utils.getVectorFromString(getXMLString(xmlFile, key.."#pos")); if x == nil or y == nil or z == nil then break; end; local rx, ry, rz = Utils.getVectorFromString(getXMLString(xmlFile, key.."#rot")); rx = math.rad(Utils.getNoNil(rx, 0)); ry = math.rad(Utils.getNoNil(ry, 0)); rz = math.rad(Utils.getNoNil(rz, 0)); baleAnimCurve:addKeyframe({ v={x, y, z, rx, ry, rz}, time = t}); keyI = keyI +1; end; if keyI > 0 then self.baleAnimCurve = baleAnimCurve; self.firstBaleMarker = firstBaleMarker; --self.baleFilename = Utils.getNoNil(getXMLString(xmlFile, "vehicle.baleAnimation#filename"), "data/maps/models/objects/strawbale/strawbaleBaler.i3d"); end; end; self.baleAnimRoot = Utils.getNoNil(Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.baleAnimation#node")), self.components[1].node); -- there is no standard bale animation, load the unload animation (for round baler) if self.firstBaleMarker == nil then local unloadAnimationName = getXMLString(xmlFile, "vehicle.baleAnimation#unloadAnimationName"); local closeAnimationName = getXMLString(xmlFile, "vehicle.baleAnimation#closeAnimationName"); local unloadAnimationSpeed = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.baleAnimation#unloadAnimationSpeed"), 1); local closeAnimationSpeed = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.baleAnimation#closeAnimationSpeed"), 1); if unloadAnimationName ~= nil and closeAnimationName ~= nil then if self.playAnimation ~= nil and self.animations ~= nil then if self.animations[unloadAnimationName] ~= nil and self.animations[closeAnimationName] ~= nil then --print("has unload animation"); self.baleUnloadAnimationName = unloadAnimationName; self.baleUnloadAnimationSpeed = unloadAnimationSpeed; self.baleCloseAnimationName = closeAnimationName; self.baleCloseAnimationSpeed = closeAnimationSpeed; self.baleDropAnimTime = getXMLFloat(xmlFile, "vehicle.baleAnimation#baleDropAnimTime"); if self.baleDropAnimTime == nil then self.baleDropAnimTime = self:getAnimationDuration(self.baleUnloadAnimationName); else self.baleDropAnimTime = self.baleDropAnimTime * 1000; end; else print("Error: Failed to find unload animations '"..unloadAnimationName.."' and '"..closeAnimationName.."' in '"..self.configFileName.."'."); end; else print("Error: There is an unload animation in '"..self.configFileName.."' but it is not a AnimatedVehicle. Change to a vehicle type which has the AnimatedVehicle specialization."); end; end; end; self.baleTypes = {}; local i = 0; while true do local key = string.format("vehicle.baleTypes.baleType(%d)", i); local t = getXMLString(xmlFile, key.."#fruitType"); local filename = getXMLString(xmlFile, key.."#filename"); if t==nil or filename==nil then break; end; local entry = {}; entry.filename = filename; local desc = FruitUtil.fruitTypes[t]; if desc ~= nil then self.baleTypes[desc.index] = entry; if self.defaultBaleType == nil then self.defaultBaleType = entry; end; end; i = i +1; end; if self.defaultBaleType == nil then self.baleTypes = nil; end; local balerSound = getXMLString(xmlFile, "vehicle.balerSound#file"); if balerSound ~= nil and balerSound ~= "" then balerSound = Utils.getFilename(balerSound, self.baseDirectory); self.balerSound = createSample("balerSound"); self.balerSoundEnabled = false; loadSample(self.balerSound, balerSound, false); self.balerSoundPitchOffset = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.balerSound#pitchOffset"), 1); self.balerSoundVolume = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.balerSound#volume"), 1); end; local balerBaleEject = getXMLString(xmlFile, "vehicle.balerBaleEject#file"); if balerBaleEject ~= nil and balerBaleEject ~= "" then balerBaleEject = Utils.getFilename(balerBaleEject, self.baseDirectory); self.balerBaleEject = createSample("balerBaleEject"); self.balerBaleEjectEnabled = false; loadSample(self.balerBaleEject, balerBaleEject, false); self.balerBaleEjectPitchOffset = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.balerBaleEject#pitchOffset"), 1); self.balerBaleEjectVolume = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.balerBaleEject#volume"), 1); end; local balerDoor = getXMLString(xmlFile, "vehicle.balerDoor#file"); if balerDoor ~= nil and balerDoor ~= "" then balerDoor = Utils.getFilename(balerDoor, self.baseDirectory); self.balerDoor = createSample("balerDoor"); self.balerDoorEnabled = false; loadSample(self.balerDoor, balerDoor, false); self.balerDoorPitchOffset = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.balerDoor#pitchOffset"), 1); self.balerDoorVolume = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.balerDoor#volume"), 1); end; local balerKnotCleaning = getXMLString(xmlFile, "vehicle.balerKnotCleaning#file"); if balerKnotCleaning ~= nil and balerKnotCleaning ~= "" then balerKnotCleaning = Utils.getFilename(balerKnotCleaning, self.baseDirectory); self.balerKnotCleaning = createSample("balerKnotCleaning"); self.balerKnotCleaningEnabled = false; self.balerKnotCleaningTime = 100000; loadSample(self.balerKnotCleaning, balerKnotCleaning, false); self.balerKnotCleaningPitchOffset = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.balerKnotCleaning#pitchOffset"), 1); self.balerKnotCleaningVolume = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.balerKnotCleaning#volume"), 1); end; self.balerUVScrollParts = {}; local i = 0; while true do local key = string.format("vehicle.balerUVScrollParts.balerUVScrollPart(%d)", i); if not hasXMLProperty(xmlFile, key) then break; end; local node = Utils.indexToObject(self.components, getXMLString(xmlFile, key.."#index")); local speed = Utils.getVectorNFromString(getXMLString(xmlFile, key.."#speed"), 2); if node ~= nil and speed then table.insert(self.balerUVScrollParts, {node=node, speed=speed}); end; i = i +1; end; self.baleLastPositionTime = 0; self.balerUnloadingState = ClaasVariant.UNLOADING_CLOSED; self.bales = {}; self.wasToFast = false; self.isTurnedOn = false; --self.pickupRotLimit = {0}; local path = Utils.getFilename("Hud/Balecounter_hud.dds", self.baseDirectory); self.balecounterHud = Overlay:new("counterOverlay", path, 0.8143, 0.8315, 0.176, 0.06638); self.baleCount = 0; self.lastBaleCount = 0; if self.isClient then self.wheatParticleSystems = {}; local i=0; while true do local key = string.format("vehicle.wheatParticleSystems.wheatParticleSystem(%d)", i); local node = getXMLString(xmlFile, key.. "#node"); if node == nil then break; end; node = Utils.indexToObject(self.components, node); if node ~= nil then local psFile = getXMLString(xmlFile, key.. "#file"); local wheatParticleSystem = {}; wheatParticleSystem.particleSystem = {}; StaticParticleSystem.loadParticleSystem(xmlFile, wheatParticleSystem.particleSystem, key, self.components, false, nil, self.baseDirectory); table.insert(self.wheatParticleSystems, wheatParticleSystem); end; i = i+1; end; self.grassParticleSystems = {}; local i=0; while true do local key = string.format("vehicle.grassParticleSystems.grassParticleSystem(%d)", i); local node = getXMLString(xmlFile, key.. "#node"); if node == nil then break; end; node = Utils.indexToObject(self.components, node); if node ~= nil then local psFile = getXMLString(xmlFile, key.. "#file"); local grassParticleSystem = {}; grassParticleSystem.particleSystem = {}; StaticParticleSystem.loadParticleSystem(xmlFile, grassParticleSystem.particleSystem, key, self.components, false, nil, self.baseDirectory); table.insert(self.grassParticleSystems, grassParticleSystem); end; i = i+1; end; end; local rotationPartNodeFillLevel = Utils.indexToObject(self.rootNode, getXMLString(xmlFile, "vehicle.rotationPartFillLevel#index")); if rotationPartNodeFillLevel ~= nil then self.rotationPartFillLevel = {}; self.rotationPartFillLevel.node = rotationPartNodeFillLevel; local x, y, z = Utils.getVectorFromString(getXMLString(xmlFile, "vehicle.rotationPartFillLevel#minRot")); self.rotationPartFillLevel.minRot = {}; self.rotationPartFillLevel.minRot[1] = Utils.degToRad(Utils.getNoNil(x, 0)); self.rotationPartFillLevel.minRot[2] = Utils.degToRad(Utils.getNoNil(y, 0)); self.rotationPartFillLevel.minRot[3] = Utils.degToRad(Utils.getNoNil(z, 0)); x, y, z = Utils.getVectorFromString(getXMLString(xmlFile, "vehicle.rotationPartFillLevel#maxRot")); self.rotationPartFillLevel.maxRot = {}; self.rotationPartFillLevel.maxRot[1] = Utils.degToRad(Utils.getNoNil(x, 0)); self.rotationPartFillLevel.maxRot[2] = Utils.degToRad(Utils.getNoNil(y, 0)); self.rotationPartFillLevel.maxRot[3] = Utils.degToRad(Utils.getNoNil(z, 0)); self.rotationPartFillLevel.rotTime = Utils.getNoNil(getXMLString(xmlFile, "vehicle.rotationPartFillLevel#rotTime"), 2)*1000; self.rotationPartFillLevel.touchRotLimit = Utils.degToRad(Utils.getNoNil(getXMLString(xmlFile, "vehicle.rotationPartFillLevel#touchRotLimit"), 10)); end; self.SetDoorPanel = SpecializationUtil.callSpecializationsFunction("SetDoorPanel"); self.SetHasRopes = SpecializationUtil.callSpecializationsFunction("SetHasRopes"); self.DoorPanel = true; self.Ropes = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.Ropes#index")); self.RopesCapacity = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.Ropes#Capacity"), 4); self.hasRopes = true; self.isLoaded = true; NoRopeSoundFile = Utils.getFilename("Sounds/NoRope.ogg", self.baseDirectory); self.NoRopeSoundId = createSample("NoRopeSound"); loadSample(self.NoRopeSoundId, NoRopeSoundFile, false); self.NoRopePlaying = false; self.NoRopesBalesCount = 0; self.setOverFlow = SpecializationUtil.callSpecializationsFunction("setOverFlow"); self.OverFlow = false; self.OverflowModel = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.OverflowModel#index")); self.speedViolationMaxTime = 5000; self.speedViolationTimer = self.speedViolationMaxTime; end; function ClaasVariant:delete() if self.balerSound ~= nil then delete(self.balerSound); self.balerSoundEnabled = false; end; if self.balerBaleEject ~= nil then delete(self.balerBaleEject); self.balerBaleEjectEnabled = false; end; if self.balerDoor ~= nil then delete(self.balerDoor); self.balerDoorEnabled = false; end; if self.balerKnotCleaning ~= nil then delete(self.balerKnotCleaning); self.balerKnotCleaningEnabled = false; end; if self.balecounterHud ~= nil then self.balecounterHud:delete(); end; for k, wheatParticleSystem in pairs(self.wheatParticleSystems) do Utils.deleteParticleSystem(wheatParticleSystem); end; for k, grassParticleSystem in pairs(self.grassParticleSystems) do Utils.deleteParticleSystem(grassParticleSystem); end; if self.NoRopeSound ~= nil then delete(self.NoRopeSound); end; end; function ClaasVariant:readStream(streamId, connection) local turnedOn = streamReadBool(streamId); self:setIsTurnedOn(turnedOn, true); local numBales = streamReadInt16(streamId); for i=1, numBales do local fruitType = streamReadInt8(streamId); ClaasVariant.createBale(self, fruitType); if self.baleAnimCurve ~= nil then local baleTime = streamReadFloat32(streamId); ClaasVariant.setBaleTime(self, i, baleTime); end; end; self.baleCount = streamReadInt32(streamId); self:SetDoorPanel(streamReadBool(streamId), true); self:SetHasRopes(streamReadBool(streamId), true); self.NoRopesBalesCount = streamReadInt32(streamId); self.OverFlow = streamReadBool(streamId); end; function ClaasVariant:writeStream(streamId, connection) streamWriteBool(streamId, self.isTurnedOn); streamWriteInt16(streamId, table.getn(self.bales)); for i=1, table.getn(self.bales) do local bale = self.bales[i]; streamWriteInt8(streamId, bale.fruitType); if self.baleAnimCurve ~= nil then streamWriteFloat32(streamId, bale.time); end; end; streamWriteInt32(streamId, self.baleCount); streamWriteBool(streamId, self.DoorPanel); streamWriteBool(streamId, self.isLoaded); streamWriteInt32(streamId, self.NoRopesBalesCount); streamWriteBool(streamId, self.OverFlow); end; function ClaasVariant:readUpdateStream(streamId, timestamp, connection) end; function ClaasVariant:writeUpdateStream(streamId, connection, dirtyMask) end; function ClaasVariant:loadFromAttributesAndNodes(xmlFile, key, resetVehicles) local numBales = getXMLInt(xmlFile, key.."#numBales"); if numBales ~= nil then for i=1, numBales do local baleKey = key..string.format(".bale(%d)", i-1); local fruitType = getXMLString(xmlFile, baleKey.."#fruitType"); local baleTime = getXMLFloat(xmlFile, baleKey.."#baleTime"); if fruitType ~= nil and (baleTime ~= nil or self.baleAnimCurve == nil) then local fruitTypeDesc = FruitUtil.fruitTypes[fruitType]; if fruitTypeDesc ~= nil then ClaasVariant.createBale(self, fruitTypeDesc.index); if self.baleAnimCurve ~= nil then ClaasVariant.setBaleTime(self, table.getn(self.bales), baleTime); end; end; end; end; end; if not resetVehicles then --moje self.baleCount = Utils.getNoNil(getXMLInt(xmlFile, key.."#baleCount"), 0); end; local NoRopesBalesCount = Utils.getNoNil(getXMLInt(xmlFile, key.."#NoRopesBalesCount"),0); self.NoRopesBalesCount = NoRopesBalesCount; self.OverFlow = Utils.getNoNil(getXMLBool(xmlFile, key.."#OverFlow"), false); return BaseMission.VEHICLE_LOAD_OK; end function ClaasVariant:getSaveAttributesAndNodes(nodeIdent) local NoRopesBalesCount= Utils.getNoNil(self.NoRopesBalesCount, 0); local attributes = 'numBales="'..table.getn(self.bales)..'"baleCount="'.. tostring(self.baleCount) ..'"NoRopesBalesCount="'..string.format("%.1f",NoRopesBalesCount)..'"OverFlow="'..tostring(self.OverFlow)..'"'; local nodes = ""; local baleNum = 0; for i=1, table.getn(self.bales) do local bale = self.bales[i]; local fruitType = "unknown"; if bale.fruitType ~= FruitUtil.FRUITTYPE_UNKNOWN then fruitType = FruitUtil.fruitIndexToDesc[bale.fruitType].name; end; if baleNum>0 then nodes = nodes.."\n"; end; nodes = nodes..nodeIdent..''; baleNum = baleNum+1; end; return attributes,nodes; end; function ClaasVariant:mouseEvent(posX, posY, isDown, isUp, button) end; function ClaasVariant:keyEvent(unicode, sym, modifier, isDown) end; function ClaasVariant:update(dt) if self:getIsActiveForInput() then if InputBinding.hasEvent(InputBinding.IMPLEMENT_EXTRA) then self:setIsTurnedOn(not self.isTurnedOn); end; if InputBinding.hasEvent(InputBinding.IMPLEMENT_EXTRA2) then if self.baleUnloadAnimationName ~= nil then if self.balerUnloadingState == ClaasVariant.UNLOADING_CLOSED then if table.getn(self.bales) > 0 then self:setIsUnloadingBale(true); end; elseif self.balerUnloadingState == ClaasVariant.UNLOADING_OPEN then self:setIsUnloadingBale(false); end; end; end; end; if self:getIsActiveForInput() then if InputBinding.hasEvent(InputBinding.ClaasVariant_PICKUP) then if self.animationParts[1].clipEndTime then self:setAnimationTime(1, self.animationParts[1].offSet); elseif self.animationParts[1].clipStartTime then self:setAnimationTime(1, self.animationParts[1].animDuration); end; end; if InputBinding.hasEvent(InputBinding.ClaasVariant_RESET) then self.baleCount = 0; end; end; --[[local newRot = Utils.getMovedLimitedValues(self.pickupRotLimit, {8}, {0}, 1, 2000, dt, not self.animationParts[1].clipEndTime); local jointDesc = self.componentJoints[1]; setJointRotationLimit(jointDesc.jointIndex, 0, true, Utils.degToRad(-newRot[1]), Utils.degToRad(newRot[1])); self.pickupRotLimit = newRot;]]-- if self.setAnimationTime ~= nil then if self.isTurnedOn then self:setAnimationTime(3, 1); else self:setAnimationTime(3, 0); end; end; if self.inrange then if InputBinding.hasEvent(InputBinding.ClaasVariant_DOOR) then self:SetDoorPanel(not self.DoorPanel); end; if not self.isLoaded then if InputBinding.hasEvent(InputBinding.ClaasVariant_RELOAD) then self:SetHasRopes(true); end; end; end; if self.NoRopesBalesCount >= self.RopesCapacity then self:SetHasRopes(false); end; if self.inrange then if self.DoorPanel then self:setAnimationTime(4, self.animationParts[4].offSet); g_currentMission:addHelpButtonText(g_i18n:getText("ClaasVariant_4"), InputBinding.ClaasVariant_DOOR); else self:setAnimationTime(4, self.animationParts[4].animDuration); g_currentMission:addHelpButtonText(g_i18n:getText("ClaasVariant_5"), InputBinding.ClaasVariant_DOOR); if not self.isLoaded then g_currentMission:addHelpButtonText(g_i18n:getText("ClaasVariant_6"), InputBinding.ClaasVariant_RELOAD); end; end; end; if self.isClient then if self.OverFlow then setVisibility(self.OverflowModel, true); self:setIsTurnedOn(false); local totalArea =0; if self.inrange then g_currentMission:addHelpButtonText(g_i18n:getText("overflow"), InputBinding.ClaasVariant_OVERFLOW); if InputBinding.hasEvent(InputBinding.ClaasVariant_OVERFLOW) then self:setOverFlow(false); end; end; else setVisibility(self.OverflowModel, false); end; if self.isTurnedOn then if self.attacherVehicle~=nil and self:doCheckSpeedLimit() and self.attacherVehicle.lastSpeed * 3000 > 15 then self.speedViolationTimer = self.speedViolationTimer - dt; if self.speedViolationTimer < 0 and self.attacherVehicle~=nil then self:setOverFlow(true); end; else self.speedViolationTimer = self.speedViolationMaxTime; end; end; end; end; function ClaasVariant:updateTick(dt) self.wasToFast = false; if self:getIsActive() then if self.animationParts[1].clipEndTime then if self.isTurnedOn then local toFast = self:doCheckSpeedLimit() and self.lastSpeed*3000 > 18; if not toFast then if self.isServer and self:allowPickingUp() then local totalArea =0; local usedFruitType = FruitUtil.FRUITTYPE_UNKNOWN; local fruitTypes = {}; for fillType,enabled in pairs(self.fillTypes) do if enabled then local fruitType = FruitUtil.fillTypeToFruitType[fillType]; if fruitType ~= nil and fruitType ~= FruitUtil.FRUITTYPE_UNKNOWN then table.insert(fruitTypes, fruitType); end; end; end; if table.getn(fruitTypes) > 0 then local cuttingAreasSend = {}; for k, cuttingArea in pairs(self.cuttingAreas) do if self:getIsAreaActive(cuttingArea) then local x,y,z = getWorldTranslation(cuttingArea.start); local x1,y1,z1 = getWorldTranslation(cuttingArea.width); local x2,y2,z2 = getWorldTranslation(cuttingArea.height); table.insert(cuttingAreasSend, {x,z,x1,z1,x2,z2}); --[[for fillType,v in pairs(self.fillTypes) do local fruitType = FruitUtil.fillTypeToFruitType[fillType]; if fruitType ~= nil and fruitType ~= FruitUtil.FRUITTYPE_UNKNOWN then local desc = FruitUtil.fruitIndexToDesc[fruitType]; local fruitTypeToCut = fruitType; if desc.autoSeedFruitType ~= nil then fruitTypeToCut = desc.autoSeedFruitType; Utils.setGrowthStateAtCutLongArea(fruitType, x, z, x1, z1, x2, z2, desc.autoSeedFruitType, 1) Utils.setGrowthStateAtWindrowArea(fruitType, x, z, x1, z1, x2, z2, desc.autoSeedFruitType, 1) end; local area = Utils.updateFruitWindrowArea(fruitTypeToCut, x, z, x1, z1, x2, z2, 0)*g_currentMission.windrowCutLongRatio; area = area + Utils.updateFruitCutLongArea(fruitTypeToCut, x, z, x1, z1, x2, z2, 0); if area > 0 then totalArea = totalArea+area; usedFruitType = fruitType; table.insert(cuttingAreasSend, {x,z,x1,z1,x2,z2,fruitType}); end; end; end;]] end; end; if table.getn(cuttingAreasSend) > 0 then totalArea, usedFruitType = BalerAreaEvent.runLocally(cuttingAreasSend, fruitTypes); if totalArea > 0 then if (table.getn(cuttingAreasSend) > 0) then g_server:broadcastEvent(BalerAreaEvent:new(cuttingAreasSend, fruitTypes)); end; end; end; end; if totalArea > 0 then local literPerPixel = g_currentMission:getFruitPixelsToSqm()*g_strawLitersPerSqm; --8000/1200 / 6 / (2*2) *12/4 / 4; local deltaLevel = totalArea * literPerPixel * self.fillScale; if self.baleUnloadAnimationName == nil then -- move all bales local deltaTime = self:getTimeFromLevel(deltaLevel); self:moveBales(deltaTime); end; local usedFillType = FruitUtil.fruitTypeToFillType[usedFruitType]; local oldFillLevel = self.fillLevel; self:setFillLevel(self.fillLevel+deltaLevel, usedFillType); if self.fillLevel == self.capacity then if self.baleTypes ~= nil then -- create bale if self.baleAnimCurve ~= nil then local restDeltaFillLevel = deltaLevel - (self.fillLevel-oldFillLevel) self:setFillLevel(restDeltaFillLevel, usedFillType); ClaasVariant.createBale(self, usedFruitType); local numBales = table.getn(self.bales); local bale = self.bales[numBales] self:moveBale(numBales, self:getTimeFromLevel(restDeltaFillLevel), true); -- note: self.bales[numBales] can not be accessed anymore since the bale might be dropped already g_server:broadcastEvent(BalerCreateBaleEvent:new(self, usedFruitType, bale.time), nil, nil, self); elseif self.baleUnloadAnimationName ~= nil then ClaasVariant.createBale(self, usedFruitType); g_server:broadcastEvent(BalerCreateBaleEvent:new(self, usedFruitType, 0), nil, nil, self); end; end; end; if usedFruitType == FruitUtil.FRUITTYPE_WHEAT or usedFruitType == FruitUtil.FRUITTYPE_BARLEY then for k, wheatParticleSystem in pairs(self.wheatParticleSystems) do Utils.setEmittingState(wheatParticleSystem.particleSystem, true); end; elseif usedFruitType == FruitUtil.FRUITTYPE_GRASS or usedFruitType == FruitUtil.FRUITTYPE_DRYGRASS then for k, grassParticleSystem in pairs(self.grassParticleSystems) do Utils.setEmittingState(grassParticleSystem.particleSystem, true); end; end; elseif totalArea == 0 then for k, wheatParticleSystem in pairs(self.wheatParticleSystems) do Utils.setEmittingState(wheatParticleSystem.particleSystem, false); end; for k, grassParticleSystem in pairs(self.grassParticleSystems) do Utils.setEmittingState(grassParticleSystem.particleSystem, false); end; end; end; else for k, wheatParticleSystem in pairs(self.wheatParticleSystems) do Utils.setEmittingState(wheatParticleSystem.particleSystem, false); end; for k, grassParticleSystem in pairs(self.grassParticleSystems) do Utils.setEmittingState(grassParticleSystem.particleSystem, false); end; end; if self.isClient then if self.balerKnotCleaning ~= nil and self.balerKnotCleaningTime <= self.time and self:getIsActiveForSound() then playSample(self.balerKnotCleaning, 1, self.balerKnotCleaningVolume, 0); setSamplePitch(self.balerKnotCleaning, self.balerKnotCleaningPitchOffset); self.balerKnotCleaningTime = self.time + 120000; self.balerKnotCleaningEnabled = true; end; if not self.balerSoundEnabled and self:getIsActiveForSound() then setSamplePitch(self.balerSound, self.balerSoundPitchOffset); playSample(self.balerSound, 0, self.balerSoundVolume, 0); self.balerSoundEnabled = true; end; end; self.wasToFast = toFast; else for k, wheatParticleSystem in pairs(self.wheatParticleSystems) do Utils.setEmittingState(wheatParticleSystem.particleSystem, false); end; for k, grassParticleSystem in pairs(self.grassParticleSystems) do Utils.setEmittingState(grassParticleSystem.particleSystem, false); end; end; if self.isClient then if not self.isTurnedOn and self.balerSoundEnabled then stopSample(self.balerSound); self.balerSoundEnabled = false; end; end; else self:setIsTurnedOn(false); for k, wheatParticleSystem in pairs(self.wheatParticleSystems) do Utils.setEmittingState(wheatParticleSystem.particleSystem, false); end; for k, grassParticleSystem in pairs(self.grassParticleSystems) do Utils.setEmittingState(grassParticleSystem.particleSystem, false); end; end; if self.balerUnloadingState == ClaasVariant.UNLOADING_OPENING then if not self.balerBaleEjectEnabled and self:getIsActiveForSound() then setSamplePitch(self.balerBaleEject, self.balerBaleEjectPitchOffset); playSample(self.balerBaleEject, 1, self.balerBaleEjectVolume, 0); self.balerBaleEjectEnabled = true; end; if not self.balerDoorEnabled and self:getIsActiveForSound() then setSamplePitch(self.balerDoor, self.balerDoorPitchOffset); playSample(self.balerDoor, 1, self.balerDoorVolume, 0); self.balerDoorEnabled = true; end; local isPlaying = self:getIsAnimationPlaying(self.baleUnloadAnimationName); local animTime = self:getRealAnimationTime(self.baleUnloadAnimationName); if not isPlaying or animTime >= self.baleDropAnimTime then if table.getn(self.bales) > 0 then ClaasVariant.dropBale(self, 1); if self.isServer then self:setFillLevel(0, self.currentFillType); end; end; if not isPlaying then self.balerUnloadingState = ClaasVariant.UNLOADING_OPEN; if self.balerBaleEjectEnabled then stopSample(self.balerBaleEject); self.balerBaleEjectEnabled = false; end; if self.balerDoorEnabled then stopSample(self.balerDoor); self.balerDoorEnabled = false; end; end; end; elseif self.balerUnloadingState == ClaasVariant.UNLOADING_CLOSING then if not self.balerDoorEnabled and self:getIsActiveForSound() then setSamplePitch(self.balerDoor, self.balerDoorPitchOffset); playSample(self.balerDoor, 1, self.balerDoorVolume, 0); self.balerDoorEnabled = true; end; if not self:getIsAnimationPlaying(self.baleCloseAnimationName) then self.balerUnloadingState = ClaasVariant.UNLOADING_CLOSED; end; if not self.balerDoorEnabled and self:getIsActiveForSound() then setSamplePitch(self.balerDoor, self.balerDoorPitchOffset); playSample(self.balerDoor, 1, self.balerDoorVolume, 0); self.balerDoorEnabled = true; end; elseif self.balerUnloadingState == ClaasVariant.UNLOADING_CLOSING then if self.balerDoorEnabled then stopSample(self.balerDoor); self.balerDoorEnabled = false; end; end; if self.isServer then if self.time > self.baleLastPositionTime+100 then for i=1, table.getn(self.bales) do local bale = self.bales[i]; bale.lastX, bale.lastY, bale.lastZ = getWorldTranslation(bale.id); end; self.baleLastPositionTime = self.time; end; end; end; if table.getn(self.bales) > self.lastBaleCount then self.baleCount = self.baleCount + 1; self.lastBaleCount = self.lastBaleCount + 1; elseif table.getn(self.bales) < self.lastBaleCount then self.lastBaleCount = self.lastBaleCount - 1; end; if self.rotationPartFillLevel ~= nil then local x, y, z = getRotation(self.rotationPartFillLevel.node); x = ((self.rotationPartFillLevel.maxRot[1] - self.rotationPartFillLevel.minRot[1]) / self.capacity) * self.fillLevel + self.rotationPartFillLevel.minRot[1]; setRotation(self.rotationPartFillLevel.node, x, y ,z); end; if self.attacherVehicle and g_currentMission.player ~= nil then -- Getting the distance between the player and the implement local nearestDistance = 4.0; --max distance allowed local px, py, pz = getWorldTranslation(self.rootNode); local vx, vy, vz = getWorldTranslation(g_currentMission.player.rootNode); local distance = Utils.vector3Length(px-vx, py-vy, pz-vz); if distance < nearestDistance then self.inrange = true; else self.inrange = false; end; end; end; function ClaasVariant:draw() if self.isClient then if self.isTurnedOn then if self:doCheckSpeedLimit() and self.lastSpeed*2200 > 13 then g_currentMission:addWarning(g_i18n:getText("ClaasVariant_8")); end; end; if self.baleUnloadAnimationName ~= nil then if self.balerUnloadingState == ClaasVariant.UNLOADING_CLOSED then if table.getn(self.bales) > 0 then g_currentMission:addHelpButtonText(g_i18n:getText("baler_unload"), InputBinding.IMPLEMENT_EXTRA2); end; elseif self.balerUnloadingState == ClaasVariant.UNLOADING_OPEN then g_currentMission:addHelpButtonText(g_i18n:getText("baler_unload_stop"), InputBinding.IMPLEMENT_EXTRA2); end; end; if self.animationParts[1].clipStartTime then g_currentMission:addHelpButtonText(g_i18n:getText("ClaasVariant_1"), InputBinding.ClaasVariant_PICKUP); elseif self.animationParts[1].clipEndTime then if self.isTurnedOn then g_currentMission:addHelpButtonText(string.format(g_i18n:getText("turn_off_OBJECT"), self.typeDesc), InputBinding.IMPLEMENT_EXTRA); else g_currentMission:addHelpButtonText(string.format(g_i18n:getText("turn_on_OBJECT"), self.typeDesc), InputBinding.IMPLEMENT_EXTRA); end; g_currentMission:addHelpButtonText(g_i18n:getText("ClaasVariant_2"), InputBinding.ClaasVariant_PICKUP); g_currentMission:addHelpButtonText(g_i18n:getText("ClaasVariant_3"), InputBinding.ClaasVariant_RESET); self.balecounterHud:render(); setTextAlignment(RenderText.ALIGN_RIGHT); setTextBold(true); setTextColor(1, 1, 1, 1); renderText(0.9653, 0.849, 0.032, tostring(self.baleCount)); end; if not self.isLoaded then g_currentMission:addWarning(g_i18n:getText("ClaasVariant_7")); end; if self.OverFlow then g_currentMission:addWarning(g_i18n:getText("ClaasVariant_OVERFLOW")); end; end; end; function ClaasVariant:onAttach(attacherVehicle) self.attacherVehicle = attacherVehicle; self:setAnimationTime(2, self.animationParts[2].animDuration); end; function ClaasVariant:onDetach() if self.deactivateOnDetach then ClaasVariant.onDeactivate(self); else ClaasVariant.onDeactivateSounds(self); end; self:setAnimationTime(2, self.animationParts[2].offSet); end; function ClaasVariant:onLeave() if self.deactivateOnLeave then ClaasVariant.onDeactivate(self); else ClaasVariant.onDeactivateSounds(self); end; end; function ClaasVariant:onDeactivate() for _, part in pairs(self.balerUVScrollParts) do setShaderParameter(part.node, "uvScrollSpeed", 0, 0, 0, 0, false); end; self.wasToFast = false; self.isTurnedOn = false; ClaasVariant.onDeactivateSounds(self) end; function ClaasVariant:onDeactivateSounds() if self.balerSoundEnabled then stopSample(self.balerSound); self.balerSoundEnabled = false; end; if self.balerBaleEjectEnabled then stopSample(self.balerBaleEject); self.balerBaleEjectEnabled = false; end; if self.balerDoorEnabled then stopSample(self.balerDoor); self.balerDoorEnabled = false; end; if self.balerKnotCleaningEnabled then stopSample(self.balerKnotCleaning); self.balerKnotCleaningEnabled = false; end; end; function ClaasVariant:setOverFlow(OverFlow, noEventSend) if OverFlow ~= self.OverFlow then self.OverFlow = OverFlow; if noEventSend == nil or noEventSend == false then if g_server ~= nil then g_server:broadcastEvent(setOverFlowEvent:new(self, OverFlow), nil, nil, self); else g_client:getServerConnection():sendEvent(setOverFlowEvent:new(self, OverFlow)); end; end; end; end; function ClaasVariant:SetDoorPanel(isDoorPanel,noEventSend) SetDoorPanelEvent.sendEvent(self, isDoorPanel, noEventSend); if isDoorPanel then self.DoorPanel = true; else self.DoorPanel = false; end; end; function ClaasVariant:SetHasRopes(isLoaded,noEventSend) self.isLoaded = isLoaded; SetHasRopesEvent.sendEvent(self, self.isLoaded, noEventSend); if self.isLoaded then self.NoRopesBalesCount = 0; setVisibility(self.Ropes, true); if self.NoRopePlaying then stopSample(self.NoRopeSoundId); self.NoRopePlaying = false; end; else setVisibility(self.Ropes, false); self:setIsTurnedOn(false); if self:getIsActiveForSound() then if not self.NoRopePlaying then playSample(self.NoRopeSoundId, 1, 1, 0); self.NoRopePlaying = true; end; end; end; end; function ClaasVariant:setIsTurnedOn(turnedOn, noEventSend) SetTurnedOnEvent.sendEvent(self, turnedOn, noEventSend) self.isTurnedOn = turnedOn; for _, part in pairs(self.balerUVScrollParts) do if self.isTurnedOn then setShaderParameter(part.node, "uvScrollSpeed", part.speed[1], part.speed[2], 0, 0, false); else setShaderParameter(part.node, "uvScrollSpeed", 0, 0, 0, 0, false); end; end; end; function ClaasVariant:setIsUnloadingBale(isUnloadingBale, noEventSend) if self.baleUnloadAnimationName ~= nil then if isUnloadingBale then if self.balerUnloadingState ~= ClaasVariant.UNLOADING_OPENING then BalerSetIsUnloadingBaleEvent.sendEvent(self, isUnloadingBale, noEventSend) self.balerUnloadingState = ClaasVariant.UNLOADING_OPENING; self:playAnimation(self.baleUnloadAnimationName, self.baleUnloadAnimationSpeed, nil, true); end; else if self.balerUnloadingState ~= ClaasVariant.UNLOADING_CLOSING then BalerSetIsUnloadingBaleEvent.sendEvent(self, isUnloadingBale, noEventSend) self.balerUnloadingState = ClaasVariant.UNLOADING_CLOSING; self:playAnimation(self.baleCloseAnimationName, self.baleCloseAnimationSpeed, nil, true); end; end; end; end; function ClaasVariant:getTimeFromLevel(level) -- level = capacity -> time = firstBaleMarker -- level = 0 -> time = 0 if self.firstBaleMarker ~= nil then return level / self.capacity * self.firstBaleMarker; end; return 0; end; function ClaasVariant:moveBales(dt) for i=table.getn(self.bales), 1, -1 do self:moveBale(i, dt); end; end; function ClaasVariant:moveBale(i, dt, noEventSend) local bale = self.bales[i]; ClaasVariant.setBaleTime(self, i, bale.time + dt, noEventSend) end; function ClaasVariant.setBaleTime(self, i, baleTime, noEventSend) if self.baleAnimCurve ~= nil then local bale = self.bales[i]; bale.time = baleTime; local v = self.baleAnimCurve:get(bale.time); setTranslation(bale.id, v[1], v[2], v[3]); setRotation(bale.id, v[4], v[5], v[6]); if bale.time >= 1 then ClaasVariant.dropBale(self, i); end; if self.isServer then if noEventSend == nil or not noEventSend then g_server:broadcastEvent(BalerSetBaleTimeEvent:new(self, i, bale.time), nil, nil, self); end; end; end; end; -- overwrite Fillable.allowFillType function ClaasVariant:allowFillType(fillType) return self.fillTypes[fillType] == true; end; function ClaasVariant:allowPickingUp() if self.baleUnloadAnimationName == nil then return true; end; return table.getn(self.bales) == 0 and self.balerUnloadingState == ClaasVariant.UNLOADING_CLOSED; end; function ClaasVariant.createBale(self, usedFruitType) local baleType = self.baleTypes[usedFruitType]; if baleType == nil then baleType = self.defaultBaleType; end; local baleRoot = Utils.loadSharedI3DFile(baleType.filename, self.baseDirectory); local baleId = getChildAt(baleRoot, 0); setRigidBodyType(baleId, "None"); link(self.baleAnimRoot, baleId); delete(baleRoot); local bale = {}; bale.id = baleId; bale.time = 0; bale.fruitType = usedFruitType; bale.filename = Utils.getFilename(baleType.filename, self.baseDirectory); bale.lastX, bale.lastY, bale.lastZ = getWorldTranslation(bale.id); table.insert(self.bales, bale); local i = table.getn(self.bales); end; function ClaasVariant.dropBale(self, baleIndex) local bale = self.bales[baleIndex]; local deltaRealTime = (self.time - self.baleLastPositionTime)/1000; local x,y,z = getWorldTranslation(bale.id); local rx,ry,rz = getWorldRotation(bale.id); --link(getRootNode(), bale.id); if self.isServer then local baleObject = Bale:new(self.isServer, self.isClient); baleObject:load(bale.filename, x,y,z,rx,ry,rz); baleObject:register(); local lx, ly, lz = bale.lastX, bale.lastY, bale.lastZ; setLinearVelocity(baleObject.nodeId, (x-lx)/deltaRealTime, (y-ly)/deltaRealTime, (z-lz)/deltaRealTime); end; delete(bale.id); table.remove(self.bales, baleIndex); -- increase bale count if variable exists (baling mission) if g_currentMission.baleCount ~= nil then g_currentMission.baleCount = g_currentMission.baleCount + 1; end; if self.NoRopesBalesCount ~= nil then self.NoRopesBalesCount = self.NoRopesBalesCount +1; end; end;