-- -- original by sven18koehler -- edited by fruktor: renamed from "AgrolinerTUW20" to "Overcharge" -- removed plane, added optional control through VariableTip, disabled discharge@tiptrigger -- Overcharge = {}; function Overcharge.prerequisitesPresent(specializations) return true; end; function Overcharge:load(xmlFile) self.setSoundState = SpecializationUtil.callSpecializationsFunction("setSoundState"); self.setUnloadingState = SpecializationUtil.callSpecializationsFunction("setUnloadingState"); self.setVehicleRpmUp = SpecializationUtil.callSpecializationsFunction("setVehicleRpmUp"); self.trailerRaycastCallback = Overcharge.trailerRaycastCallback; local hydraulicsCount = Utils.getNoNil(getXMLInt(xmlFile, "vehicle.hydraulics#count"), 0); self.hydraulics = {}; for i=1, hydraulicsCount do local hydraulicName = string.format("vehicle.hydraulics.hydraulic%d", i); self.hydraulics[i] = {}; self.hydraulics[i].node = Utils.indexToObject(self.components, getXMLString(xmlFile, hydraulicName .. "#index")); self.hydraulics[i].punch = Utils.indexToObject(self.components, getXMLString(xmlFile, hydraulicName .. "#punch")); self.hydraulics[i].translationPunch = Utils.indexToObject(self.components, getXMLString(xmlFile, hydraulicName .. "#punchFixpoint")); self.hydraulics[i].fixPoint = Utils.indexToObject(self.components, getXMLString(xmlFile, hydraulicName .. "#fixpoint")); if self.hydraulics[i].punch ~= nil and self.hydraulics[i].translationPunch ~= nil then local ax, ay, az = getWorldTranslation(self.hydraulics[i].punch); local bx, by, bz = getWorldTranslation(self.hydraulics[i].translationPunch); self.hydraulics[i].punchDistance = Utils.vector3Length(ax-bx, ay-by, az-bz); end; self.hydraulics[i].doScale = Utils.getNoNil(getXMLBool(xmlFile, hydraulicName .. "#doScale"), false); end; self.unloadingSounds = {}; self.unloadingSounds.startSound = {}; local startSound = getXMLString(xmlFile, "vehicle.unloadingSounds#startFile"); if startSound ~= nil and startSound ~= "" then startSound = Utils.getFilename(startSound, self.baseDirectory); self.unloadingSounds.startSound.file = createSample("startSound"); self.unloadingSounds.startSound.enabled = false; self.unloadingSounds.startSound.duration = getSampleDuration(self.unloadingSounds.startSound.file); loadSample(self.unloadingSounds.startSound.file, startSound, false); end; self.unloadingSounds.loopSound = {}; local loopSound = getXMLString(xmlFile, "vehicle.unloadingSounds#loopFile"); if loopSound ~= nil and loopSound ~= "" then loopSound = Utils.getFilename(loopSound, self.baseDirectory); self.unloadingSounds.loopSound.file = createSample("loopSound"); self.unloadingSounds.loopSound.enabled = false; loadSample(self.unloadingSounds.loopSound.file, loopSound, false); end; self.coilSound = {}; local coilSound = getXMLString(xmlFile, "vehicle.coilSound#file"); if coilSound ~= nil and coilSound ~= "" then coilSound = Utils.getFilename(coilSound, self.baseDirectory); self.coilSound.file = createSample("coilSound"); self.coilSound.enabled = false; self.coilSound.duration = getSampleDuration(self.coilSound.file); loadSample(self.coilSound.file, coilSound, false); self.coilSound.pitchOffset = getXMLFloat(xmlFile, "vehicle.coilSound#pitchOffset"); self.coilSound.volume = getXMLFloat(xmlFile, "vehicle.coilSound#volume"); end; self.playLoopSoundTime = 0; self.triggerNode = {}; self.triggerNode.index = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.unloadingTrigger#node")); self.triggerNode.distance = Utils.getNoNil(getXMLInt(xmlFile, "vehicle.unloadingTrigger#maxDistance"), 1); self.unloadingTipTrigger = nil; self.pipe = {}; self.pipe.node = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.pipe#index")); self.pipe.distance = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.pipe#raycastDistance"), 7); self.pipe.noFold = false; self.pipe.animOpenIdx = getXMLInt(xmlFile, "vehicle.pipe#animOpenIdx"); if self.pipe.animOpenIdx == nil then self.pipe.noFold = true; end; self.pipe.noMove = false; self.pipe.animMoveIdx = getXMLInt(xmlFile, "vehicle.pipe#animMoveIdx"); if self.pipe.animMoveIdx == nil then self.pipe.noMove = true; end; self.pipe.out = false; self.unloadingCapacity = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.unloadingCapacity"), 100); self.saveMinRpm = 0; self.planeOpen = false; self.allowFillFromAir = false; self.isUnloading = false; self.isLoading = true; self.overchargeDone = true; local path = Utils.getFilename("overlay.png", self.baseDirectory); self.unloadingOverlay = Overlay:new("hudPDAControl", path, g_currentMission.fruitSymbolX, g_currentMission.fruitSymbolY-0.11, g_currentMission.fruitSymbolSize, g_currentMission.fruitSymbolSize * (4 / 3)); end; function Overcharge:delete() if self.unloadingSounds.loopSound.file ~= nil then delete(self.unloadingSounds.loopSound.file); end; if self.unloadingSounds.startSound.file ~= nil then delete(self.unloadingSounds.startSound.file); end; end; function Overcharge:readStream(streamId, connection) self.isLoading = true; local isUnloading = streamReadBool(streamId); self:setUnloadingState(isUnloading, true); end; function Overcharge:writeStream(streamId, connection) streamWriteBool(streamId, self.isUnloading); end; function Overcharge:mouseEvent(posX, posY, isDown, isUp, button) end; function Overcharge:keyEvent(unicode, sym, modifier, isDown) end; function Overcharge:update(dt) if self.varTip ~= nil then if self.varTip.enableOvercharge == false then return; end; end; if self:getIsActiveForInput() then if ((self.varTip ~= nil)and(self.varTip.trailerNr == self.varTip.activeTrailerIdx)) or self.varTip == nil then if InputBinding.hasEvent(InputBinding.IMPLEMENT_EXTRA) and self.pipe.noFold == false then if self.pipe.out then self:setAnimationTime(1, self.animationParts[self.pipe.animOpenIdx].offSet, false); self:setUnloadingState(false); else self:setAnimationTime(1, self.animationParts[self.pipe.animOpenIdx].animDuration, false); end; end; if InputBinding.hasEvent(InputBinding.IMPLEMENT_EXTRA2) then self:setUnloadingState(not self.isUnloading); end; end; end; if ((self.varTip ~= nil)and(self.varTip.trailerNr == self.varTip.activeTrailerIdx)) or self.varTip == nil then self.overchargeDone = false; --[[ if self.animationParts[1].inputDone and self.animationParts[1].clipEndTime then self.pipe.out = true; if InputBinding.isPressed(InputBinding.OVERCHARGE_DOWN) and self:getIsActiveForInput() then self:setAnimationTime(2, self.animationParts[2].currentPosition+(self.animationParts[2].offSet+dt), false); elseif InputBinding.isPressed(InputBinding.OVERCHARGE_UP) and self:getIsActiveForInput() then self:setAnimationTime(2, self.animationParts[2].currentPosition-(self.animationParts[2].offSet+dt), false); end; elseif not self.animationParts[1].clipEndTime then self:setAnimationTime(2, self.animationParts[2].startPosition, false); self.pipe.out = false; end; if self.animationParts[1].clipStartTime and self.animationParts[2].clipStartTime then self.overchargeDone = true; end;]]-- if self.pipe.noFold == true then self.pipe.out = true; if self.pipe.noMove == false then if InputBinding.isPressed(InputBinding.OVERCHARGE_DOWN) and self:getIsActiveForInput() then self:setAnimationTime(2, self.animationParts[self.pipe.animMoveIdx].currentPosition+(self.animationParts[self.pipe.animMoveIdx].offSet+dt), false); elseif InputBinding.isPressed(InputBinding.OVERCHARGE_UP) and self:getIsActiveForInput() then self:setAnimationTime(2, self.animationParts[self.pipe.animMoveIdx].currentPosition-(self.animationParts[self.pipe.animMoveIdx].offSet+dt), false); end; end; else if self.animationParts[self.pipe.animOpenIdx].inputDone and self.animationParts[self.pipe.animOpenIdx].clipEndTime then self.pipe.out = true; if self.pipe.noMove == false then if InputBinding.isPressed(InputBinding.OVERCHARGE_DOWN) and self:getIsActiveForInput() then self:setAnimationTime(2, self.animationParts[self.pipe.animMoveIdx].currentPosition+(self.animationParts[self.pipe.animMoveIdx].offSet+dt), false); elseif InputBinding.isPressed(InputBinding.OVERCHARGE_UP) and self:getIsActiveForInput() then self:setAnimationTime(2, self.animationParts[self.pipe.animMoveIdx].currentPosition-(self.animationParts[self.pipe.animMoveIdx].offSet+dt), false); end; end; elseif not self.animationParts[self.pipe.animOpenIdx].clipEndTime then self.pipe.out = false; if self.pipe.noMove == false then self:setAnimationTime(2, self.animationParts[self.pipe.animMoveIdx].startPosition, false); end; end; end; if self.pipe.noMove == true and self.pipe.noFold == false then if self.animationParts[self.pipe.animOpenIdx].clipStartTime then self.overchargeDone = true; end; elseif self.pipe.noMove == false and self.pipe.noFold == true then if self.animationParts[self.pipe.animMoveIdx].clipStartTime then self.overchargeDone = true; end; else if self.animationParts[self.pipe.animOpenIdx].clipStartTime and self.animationParts[self.pipe.animMoveIdx].clipStartTime then self.overchargeDone = true; end; end; end; --self.planeOpen = self.animationParts[3].clipEndTime and self.animationParts[3].inputDone; self.planeOpen = true; self.allowFillFromAir = self.planeOpen; local hydraulicSoundEnabled = false; local coilSoundEnabled = false; if self.pipe.noFold == false then if not self.animationParts[self.pipe.animOpenIdx].inputDone then hydraulicSoundEnabled = true; end; end; if self.pipe.noMove == false then if not self.animationParts[self.pipe.animMoveIdx].inputDone then if self.coilSound.file == nil then hydraulicSoundEnabled = true; else coilSoundEnabled = true; end; end; end; if hydraulicSoundEnabled then if not self.hydraulicSoundEnabled and self.hydraulicSound ~= nil and self:getIsActiveForSound() then playSample(self.hydraulicSound, 0, self.hydraulicSoundVolume, 0); setSamplePitch(self.hydraulicSound, self.hydraulicSoundPitchOffset-0.4); self.hydraulicSoundEnabled = true; end; else if self.hydraulicSoundEnabled then stopSample(self.hydraulicSound); self.hydraulicSoundEnabled = false; end; end; if coilSoundEnabled then if not self.coilSoundEnabled and self.coilSound ~= nil and self:getIsActiveForSound() then playSample(self.coilSound.file, 0, self.coilSound.volume, 0); setSamplePitch(self.coilSound.file, self.coilSound.pitchOffset); self.coilSoundEnabled = true; end; else if self.coilSoundEnabled then stopSample(self.coilSound.file); self.coilSoundEnabled = false; end; end; end; function Overcharge:updateTick(dt) if self.varTip ~= nil then if self.varTip.enableOvercharge == false then return; end; end; for i=1, table.getn(self.hydraulics) do local ax, ay, az = getWorldTranslation(self.hydraulics[i].node); local bx, by, bz = getWorldTranslation(self.hydraulics[i].fixPoint); local x, y, z = worldDirectionToLocal(getParent(self.hydraulics[i].node), bx-ax, by-ay, bz-az); setDirection(self.hydraulics[i].node, x, y, z, 0, 1, 0); local distance = Utils.vector3Length(ax-bx, ay-by, az-bz); if self.hydraulics[i].punch ~= nil and self.hydraulics[i].punchDistance ~= nil and self.hydraulics[i].translationPunch then if self.hydraulics[i].doScale then local xScale, yScale, zScale = getScale(self.hydraulics[i].punch); local newScale = xScale * (distance / self.hydraulics[i].punchDistance); setScale(self.hydraulics[i].punch, 1, 1, newScale); else setTranslation(self.hydraulics[i].punch, 0, 0, distance-self.hydraulics[i].punchDistance); setRotation(self.hydraulics[i].punch, 0, 0, Utils.degToRad(90)); end; end; end; self.trailerFoundId = 0; self.unloadingTipTrigger = nil; if self.fillLevel > 0 and self.pipe.out then for k, tipTrigger in pairs(g_currentMission.tipTriggers) do local trailerX, trailerY, trailerZ = getWorldTranslation(self.triggerNode.index); local triggerX, triggerY, triggerZ = getWorldTranslation(tipTrigger.triggerId); local distance = Utils.vector3Length(trailerX-triggerX, trailerY-triggerY, trailerZ-triggerZ); if distance < self.triggerNode.distance then self.unloadingTipTrigger = tipTrigger; end; end; if self.unloadingTipTrigger == nil then local x, y, z = getWorldTranslation(self.pipe.node); local dx, dy, dz = localDirectionToWorld(self.pipe.node, 0, -0.5, 0); raycastAll(x, y, z, dx, dy, dz, "trailerRaycastCallback", self.pipe.distance, self); end; end; if self:getIsActive() then local renderOverlay = false; local deltaLevel = 0; if self.trailerFoundId ~= nil and self.trailerFoundId ~= 0 then --print("trailer found!"); local trailer = g_currentMission.nodeToVehicle[self.trailerFoundId]; if trailer ~= nil and trailer ~= self and trailer.allowFillFromAir and trailer.capacity ~= trailer.fillLevel and (trailer:allowFillType(Fillable.fillTypeNameToInt["seeds"], true) or trailer:allowFillType(self.currentFillType, true) )then -- Automatischer Überlademodus if self.isUnloading == false and self.attacherVehicle ~= nil and self.attacherVehicle.drive and self.attacherVehicle.lastSpeedReal == 0 then self:setUnloadingState(true); end; if self.isUnloading then deltaLevel = math.min(self.unloadingCapacity*dt/1000.0, trailer.capacity-trailer.fillLevel); if trailer:allowFillType(Fillable.fillTypeNameToInt["seeds"], true) then trailer:setFillLevel(trailer.fillLevel+deltaLevel, Fillable.fillTypeNameToInt["seeds"]); else trailer:setFillLevel(trailer.fillLevel+deltaLevel, self.currentFillType); end; else renderOverlay = true; end; elseif self.isUnloading then self:setUnloadingState(false); end; --## elseif self.unloadingTipTrigger ~= nil then local tipTrigger = self.unloadingTipTrigger; local fruitType = FruitUtil.fillTypeToFruitType[self.currentFillType]; local fruitAccept = tipTrigger.acceptedFruitTypes[fruitType]; if tipTrigger.isFarmTrigger and fruitAccept then if self.isUnloading then deltaLevel = self.unloadingCapacity*dt/1000.0; g_currentMission.missionStats.farmSiloAmounts[self.currentFillType] = g_currentMission.missionStats.farmSiloAmounts[self.currentFillType] + deltaLevel; else renderOverlay = true; end; elseif fruitAccept then if self.isUnloading then deltaLevel = self.unloadingCapacity*dt/1000.0; local priceMultiplier = tipTrigger.priceMultipliers[fruitType]; local difficultyMultiplier = math.max(3 * (3 - g_currentMission.missionStats.difficulty), 1); local money = FruitUtil.fruitIndexToDesc[fruitType].pricePerLiter * priceMultiplier * difficultyMultiplier * deltaLevel; g_currentMission:addSharedMoney(money); else renderOverlay = true; end; else if FruitUtil.fruitIndexToDesc[fruitType].name ~= nil then g_currentMission:addWarning(g_i18n:getText(FruitUtil.fruitIndexToDesc[fruitType].name) .. g_i18n:getText("notAcceptedHere"), 0.018, 0.033); end; end; if deltaLevel > 0 then tipTrigger:updateMoving(deltaLevel); end; --## elseif self.isUnloading then self:setUnloadingState(false); end; self.fillLevel = self.fillLevel-deltaLevel; if deltaLevel == 0 and self.isUnloading then self:setUnloadingState(false); end; if self.fillLevel <= 0.0 then self.fillLevel = 0.0; if self.isUnloading then self:setUnloadingState(false); end; Utils.setEmittingState(self.dischargeParticleSystems[self.currentFillType], self.isUnloading); self.currentFillType = Fillable.FRUITTYPE_UNKNOWN; end; self:setFillLevel(self.fillLevel, self.currentFillType); self.renderOverlay = renderOverlay; Utils.setEmittingState(self.dischargeParticleSystems[self.currentFillType], self.isUnloading); self:setSoundState(self.isUnloading); if self.attacherVehicle ~= nil then if self.attacherVehicle.motor ~= nil then self:setVehicleRpmUp(dt, self.isUnloading); end; end; end; end; function Overcharge:trailerRaycastCallback(transformId, x, y, z, distance) local vehicle = g_currentMission.nodeToVehicle[transformId]; if vehicle ~= nil then --print("vehicle="..tostring(vehicle).." transformId="..tostring(transformId).." dist="..distance); if vehicle.exactFillRootNode == transformId then self.trailerFoundId = transformId; return false; end; end; return true; end; function Overcharge:setUnloadingState(state, noEventSend) SetUnloadingEvent.sendEvent(self, state, noEventSend); self.isUnloading = state; end; function Overcharge:setSoundState(value) if value == true then if not self.unloadingSounds.startSound.enabled and self:getIsActiveForSound() and self.unloadingSounds.startSound.file ~= nil then playSample(self.unloadingSounds.startSound.file, 1, 0.35, 0); setSamplePitch(self.unloadingSounds.startSound.file ,1); self.unloadingSounds.startSound.enabled = true; self.playLoopSoundTime = self.time + self.unloadingSounds.startSound.duration; end; if self.playLoopSoundTime <= self.time and not self.unloadingSounds.loopSound.enabled and self:getIsActiveForSound() and self.unloadingSounds.loopSound.file ~= nil then playSample(self.unloadingSounds.loopSound.file, 0, 0.35, 0); setSamplePitch(self.unloadingSounds.loopSound.file,1); self.unloadingSounds.loopSound.enabled = true; end; else if self.unloadingSounds.loopSound.enabled and self.unloadingSounds.loopSound.file ~= nil then stopSample(self.unloadingSounds.loopSound.file); self.unloadingSounds.loopSound.enabled = false; end; if self.unloadingSounds.startSound.enabled and self.unloadingSounds.startSound.file ~= nil then stopSample(self.unloadingSounds.startSound.file); self.unloadingSounds.startSound.enabled = false; end; end; end; function Overcharge:setVehicleRpmUp(dt, isActive) if self.attacherVehicle ~= nil and self.saveMinRpm ~= 0 and self.attacherVehicle.motor ~= nil then if dt ~= nil then if isActive == true then self.attacherVehicle.motor.minRpm = math.max(self.attacherVehicle.motor.minRpm-(dt*2), -1200); else self.attacherVehicle.motor.minRpm = math.min(self.attacherVehicle.motor.minRpm+(dt*5), self.saveMinRpm); end; else self.attacherVehicle.motor.minRpm = self.saveMinRpm; end; if self.attacherVehicle.isMotorStarted then local fuelUsed = 0.0000011*math.abs(self.attacherVehicle.motor.minRpm); self.attacherVehicle:setFuelFillLevel(self.attacherVehicle.fuelFillLevel-fuelUsed); g_currentMission.missionStats.fuelUsageTotal = g_currentMission.missionStats.fuelUsageTotal + fuelUsed; g_currentMission.missionStats.fuelUsageSession = g_currentMission.missionStats.fuelUsageSession + fuelUsed; end; end; end; function Overcharge:draw() if self.varTip ~= nil then if self.varTip.enableOvercharge == false then return; end; end; if ((self.varTip ~= nil)and(self.varTip.trailerNr == self.varTip.activeTrailerIdx)) or self.varTip == nil then if self.pipe.out then if self.pipe.noFold == false then g_currentMission:addHelpButtonText(g_i18n:getText("OVERCHARGE_1"), InputBinding.IMPLEMENT_EXTRA); if self.pipe.noMove == false then if self.animationParts[self.pipe.animOpenIdx].inputDone then g_currentMission:addExtraPrintText(string.format(""..InputBinding.getKeyNamesOfDigitalAction(InputBinding.OVERCHARGE_UP) .. " / " .. InputBinding.getKeyNamesOfDigitalAction(InputBinding.OVERCHARGE_DOWN)..": "..g_i18n:getText("OVERCHARGE_2").."")); if self.isUnloading then g_currentMission:addHelpButtonText(g_i18n:getText("OVERCHARGE_4"), InputBinding.IMPLEMENT_EXTRA2); elseif self.renderOverlay then self.unloadingOverlay:render(); g_currentMission:addHelpButtonText(g_i18n:getText("OVERCHARGE_3"), InputBinding.IMPLEMENT_EXTRA2); end; end; else if self.isUnloading then g_currentMission:addHelpButtonText(g_i18n:getText("OVERCHARGE_4"), InputBinding.IMPLEMENT_EXTRA2); elseif self.renderOverlay then self.unloadingOverlay:render(); g_currentMission:addHelpButtonText(g_i18n:getText("OVERCHARGE_3"), InputBinding.IMPLEMENT_EXTRA2); end; end; else if self.pipe.noMove == false then g_currentMission:addExtraPrintText(string.format(""..InputBinding.getKeyNamesOfDigitalAction(InputBinding.OVERCHARGE_UP) .. " / " .. InputBinding.getKeyNamesOfDigitalAction(InputBinding.OVERCHARGE_DOWN)..": "..g_i18n:getText("OVERCHARGE_2").."")); if self.isUnloading then g_currentMission:addHelpButtonText(g_i18n:getText("OVERCHARGE_4"), InputBinding.IMPLEMENT_EXTRA2); elseif self.renderOverlay then self.unloadingOverlay:render(); g_currentMission:addHelpButtonText(g_i18n:getText("OVERCHARGE_3"), InputBinding.IMPLEMENT_EXTRA2); end; else if self.isUnloading then g_currentMission:addHelpButtonText(g_i18n:getText("OVERCHARGE_4"), InputBinding.IMPLEMENT_EXTRA2); elseif self.renderOverlay then self.unloadingOverlay:render(); g_currentMission:addHelpButtonText(g_i18n:getText("OVERCHARGE_3"), InputBinding.IMPLEMENT_EXTRA2); end; end; end; else if self.pipe.noFold == false then g_currentMission:addHelpButtonText(g_i18n:getText("OVERCHARGE_5"), InputBinding.IMPLEMENT_EXTRA); end; end; end; end; function Overcharge:onAttach(attacherVehicle) self.attacherVehicle = attacherVehicle; if self.attacherVehicleCopy == nil then self.attacherVehicleCopy = self.attacherVehicle; end; if self.attacherVehicle.motor ~= nil then self.saveMinRpm = self.attacherVehicle.motor.minRpm; else if self.attacherVehicle.saveMinRpm ~= nil then self.saveMinRpm = self.attacherVehicle.saveMinRpm; else self.attacherVehicle.saveMinRpm = 100; end; end; end; function Overcharge:onDetach() if self.deactivateOnDetach then self:onDeactivate(self); else self:onDeactivateSounds(self); end; for k, steerable in pairs(g_currentMission.steerables) do if self.attacherVehicleCopy == steerable then steerable.motor.minRpm = self.saveMinRpm; self.attacherVehicleCopy = nil; end; end; end; function Overcharge:onLeave() if self.deactivateOnLeave then self:onDeactivate(self); else self:onDeactivateSounds(self); end; end; function Overcharge:onDeactivate() self:setUnloadingState(false); self.unloadingTipTrigger = nil; self:setVehicleRpmUp(nil, false); for k, particle in pairs(self.dischargeParticleSystems) do Utils.setEmittingState(particle, false); end; self:onDeactivateSounds(self); end; function Overcharge:onDeactivateSounds() self:setSoundState(false); end;