180 | function WoodUnloadTrigger:calculateWoodBaseValue(objectId) |
181 | local volume = getVolume(objectId) |
182 | local splitType = g_splitTypeManager:getSplitTypeByIndex(getSplitType(objectId)) |
183 | local sizeX, sizeY, sizeZ, numConvexes, numAttachments = getSplitShapeStats(objectId) |
184 | |
185 | return self:calculateWoodBaseValueForData(volume, splitType, sizeX, sizeY, sizeZ, numConvexes, numAttachments) |
186 | end |
190 | function WoodUnloadTrigger:calculateWoodBaseValueForData(volume, splitType, sizeX, sizeY, sizeZ, numConvexes, numAttachments) |
191 | local qualityScale = 1 |
192 | local lengthScale = 1 |
193 | local defoliageScale = 1 |
194 | if sizeX ~= nil and volume > 0 then |
195 | local bvVolume = sizeX*sizeY*sizeZ |
196 | local volumeRatio = bvVolume / volume |
197 | local volumeQuality = 1-math.sqrt(MathUtil.clamp((volumeRatio-3)/7, 0,1)) * 0.95 -- ratio <= 3: 100%, ratio >= 10: 5% |
198 | local convexityQuality = 1-MathUtil.clamp((numConvexes-2)/(6-2), 0,1) * 0.95 -- 0-2: 100%:, >= 6: 5% |
199 | |
200 | local maxSize = math.max(sizeX, sizeY, sizeZ) |
201 | -- 1m: 60%, 6-11m: 120%, 19m: 60% |
202 | if maxSize < 11 then |
203 | lengthScale = 0.6 + math.min(math.max((maxSize-1)/5, 0), 1)*0.6 |
204 | else |
205 | lengthScale = 1.2 - math.min(math.max((maxSize-11)/8, 0), 1)*0.6 |
206 | end |
207 | |
208 | local minQuality = math.min(convexityQuality, volumeQuality) |
209 | local maxQuality = math.max(convexityQuality, volumeQuality) |
210 | qualityScale = minQuality + (maxQuality - minQuality) * 0.3 -- use 70% of min quality |
211 | |
212 | defoliageScale = 1-math.min(numAttachments/15, 1) * 0.8 -- #attachments 0: 100%, >=15: 20% |
213 | end |
214 | |
215 | -- Only take 33% into account of the quality criteria on low |
216 | qualityScale = MathUtil.lerp(1, qualityScale, g_currentMission.missionInfo.economicDifficulty / 3) |
217 | defoliageScale = MathUtil.lerp(1, defoliageScale, g_currentMission.missionInfo.economicDifficulty / 3) |
218 | |
219 | return volume * 1000, splitType.pricePerLiter * qualityScale * defoliageScale * lengthScale |
220 | end |
56 | function WoodUnloadTrigger:load(components, xmlFile, xmlNode, target, i3dMappings, rootNode) |
57 | local triggerNodeKey = xmlNode .. "#triggerNode" |
58 | self.triggerNode = xmlFile:getValue(triggerNodeKey, nil, components, i3dMappings) |
59 | if self.triggerNode ~= nil then |
60 | local colMask = getCollisionMask(self.triggerNode) |
61 | if bitAND(SplitTypeManager.COLLISIONMASK_TRIGGER, colMask) == 0 then |
62 | Logging.xmlWarning(xmlFile, "Invalid collision mask for wood trigger '%s'. Bit 24 needs to be set!", triggerNodeKey) |
63 | return false |
64 | end |
65 | addTrigger(self.triggerNode, "woodTriggerCallback", self) |
66 | else |
67 | return false |
68 | end |
69 | |
70 | |
71 | local activationTrigger = xmlFile:getValue(xmlNode .. "#activationTriggerNode", nil, components, i3dMappings) |
72 | if activationTrigger ~= nil then |
73 | if not CollisionFlag.getHasFlagSet(activationTrigger, CollisionFlag.TRIGGER_PLAYER) then |
74 | Logging.xmlWarning(xmlFile, "Missing collision mask bit '%d'. Please add this bit to sell trigger node '%s' in 'placeable.woodSellingStation#sellTrigger'.", CollisionFlag.getBit(CollisionFlag.TRIGGER_PLAYER), getName(activationTrigger)) |
75 | return false |
76 | end |
77 | |
78 | self.activationTrigger = activationTrigger |
79 | if self.activationTrigger ~= nil then |
80 | addTrigger(self.activationTrigger, "woodSellTriggerCallback", self) |
81 | end |
82 | end |
83 | |
84 | self.trainSystemId = xmlFile:getValue(xmlNode .. "#trainSystemId") |
85 | self.trainSystem = nil |
86 | |
87 | if target ~= nil then |
88 | self:setTarget(target) |
89 | end |
90 | |
91 | |
92 | self.activatable = PlaceableWoodSellingStationActivatable.new(self) |
93 | |
94 | return true |
95 | end |
134 | function WoodUnloadTrigger:processWood(farmId, noEventSend) |
135 | if not self.isServer then |
136 | g_client:getServerConnection():sendEvent(WoodUnloadTriggerEvent.new(self, farmId)) |
137 | return |
138 | end |
139 | |
140 | local soldWood = false |
141 | local totalMass = 0 |
142 | local isFull = false |
143 | |
144 | for _, nodeId in pairs(self.woodInTrigger) do |
145 | if entityExists(nodeId) then |
146 | soldWood = true |
147 | |
148 | local volume, qualityScale = self:calculateWoodBaseValue(nodeId) |
149 | self.extraAttributes.price = qualityScale -- override price and use actual values from splitTypes |
150 | |
151 | if g_currentMission:getIsServer() then |
152 | -- Do not sell if capacity is constrained (e.g. production points) |
153 | if self.target.getFillUnitFreeCapacity == nil or self.target:getFillUnitFreeCapacity(nil, FillType.WOOD, farmId) > volume * 0.9 then |
154 | self.target:addFillUnitFillLevel(farmId, nil, volume, FillType.WOOD, ToolType.undefined, nil, self.extraAttributes) |
155 | |
156 | -- object will be deleted on client automatically |
157 | delete(nodeId) |
158 | else |
159 | isFull = true |
160 | end |
161 | end |
162 | end |
163 | |
164 | if isFull then |
165 | break |
166 | else |
167 | self.woodInTrigger[nodeId] = nil |
168 | end |
169 | end |
170 | |
171 | if soldWood then |
172 | if g_currentMission:getIsServer() then |
173 | g_currentMission:farmStats():updateStats("woodTonsSold", totalMass) |
174 | end |
175 | end |
176 | end |
224 | function WoodUnloadTrigger:update(dt) |
225 | WoodUnloadTrigger:superClass().update(self, dt) |
226 | |
227 | if self.isServer then |
228 | local farmId = self.target:getOwnerFarmId() |
229 | |
230 | if self.trainSystemId ~= nil then |
231 | if self.trainSystem == nil then |
232 | for _, trainSystem in pairs(g_currentMission.trainSystems) do |
233 | if trainSystem.mapBoundId == self.trainSystemId then |
234 | self.trainSystem = trainSystem |
235 | break |
236 | end |
237 | end |
238 | end |
239 | |
240 | if self.trainSystem ~= nil then |
241 | farmId = self.trainSystem.spec_trainSystem.lastRentFarmId |
242 | end |
243 | end |
244 | |
245 | if farmId ~= FarmManager.SPECTATOR_FARM_ID then |
246 | self:processWood(farmId) |
247 | end |
248 | end |
249 | end |
260 | function WoodUnloadTrigger:woodTriggerCallback(triggerId, otherId, onEnter, onLeave, onStay, otherShapeId) |
261 | if otherId ~= 0 then |
262 | local splitType = g_splitTypeManager:getSplitTypeByIndex(getSplitType(otherId)) |
263 | if splitType ~= nil and splitType.pricePerLiter > 0 then |
264 | if onEnter then |
265 | self.woodInTrigger[otherId] = otherId |
266 | if self.trainSystemId ~= nil then |
267 | self:raiseActive() -- needed for train wood selling without trigger activation |
268 | end |
269 | else |
270 | self.woodInTrigger[otherId] = nil |
271 | end |
272 | end |
273 | end |
274 | end |