-- -- Trailer -- This is the specialization for trailers -- Note: tipState is not perfectly synced. It is only as accurate as the animation, which is only triggered by start and end events -- lastFillDelta is only valid on the server -- -- @author Stefan Geiger -- @date 30/11/08 -- -- Copyright (C) GIANTS Software GmbH, Confidential, All Rights Reserved. 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.getCurrentFruitType = Trailer.getCurrentFruitType; --self.setFillLevel = SpecializationUtil.callSpecializationsFunction("setFillLevel"); self.setCreateHeapTrigger = SpecializationUtil.callSpecializationsFunction("setCreateHeapTrigger"); self.lastFillDelta = 0; self.tipDischargeEndTime = getXMLFloat(xmlFile, "vehicle.tipDischargeEndTime#value"); local tipAnimRootNode = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.tipAnimation#rootNode")); self.tipAnimRootNode = tipAnimRootNode; 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; end; self.allowTipDischarge = Utils.getNoNil(getXMLBool(xmlFile, "vehicle.allowTipDischarge#value"), true); -- HeapTipTrigger self.create_HeapTipTrigger = false; 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; 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) -- HeapTipTrigger self.printStrg = false; if self:getIsActiveForInput() then self.printStrg = false; if g_currentMission.trailerInTipRange == nil and ( self.tipState == Trailer.TIPSTATE_CLOSED or self.tipState == Trailer.TIPSTATE_CLOSING ) and not self.create_HeapTipTrigger then if Input.isKeyPressed(Input.KEY_lctrl) and InputBinding.hasEvent(InputBinding.ACTIVATE_OBJECT) then self.create_HeapTipTrigger = true; --[[ if g_server == nil then g_client:getServerConnection():sendEvent(HeapTipTriggerMPEvent:new(self)); end; ]]-- if g_server ~= nil then g_server:broadcastEvent(HeapTipTriggerMPEvent:new(self), nil, nil, vehicle); else g_client:getServerConnection():sendEvent(HeapTipTriggerMPEvent:new(self)); end; end; if Input.isKeyPressed(Input.KEY_lctrl) then self.printStrg = true; else self.printStrg = false; end; end; end; if self.create_HeapTipTrigger then if self.isServer then local x,y,z = getWorldTranslation(self.tipReferencePoint); local xr,yr,zr = getWorldRotation(self.tipReferencePoint); g_currentMission:loadVehicle(getUserProfileAppPath().."mods/HeapTipTrigger/heapTipTrigger.xml", x, 0.5, z, yr); end; self.create_HeapTipTrigger = false; end; end; function Trailer:updateTick(dt) if self.tipState == Trailer.TIPSTATE_OPENING or self.tipState == Trailer.TIPSTATE_OPEN 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 x,y,z = getRotation(self.tipAnimRootNode); local m = ((x*x)+0.03)*(self.capacity/self.tipDischargeEndTime); --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; 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 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 getAnimTrackTime(self.tipAnimCharSet, 0) > self.tipDischargeEndTime and self.fillLevel <= 0 then self:onEndTip(true); 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; 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 then Utils.setEmittingState(self.dischargeParticleSystems[self.currentFillType], true); else Utils.setEmittingState(self.dischargeParticleSystems[self.currentFillType], 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; if self.printStrg then g_currentMission:addExtraPrintText("HeapTipTrigger erstellen: STRG+"..InputBinding.getKeyNamesOfDigitalAction(InputBinding.ACTIVATE_OBJECT)); 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; 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(fillLevel, fillType, force) if self.isClient then -- disable particle system if we switch the type or if the trailer is empty if self.currentFillType ~= fillType or fillLevel <= 0 then Utils.setEmittingState(self.dischargeParticleSystems[self.currentFillType], false); end; end; 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; function Trailer:setCreateHeapTrigger(state) self.create_HeapTipTrigger = state; end; -- HeapTipTrigger HeapTipTriggerMPEvent = {}; HeapTipTriggerMPEvent_mt = Class(HeapTipTriggerMPEvent, Event); InitEventClass(HeapTipTriggerMPEvent, "HeapTipTriggerMPEvent"); function HeapTipTriggerMPEvent:emptyNew() local self = Event:new(HeapTipTriggerMPEvent_mt); self.className="HeapTipTriggerMPEvent"; return self; end; function HeapTipTriggerMPEvent:new(object) local self = HeapTipTriggerMPEvent:emptyNew() self.object = object; return self; end; function HeapTipTriggerMPEvent:readStream(streamId, connection) local id = streamReadInt32(streamId) self.object = networkGetObject(id); self.object.create_HeapTipTrigger = streamReadBool(streamId); self.object:setCreateHeapTrigger(self.object.create_HeapTipTrigger); end; function HeapTipTriggerMPEvent:writeStream(streamId, connection) streamWriteInt32(streamId, networkGetObjectId(self.object)); streamWriteBool(streamId, self.object.create_HeapTipTrigger); end;