-- -- Trailer; Abladescript für Ladewagen -- -- Source: Realloading.lua by Face; Pöttinger Jumbo SFM-Modding -- -- M@D Author & Support: Heady -- M@D date: 12.12.2010 -- -- > Copyright (C) Heady - www.planet-ls.de < -- -- -- >>> source("dataS/scripts/vehicles/specializations/TrailerToggleTipEvent.lua"); Trailer = {}; Trailer.TIPSTATE_CLOSED = 0; Trailer.TIPSTATE_OPENING = 1; Trailer.TIPSTATE_OPEN = 2; Trailer.TIPSTATE_CLOSING = 3; function Trailer.prerequisitesPresent(specializations) if not SpecializationUtil.hasSpecialization(Fillable, specializations) then print("Warning: Specialization trailer now needs the specialization fillable"); end; return SpecializationUtil.hasSpecialization(Attachable, specializations) and SpecializationUtil.hasSpecialization(Fillable, specializations); end; function Trailer:load(xmlFile) self.toggleTipState = SpecializationUtil.callSpecializationsFunction("toggleTipState"); self.onStartTip = SpecializationUtil.callSpecializationsFunction("onStartTip"); self.onEndTip = SpecializationUtil.callSpecializationsFunction("onEndTip"); --self.allowFillType = Trailer.allowFillType; --self.setFillLevel = Utils.prependedFunction(self.setFillLevel, Trailer.setFillLevel); self.setFillLevel = Utils.overwrittenFunction(self.setFillLevel, Trailer.setFillLevel); self.getCurrentFruitType = Trailer.getCurrentFruitType; --self.setFillLevel = SpecializationUtil.callSpecializationsFunction("setFillLevel"); self.lastFillDelta = 0; self.tipDischargeEndTime = getXMLFloat(xmlFile, "vehicle.tipDischargeEndTime#value"); local tipAnimRootNode = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.tipAnimation#rootNode")); self.tipAnimCharSet = 0; if tipAnimRootNode ~= nil and tipAnimRootNode ~= 0 then self.tipAnimCharSet = getAnimCharacterSet(tipAnimRootNode); if self.tipAnimCharSet ~= 0 then local clip = getAnimClipIndex(self.tipAnimCharSet, getXMLString(xmlFile, "vehicle.tipAnimation#clip")); assignAnimTrackClip(self.tipAnimCharSet, 0, clip); setAnimTrackLoopState(self.tipAnimCharSet, 0, false); self.tipAnimSpeedScale = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.tipAnimation#speedScale"), 1); self.tipAnimDuration = getAnimClipDuration(self.tipAnimCharSet, clip); if self.tipDischargeEndTime == nil then self.tipDischargeEndTime = self.tipAnimDuration*2.0; end; end; end; self.tipState = Trailer.TIPSTATE_CLOSED; self.tipReferencePoint = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.tipReferencePoint#index")); if self.tipReferencePoint == nil then self.tipReferencePoint = self.components[1].node; end; if self.isClient then self.dischargeParticleSystems = {}; --Utils.loadParticleSystem(xmlFile, self.dischargeParticleSystems, "vehicle.dischargeParticleSystem", self.components, false, nil, self.baseDirectory) --Utils.loadParticleSystem(xmlFile, self.chopperParticleSystems, "vehicle.chopperParticleSystem", self.components, false, "$data/vehicles/particleSystems/threshingChopperParticleSystem.i3d", self.baseDirectory); local i = 0; while true do local key = string.format("vehicle.dischargeParticleSystems.dischargeParticleSystem(%d)", i); local t = getXMLString(xmlFile, key .. "#type"); if t == nil then break; end; local desc = FruitUtil.fruitTypes[t]; if desc ~= nil then local fillType = FruitUtil.fruitTypeToFillType[desc.index]; local currentPS = {}; local particleNode = Utils.loadParticleSystem(xmlFile, currentPS, key, self.components, false, "$data/vehicles/particleSystems/trailerDischargeParticleSystem.i3d", self.baseDirectory); self.dischargeParticleSystems[fillType] = currentPS; if self.defaultdischargeParticleSystems == nil then self.defaultdischargeParticleSystems = currentPS; end; end; i = i + 1; end; self.hydraulicSoundEnabled = false; local hydraulicSound = getXMLString(xmlFile, "vehicle.hydraulicSound#file"); if hydraulicSound ~= nil and hydraulicSound ~= "" then hydraulicSound = Utils.getFilename(hydraulicSound, self.baseDirectory); self.hydraulicSound = createSample("hydraulicSound"); loadSample(self.hydraulicSound, hydraulicSound, false); self.hydraulicSoundPitchOffset = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.hydraulicSound#pitchOffset"), 1); self.hydraulicSoundVolume = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.hydraulicSound#pitchMax"), 2.0); end; self.fillSoundEnabled = false; local fillSound = getXMLString(xmlFile, "vehicle.fillSound#file"); if fillSound ~= nil and fillSound ~= "" then fillSound = Utils.getFilename(fillSound, self.baseDirectory); self.fillSound = createSample("fillSound"); loadSample(self.fillSound, fillSound, false); self.fillSoundPitchOffset = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.fillSound#pitchOffset"), 1); self.fillSoundVolume = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.fillSound#pitchMax"), 2.0); end; self.unloadingSoundEnabled = false; local unloadingSound = getXMLString(xmlFile, "vehicle.unloadingSound#file"); if unloadingSound ~= nil and unloadingSound ~= "" then unloadingSound = Utils.getFilename(unloadingSound, self.baseDirectory); self.unloadingSound = createSample("unloadingSound"); loadSample(self.unloadingSound, unloadingSound, false); self.unloadingSoundPitchOffset = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.unloadingSound#pitchOffset"), 1); self.unloadingSoundVolume = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.unloadingSound#Volume"), 2.0); end; end; self.realLoading = {}; self.realLoading.trailerEnd = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.realLoading#trailerEnd")); local x,y,z = getTranslation(self.realLoading.trailerEnd); self.realLoading.maxDistance = math.abs(z); self.realLoading.maxZ = z; self.realLoading.minZ = 0; self.realLoading.unloadingTime = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.realLoading#unloadingTime"), 10)*1000; self.lastFillPlane = nil; self.emptyTime = 1000; self.setTime = true; self.lastFillType = Fillable.FILLTYPE_UNKNOWN; self.lastFillLevel = 0; self.tipDischargeEndTime = self.realLoading.unloadingTime; self.allowTipDischarge = Utils.getNoNil(getXMLBool(xmlFile, "vehicle.allowTipDischarge#value"), true); end; function Trailer:delete() for _, particleSystem in pairs(self.dischargeParticleSystems) do Utils.deleteParticleSystem(particleSystem); end; if self.hydraulicSound ~= nil then delete(self.hydraulicSound); end; if self.fillSound ~= nil then delete(self.fillSound); end; if self.unloadingSound ~= nil then delete(self.unloadingSound); end; end; function Trailer:mouseEvent(posX, posY, isDown, isUp, button) end; function Trailer:keyEvent(unicode, sym, modifier, isDown) end; function Trailer:readStream(streamId, connection) if connection:getIsServer() then --local fillLevel = streamReadFloat32(streamId); --local fillType = streamReadInt8(streamId); local tipState = streamReadUIntN(streamId, 2); --self:setFillLevel(fillLevel, fillType); if tipState ~= Trailer.TIPSTATE_CLOSED and self.tipAnimCharSet ~= 0 then local animTime = streamReadFloat32(streamId); Trailer.setAnimTime(self, animTime); if tipState ~= Trailer.TIPSTATE_CLOSING then self:onStartTip(nil, true); else self:onEndTip(true); end; else Trailer.setAnimTime(self, 0); end; self.tipState = tipState; end; end; function Trailer:writeStream(streamId, connection) if not connection:getIsServer() then --streamWriteFloat32(streamId, self.fillLevel); --streamWriteInt8(streamId, self.fillType); assert(self.tipState >= 0 and self.tipState <= 3); streamWriteUIntN(streamId, self.tipState, 2); if self.tipState ~= Trailer.TIPSTATE_CLOSED and self.tipAnimCharSet ~= 0 then local animTime = getAnimTrackTime(self.tipAnimCharSet, 0); streamWriteFloat32(streamId, animTime); end; end; end; --[[function Trailer:readUpdateStream(streamId, timestamp, connection) end; function Trailer:writeUpdateStream(streamId, connection, dirtyMask) end;]] function Trailer:update(dt) local prozent = self.FillLevel/self.Capacity*100; if prozent > 0 then renderText(0.78, 0.84, 0.02, "Joskin Silospace: " .. tostring(self.FillLevel-1) .. "(" .. tostring(prozent) .. "%)"); end; end; function Trailer:updateTick(dt) --### trailer unloading if self.tipState == Trailer.TIPSTATE_OPENING or self.tipState == Trailer.TIPSTATE_OPEN then self.isReadyForUnloading = false; local isEmpty = false; if self.fillLevel > 0 then if self.tipState == Trailer.TIPSTATE_OPEN then local grainPlane; if self.currentFillType ~= Fillable.FILLTYPE_UNKNOW then if self.currentFillType == Fillable.FILLTYPE_CHAFF then grainPlane = self.fillPlanes["chaff_unload"]; self.currentFillPlane = grainPlane; elseif self.currentFillType == Fillable.FILLTYPE_GRASS then -- grass --grainPlane = self.fillPlanes[Fillable.fillTypeIntToName[self.currentFillType]]; grainPlane = self.fillPlanes["grass_unload"]; self.currentFillPlane = grainPlane; end; local t = self.fillLevel/self.capacity; 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.fillLevel > 0); end; end; if grainPlane == nil then grainPlane = self.defaultFillPlane; end; self.lastFillPlane = grainPlane; self.lastFillType = self.currentFillType; local x,y,z = getTranslation(grainPlane.nodes[1].node); local currentPosition = (self.realLoading.maxDistance - math.abs(z)) / self.realLoading.maxDistance; local xScale,yScale,zScale = getScale(grainPlane.nodes[1].node); if currentPosition <= zScale then self.isReadyForUnloading = true; end; local curX,curY,curZ = getTranslation(grainPlane.nodes[1].node); local newZ = Utils.getMovedLimitedValues({curZ}, {self.realLoading.maxZ}, {self.realLoading.minZ}, 1, self.realLoading.unloadingTime, dt, false); setTranslation(grainPlane.nodes[1].node, curX,curY, newZ[1]); end; else self.isReadyForUnloading = true; isEmpty = true; end; if self.isReadyForUnloading then if self.isServer then if self.currentFillType ~= Fillable.FILLTYPE_UNKNOW and self.currentTipTrigger ~= nil and g_currentMission:getIsTrailerInTipRange(self, self.currentTipTrigger) then local fillType = self.currentFillType; local m = self.capacity/(self.tipDischargeEndTime/self.tipAnimSpeedScale); local curFill = self.fillLevel; self:setFillLevel(self.fillLevel - (m* dt), self.currentFillType); local fillDelta = self.fillLevel - curFill; self.lastFillDelta = fillDelta; if self.currentTipTrigger.isFarmTrigger then -- put load into storage local siloFillType = fillType; if fillType == Fillable.FILLTYPE_DRYGRASS then siloFillType = Fillable.FILLTYPE_GRASS end; g_currentMission:setSiloAmount(siloFillType, g_currentMission:getSiloAmount(siloFillType)- fillDelta); else -- increase money according to price of current fill type local fruitType = FruitUtil.fillTypeToFruitType[fillType]; if fruitType ~= nil then local priceMultiplier = self.currentTipTrigger.priceMultipliers[fruitType]; --local difficultyMultiplier = 2 ^ (3 - g_currentMission.missionStats.difficulty); local difficultyMultiplier = math.max(3 * (3 - g_currentMission.missionStats.difficulty), 1); -- 1 3 6 local money = FruitUtil.fruitIndexToDesc[fruitType].pricePerLiter * priceMultiplier * difficultyMultiplier * -fillDelta; --g_currentMission.missionStats.money = g_currentMission.missionStats.money + money; g_currentMission:addSharedMoney(money); end; end; if fillDelta < 0 then self.currentTipTrigger:updateMoving(-fillDelta); end; else self:onEndTip(); end; end; end; if self.isClient then if self.tipState == Trailer.TIPSTATE_OPENING 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; end; -- check if we still are in opening/open state. Maybe we ended tipping before. if (self.tipState == Trailer.TIPSTATE_OPENING or self.tipState == Trailer.TIPSTATE_OPEN) and self.fillLevel > 0 and self.isReadyForUnloading then if not self.fillSoundEnabled and self.fillSound ~= nil and self:getIsActiveForSound() then playSample(self.fillSound, 0, self.fillSoundVolume, 0); self.fillSoundEnabled = true; end; else if self.fillSoundEnabled then stopSample(self.fillSound); self.fillSoundEnabled = false; end; end; end; if self.tipState == Trailer.TIPSTATE_OPENING then if getAnimTrackTime(self.tipAnimCharSet, 0) > self.tipAnimDuration then self.tipState = Trailer.TIPSTATE_OPEN; if self.hydraulicSoundEnabled then stopSample(self.hydraulicSound); self.hydraulicSoundEnabled = false; end; end; else if isEmpty then self.emptyTime = self.emptyTime - dt; end; if self.emptyTime <= 0 then self.setTime = true; self:onEndTip(); end; end; if self.isClient then if not self.unloadingSoundEnabled and self.tipState == Trailer.TIPSTATE_OPEN and self:getIsActiveForSound() then playSample(self.unloadingSound, 0, self.unloadingSoundVolume, 0); self.unloadingSoundEnabled = true; end; else if self.unloadingSoundEnabled then stopSample(self.unloadingSound); self.unloadingSoundEnabled = false; end; end; elseif self.tipState == Trailer.TIPSTATE_CLOSING then if self.isClient 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); self.hydraulicSoundEnabled = true; end; if self.unloadingSoundEnabled then stopSample(self.unloadingSound); self.unloadingSoundEnabled = false; end; end; if getAnimTrackTime(self.tipAnimCharSet, 0) <= 0.0 then if self.hydraulicSoundEnabled then stopSample(self.hydraulicSound); self.hydraulicSoundEnabled = false; end; disableAnimTrack(self.tipAnimCharSet, 0); self.tipState = Trailer.TIPSTATE_CLOSED; end; end; if self.isClient then if (self.tipState == Trailer.TIPSTATE_OPENING or self.tipState == Trailer.TIPSTATE_OPEN) and self.fillLevel > 0 and self.isReadyForUnloading then Utils.setEmittingState(self.dischargeParticleSystems[self.lastFillType], true); else Utils.setEmittingState(self.dischargeParticleSystems[self.lastFillType], false); end; end; end; function Trailer:draw() if self.isClient then if self.currentFillType ~= Fillable.FILLTYPE_UNKNOWN then local fruitType = FruitUtil.fillTypeToFruitType[self.currentFillType]; if fruitType ~= nil then g_currentMission:setFruitOverlayFruitType(fruitType); end; end; end; end; function Trailer:toggleTipState(currentTipTrigger) if self.tipState == Trailer.TIPSTATE_CLOSED or self.tipState == Trailer.TIPSTATE_CLOSING then self:onStartTip(currentTipTrigger); else self:onEndTip(); end; end; function Trailer:onStartTip(currentTipTrigger, noEventSend) if noEventSend == nil or noEventSend == false then if g_server ~= nil then g_server:broadcastEvent(TrailerToggleTipEvent:new(self, true, currentTipTrigger)); else g_client:getServerConnection():sendEvent(TrailerToggleTipEvent:new(self, true, currentTipTrigger)); end; end; self.currentTipTrigger = currentTipTrigger; if self.tipAnimCharSet ~= 0 then if getAnimTrackTime(self.tipAnimCharSet, 0) < 0.0 then setAnimTrackTime(self.tipAnimCharSet, 0, 0.0); end; setAnimTrackSpeedScale(self.tipAnimCharSet, 0, self.tipAnimSpeedScale); enableAnimTrack(self.tipAnimCharSet, 0); end; self.tipState = Trailer.TIPSTATE_OPENING; end; function Trailer:onEndTip(noEventSend) if noEventSend == nil or noEventSend == false then if g_server ~= nil then g_server:broadcastEvent(TrailerToggleTipEvent:new(self, false), nil, nil, self); else g_client:getServerConnection():sendEvent(TrailerToggleTipEvent:new(self, false), nil, nil, self); end; end; self.currentTipTrigger = nil; if self.tipAnimCharSet ~= 0 then if getAnimTrackTime(self.tipAnimCharSet, 0) > self.tipAnimDuration then setAnimTrackTime(self.tipAnimCharSet, 0, self.tipAnimDuration); end; setAnimTrackSpeedScale(self.tipAnimCharSet, 0, -self.tipAnimSpeedScale); enableAnimTrack(self.tipAnimCharSet, 0); end; if self.fillSoundEnabled then stopSample(self.fillSound); self.fillSoundEnabled = false; end; self.tipState = Trailer.TIPSTATE_CLOSING; if self.lastFillPlane ~= nil then --setTranslation(self.lastFillPlane.nodes[1].node, 0,0,0); end; end; --client --[[function Trailer:allowFillType(fillType, allowEmptying) local allowed = false; if self.fillTypes[fillType] then if self.currentFillType ~= FruitUtil.FRUITTYPE_UNKNOWN then if self.currentFillType ~= fillType then if self.fillLevel / self.capacity <= self.minThreshold then allowed = true; -- fill level is low enough to be overridden if allowEmptying then self.fillLevel = 0; -- empty the trailer end; end; else allowed = true; -- fill type is the same as the trailer's current fill type end; else allowed = true; -- trailer is empty --> FruitUtil.FRUITTYPE_UNKNOWN end; end; return allowed; end;]] function Trailer:getCurrentFruitType() local fruitType = FruitUtil.fillTypeToFruitType[self.currentFillType]; if fruitType == nil then fruitType = FruitUtil.FRUITTYPE_UNKNOWN; end; return fruitType; end; function Trailer:setFillLevel(superFunc, fillLevel, fillType, force) if (force == nil or force == false) and not self:allowFillType(fillType, false) then return end; if self.fillLevel <= 0 then if self.setTime then self.emptyTime = 1000; self.setTime = false; end; end; self.currentFillType = fillType; self.fillLevel = fillLevel; if self.fillLevel > self.capacity then self.fillLevel = self.capacity; end; if self.fillLevel < 0 then self.fillLevel = 0; self.currentFillType = Fillable.FILLTYPE_UNKNOWN; end; if self.isClient then if self.currentFillPlane ~= nil then for _, node in ipairs(self.currentFillPlane.nodes) do setVisibility(node.node, false); end; self.currentFillPlane = nil; end; if self.fillPlanes ~= nil and self.defaultFillPlane ~= nil and fillType ~= Fillable.FILLTYPE_UNKNOWN then local fillTypeName = Fillable.fillTypeIntToName[fillType]; if (self.tipState == Trailer.TIPSTATE_OPENING or self.tipState == Trailer.TIPSTATE_OPEN) then if (self.currentFillType == Fillable.FILLTYPE_CHAFF) then fillTypeName = "chaff_unload"; elseif self.currentFillType == Fillable.FILLTYPE_GRASS then fillTypeName = "grass_unload"; end; end; local fillPlane = self.fillPlanes[fillTypeName]; if fillPlane == nil then fillPlane = self.defaultFillPlane; end; local t = self.fillLevel/self.capacity; for _, node in ipairs(fillPlane.nodes) do local x,y,z, rx,ry,rz, sx,sy,sz = node.animCurve:get(t); --if self.tipState ~= Trailer.TIPSTATE_OPEN then if self.fillLevel > self.lastFillLevel then setTranslation(node.node, x, y, z); end; setRotation(node.node, rx, ry, rz); setScale(node.node, sx, sy, sz); setVisibility(node.node, self.fillLevel > 0); end; self.currentFillPlane = fillPlane; end; self.lastFillLevel = self.fillLevel; end; function Trailer:onAttach(attacherVehicle) end; function Trailer:onDetach() if self.deactivateOnDetach then Trailer.onDeactivate(self); else Trailer.onDeactivateSounds(self); end; end; function Trailer:onLeave() if self.deactivateOnLeave then Trailer.onDeactivate(self); else Trailer.onDeactivateSounds(self); end; end; function Trailer:onDeactivate() Trailer.onDeactivateSounds(self); end; function Trailer:onDeactivateSounds() if self.fillSoundEnabled then stopSample(self.fillSound); self.fillSoundEnabled = false; end; if self.hydraulicSoundEnabled then stopSample(self.hydraulicSound); self.hydraulicSoundEnabled = false; end; end; function Trailer.setAnimTime(self, animTime) if self.tipAnimCharSet ~= 0 then enableAnimTrack(self.tipAnimCharSet, 0); setAnimTrackTime(self.tipAnimCharSet, 0, animTime, true); disableAnimTrack(self.tipAnimCharSet, 0); end; end;