Copyright (c) 2008-2015 GIANTS Software GmbH, Confidential, All Rights Reserved.
This document is to be published solely by ls-mods.de
1 | -- |
2 | -- Fillable |
3 | -- This is the specialization for fillable vehicles, such as trailers or sprayers |
4 | -- |
5 | -- @author Stefan Geiger |
6 | -- @date 22/07/10 |
7 | -- |
8 | -- Copyright (C) GIANTS Software GmbH, Confidential, All Rights Reserved. |
9 | |
10 | source("dataS/scripts/vehicles/specializations/FillActivatable.lua"); |
11 | |
12 | Fillable = {}; |
13 | |
14 | Fillable.NUM_FILLTYPES = 0; |
15 | Fillable.FILLTYPE_UNKNOWN = 0; |
16 | Fillable.FILLTYPE_START_TOTAL_AMOUNT = 500000; |
17 | |
18 | Fillable.fillTypeNameToInt = {} |
19 | Fillable.fillTypeIntToName = {} |
20 | |
21 | Fillable.fillTypeIndexToDesc = {} |
22 | Fillable.fillTypeNameToDesc = {} |
23 | Fillable.economyFillTypes = {}; |
24 | |
25 | Fillable.defaultFillPlaneMaterial = nil; |
26 | Fillable.defaultFillIconMaterial = nil; |
27 | |
28 | Fillable.MATERIAL_TYPE_FILLPLANE = 1; |
29 | Fillable.MATERIAL_TYPE_ICON = 2; |
30 | Fillable.MATERIAL_TYPE_UNLOADING = 3; |
31 | Fillable.MATERIAL_TYPE_SMOKE = 4; |
32 | Fillable.MATERIAL_TYPE_STRAW = 5; |
33 | Fillable.MATERIAL_TYPE_CHOPPER = 6; |
34 | |
35 | Fillable.defaultMaterials = {}; |
36 | |
37 | Fillable.fillTypeNameToInt["unknown"] = Fillable.FILLTYPE_UNKNOWN; |
38 | Fillable.fillTypeIntToName[Fillable.FILLTYPE_UNKNOWN] = "unknown"; |
39 | |
40 | Fillable.sendNumBits = 6; |
41 | |
42 | function Fillable.registerFillType(name, nameI18N, pricePerLiter, partOfEconomy, hudOverlayFilename, hudOverlayFilenameSmall, massPerLiter, maxPhysicalSurfaceAngle) |
43 | local key = "FILLTYPE_"..string.upper(name); |
44 | if Fillable[key] == nil then |
45 | if Fillable.NUM_FILLTYPES >= 64 then |
46 | print("Error: Fillable.registerFillType too many fill types. Only 64 fill types are supported"); |
47 | return; |
48 | end; |
49 | Fillable.NUM_FILLTYPES = Fillable.NUM_FILLTYPES+1; |
50 | Fillable[key] = Fillable.NUM_FILLTYPES; |
51 | Fillable.fillTypeNameToInt[name] = Fillable.NUM_FILLTYPES; |
52 | Fillable.fillTypeIntToName[Fillable.NUM_FILLTYPES] = name; |
53 | |
54 | local desc = {}; |
55 | desc.name = name; |
56 | desc.nameI18N = nameI18N; |
57 | if desc.nameI18N == nil then |
58 | desc.nameI18N = name; |
59 | if g_i18n:hasText(desc.nameI18N) then |
60 | desc.nameI18N = g_i18n:getText(name); |
61 | end |
62 | end |
63 | desc.index = Fillable.NUM_FILLTYPES; |
64 | desc.massPerLiter = Utils.getNoNil(massPerLiter, 0.0001); |
65 | desc.pricePerLiter = Utils.getNoNil(pricePerLiter, 0); |
66 | desc.previousHourPrice = desc.pricePerLiter; |
67 | desc.startPricePerLiter = desc.pricePerLiter; |
68 | desc.totalAmount = Fillable.FILLTYPE_START_TOTAL_AMOUNT; |
69 | desc.partOfEconomy = Utils.getNoNil(partOfEconomy, false); |
70 | desc.hudOverlayFilename = hudOverlayFilename; |
71 | desc.hudOverlayFilenameSmall = Utils.getNoNil(hudOverlayFilenameSmall, hudOverlayFilename); |
72 | desc.materials = {} |
73 | desc.maxPhysicalSurfaceAngle = maxPhysicalSurfaceAngle; |
74 | |
75 | Fillable.fillTypeIndexToDesc[Fillable.NUM_FILLTYPES] = desc; |
76 | Fillable.fillTypeNameToDesc[name] = desc; |
77 | |
78 | if desc.partOfEconomy then |
79 | table.insert(Fillable.economyFillTypes, desc); |
80 | end; |
81 | end; |
82 | return Fillable[key]; |
83 | end; |
84 | |
85 | function Fillable.onCreateFillMaterial(_, id) |
86 | local fillType = getUserAttribute(id, "fillType"); |
87 | if fillType == nil then |
88 | print("Warning: No fillType '"..tostring(fillType).."' given for Fillable.onCreateMaterial"); |
89 | return |
90 | end; |
91 | |
92 | local desc = Fillable.fillTypeNameToDesc[fillType]; |
93 | if desc == nil then |
94 | print("Warning: Unkown fillType '"..tostring(fillType).."' for Fillable.onCreateFillMaterial"); |
95 | return; |
96 | end; |
97 | |
98 | local matTypeName = getUserAttribute(id, "materialType"); |
99 | if matTypeName == nil then |
100 | print("Warning: No materialtype given for filltype '"..tostring(fillType).."' for Fillable.onCreateFillMaterial"); |
101 | return; |
102 | end; |
103 | |
104 | local materialType = Fillable.getMaterialType(matTypeName); |
105 | if materialType == nil then |
106 | print("Warning: Unkown materialtype '"..matTypeName.."' given for filltype '"..tostring(fillType).."' for Fillable.onCreateFillMaterial"); |
107 | return; |
108 | end; |
109 | |
110 | local matIdStr = Utils.getNoNil(getUserAttribute(id, "materialId"), 1); |
111 | if tonumber(matIdStr) == nil then |
112 | print("Warning: Invalid materialId '"..matIdStr.."' for "..desc.name.."-"..matTypeName.."!") |
113 | return; |
114 | end; |
115 | |
116 | local materialId = tonumber(matIdStr); |
117 | |
118 | if desc.materials[materialType] == nil then |
119 | desc.materials[materialType] = {}; |
120 | end; |
121 | local materials = desc.materials[materialType]; |
122 | if Fillable.defaultMaterials[materialType] == nil then |
123 | Fillable.defaultMaterials[materialType] = {}; |
124 | end; |
125 | local default = Fillable.defaultMaterials[materialType]; |
126 | |
127 | materials[materialId] = getMaterial(id, 0); |
128 | if default[materialId] == nil then |
129 | default[materialId] = materials[materialId]; |
130 | end; |
131 | end; |
132 | |
133 | function Fillable.getFillMaterial(fillType, materialType, materialTypeId) |
134 | if materialType == nil or materialTypeId == nil then |
135 | return nil; |
136 | end; |
137 | |
138 | local desc = Fillable.fillTypeIndexToDesc[fillType]; |
139 | if desc == nil then |
140 | return; |
141 | end; |
142 | |
143 | local types = desc.materials[materialType]; |
144 | local isDefault = false; |
145 | |
146 | if types == nil then |
147 | types = Fillable.defaultMaterials[materialType]; |
148 | isDefault = true; |
149 | end; |
150 | if types == nil then |
151 | return nil; |
152 | end; |
153 | |
154 | if types[materialTypeId] ~= nil then |
155 | return types[materialTypeId], isDefault; |
156 | end; |
157 | |
158 | return types[1], isDefault; |
159 | end; |
160 | |
161 | function Fillable.getMaterialType(materialTypeName) |
162 | local key = "MATERIAL_TYPE_"..string.upper(materialTypeName); |
163 | return Fillable[key]; |
164 | end; |
165 | |
166 | function Fillable.cleanup() |
167 | for i=1, Fillable.NUM_FILLTYPES do |
168 | local desc = Fillable.fillTypeIndexToDesc[i]; |
169 | if desc ~= nil then |
170 | desc.materials = {}; |
171 | end; |
172 | end; |
173 | Fillable.defaultMaterials = {}; |
174 | end; |
175 | |
176 | function Fillable.addFillTypeToEconomy(index) |
177 | if not Fillable.fillTypeIndexToDesc[index].partOfEconomy then |
178 | Fillable.fillTypeIndexToDesc[index].partOfEconomy = true; |
179 | table.insert(Fillable.economyFillTypes, desc); |
180 | end |
181 | end; |
182 | |
183 | function Fillable.prerequisitesPresent(specializations) |
184 | return true; |
185 | end; |
186 | |
187 | function Fillable:load(xmlFile) |
188 | |
189 | self.allowFillType = Fillable.allowFillType; |
190 | self.resetFillLevelIfNeeded = Fillable.resetFillLevelIfNeeded; |
191 | self.setFillLevel = Fillable.setFillLevel; |
192 | self.getFillLevel = Fillable.getFillLevel; |
193 | self.getCapacity = Fillable.getCapacity; |
194 | self.setCapacity = Fillable.setCapacity; |
195 | self.getAllowFillFromAir = Fillable.getAllowFillFromAir; |
196 | self.getFirstEnabledFillType = Fillable.getFirstEnabledFillType; |
197 | self.setLastValidFillType = SpecializationUtil.callSpecializationsFunction("setLastValidFillType"); |
198 | self.getOverlayFillType = Fillable.getOverlayFillType; |
199 | self.getDoRenderFillType = Fillable.getDoRenderFillType; |
200 | self.updateMeasurementNode = Fillable.updateMeasurementNode; |
201 | self.attachPipe = Fillable.attachPipe; |
202 | self.detachPipe = Fillable.detachPipe; |
203 | |
204 | self.supportsFillTriggers = Utils.getNoNil(getXMLBool(xmlFile, "vehicle.supportsFillTriggers#value"), self.supportsFillTriggers); |
205 | |
206 | if self.supportsFillTriggers then |
207 | assert(self.setIsFilling == nil, "Fillable needs to be the first specialization which implements setIsFilling"); |
208 | self.setIsFilling = Fillable.setIsFilling; |
209 | self.addFillTrigger = Fillable.addFillTrigger; |
210 | self.removeFillTrigger = Fillable.removeFillTrigger; |
211 | self.fillLitersPerSecond = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.fillLitersPerSecond"), 500); |
212 | local unitFillTime = getXMLFloat(xmlFile, "vehicle.unitFillTime"); |
213 | if unitFillTime ~= nil then |
214 | self.unitFillTime = unitFillTime * 1000; |
215 | end; |
216 | self.currentFillTime = 0; |
217 | self.fillTriggers = {}; |
218 | self.fillActivatable = FillActivatable:new(self); |
219 | self.isFilling = false; |
220 | end; |
221 | |
222 | self.fillLevel = 0; |
223 | self.capacity = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.capacity"), 0.0); |
224 | self.fillTypeChangeThreshold = 0.05; -- fill level percentage that still allows overriding with another fill type |
225 | |
226 | self.synchronizeFillLevel = true; |
227 | self.synchronizeFullFillLevel = false; |
228 | |
229 | self.fillRootNode = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.fillRootNode#index")); |
230 | if self.fillRootNode == nil then |
231 | self.fillRootNode = self.components[1].node; |
232 | end; |
233 | |
234 | self.fillMassNode = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.fillMassNode#index")); |
235 | if self.fillMassNode == nil then |
236 | self.fillMassNode = self.fillRootNode; |
237 | end; |
238 | |
239 | self.exactFillRootNode = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.exactFillRootNode#index")); |
240 | if self.exactFillRootNode == nil then |
241 | self.exactFillRootNode = self.fillRootNode; |
242 | end; |
243 | |
244 | self.fillAutoAimTargetNode = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.fillAutoAimTargetNode#index")); |
245 | if self.fillAutoAimTargetNode == nil then |
246 | self.fillAutoAimTargetNode = self.exactFillRootNode; |
247 | end; |
248 | |
249 | self.attacherPipeRef = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.attacherPipe#refIndex")); |
250 | self.attacherPipeRefVehicle = nil; |
251 | |
252 | self.attacherPipe = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.attacherPipe#index")); |
253 | self.attacherPipeVehicle = nil; |
254 | |
255 | |
256 | self.fillTypes = {}; |
257 | self.fillTypes[Fillable.FILLTYPE_UNKNOWN] = true; |
258 | |
259 | local fillTypes = getXMLString(xmlFile, "vehicle.fillTypes#fillTypes"); |
260 | if fillTypes ~= nil then |
261 | local types = Utils.splitString(" ", fillTypes); |
262 | for k,v in pairs(types) do |
263 | local fillType = Fillable.fillTypeNameToInt[v]; |
264 | if fillType ~= nil then |
265 | self.fillTypes[fillType] = true; |
266 | else |
267 | print("Warning: '"..self.configFileName.. "' has invalid fillType '"..v.."'."); |
268 | end; |
269 | end; |
270 | end; |
271 | local fruitTypes = getXMLString(xmlFile, "vehicle.fillTypes#fruitTypes"); |
272 | if fruitTypes ~= nil then |
273 | local types = Utils.splitString(" ", fruitTypes); |
274 | for k,v in pairs(types) do |
275 | local fillType = Fillable.fillTypeNameToInt[v]; |
276 | if fillType ~= nil then |
277 | self.fillTypes[fillType] = true; |
278 | else |
279 | print("Warning: '"..self.configFileName.. "' has invalid fillType '"..v.."'."); |
280 | end; |
281 | end; |
282 | end; |
283 | |
284 | self.currentFillType = Fillable.FILLTYPE_UNKNOWN; |
285 | self.lastValidFillType = Fillable.FILLTYPE_UNKNOWN; |
286 | |
287 | if self.isServer then |
288 | self.sentFillType = self.currentFillType; |
289 | self.sentFillLevel = self.fillLevel; |
290 | end; |
291 | |
292 | self.alsoUseFillVolumeLoadInfoForDischarge = Utils.getNoNil(getXMLBool(xmlFile, "vehicle.alsoUseFillVolumeLoadInfoForDischarge"), false); |
293 | |
294 | if self.isClient then |
295 | self.fillVolumes = {}; |
296 | self.fillVolumeDeformers = {}; |
297 | local i = 0; |
298 | while true do |
299 | local key = string.format("vehicle.fillVolumes.fillVolume(%d)", i); |
300 | if not hasXMLProperty(xmlFile, key) then |
301 | break; |
302 | end; |
303 | |
304 | local fillVolume = {}; |
305 | |
306 | fillVolume.baseNode = Utils.indexToObject(self.components, getXMLString(xmlFile, key.."#index")); |
307 | fillVolume.allSidePlanes = Utils.getNoNil(getXMLBool(xmlFile, key.."#allSidePlanes"), true); |
308 | |
309 | local defaultFillType = getXMLString(xmlFile, key.."#defaultFillType"); |
310 | if defaultFillType ~= nil then |
311 | |
312 | local fillType = Fillable.fillTypeNameToInt[defaultFillType]; |
313 | if fillType ~= nil then |
314 | fillVolume.defaultFillType = fillType; |
315 | else |
316 | print("Warning: Invalid defaultFillType '"..tostring(defaultFillType).."' in '"..self.configFileName.."'"); |
317 | end; |
318 | end; |
319 | |
320 | fillVolume.maxDelta = Utils.getNoNil(getXMLFloat(xmlFile, key.."#maxDelta"), 1.0); |
321 | fillVolume.maxSurfaceAngle = math.rad( Utils.getNoNil(getXMLFloat(xmlFile, key.."#maxAllowedHeapAngle"), 35) ); |
322 | |
323 | local maxPhysicalSurfaceAngle = math.rad(35); |
324 | fillVolume.maxSubDivEdgeLength = Utils.getNoNil(getXMLFloat(xmlFile, key.."#maxSubDivEdgeLength"), 0.9); |
325 | |
326 | fillVolume.volume = createFillPlaneShape(fillVolume.baseNode, "fillPlane", self.capacity, fillVolume.maxDelta, fillVolume.maxSurfaceAngle, maxPhysicalSurfaceAngle, fillVolume.maxSubDivEdgeLength, fillVolume.allSidePlanes); |
327 | |
328 | fillVolume.deformers = {}; |
329 | if fillVolume.volume ~= nil then |
330 | local j = 0; |
331 | while true do |
332 | local node = Utils.indexToObject(self.components, getXMLString(xmlFile, key..".deformNode("..j..")#index")); |
333 | if node == nil then |
334 | break; |
335 | end; |
336 | local initPos = { localToLocal(node, fillVolume.baseNode, 0,0,0) }; |
337 | local polyline = findPolyline(fillVolume.volume, initPos[1],initPos[3]); |
338 | self.fillVolumeDeformers[node] = {node=node, initPos=initPos, posX=initPos[1], posZ=initPos[3], polyline=polyline, volume=fillVolume.volume, baseNode=fillVolume.baseNode}; |
339 | j = j + 1; |
340 | end; |
341 | end; |
342 | |
343 | fillVolume.scrollSpeedDischarge = { Utils.getVectorFromString(Utils.getNoNil(getXMLString(xmlFile, key.."#scrollSpeedDischarge"), "0 0 0")) }; |
344 | fillVolume.scrollSpeedLoad = { Utils.getVectorFromString(Utils.getNoNil(getXMLString(xmlFile, key.."#scrollSpeedLoad"), "0 0 0")) }; |
345 | for i=1,3 do |
346 | fillVolume.scrollSpeedDischarge[i] = fillVolume.scrollSpeedDischarge[i] / 1000; |
347 | fillVolume.scrollSpeedLoad[i] = fillVolume.scrollSpeedLoad[i] / 1000; |
348 | end; |
349 | fillVolume.uvPosition = {0, 0, 0}; |
350 | |
351 | if fillVolume.volume ~= nil and fillVolume.volume ~= 0 then |
352 | link(fillVolume.baseNode, fillVolume.volume); |
353 | table.insert(self.fillVolumes, fillVolume); |
354 | end; |
355 | |
356 | i = i + 1; |
357 | end; |
358 | |
359 | |
360 | self.fillVolumeHeights = {}; |
361 | self.fillVolumeHeightRefNodeToFillVolumeHeight = {}; |
362 | local i=0; |
363 | while true do |
364 | local key = string.format("vehicle.fillVolumeHeights.fillVolumeHeight(%d)", i); |
365 | if not hasXMLProperty(xmlFile, key) then |
366 | break; |
367 | end; |
368 | local volumeHeight = {}; |
369 | volumeHeight.fillVolumeId = getXMLInt(xmlFile, key.."#fillVolumeId"); |
370 | volumeHeight.volumeHeightIsDirty = false; |
371 | |
372 | volumeHeight.refNodes = {}; |
373 | local j=0; |
374 | while true do |
375 | local refNode = Utils.indexToObject(self.components, getXMLString(xmlFile, string.format("%s.refNode(%d)#index", key, j))); |
376 | if refNode == nil then |
377 | break; |
378 | end; |
379 | table.insert(volumeHeight.refNodes, {refNode=refNode}); |
380 | self.fillVolumeHeightRefNodeToFillVolumeHeight[refNode] = volumeHeight; |
381 | j=j+1; |
382 | end; |
383 | |
384 | volumeHeight.nodes = {}; |
385 | local j=0; |
386 | while true do |
387 | local node = Utils.indexToObject(self.components, getXMLString(xmlFile, string.format("%s.node(%d)#index", key, j))); |
388 | if node == nil then |
389 | break; |
390 | end; |
391 | if node ~= nil then |
392 | local baseScale = { Utils.getVectorFromString(Utils.getNoNil(getXMLString(xmlFile, string.format("%s.node(%d)#baseScale", key, j)), "1 1 1")) }; |
393 | local scaleAxis = { Utils.getVectorFromString(Utils.getNoNil(getXMLString(xmlFile, string.format("%s.node(%d)#scaleAxis", key, j)), "0 0 0")) }; |
394 | local scaleMax = { Utils.getVectorFromString(Utils.getNoNil(getXMLString(xmlFile, string.format("%s.node(%d)#scaleMax", key, j)), "0 0 0")) }; |
395 | local basePosition = { getTranslation(node) }; |
396 | local transAxis = { Utils.getVectorFromString(Utils.getNoNil(getXMLString(xmlFile, string.format("%s.node(%d)#transAxis", key, j)), "0 0 0")) }; |
397 | local transMax = { Utils.getVectorFromString(Utils.getNoNil(getXMLString(xmlFile, string.format("%s.node(%d)#transMax", key, j)), "0 0 0")) }; |
398 | local orientateToWorldY = Utils.getNoNil(getXMLBool(xmlFile, string.format("%s.node(%d)#orientateToWorldY", key, j)), false); |
399 | table.insert(volumeHeight.nodes, {node=node, baseScale=baseScale, scaleAxis=scaleAxis, scaleMax=scaleMax, basePosition=basePosition, transAxis=transAxis, transMax=transMax, orientateToWorldY=orientateToWorldY}); |
400 | end; |
401 | j=j+1; |
402 | end; |
403 | |
404 | table.insert(self.fillVolumeHeights, volumeHeight); |
405 | i=i+1; |
406 | end; |
407 | |
408 | local fillPlanesRotDeg = Utils.getNoNil(getXMLBool(xmlFile, "vehicle.fillPlanes#rotationDegrees"), false); |
409 | self.fillPlanes = {}; |
410 | local i = 0; |
411 | while true do |
412 | local key = string.format("vehicle.fillPlanes.fillPlane(%d)", i); |
413 | if not hasXMLProperty(xmlFile, key) then |
414 | break; |
415 | end; |
416 | local fillPlane = {}; |
417 | fillPlane.nodes = {}; |
418 | local fillType = getXMLString(xmlFile, key.."#type"); |
419 | if fillType ~= nil then |
420 | local nodeI = 0; |
421 | while true do |
422 | local nodeKey = key..string.format(".node(%d)", nodeI); |
423 | if not hasXMLProperty(xmlFile, nodeKey) then |
424 | break; |
425 | end; |
426 | local node = Utils.indexToObject(self.components, getXMLString(xmlFile, nodeKey.."#index")); |
427 | if node ~= nil then |
428 | local defaultX, defaultY, defaultZ = getTranslation(node); |
429 | local defaultRX, defaultRY, defaultRZ = getRotation(node); |
430 | setVisibility(node, false); |
431 | |
432 | local animCurve = AnimCurve:new(linearInterpolatorTransRotScale); |
433 | local keyI = 0; |
434 | while true do |
435 | local animKey = nodeKey..string.format(".key(%d)", keyI); |
436 | local keyTime = getXMLFloat(xmlFile, animKey.."#time"); |
437 | local x,y,z = Utils.getVectorFromString(getXMLString(xmlFile, animKey.."#translation")); |
438 | if y == nil then |
439 | y = getXMLFloat(xmlFile, animKey.."#y"); |
440 | end; |
441 | local rx,ry,rz = Utils.getVectorFromString(getXMLString(xmlFile, animKey.."#rotation")); |
442 | local sx,sy,sz = Utils.getVectorFromString(getXMLString(xmlFile, animKey.."#scale")); |
443 | if keyTime == nil then |
444 | break; |
445 | end; |
446 | local x = Utils.getNoNil(x, defaultX); |
447 | local y = Utils.getNoNil(y, defaultY); |
448 | local z = Utils.getNoNil(z, defaultZ); |
449 | if fillPlanesRotDeg then |
450 | rx = Utils.getNoNilRad(rx, defaultRX); |
451 | ry = Utils.getNoNilRad(ry, defaultRY); |
452 | rz = Utils.getNoNilRad(rz, defaultRZ); |
453 | else |
454 | rx = Utils.getNoNil(rx, defaultRX); |
455 | ry = Utils.getNoNil(ry, defaultRY); |
456 | rz = Utils.getNoNil(rz, defaultRZ); |
457 | end |
458 | local sx = Utils.getNoNil(sx, 1); |
459 | local sy = Utils.getNoNil(sy, 1); |
460 | local sz = Utils.getNoNil(sz, 1); |
461 | animCurve:addKeyframe({x=x, y=y, z=z, rx=rx, ry=ry, rz=rz, sx=sx, sy=sy, sz=sz, time = keyTime}); |
462 | keyI = keyI +1; |
463 | end; |
464 | if keyI == 0 then |
465 | local minY, maxY = Utils.getVectorFromString(getXMLString(xmlFile, nodeKey.."#minMaxY")); |
466 | local minY = Utils.getNoNil(minY, defaultY); |
467 | local maxY = Utils.getNoNil(maxY, defaultY); |
468 | animCurve:addKeyframe({x=defaultX, y=minY, z=defaultZ, rx=defaultRX, ry=defaultRY, rz=defaultRZ, sx=1, sy=1, sz=1, time = 0}); |
469 | animCurve:addKeyframe({x=defaultX, y=maxY, z=defaultZ, rx=defaultRX, ry=defaultRY, rz=defaultRZ, sx=1, sy=1, sz=1, time = 1}); |
470 | end; |
471 | local alwaysVisible = Utils.getNoNil(getXMLBool(xmlFile, nodeKey.."#alwaysVisible"), false); |
472 | table.insert(fillPlane.nodes, {node=node, animCurve = animCurve, alwaysVisible=alwaysVisible}); |
473 | end; |
474 | nodeI = nodeI +1; |
475 | end; |
476 | if table.getn(fillPlane.nodes) > 0 then |
477 | if self.defaultFillPlane == nil then |
478 | self.defaultFillPlane = fillPlane; |
479 | end; |
480 | self.fillPlanes[fillType] = fillPlane; |
481 | end; |
482 | end; |
483 | i = i +1; |
484 | end; |
485 | if self.defaultFillPlane==nil then |
486 | self.fillPlanes = nil; |
487 | end; |
488 | |
489 | if self.fillPlanes == nil then |
490 | Fillable.loadDeprecatedTrailerGrainPlane(self, xmlFile); |
491 | --[[if hasXMLProperty(xmlFile, "vehicle.grainPlane") then |
492 | print("Warning: '"..self.configFileName.. "' uses old grainPlane format, which is not supported anymore."); |
493 | end;]] |
494 | end; |
495 | |
496 | self.measurementNode = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.measurementNode#index")); |
497 | self.measurementTime = 0; |
498 | end; |
499 | |
500 | self.allowFillFromAir = Utils.getNoNil(getXMLBool(xmlFile, "vehicle.allowFillFromAir#value"), true); |
501 | |
502 | local unloadTriggerNode = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.unloadTrigger#index")); |
503 | if unloadTriggerNode ~= nil then |
504 | self.unloadTrigger = FillTrigger:new(); |
505 | self.unloadTrigger:load(unloadTriggerNode, self.currentFillType, self); |
506 | end; |
507 | |
508 | if self.isServer then |
509 | local shovelFillTriggerId = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.shovelFillTrigger#index")); |
510 | if shovelFillTriggerId ~= nil then |
511 | local shovelFillTrigger = FillableShovelFillTrigger:new(); |
512 | if shovelFillTrigger:load(shovelFillTriggerId, self) then |
513 | self.shovelFillTrigger = shovelFillTrigger; |
514 | else |
515 | shovelFillTrigger:delete(); |
516 | end |
517 | end |
518 | end; |
519 | |
520 | |
521 | self.fillVolumeLoadInfo = {}; |
522 | self.fillVolumeLoadInfo.name = "fillVolumeLoadInfo"; |
523 | |
524 | self.fillVolumeUnloadInfo = {}; |
525 | self.fillVolumeUnloadInfo.name = "fillVolumeUnloadInfo"; |
526 | |
527 | self.fillVolumeDischargeInfo = {}; |
528 | self.fillVolumeDischargeInfo.name = "fillVolumeDischargeInfo"; |
529 | |
530 | for _,tbl in pairs( {self.fillVolumeLoadInfo, self.fillVolumeUnloadInfo, self.fillVolumeDischargeInfo} ) do |
531 | local indexString = getXMLString(xmlFile, "vehicle."..tbl.name.."#index"); |
532 | if indexString == nil then |
533 | tbl.node = createTransformGroup(tbl.name); |
534 | link(self.components[1].node, tbl.node); |
535 | setTranslation(tbl.node, 0,0,0); |
536 | else |
537 | tbl.node = Utils.indexToObject(self.components, indexString); |
538 | end; |
539 | tbl.width = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle."..tbl.name.."#width"), 1.0); |
540 | tbl.length = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle."..tbl.name.."#length"), 1.0); |
541 | |
542 | if tbl.name == "fillVolumeDischargeInfo" then |
543 | tbl.alsoUseLoadInfoForDischarge = Utils.getNoNil(getXMLBool(xmlFile, "vehicle."..tbl.name.."#alsoUseLoadInfoForDischarge"), false); |
544 | tbl.loadInfoSizeScale = Utils.getVectorNFromString( Utils.getNoNil(getXMLString(xmlFile, "vehicle."..tbl.name.."#loadInfoSizeScale"), "1 1"), 2 ); |
545 | tbl.loadInfoFillFactor = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle."..tbl.name.."#loadInfoFillFactor"), 0.6); |
546 | end; |
547 | end; |
548 | |
549 | |
550 | self:setFillLevel(0, Fillable.FILLTYPE_UNKNOWN); |
551 | |
552 | setUserAttribute(self.fillRootNode, "vehicleType", "Integer", 2); |
553 | |
554 | self.fillableDirtyFlag = self:getNextDirtyFlag(); |
555 | end; |
556 | |
557 | function Fillable:postLoad(xmlFile) |
558 | local startFillLevel = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.startFillLevel"), 0); |
559 | if startFillLevel > 0 then |
560 | local firstFillType = self:getFirstEnabledFillType(); |
561 | |
562 | if firstFillType ~= Fillable.FILLTYPE_UNKNOWN then |
563 | self:setFillLevel(startFillLevel, firstFillType, true); |
564 | end; |
565 | end; |
566 | end; |
567 | |
568 | function Fillable.loadDeprecatedTrailerGrainPlane(self, xmlFile) |
569 | |
570 | --self.grainPlane = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.grainPlane#index")); |
571 | if self.isClient then |
572 | if self.fillPlanes == nil then |
573 | local defaultX, defaultY, defaultZ; |
574 | local defaultRX, defaultRY, defaultRZ; |
575 | local defaultSX, defaultSY, defaultSZ; |
576 | |
577 | self.fillPlanes = {}; |
578 | local i = 0; |
579 | while true do |
580 | local key = string.format("vehicle.grainPlane.node(%d)", i); |
581 | local fruitType = getXMLString(xmlFile, key.."#type"); |
582 | local index = getXMLString(xmlFile, key.."#index"); |
583 | if fruitType==nil or index==nil then |
584 | break; |
585 | end; |
586 | local node = Utils.indexToObject(self.components, index); |
587 | if node ~= nil then |
588 | defaultX, defaultY, defaultZ = getTranslation(node); |
589 | defaultRX, defaultRY, defaultRZ = getRotation(node); |
590 | defaultSX, defaultSY, defaultSZ = getScale(node); |
591 | |
592 | setVisibility(node, false); |
593 | local fillPlane = {}; |
594 | fillPlane.nodes = {}; |
595 | table.insert(fillPlane.nodes, {node=node, alwaysVisible=false}); -- animCurve is filled below |
596 | -- Node: fruitType names are equal to the fillType names |
597 | self.fillPlanes[fruitType] = fillPlane; |
598 | |
599 | if self.defaultFillPlane == nil then |
600 | self.defaultFillPlane = fillPlane; |
601 | end; |
602 | end; |
603 | i = i +1; |
604 | end; |
605 | if self.defaultFillPlane==nil then |
606 | self.fillPlanes = nil; |
607 | else |
608 | local animCurve = AnimCurve:new(linearInterpolatorTransRotScale); |
609 | local keyI = 0; |
610 | while true do |
611 | local animKey = string.format("vehicle.grainPlane.key(%d)", keyI); |
612 | local keyTime = getXMLFloat(xmlFile, animKey.."#time"); |
613 | local y = getXMLFloat(xmlFile, animKey.."#y"); |
614 | local sx,sy,sz = Utils.getVectorFromString(getXMLString(xmlFile, animKey.."#scale")); |
615 | if keyTime == nil then |
616 | break; |
617 | end; |
618 | local x = Utils.getNoNil(x, defaultX); |
619 | local y = Utils.getNoNil(y, defaultY); |
620 | local z = Utils.getNoNil(z, defaultZ); |
621 | local rx = Utils.getNoNil(rx, defaultRX); |
622 | local ry = Utils.getNoNil(ry, defaultRY); |
623 | local rz = Utils.getNoNil(rz, defaultRZ); |
624 | local sx = Utils.getNoNil(sx, defaultSX); |
625 | local sy = Utils.getNoNil(sy, defaultSY); |
626 | local sz = Utils.getNoNil(sz, defaultSZ); |
627 | animCurve:addKeyframe({x=x, y=y, z=z, rx=rx, ry=ry, rz=rz, sx=sx, sy=sy, sz=sz, time = keyTime}); |
628 | keyI = keyI +1; |
629 | end; |
630 | if keyI == 0 then |
631 | local minY, maxY = Utils.getVectorFromString(getXMLString(xmlFile, "vehicle.grainPlane#minMaxY")); |
632 | local minY = Utils.getNoNil(minY, defaultY); |
633 | local maxY = Utils.getNoNil(maxY, defaultY); |
634 | animCurve:addKeyframe({x=defaultX, y=minY, z=defaultZ, rx=defaultRX, ry=defaultRY, rz=defaultRZ, sx=defaultSX, sy=defaultSY, sz=defaultSZ, time = 0}); |
635 | animCurve:addKeyframe({x=defaultX, y=maxY, z=defaultZ, rx=defaultRX, ry=defaultRY, rz=defaultRZ, sx=defaultSX, sy=defaultSY, sz=defaultSZ, time = 1}); |
636 | end; |
637 | |
638 | for _, fillPlane in pairs(self.fillPlanes) do |
639 | fillPlane.nodes[1].animCurve = animCurve; |
640 | end; |
641 | end; |
642 | end; |
643 | end; |
644 | |
645 | end; |
646 | |
647 | function Fillable:delete() |
648 | if self.unloadTrigger ~= nil then |
649 | self.unloadTrigger:delete(); |
650 | self.unloadTrigger = nil; |
651 | end; |
652 | |
653 | for _, fillVolume in pairs(self.fillVolumes) do |
654 | if fillVolume.volume ~= nil then |
655 | delete(fillVolume.volume); |
656 | end; |
657 | fillVolume.volume = nil; |
658 | end; |
659 | |
660 | if self.fillActivatable ~= nil then |
661 | g_currentMission:removeActivatableObject(self.fillActivatable); |
662 | end; |
663 | if self.shovelFillTrigger ~= nil then |
664 | self.shovelFillTrigger:delete(); |
665 | end |
666 | end; |
667 | |
668 | function Fillable:loadFromAttributesAndNodes(xmlFile, key, resetVehicles) |
669 | if self.synchronizeFillLevel then |
670 | local fillLevel = getXMLFloat(xmlFile, key.."#fillLevel"); |
671 | local fillType = getXMLString(xmlFile, key.."#fillType"); |
672 | if fillLevel ~= nil and fillType ~= nil then |
673 | local fillTypeInt = Fillable.fillTypeNameToInt[fillType]; |
674 | if fillTypeInt ~= nil then |
675 | local fillSourceStruct = {x=0,y=0,z=0, d1x=0,d1y=0,d1z=0, d2x=0,d2y=0,d2z=0}; |
676 | self:setFillLevel(fillLevel, fillTypeInt, false, fillSourceStruct); |
677 | end; |
678 | end; |
679 | end |
680 | return BaseMission.VEHICLE_LOAD_OK; |
681 | end; |
682 | |
683 | function Fillable:getSaveAttributesAndNodes(nodeIdent) |
684 | if self.synchronizeFillLevel then |
685 | local fillType = Fillable.fillTypeIntToName[self.currentFillType]; |
686 | if fillType == nil then |
687 | fillType = "unknown"; |
688 | end; |
689 | local attributes = 'fillLevel="'..self.fillLevel..'" fillType="'..fillType..'"'; |
690 | return attributes, nil; |
691 | end |
692 | return nil; |
693 | end; |
694 | |
695 | function Fillable:getXMLStatsAttributes() |
696 | local fillType = Fillable.fillTypeIntToName[self.currentFillType]; |
697 | if fillType == nil then |
698 | fillType = "unknown"; |
699 | end |
700 | return string.format('fillLevel="%.3f" fillType="%s"', self:getFillLevel(self.currentFillType), Utils.encodeToHTML(tostring(fillType))); |
701 | end |
702 | |
703 | function Fillable:addNodeVehicleMapping(list) |
704 | list[self.fillRootNode] = self; |
705 | list[self.exactFillRootNode] = self; |
706 | end; |
707 | |
708 | function Fillable:removeNodeVehicleMapping(list) |
709 | list[self.fillRootNode] = nil; |
710 | list[self.exactFillRootNode] = nil; |
711 | end; |
712 | |
713 | function Fillable:mouseEvent(posX, posY, isDown, isUp, button) |
714 | end; |
715 | |
716 | function Fillable:keyEvent(unicode, sym, modifier, isDown) |
717 | end; |
718 | |
719 | function Fillable:readStream(streamId, connection) |
720 | if connection:getIsServer() then |
721 | if self.synchronizeFillLevel then |
722 | local fillLevel = streamReadFloat32(streamId); |
723 | local fillType = streamReadUIntN(streamId, Fillable.sendNumBits); |
724 | local fillSourceStruct = {x=0,y=0,z=0, d1x=0,d1y=0,d1z=0, d2x=0,d2y=0,d2z=0}; |
725 | self:setFillLevel(fillLevel, fillType, false, fillSourceStruct); |
726 | |
727 | local lastValidFillType = streamReadUIntN(streamId, Fillable.sendNumBits); |
728 | self:setLastValidFillType(lastValidFillType, true); |
729 | end |
730 | end |
731 | if self.supportsFillTriggers then |
732 | local isFilling = streamReadBool(streamId); |
733 | self:setIsFilling(isFilling, true); |
734 | end; |
735 | end; |
736 | |
737 | function Fillable:writeStream(streamId, connection) |
738 | if not connection:getIsServer() then |
739 | if self.synchronizeFillLevel then |
740 | streamWriteFloat32(streamId, self.fillLevel); |
741 | streamWriteUIntN(streamId, self.currentFillType, Fillable.sendNumBits); |
742 | streamWriteUIntN(streamId, self.lastValidFillType, Fillable.sendNumBits); |
743 | end |
744 | end; |
745 | if self.supportsFillTriggers then |
746 | streamWriteBool(streamId, self.isFilling); |
747 | end; |
748 | end; |
749 | |
750 | function Fillable:readUpdateStream(streamId, timestamp, connection) |
751 | if connection:getIsServer() then |
752 | if self.synchronizeFillLevel then |
753 | if streamReadBool(streamId) then |
754 | local fillLevel; |
755 | if self.synchronizeFullFillLevel then |
756 | fillLevel = streamReadFloat32(streamId); |
757 | else |
758 | fillLevel = streamReadUInt16(streamId)/65535*self:getCapacity(); |
759 | end |
760 | local fillType = streamReadUIntN(streamId, Fillable.sendNumBits); |
761 | |
762 | self:setFillLevel(fillLevel, fillType, true); |
763 | local lastValidFillType = streamReadUIntN(streamId, Fillable.sendNumBits); |
764 | self:setLastValidFillType(lastValidFillType, lastValidFillType ~= self.lastValidFillType); -- or self.currentFillType == Fillable.FILLTYPE_UNKNOWN |
765 | end; |
766 | end |
767 | end; |
768 | end; |
769 | |
770 | function Fillable:writeUpdateStream(streamId, connection, dirtyMask) |
771 | if not connection:getIsServer() then |
772 | if self.synchronizeFillLevel then |
773 | if streamWriteBool(streamId, bitAND(dirtyMask, self.fillableDirtyFlag) ~= 0) then |
774 | if self.synchronizeFullFillLevel then |
775 | streamWriteFloat32(streamId, self.fillLevel); |
776 | else |
777 | local percent = 0; |
778 | if self:getCapacity() ~= 0 then |
779 | percent = Utils.clamp(self.fillLevel / self:getCapacity(), 0, 1); |
780 | end; |
781 | streamWriteUInt16(streamId, math.floor(percent*65535)); |
782 | end |
783 | streamWriteUIntN(streamId, self.currentFillType, Fillable.sendNumBits); |
784 | streamWriteUIntN(streamId, self.lastValidFillType, Fillable.sendNumBits); |
785 | end; |
786 | end |
787 | end; |
788 | end; |
789 | |
790 | function Fillable:update(dt) |
791 | if self.firstTimeRun then |
792 | if self.isServer then |
793 | if self.emptyMass == nil then |
794 | self.emptyMass = getMass(self.fillMassNode); |
795 | self.currentMass = self.emptyMass; |
796 | end; |
797 | local massScale = 0; |
798 | if self.currentFillType ~= nil and self.currentFillType ~= Fillable.FILLTYPE_UNKNOWN then |
799 | massScale = Fillable.fillTypeIndexToDesc[self.currentFillType].massPerLiter; |
800 | end; |
801 | local newMass = self.emptyMass + self.fillLevel*massScale; |
802 | if newMass ~= self.currentMass then |
803 | setMass(self.fillMassNode, newMass); |
804 | self.currentMass = newMass; |
805 | end; |
806 | end; |
807 | end; |
808 | if self.isClient and (self:getIsActive() or self.tipState == Trailer.TIPSTATE_OPENING or self.tipState == Trailer.TIPSTATE_OPEN) then |
809 | for _,deformer in pairs(self.fillVolumeDeformers) do |
810 | if deformer.deformerIsDirty and deformer.polyline ~= nil and deformer.polyline ~= -1 then |
811 | deformer.deformerIsDirty = false; |
812 | local posX, _, posZ = localToLocal(deformer.node, deformer.baseNode, 0,0,0); |
813 | local dirty = false; |
814 | if math.abs(posX - deformer.posX) > 0.0001 or math.abs(posZ - deformer.posZ) > 0.0001 then |
815 | deformer.lastPosX = posX; |
816 | deformer.lastPosZ = posZ; |
817 | local dx = posX - deformer.initPos[1]; |
818 | local dz = posZ - deformer.initPos[3]; |
819 | setPolylineTranslation(deformer.volume, deformer.polyline, dx,dz); |
820 | end; |
821 | end; |
822 | end; |
823 | |
824 | for _,fillVolumeHeight in pairs(self.fillVolumeHeights) do |
825 | if fillVolumeHeight.volumeHeightIsDirty == true and self.fillVolumes[fillVolumeHeight.fillVolumeId] ~= nil then |
826 | fillVolumeHeight.volumeHeightIsDirty = false; |
827 | |
828 | local baseNode = self.fillVolumes[fillVolumeHeight.fillVolumeId].baseNode; |
829 | local volumeNode = self.fillVolumes[fillVolumeHeight.fillVolumeId].volume; |
830 | |
831 | if baseNode ~= nil and volumeNode ~= nil then |
832 | |
833 | local minHeight = math.huge; |
834 | for _,refNode in pairs(fillVolumeHeight.refNodes) do |
835 | local x,_,z = localToLocal(refNode.refNode, baseNode, 0,0,0); |
836 | minHeight = math.min(minHeight, getFillPlaneHeightAtLocalPos(volumeNode, x,z)); |
837 | end; |
838 | |
839 | for _,node in pairs(fillVolumeHeight.nodes) do |
840 | local sx = node.scaleAxis[1]*minHeight; |
841 | local sy = node.scaleAxis[2]*minHeight; |
842 | local sz = node.scaleAxis[3]*minHeight; |
843 | if node.scaleMax[1] > 0 then |
844 | sx = math.min(node.scaleMax[1], sx); |
845 | end |
846 | if node.scaleMax[2] > 0 then |
847 | sy = math.min(node.scaleMax[2], sy); |
848 | end |
849 | if node.scaleMax[3] > 0 then |
850 | sz = math.min(node.scaleMax[3], sz); |
851 | end |
852 | local tx = node.transAxis[1]*minHeight; |
853 | local ty = node.transAxis[2]*minHeight; |
854 | local tz = node.transAxis[3]*minHeight; |
855 | if node.transMax[1] > 0 then |
856 | tx = math.min(node.transMax[1], tx); |
857 | end |
858 | if node.transMax[2] > 0 then |
859 | ty = math.min(node.transMax[2], ty); |
860 | end |
861 | if node.transMax[3] > 0 then |
862 | tz = math.min(node.transMax[3], tz); |
863 | end |
864 | |
865 | setScale(node.node, node.baseScale[1]+sx, node.baseScale[2]+sy, node.baseScale[3]+sz); |
866 | setTranslation(node.node, node.basePosition[1]+tx, node.basePosition[2]+ty, node.basePosition[3]+tz); |
867 | |
868 | if node.orientateToWorldY then |
869 | local dx,dy,dz = localDirectionToWorld(getParent(node.node), 0,1,0); |
870 | local alpha = math.acos(dy); |
871 | setRotation(node.node, alpha,0,0); |
872 | end; |
873 | end; |
874 | end; |
875 | end; |
876 | end; |
877 | end; |
878 | end; |
879 | |
880 | function Fillable:setMovingToolDirty(node) |
881 | if self.fillVolumeDeformers[node] ~= nil then |
882 | self.fillVolumeDeformers[node].deformerIsDirty = true; |
883 | end; |
884 | if self.fillVolumeHeightRefNodeToFillVolumeHeight[node] ~= nil then |
885 | self.fillVolumeHeightRefNodeToFillVolumeHeight[node].volumeHeightIsDirty = true; |
886 | end; |
887 | end |
888 | |
889 | function Fillable:updateTick(dt) |
890 | if self.isServer then |
891 | if self.synchronizeFillLevel then |
892 | if self.fillLevel ~= self.sentFillLevel or self.currentFillType ~= self.sentFillType or self.lastValidFillType ~= self.sentLastValidFillType then |
893 | self:raiseDirtyFlags(self.fillableDirtyFlag); |
894 | self.sentLastValidFillType = self.lastValidFillType; |
895 | self.sentFillLevel = self.fillLevel; |
896 | self.sentFillType = self.currentFillType; |
897 | end; |
898 | end |
899 | if self.shovelFillTrigger ~= nil then |
900 | self.shovelFillTrigger:update(dt); |
901 | end |
902 | |
903 | if self.isFilling then |
904 | local delta = 0; |
905 | local doFill = false; |
906 | if self.fillTrigger ~= nil then |
907 | if self.unitFillTime ~= nil then |
908 | self.currentFillTime = self.currentFillTime - dt; |
909 | if self.currentFillTime <= 0 then |
910 | doFill = true; |
911 | delta = self.fillTrigger:fill(self, 1); |
912 | self.currentFillTime = self.unitFillTime; |
913 | end; |
914 | else |
915 | delta = self.fillLitersPerSecond*dt*0.001; |
916 | delta = self.fillTrigger:fill(self, delta); |
917 | doFill = true; |
918 | end; |
919 | end |
920 | |
921 | if delta <= 0 and doFill then |
922 | self:setIsFilling(false); |
923 | end; |
924 | end |
925 | end; |
926 | |
927 | if self.isClient then |
928 | if self.measurementTime > 0 then |
929 | self.measurementTime = self.measurementTime - dt; |
930 | if self.measurementTime <= 0 then |
931 | self:updateMeasurementNode(); |
932 | end; |
933 | end; |
934 | end; |
935 | end; |
936 | |
937 | function Fillable:draw() |
938 | if self.isClient then |
939 | if self:getDoRenderFillType() then |
940 | local fillType = self:getOverlayFillType() |
941 | if fillType ~= Fillable.FILLTYPE_UNKNOWN then |
942 | g_currentMission:setFillTypeOverlayFillType(fillType); |
943 | end; |
944 | end; |
945 | end; |
946 | end; |
947 | |
948 | function Fillable:getAdditiveClientMass() |
949 | local massScale = 0; |
950 | if self.currentFillType ~= nil and self.currentFillType ~= Fillable.FILLTYPE_UNKNOWN then |
951 | massScale = Fillable.fillTypeIndexToDesc[self.currentFillType].massPerLiter; |
952 | end; |
953 | return self.fillLevel*massScale; |
954 | end; |
955 | |
956 | function Fillable:resetFillLevelIfNeeded(fillType) |
957 | if self.currentFillType ~= fillType then |
958 | self.fillLevel = 0; |
959 | end; |
960 | end; |
961 | |
962 | function Fillable:allowFillType(fillType, allowEmptying) |
963 | local allowed = false; |
964 | |
965 | if self.fillTypes[fillType] then |
966 | if self.currentFillType ~= Fillable.FILLTYPE_UNKNOWN then |
967 | if self.currentFillType ~= fillType then |
968 | if self.fillLevel <= self:getCapacity()*self.fillTypeChangeThreshold then |
969 | allowed = true; -- fill level is low enough to be overridden |
970 | if allowEmptying then |
971 | self.fillLevel = 0; -- empty the trailer |
972 | end; |
973 | end; |
974 | else |
975 | allowed = true; -- fill type is the same as the trailer's current fill type |
976 | end; |
977 | else |
978 | allowed = true; -- fillable is empty --> Fillable.FILLTYPE_UNKNOWN |
979 | end; |
980 | end; |
981 | |
982 | return allowed; |
983 | end; |
984 | |
985 | function Fillable:getFillLevel(fillType) |
986 | if fillType == self.currentFillType then |
987 | return self.fillLevel; |
988 | end |
989 | return 0; |
990 | end |
991 | |
992 | function Fillable:setFillLevel(fillLevel, fillType, force, fillSourceStruct) |
993 | if (force == nil or force == false) and not self:allowFillType(fillType, false) then |
994 | return |
995 | end; |
996 | |
997 | if fillType ~= self.currentFillType then |
998 | local maxPhysicalSurfaceAngle; |
999 | if FruitUtil.fruitIndexToDesc[fillType] ~= nil then |
1000 | maxPhysicalSurfaceAngle = FruitUtil.fruitIndexToDesc[fillType].maxPhysicalSurfaceAngle; |
1001 | elseif Fillable.fillTypeIntToName[fillType] ~= nil then |
1002 | maxPhysicalSurfaceAngle = Fillable.fillTypeIntToName[fillType].maxPhysicalSurfaceAngle; |
1003 | end; |
1004 | if maxPhysicalSurfaceAngle ~= nil then |
1005 | for _,fillVolume in pairs(self.fillVolumes) do |
1006 | if fillVolume.volume ~= nil then |
1007 | setFillPlaneMaxPhysicalSurfaceAngle(fillVolume.volume, maxPhysicalSurfaceAngle); |
1008 | end; |
1009 | end; |
1010 | end; |
1011 | end; |
1012 | |
1013 | if fillLevel < self.fillLevel and fillLevel < 0.0000001 then |
1014 | fillLevel = 0; |
1015 | end; |
1016 | |
1017 | local delta = math.min(fillLevel-self.fillLevel, self.capacity); |
1018 | local lastFillType = self.currentFillType; |
1019 | self.currentFillType = fillType; |
1020 | self.fillLevel = fillLevel; |
1021 | |
1022 | if self:getCapacity() == 0 then |
1023 | self.fillLevel = math.max(fillLevel, 0); |
1024 | else |
1025 | self.fillLevel = Utils.clamp(fillLevel, 0, self:getCapacity()); |
1026 | end; |
1027 | |
1028 | if self.fillLevel <= 0 then |
1029 | self.fillLevel = 0; |
1030 | self.currentFillType = Fillable.FILLTYPE_UNKNOWN; |
1031 | end; |
1032 | |
1033 | if self.unloadTrigger ~= nil then |
1034 | self.unloadTrigger.fillType = self.currentFillType; |
1035 | end; |
1036 | |
1037 | if self.isClient then |
1038 | if self.currentFillPlane ~= nil then |
1039 | for _, node in ipairs(self.currentFillPlane.nodes) do |
1040 | setVisibility(node.node, false); |
1041 | end; |
1042 | self.currentFillPlane = nil; |
1043 | end; |
1044 | if self.fillPlanes ~= nil and self.defaultFillPlane ~= nil then |
1045 | local fillPlane; |
1046 | local t = 0; |
1047 | if fillType ~= Fillable.FILLTYPE_UNKNOWN then |
1048 | local fillTypeName = Fillable.fillTypeIntToName[fillType]; |
1049 | fillPlane = self.fillPlanes[fillTypeName]; |
1050 | t = self.fillLevel/self:getCapacity(); |
1051 | end |
1052 | if fillPlane == nil then |
1053 | fillPlane = self.defaultFillPlane |
1054 | end |
1055 | for _, node in ipairs(fillPlane.nodes) do |
1056 | local x,y,z, rx,ry,rz, sx,sy,sz = node.animCurve:get(t); |
1057 | |
1058 | setTranslation(node.node, x, y, z); |
1059 | setRotation(node.node, rx, ry, rz); |
1060 | setScale(node.node, sx, sy, sz); |
1061 | setVisibility(node.node, self.fillLevel > 0 or node.alwaysVisible); |
1062 | end; |
1063 | self.currentFillPlane = fillPlane; |
1064 | end; |
1065 | |
1066 | local materialId = nil; |
1067 | local isDefault = false; |
1068 | if self.currentFillType ~= Fillable.FILLTYPE_UNKNOWN and self.currentFillType ~= lastFillType then |
1069 | local usedFillType = self.currentFillType; |
1070 | if self.forcedFillPlaneType ~= nil then |
1071 | usedFillType = self.forcedFillPlaneType; |
1072 | end; |
1073 | materialId, isDefault = Fillable.getFillMaterial(usedFillType, Fillable.MATERIAL_TYPE_FILLPLANE, 1); |
1074 | end; |
1075 | |
1076 | for _, fillVolume in pairs(self.fillVolumes) do |
1077 | |
1078 | setVisibility(fillVolume.volume, self.fillLevel > 0); |
1079 | |
1080 | if self.currentFillType ~= Fillable.FILLTYPE_UNKNOWN and self.currentFillType ~= lastFillType then |
1081 | if isDefault and fillVolume.defaultFillType ~= nil then |
1082 | materialId = Fillable.getFillMaterial(fillVolume.defaultFillType, Fillable.MATERIAL_TYPE_FILLPLANE, 1); |
1083 | end; |
1084 | if materialId ~= nil then |
1085 | setMaterial(fillVolume.volume, materialId, 0); |
1086 | end; |
1087 | end; |
1088 | |
1089 | if fillSourceStruct ~= nil then |
1090 | local fss = fillSourceStruct; |
1091 | if Vehicle.debugRendering then |
1092 | drawDebugLine( fss.x, fss.y, fss.z, 1,0,0, fss.x+fss.d1x, fss.y+fss.d1y, fss.z+fss.d1z, 1,0,0 ); |
1093 | drawDebugLine( fss.x, fss.y, fss.z, 0,0,1, fss.x+fss.d2x, fss.y+fss.d2y, fss.z+fss.d2z, 0,0,1 ); |
1094 | drawDebugPoint( fss.x, fss.y, fss.z, 1,1,1,1 ); |
1095 | drawDebugPoint( fss.x+fss.d1x, fss.y+fss.d1y, fss.z+fss.d1z, 1,0,0,1 ); |
1096 | drawDebugPoint( fss.x+fss.d2x, fss.y+fss.d2y, fss.z+fss.d2z, 0,0,1,1 ); |
1097 | end; |
1098 | fss.x = fss.x - (fss.d1x + fss.d2x) / 2; |
1099 | fss.y = fss.y - (fss.d1y + fss.d2y) / 2; |
1100 | fss.z = fss.z - (fss.d1z + fss.d2z) / 2; |
1101 | fillPlaneAdd(fillVolume.volume, delta, fss.x,fss.y,fss.z, fss.d1x,fss.d1y,fss.d1z, fss.d2x,fss.d2y,fss.d2z); |
1102 | else |
1103 | local x,y,z = localToWorld(fillVolume.volume, 0,100,0); |
1104 | local d1x,d1y,d1z = localDirectionToWorld(fillVolume.volume, 5,0,0); |
1105 | local d2x,d2y,d2z = localDirectionToWorld(fillVolume.volume, 0,0,5); |
1106 | x = x - (d1x+d2x)/2; |
1107 | y = y - (d1y+d2y)/2; |
1108 | z = z - (d1z+d2z)/2; |
1109 | fillPlaneAdd(fillVolume.volume, delta, x,y,z, d1x,d1y,d1z, d2x,d2y,d2z); |
1110 | end; |
1111 | end; |
1112 | end; |
1113 | |
1114 | if self.currentFillType ~= Fillable.FILLTYPE_UNKNOWN then |
1115 | self:setLastValidFillType(fillType, self.lastValidFillType ~= self.currentFillType); |
1116 | end |
1117 | |
1118 | self.measurementTime = 5000; |
1119 | self:updateMeasurementNode(); |
1120 | end; |
1121 | |
1122 | function Fillable:setCapacity(capacity) |
1123 | self.capacity = capacity; |
1124 | end; |
1125 | |
1126 | function Fillable:getCapacity() |
1127 | return self.capacity; |
1128 | end; |
1129 | |
1130 | function Fillable:getAllowFillFromAir() |
1131 | return self.allowFillFromAir; |
1132 | end; |
1133 | |
1134 | function Fillable:getFirstEnabledFillType() |
1135 | for fillType, enabled in pairs(self.fillTypes) do |
1136 | if fillType ~= Fillable.FILLTYPE_UNKNOWN and enabled then |
1137 | return fillType; |
1138 | end |
1139 | end |
1140 | return Fillable.FILLTYPE_UNKNOWN; |
1141 | end |
1142 | |
1143 | function Fillable:setLastValidFillType(lastValidFillType, hasChanged) |
1144 | if self.lastValidFillType ~= lastValidFillType then |
1145 | self.lastValidFillType = lastValidFillType; |
1146 | end; |
1147 | end; |
1148 | |
1149 | function Fillable:getOverlayFillType() |
1150 | if self.currentFillType ~= Fillable.FILLTYPE_UNKNOWN then |
1151 | return self.currentFillType; |
1152 | end; |
1153 | return Fillable.FILLTYPE_UNKNOWN; |
1154 | end; |
1155 | |
1156 | function Fillable:getDoRenderFillType() |
1157 | return self.currentFillType ~= Fillable.FILLTYPE_UNKNOWN; |
1158 | end; |
1159 | |
1160 | function Fillable:setIsFilling(isFilling, noEventSend) |
1161 | SetIsFillingEvent.sendEvent(self, isFilling, noEventSend) |
1162 | if self.isFilling ~= isFilling then |
1163 | self.isFilling = isFilling; |
1164 | |
1165 | self.fillTrigger = nil; |
1166 | if isFilling then |
1167 | -- find the first trigger which is activable |
1168 | for i, trigger in ipairs(self.fillTriggers) do |
1169 | if trigger:getIsActivatable(self) then |
1170 | self.fillTrigger = trigger; |
1171 | break; |
1172 | end; |
1173 | end; |
1174 | end |
1175 | end; |
1176 | end; |
1177 | |
1178 | function Fillable:addFillTrigger(trigger) |
1179 | if table.getn(self.fillTriggers) == 0 then |
1180 | g_currentMission:addActivatableObject(self.fillActivatable); |
1181 | end; |
1182 | table.insert(self.fillTriggers, trigger); |
1183 | end; |
1184 | |
1185 | function Fillable:removeFillTrigger(trigger) |
1186 | for i=1, table.getn(self.fillTriggers) do |
1187 | if self.fillTriggers[i] == trigger then |
1188 | table.remove(self.fillTriggers, i); |
1189 | break; |
1190 | end; |
1191 | end; |
1192 | if table.getn(self.fillTriggers) == 0 then |
1193 | if self.isServer then |
1194 | self:setIsFilling(false); |
1195 | end; |
1196 | g_currentMission:removeActivatableObject(self.fillActivatable); |
1197 | end; |
1198 | end; |
1199 | |
1200 | function Fillable:updateMeasurementNode() |
1201 | if self.measurementNode ~= nil then |
1202 | local isWorking = 1; |
1203 | if self.measurementTime <= 0 then |
1204 | isWorking = 0; |
1205 | end; |
1206 | setShaderParameter(self.measurementNode, "fillLevel", self.fillLevel/self:getCapacity(), isWorking, 0, 0, false); |
1207 | end; |
1208 | end; |
1209 | |
1210 | function Fillable:onAttach(attacherVehicle) |
1211 | if self.attacherPipe ~= nil then |
1212 | local vehicle = Fillable.findAttacherPipeVehicle(self:getRootAttacherVehicle(), true); |
1213 | if vehicle ~= nil then |
1214 | self:attachPipe(self, vehicle); |
1215 | end; |
1216 | elseif self.attacherPipeRef ~= nil then |
1217 | local vehicle = Fillable.findAttacherPipeVehicle(self:getRootAttacherVehicle(), false); |
1218 | if vehicle ~= nil then |
1219 | self:attachPipe(vehicle, self); |
1220 | end; |
1221 | end; |
1222 | end; |
1223 | |
1224 | function Fillable:onDetach() |
1225 | self:detachPipe(); |
1226 | end; |
1227 | |
1228 | function Fillable:attachPipe(pipeVehicle, refVehicle) |
1229 | pipeVehicle.attacherPipeRefVehicle = refVehicle; |
1230 | refVehicle.attacherPipeVehicle = pipeVehicle; |
1231 | |
1232 | for _, movingPart in pairs(pipeVehicle.activeDirtyMovingParts) do |
1233 | if movingPart.node == pipeVehicle.attacherPipe then |
1234 | movingPart.referenceFrame = refVehicle.attacherPipeRef; |
1235 | movingPart.referenceFrameOffset = {0,0,0}; |
1236 | Cylindered.updateMovingPart(self, movingPart, false); |
1237 | break; |
1238 | end; |
1239 | end; |
1240 | end; |
1241 | |
1242 | function Fillable:detachPipe() |
1243 | if self.attacherPipeRefVehicle ~= nil then |
1244 | for _, movingPart in pairs(self.activeDirtyMovingParts) do |
1245 | if movingPart.node == self.attacherPipe then |
1246 | movingPart.referenceFrame = self.rootNode; |
1247 | break; |
1248 | end; |
1249 | end; |
1250 | self.attacherPipeRefVehicle.attacherPipeVehicle = nil; |
1251 | self.attacherPipeRefVehicle = nil; |
1252 | end; |
1253 | if self.attacherPipeVehicle ~= nil then |
1254 | for _, movingPart in pairs(self.attacherPipeVehicle.activeDirtyMovingParts) do |
1255 | if movingPart.node == self.attacherPipeVehicle.attacherPipe then |
1256 | movingPart.referenceFrame = self.attacherPipeVehicle.rootNode; |
1257 | break; |
1258 | end; |
1259 | end; |
1260 | self.attacherPipeVehicle.attacherPipeRefVehicle = nil; |
1261 | local attacherPipeVehicle = self.attacherPipeVehicle; |
1262 | self.attacherPipeVehicle = nil; |
1263 | -- try to reattach |
1264 | local vehicle = Fillable.findAttacherPipeVehicle(attacherPipeVehicle:getRootAttacherVehicle(), true); |
1265 | if vehicle ~= nil then |
1266 | attacherPipeVehicle:attachPipe(attacherPipeVehicle, vehicle); |
1267 | end; |
1268 | end; |
1269 | end; |
1270 | |
1271 | function Fillable.findAttacherPipeVehicle(currentVehicle, lookForPipeRef) |
1272 | if lookForPipeRef then |
1273 | if currentVehicle.attacherPipeRef ~= nil and currentVehicle.attacherPipeRefVehicle == nil then |
1274 | return currentVehicle; |
1275 | end; |
1276 | else |
1277 | if currentVehicle.attacherPipe ~= nil and currentVehicle.attacherPipeVehicle == nil then |
1278 | return currentVehicle; |
1279 | end; |
1280 | end; |
1281 | for _,implement in pairs(currentVehicle.attachedImplements) do |
1282 | if implement.object ~= nil then |
1283 | local ret = Fillable.findAttacherPipeVehicle(implement.object, lookForPipeRef); |
1284 | if ret ~= nil then |
1285 | return ret; |
1286 | end |
1287 | end |
1288 | end |
1289 | return nil; |
1290 | end
|
Copyright (c) 2008-2015 GIANTS Software GmbH, Confidential, All Rights Reserved.
This document is to be published solely by ls-mods.de