520 | function Wearable:addToLocalWearableNode(node, updateFunc, customIndex, extraParams) |
521 | local spec = self.spec_wearable |
522 | |
523 | local nodeData = {} |
524 | |
525 | --if wearableNode already exists we add node to existing wearableNode |
526 | if customIndex ~= nil then |
527 | if spec.wearableNodesByIndex[customIndex] ~= nil then |
528 | table.insert(spec.wearableNodesByIndex[customIndex].nodes, node) |
529 | return |
530 | else |
531 | spec.wearableNodesByIndex[customIndex] = nodeData |
532 | end |
533 | end |
534 | |
535 | --if wearableNode doesn't exists we create a new one |
536 | nodeData.nodes = {node} |
537 | nodeData.updateFunc = updateFunc |
538 | nodeData.wearAmount = 0 |
539 | nodeData.wearAmountSent = 0 |
540 | if extraParams ~= nil then |
541 | for i, v in pairs(extraParams) do |
542 | nodeData[i] = v |
543 | end |
544 | end |
545 | |
546 | table.insert(spec.wearableNodes, nodeData) |
547 | end |
34 | function Wearable.initSpecialization() |
35 | g_storeManager:addSpecType("wearable", "shopListAttributeIconCondition", Wearable.loadSpecValueCondition, Wearable.getSpecValueCondition, "vehicle") |
36 | |
37 | local schema = Vehicle.xmlSchema |
38 | schema:setXMLSpecializationType("Wearable") |
39 | |
40 | schema:register(XMLValueType.FLOAT, "vehicle.wearable#wearDuration", "Duration until fully worn (minutes)", 600) |
41 | schema:register(XMLValueType.FLOAT, "vehicle.wearable#workMultiplier", "Multiplier while working", 20) |
42 | schema:register(XMLValueType.FLOAT, "vehicle.wearable#fieldMultiplier", "Multiplier while on field", 2) |
43 | schema:register(XMLValueType.BOOL, "vehicle.wearable#showOnHud", "Show the damage on the hud", true) |
44 | |
45 | schema:setXMLSpecializationType() |
46 | |
47 | local schemaSavegame = Vehicle.xmlSchemaSavegame |
48 | schemaSavegame:register(XMLValueType.FLOAT, "vehicles.vehicle(?).wearable.wearNode(?)#amount", "Wear amount") |
49 | schemaSavegame:register(XMLValueType.FLOAT, "vehicles.vehicle(?).wearable#damage", "Damage amount") |
50 | end |
105 | function Wearable:onLoad(savegame) |
106 | local spec = self.spec_wearable |
107 | |
108 | spec.wearableNodes = {} |
109 | spec.wearableNodesByIndex = {} |
110 | self:addToLocalWearableNode(nil, Wearable.updateWearAmount, nil, nil) -- create global / default wearableNode |
111 | |
112 | spec.wearDuration = self.xmlFile:getValue("vehicle.wearable#wearDuration", 600) * 60 * 1000 -- default 600min / 10h |
113 | if spec.wearDuration ~= 0 then |
114 | spec.wearDuration = 1 / spec.wearDuration |
115 | end |
116 | |
117 | spec.totalAmount = 0 |
118 | |
119 | spec.damage = 0 |
120 | spec.damageByCurve = 0 |
121 | spec.damageSent = 0 |
122 | |
123 | spec.workMultiplier = self.xmlFile:getValue("vehicle.wearable#workMultiplier", 20) |
124 | spec.fieldMultiplier = self.xmlFile:getValue("vehicle.wearable#fieldMultiplier", 2) |
125 | |
126 | spec.showOnHud = self.xmlFile:getValue("vehicle.wearable#showOnHud", true) |
127 | |
128 | spec.dirtyFlag = self:getNextDirtyFlag() |
129 | end |
133 | function Wearable:onLoadFinished(savegame) |
134 | local spec = self.spec_wearable |
135 | |
136 | if savegame ~= nil then |
137 | spec.damage = savegame.xmlFile:getValue(savegame.key .. ".wearable#damage", 0) |
138 | spec.damageByCurve = math.max(spec.damage - 0.3, 0) / 0.7 |
139 | end |
140 | |
141 | -- getting als wearable nodes in postLoad to make sure also linked nodes are wearable |
142 | if spec.wearableNodes ~= nil then |
143 | for _, component in pairs(self.components) do |
144 | self:addAllSubWearableNodes(component.node) |
145 | end |
146 | |
147 | if savegame ~= nil then |
148 | for i, nodeData in ipairs(spec.wearableNodes) do |
149 | local nodeKey = string.format("%s.wearable.wearNode(%d)", savegame.key, i-1) |
150 | local amount = savegame.xmlFile:getValue(nodeKey.."#amount", 0) |
151 | self:setNodeWearAmount(nodeData, amount, true) |
152 | end |
153 | else |
154 | for _, nodeData in ipairs(spec.wearableNodes) do |
155 | self:setNodeWearAmount(nodeData, 0, true) |
156 | end |
157 | end |
158 | end |
159 | end |
178 | function Wearable:onReadStream(streamId, connection) |
179 | local spec = self.spec_wearable |
180 | |
181 | self:setDamageAmount(streamReadUIntN(streamId, Wearable.SEND_NUM_BITS) / Wearable.SEND_MAX_VALUE, true) |
182 | |
183 | if spec.wearableNodes ~= nil then |
184 | for _, nodeData in ipairs(spec.wearableNodes) do |
185 | local wearAmount = streamReadUIntN(streamId, Wearable.SEND_NUM_BITS) / Wearable.SEND_MAX_VALUE |
186 | self:setNodeWearAmount(nodeData, wearAmount, true) |
187 | end |
188 | end |
189 | end |
207 | function Wearable:onReadUpdateStream(streamId, timestamp, connection) |
208 | local spec = self.spec_wearable |
209 | |
210 | if connection:getIsServer() then |
211 | if streamReadBool(streamId) then |
212 | self:setDamageAmount(streamReadUIntN(streamId, Wearable.SEND_NUM_BITS) / Wearable.SEND_MAX_VALUE, true) |
213 | |
214 | if spec.wearableNodes ~= nil then |
215 | for _, nodeData in ipairs(spec.wearableNodes) do |
216 | local wearAmount = streamReadUIntN(streamId, Wearable.SEND_NUM_BITS) / Wearable.SEND_MAX_VALUE |
217 | self:setNodeWearAmount(nodeData, wearAmount, true) |
218 | end |
219 | end |
220 | end |
221 | end |
222 | end |
244 | function Wearable:onUpdateTick(dt, isActive, isActiveForInput, isSelected) |
245 | local spec = self.spec_wearable |
246 | |
247 | if spec.wearableNodes ~= nil then |
248 | if self.isServer then |
249 | local changeAmount = self:updateDamageAmount(dt) |
250 | if changeAmount ~= 0 then |
251 | self:setDamageAmount(spec.damage + changeAmount) |
252 | end |
253 | |
254 | for _, nodeData in ipairs(spec.wearableNodes) do |
255 | local changedAmount = nodeData.updateFunc(self, nodeData, dt) |
256 | if changedAmount ~= 0 then |
257 | self:setNodeWearAmount(nodeData, self:getNodeWearAmount(nodeData) + changedAmount) |
258 | end |
259 | end |
260 | end |
261 | end |
262 | end |
226 | function Wearable:onWriteUpdateStream(streamId, connection, dirtyMask) |
227 | local spec = self.spec_wearable |
228 | |
229 | if not connection:getIsServer() then |
230 | if streamWriteBool(streamId, bitAND(dirtyMask, spec.dirtyFlag) ~= 0) then |
231 | streamWriteUIntN(streamId, math.floor(spec.damage * Wearable.SEND_MAX_VALUE + 0.5), Wearable.SEND_NUM_BITS) |
232 | |
233 | if spec.wearableNodes ~= nil then |
234 | for _, nodeData in ipairs(spec.wearableNodes) do |
235 | streamWriteUIntN(streamId, math.floor(self:getNodeWearAmount(nodeData) * Wearable.SEND_MAX_VALUE + 0.5), Wearable.SEND_NUM_BITS) |
236 | end |
237 | end |
238 | end |
239 | end |
240 | end |
90 | function Wearable.registerEventListeners(vehicleType) |
91 | SpecializationUtil.registerEventListener(vehicleType, "onLoad", Wearable) |
92 | SpecializationUtil.registerEventListener(vehicleType, "onLoadFinished", Wearable) |
93 | |
94 | if not GS_IS_MOBILE_VERSION then |
95 | SpecializationUtil.registerEventListener(vehicleType, "onReadStream", Wearable) |
96 | SpecializationUtil.registerEventListener(vehicleType, "onWriteStream", Wearable) |
97 | SpecializationUtil.registerEventListener(vehicleType, "onReadUpdateStream", Wearable) |
98 | SpecializationUtil.registerEventListener(vehicleType, "onWriteUpdateStream", Wearable) |
99 | SpecializationUtil.registerEventListener(vehicleType, "onUpdateTick", Wearable) |
100 | end |
101 | end |
54 | function Wearable.registerFunctions(vehicleType) |
55 | SpecializationUtil.registerFunction(vehicleType, "addAllSubWearableNodes", Wearable.addAllSubWearableNodes) |
56 | SpecializationUtil.registerFunction(vehicleType, "addDamageAmount", Wearable.addDamageAmount) |
57 | SpecializationUtil.registerFunction(vehicleType, "addToGlobalWearableNode", Wearable.addToGlobalWearableNode) |
58 | SpecializationUtil.registerFunction(vehicleType, "addToLocalWearableNode", Wearable.addToLocalWearableNode) |
59 | SpecializationUtil.registerFunction(vehicleType, "addWearableNodes", Wearable.addWearableNodes) |
60 | SpecializationUtil.registerFunction(vehicleType, "addWearAmount", Wearable.addWearAmount) |
61 | SpecializationUtil.registerFunction(vehicleType, "getDamageAmount", Wearable.getDamageAmount) |
62 | SpecializationUtil.registerFunction(vehicleType, "getDamageShowOnHud", Wearable.getDamageShowOnHud) |
63 | SpecializationUtil.registerFunction(vehicleType, "getNodeWearAmount", Wearable.getNodeWearAmount) |
64 | SpecializationUtil.registerFunction(vehicleType, "getUsageCausesDamage", Wearable.getUsageCausesDamage) |
65 | SpecializationUtil.registerFunction(vehicleType, "getUsageCausesWear", Wearable.getUsageCausesWear) |
66 | SpecializationUtil.registerFunction(vehicleType, "getWearMultiplier", Wearable.getWearMultiplier) |
67 | SpecializationUtil.registerFunction(vehicleType, "getWearTotalAmount", Wearable.getWearTotalAmount) |
68 | SpecializationUtil.registerFunction(vehicleType, "getWorkWearMultiplier", Wearable.getWorkWearMultiplier) |
69 | SpecializationUtil.registerFunction(vehicleType, "removeWearableNode", Wearable.removeWearableNode) |
70 | SpecializationUtil.registerFunction(vehicleType, "repaintVehicle", Wearable.repaintVehicle) |
71 | SpecializationUtil.registerFunction(vehicleType, "repairVehicle", Wearable.repairVehicle) |
72 | SpecializationUtil.registerFunction(vehicleType, "setDamageAmount", Wearable.setDamageAmount) |
73 | SpecializationUtil.registerFunction(vehicleType, "setNodeWearAmount", Wearable.setNodeWearAmount) |
74 | SpecializationUtil.registerFunction(vehicleType, "updateDamageAmount", Wearable.updateDamageAmount) |
75 | SpecializationUtil.registerFunction(vehicleType, "updateWearAmount", Wearable.updateWearAmount) |
76 | SpecializationUtil.registerFunction(vehicleType, "validateWearableNode", Wearable.validateWearableNode) |
77 | end |
404 | function Wearable:repairVehicle() |
405 | if self.isServer then |
406 | g_currentMission:addMoney(-self:getRepairPrice(), self:getOwnerFarmId(), MoneyType.VEHICLE_REPAIR, true, true) |
407 | |
408 | self:setDamageAmount(0) |
409 | |
410 | self:raiseDirtyFlags(self.spec_wearable.dirtyFlag) |
411 | |
412 | local total = g_currentMission:farmStats(self:getOwnerFarmId()):updateStats("repairVehicleCount", 1) |
413 | g_achievementManager:tryUnlock("VehicleRepairFirst", total) |
414 | g_achievementManager:tryUnlock("VehicleRepair", total) |
415 | end |
416 | end |
163 | function Wearable:saveToXMLFile(xmlFile, key, usedModNames) |
164 | local spec = self.spec_wearable |
165 | |
166 | xmlFile:setValue(key.."#damage", spec.damage) |
167 | |
168 | if spec.wearableNodes ~= nil then |
169 | for i, nodeData in ipairs(spec.wearableNodes) do |
170 | local nodeKey = string.format("%s.wearNode(%d)", key, i-1) |
171 | xmlFile:setValue(nodeKey.."#amount", self:getNodeWearAmount(nodeData)) |
172 | end |
173 | end |
174 | end |
266 | function Wearable:setDamageAmount(amount, force) |
267 | local spec = self.spec_wearable |
268 | |
269 | spec.damage = math.min(math.max(amount, 0), 1) |
270 | spec.damageByCurve = math.max(spec.damage - 0.3, 0) / 0.7 |
271 | |
272 | local diff = spec.damageSent - spec.damage |
273 | if math.abs(diff) > Wearable.SEND_THRESHOLD or force then |
274 | if self.isServer then |
275 | self:raiseDirtyFlags(spec.dirtyFlag) |
276 | spec.damageSent = spec.damage |
277 | end |
278 | end |
279 | end |
350 | function Wearable:setNodeWearAmount(nodeData, wearAmount, force) |
351 | local spec = self.spec_wearable |
352 | nodeData.wearAmount = MathUtil.clamp(wearAmount, 0, 1) |
353 | |
354 | local diff = nodeData.wearAmountSent - nodeData.wearAmount |
355 | if math.abs(diff) > Wearable.SEND_THRESHOLD or force then |
356 | for _, node in pairs(nodeData.nodes) do |
357 | local _, y, z, w = getShaderParameter(node, "RDT") |
358 | setShaderParameter(node, "RDT", nodeData.wearAmount, y, 0, w, false) |
359 | end |
360 | |
361 | if self.isServer then |
362 | self:raiseDirtyFlags(spec.dirtyFlag) |
363 | nodeData.wearAmountSent = nodeData.wearAmount |
364 | end |
365 | |
366 | -- calculate total wearable amount |
367 | spec.totalAmount = 0 |
368 | for i = 1, #spec.wearableNodes do |
369 | spec.totalAmount = spec.totalAmount + spec.wearableNodes[i].wearAmount |
370 | end |
371 | spec.totalAmount = spec.totalAmount / #spec.wearableNodes |
372 | end |
373 | end |
294 | function Wearable:updateDamageAmount(dt) |
295 | local spec = self.spec_wearable |
296 | if self:getUsageCausesDamage() then |
297 | local factor = 1 |
298 | if self.lifetime ~= nil and self.lifetime ~= 0 then |
299 | local ageMultiplier = 0.15 * math.min(self.age / self.lifetime, 1) |
300 | local operatingTime = self.operatingTime / (1000*60*60) |
301 | local operatingTimeMultiplier = 0.85 * math.min(operatingTime / (self.lifetime * EconomyManager.LIFETIME_OPERATINGTIME_RATIO), 1) |
302 | |
303 | factor = 1 + EconomyManager.MAX_DAILYUPKEEP_MULTIPLIER * (ageMultiplier + operatingTimeMultiplier) |
304 | end |
305 | |
306 | return dt * spec.wearDuration * 0.35 * factor |
307 | else |
308 | return 0 |
309 | end |
310 | end |
591 | function Wearable:updateDebugValues(values) |
592 | local spec = self.spec_wearable |
593 | |
594 | local changedAmount = self:updateDamageAmount(3600000) |
595 | table.insert(values, {name="Damage", value=string.format("%.4f a/h (%.2f)", changedAmount, self:getDamageAmount())}) |
596 | |
597 | if spec.wearableNodes ~= nil then |
598 | if self.isServer then |
599 | for i, nodeData in ipairs(spec.wearableNodes) do |
600 | changedAmount = nodeData.updateFunc(self, nodeData, 3600000) |
601 | table.insert(values, {name="WearableNode"..i, value=string.format("%.4f a/h (%.6f)", changedAmount, self:getNodeWearAmount(nodeData))}) |
602 | end |
603 | end |
604 | end |
605 | end |