399 | function Shovel:getCanShovelAtPosition(shovelNode) |
400 | if shovelNode == nil then |
401 | return false |
402 | end |
403 | |
404 | local sx,_,sz = localToWorld(shovelNode.node, -shovelNode.width, 0, 0) |
405 | local activeFarm = self:getActiveFarm() |
406 | |
407 | local ex,_,ez = localToWorld(shovelNode.node, shovelNode.width, 0, 0) |
408 | local isStartOwned = g_currentMission.accessHandler:canFarmAccessLand(activeFarm, sx, sz) |
409 | if not isStartOwned then |
410 | return false |
411 | end |
412 | |
413 | return g_currentMission.accessHandler:canFarmAccessLand(activeFarm, ex, ez) |
414 | end |
246 | function Shovel:getShovelNodeIsActive(shovelNode) |
247 | local isActive = true |
248 | |
249 | if shovelNode.needsMovement then |
250 | local x,y,z = getWorldTranslation(shovelNode.node) |
251 | local _,_,dz = worldToLocal(shovelNode.node, shovelNode.lastPosition[1], shovelNode.lastPosition[2], shovelNode.lastPosition[3]) |
252 | isActive = isActive and dz < 0 |
253 | |
254 | shovelNode.lastPosition[1] = x |
255 | shovelNode.lastPosition[2] = y |
256 | shovelNode.lastPosition[3] = z |
257 | end |
258 | |
259 | if shovelNode.maxPickupAngle ~= nil then |
260 | local _,dy,_ = localDirectionToWorld(shovelNode.node, 0, 0, 1) |
261 | local angle = math.acos(dy) |
262 | if angle > shovelNode.maxPickupAngle then |
263 | return false |
264 | end |
265 | end |
266 | |
267 | if shovelNode.needsAttacherVehicle then |
268 | if self.getAttacherVehicle ~= nil and self:getAttacherVehicle() == nil then |
269 | return false |
270 | end |
271 | end |
272 | |
273 | return isActive |
274 | end |
313 | function Shovel:handleDischargeRaycast(superFunc, dischargeNode, hitObject, hitShape, hitDistance, hitFillUnitIndex, hitTerrain) |
314 | if self.spec_shovel.shovelDischargeInfo.node ~= nil then |
315 | if hitObject ~= nil then |
316 | local fillType = self:getDischargeFillType(dischargeNode) |
317 | local allowFillType = hitObject:getFillUnitAllowsFillType(hitFillUnitIndex, fillType) |
318 | if allowFillType and hitObject:getFillUnitFreeCapacity(hitFillUnitIndex, fillType, self:getOwnerFarmId()) > 0 then |
319 | self:setDischargeState(Dischargeable.DISCHARGE_STATE_OBJECT, true) |
320 | else |
321 | if self:getDischargeState() == Dischargeable.DISCHARGE_STATE_OBJECT then |
322 | self:setDischargeState(Dischargeable.DISCHARGE_STATE_OFF, true) |
323 | end |
324 | end |
325 | else |
326 | local fillLevel = self:getFillUnitFillLevel(dischargeNode.fillUnitIndex) |
327 | if fillLevel > 0 and self:getShovelTipFactor() > 0 then |
328 | if self:getCanDischargeToGround(dischargeNode) then |
329 | self:setDischargeState(Dischargeable.DISCHARGE_STATE_GROUND, true) |
330 | else |
331 | if self:getIsActiveForInput(true) then |
332 | if not self:getCanDischargeToLand(dischargeNode) then |
333 | g_currentMission:showBlinkingWarning(g_i18n:getText("warning_youDontHaveAccessToThisLand"), 5000) |
334 | elseif not self:getCanDischargeAtPosition(dischargeNode) then |
335 | g_currentMission:showBlinkingWarning(g_i18n:getText("warning_actionNotAllowedHere"), 5000) |
336 | end |
337 | end |
338 | end |
339 | end |
340 | end |
341 | else |
342 | superFunc(self, dischargeNode, hitObject, hitShape, hitDistance, hitFillUnitIndex, hitTerrain) |
343 | end |
344 | end |
215 | function Shovel:loadShovelNode(xmlFile, key, shovelNode) |
216 | shovelNode.node = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, key .. "#node"), self.i3dMappings) |
217 | if shovelNode.node == nil then |
218 | g_logManager:xmlWarning(self.configFileName, "Missing 'node' for shovelNode '%s'!", key) |
219 | return false |
220 | end |
221 | |
222 | shovelNode.fillUnitIndex = Utils.getNoNil( getXMLInt(xmlFile, key .. "#fillUnitIndex"), 1) |
223 | shovelNode.loadInfoIndex = Utils.getNoNil( getXMLInt(xmlFile, key .. "#loadInfoIndex"), 1) |
224 | |
225 | shovelNode.width = Utils.getNoNil(getXMLFloat(xmlFile, key .. "#width"), 1) |
226 | shovelNode.length = Utils.getNoNil(getXMLFloat(xmlFile, key .. "#length"), 0.5) |
227 | shovelNode.yOffset = Utils.getNoNil(getXMLFloat(xmlFile, key .. "#yOffset"), 0) |
228 | shovelNode.zOffset = Utils.getNoNil(getXMLFloat(xmlFile, key .. "#zOffset"), 0) |
229 | |
230 | shovelNode.needsMovement = Utils.getNoNil(getXMLBool(xmlFile, key .. "#needsMovement"), true) |
231 | shovelNode.lastPosition = {0, 0, 0} |
232 | |
233 | shovelNode.fillLitersPerSecond = (getXMLFloat(xmlFile, key .. "#fillLitersPerSecond") or math.huge) / 1000 |
234 | shovelNode.maxPickupAngle = getXMLFloat(xmlFile, key .. "#maxPickupAngle") |
235 | if shovelNode.maxPickupAngle ~= nil then |
236 | shovelNode.maxPickupAngle = math.rad(shovelNode.maxPickupAngle) |
237 | end |
238 | |
239 | shovelNode.needsAttacherVehicle = Utils.getNoNil(getXMLBool(xmlFile, key .. "#needsAttacherVehicle"), true) |
240 | |
241 | return true |
242 | end |
54 | function Shovel:onLoad(savegame) |
55 | local spec = self.spec_shovel |
56 | |
57 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.shovel#pickUpNode", "vehicle.shovel.shovelNode#node") --FS17 to FS19 |
58 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.shovel#pickUpWidth", "vehicle.shovel.shovelNode#width") --FS17 to FS19 |
59 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.shovel#pickUpLength", "vehicle.shovel.shovelNode#length") --FS17 to FS19 |
60 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.shovel#pickUpYOffset") --FS17 to FS19 |
61 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.shovel#pickUpRequiresMovement", "vehicle.shovel.shovelNode#needsMovement") --FS17 to FS19 |
62 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.shovel#pickUpNeedsToBeTurnedOn", "vehicle.shovel.shovelNode#needsActivation") --FS17 to FS19 |
63 | |
64 | spec.ignoreFillUnitFillType = Utils.getNoNil(getXMLBool(self.xmlFile, "vehicle.shovel#ignoreFillUnitFillType"), false) |
65 | |
66 | spec.shovelNodes = {} |
67 | local i = 0 |
68 | while true do |
69 | local key = string.format("vehicle.shovel.shovelNode(%d)", i) |
70 | if not hasXMLProperty(self.xmlFile, key) then |
71 | break |
72 | end |
73 | |
74 | local shovelNode = {} |
75 | |
76 | if self:loadShovelNode(self.xmlFile, key, shovelNode) then |
77 | table.insert(spec.shovelNodes, shovelNode) |
78 | end |
79 | i = i + 1 |
80 | end |
81 | |
82 | spec.shovelDischargeInfo = {} |
83 | spec.shovelDischargeInfo.dischargeNodeIndex = Utils.getNoNil( getXMLInt(self.xmlFile, "vehicle.shovel.dischargeInfo#dischargeNodeIndex"), 1) |
84 | spec.shovelDischargeInfo.node = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.shovel.dischargeInfo#node"), self.i3dMappings) |
85 | |
86 | if spec.shovelDischargeInfo.node ~= nil then |
87 | local minSpeedAngle = getXMLFloat(self.xmlFile, "vehicle.shovel.dischargeInfo#minSpeedAngle") |
88 | local maxSpeedAngle = getXMLFloat(self.xmlFile, "vehicle.shovel.dischargeInfo#maxSpeedAngle") |
89 | if minSpeedAngle == nil or maxSpeedAngle == nil then |
90 | g_logManager:xmlWarning(self.configFileName, "Missing 'minSpeedAngle' or 'maxSpeedAngle' for dischargeNode 'vehicle.shovel.dischargeInfo'") |
91 | return false |
92 | end |
93 | spec.shovelDischargeInfo.minSpeedAngle = math.rad(minSpeedAngle) |
94 | spec.shovelDischargeInfo.maxSpeedAngle = math.rad(maxSpeedAngle) |
95 | end |
96 | |
97 | if self.isClient then |
98 | spec.fillEffects = g_effectManager:loadEffect(self.xmlFile, "vehicle.shovel.fillEffect", self.components, self, self.i3dMappings) |
99 | end |
100 | |
101 | spec.effectDirtyFlag = self:getNextDirtyFlag() |
102 | spec.loadingFillType = FillType.UNKNOWN |
103 | end |
154 | function Shovel:onUpdateTick(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected) |
155 | local spec = self.spec_shovel |
156 | |
157 | if self.isServer then |
158 | local validPickupFillType = FillType.UNKNOWN |
159 | for _,shovelNode in pairs(spec.shovelNodes) do |
160 | if self:getShovelNodeIsActive(shovelNode) then |
161 | local fillLevel = self:getFillUnitFillLevel(shovelNode.fillUnitIndex) |
162 | local capacity = self:getFillUnitCapacity(shovelNode.fillUnitIndex) |
163 | if fillLevel < capacity then |
164 | local pickupFillType = self:getFillUnitFillType(shovelNode.fillUnitIndex) |
165 | if fillLevel / capacity < self:getFillTypeChangeThreshold() then |
166 | pickupFillType = FillType.UNKNOWN |
167 | end |
168 | local freeCapacity = math.min(capacity - fillLevel, shovelNode.fillLitersPerSecond * dt) |
169 | local sx,sy,sz = localToWorld(shovelNode.node, -shovelNode.width, shovelNode.yOffset, shovelNode.zOffset) |
170 | local ex,ey,ez = localToWorld(shovelNode.node, shovelNode.width, shovelNode.yOffset, shovelNode.zOffset) |
171 | local innerRadius = shovelNode.length |
172 | local radius = nil |
173 | |
174 | if self:getCanShovelAtPosition(shovelNode) then |
175 | if pickupFillType == FillType.UNKNOWN or spec.ignoreFillUnitFillType then |
176 | pickupFillType = DensityMapHeightUtil.getFillTypeAtLine(sx,sy,sz, ex,ey,ez, innerRadius) |
177 | end |
178 | |
179 | if pickupFillType ~= FillType.UNKNOWN and self:getFillUnitSupportsFillType(shovelNode.fillUnitIndex, pickupFillType) then |
180 | local fillDelta, lineOffset = DensityMapHeightUtil.tipToGroundAroundLine(self, -freeCapacity, pickupFillType, sx,sy,sz, ex,ey,ez, innerRadius, radius, shovelNode.lineOffset, true, nil) |
181 | shovelNode.lineOffset = lineOffset |
182 | |
183 | if fillDelta < 0 then |
184 | local loadInfo = self:getFillVolumeLoadInfo(shovelNode.loadInfoIndex) |
185 | self:addFillUnitFillLevel(self:getOwnerFarmId(), shovelNode.fillUnitIndex, -fillDelta, pickupFillType, ToolType.UNDEFINED, loadInfo) |
186 | validPickupFillType = pickupFillType |
187 | |
188 | -- call fill level changed callack to inform bunker silo about change |
189 | self:notifiyBunkerSilo(fillDelta, pickupFillType) |
190 | end |
191 | end |
192 | end |
193 | end |
194 | end |
195 | end |
196 | |
197 | if spec.loadingFillType ~= validPickupFillType then |
198 | spec.loadingFillType = validPickupFillType |
199 | self:raiseDirtyFlags(spec.effectDirtyFlag) |
200 | end |
201 | end |
202 | |
203 | if self.isClient then |
204 | if spec.loadingFillType ~= FillType.UNKNOWN then |
205 | g_effectManager:setFillType(spec.fillEffects, spec.loadingFillType) |
206 | g_effectManager:startEffects(spec.fillEffects) |
207 | else |
208 | g_effectManager:stopEffects(spec.fillEffects) |
209 | end |
210 | end |
211 | end |
42 | function Shovel.registerEventListeners(vehicleType) |
43 | SpecializationUtil.registerEventListener(vehicleType, "onLoad", Shovel) |
44 | SpecializationUtil.registerEventListener(vehicleType, "onReadStream", Shovel) |
45 | SpecializationUtil.registerEventListener(vehicleType, "onWriteStream", Shovel) |
46 | SpecializationUtil.registerEventListener(vehicleType, "onReadUpdateStream", Shovel) |
47 | SpecializationUtil.registerEventListener(vehicleType, "onWriteUpdateStream", Shovel) |
48 | SpecializationUtil.registerEventListener(vehicleType, "onDelete", Shovel) |
49 | SpecializationUtil.registerEventListener(vehicleType, "onUpdateTick", Shovel) |
50 | end |
30 | function Shovel.registerOverwrittenFunctions(vehicleType) |
31 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "getDischargeNodeEmptyFactor", Shovel.getDischargeNodeEmptyFactor) |
32 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "handleDischarge", Shovel.handleDischarge) |
33 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "handleDischargeOnEmpty", Shovel.handleDischargeOnEmpty) |
34 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "handleDischargeRaycast", Shovel.handleDischargeRaycast) |
35 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "getCanToggleDischargeToObject", Shovel.getCanToggleDischargeToObject) |
36 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "getCanToggleDischargeToGround", Shovel.getCanToggleDischargeToGround) |
37 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "getWearMultiplier", Shovel.getWearMultiplier) |
38 | end |
418 | function Shovel:updateDebugValues(values) |
419 | local spec = self.spec_shovel |
420 | |
421 | local info = spec.shovelDischargeInfo |
422 | if info.node ~= nil then |
423 | local _,dy,_ = localDirectionToWorld(info.node, 0,0,1) |
424 | local angle = math.acos(dy) |
425 | table.insert(values, {name="angle", value=math.deg(angle)}) |
426 | table.insert(values, {name="minSpeedAngle", value=math.deg(info.minSpeedAngle)}) |
427 | table.insert(values, {name="maxSpeedAngle", value=math.deg(info.maxSpeedAngle)}) |
428 | |
429 | if angle > info.minSpeedAngle then |
430 | local factor = math.max(0, math.min(1.0, (angle - info.minSpeedAngle) / (info.maxSpeedAngle - info.minSpeedAngle))) |
431 | table.insert(values, {name="factor", value=factor}) |
432 | else |
433 | table.insert(values, {name="factor", value="Out of Range - 0"}) |
434 | end |
435 | end |
436 | end |