Script v1.7.1.0
- AI
- Animals
- Contracts
- Debug
- Economy
- Effects
- Events
- Farms
- GUI
- Handtools
- I3d
- Materials
- Misc
- Objects
- Placeables
- Player
- Shop
- Sounds
- Specializations
- Triggers
- Utils
- Vehicles
- Weather
Engine v1.7.1.0
- AI
- Animation
- Camera
- Entity
- Fillplanes
- General
- I3D
- Input
- Lighting
- Math
- Network
- Node
- Overlays
- Particle System
- Physics
- Rendering
- Scenegraph
- Shape
- Sound
- Spline
- String
- Terrain Detail
- Text Rendering
- Tire Track
- XML
- general
Foundation Reference
StumpCutter
DescriptionSpecialization for stump cutter allowing to cut/remove tree trunks/split shapesFunctions
- crushSplitShape
- getDirtMultiplier
- getWearMultiplier
- onDeactivate
- onDelete
- onLoad
- onTurnedOff
- onTurnedOn
- onUpdateTick
- prerequisitesPresent
- registerEventListeners
- registerFunctions
- registerOverwrittenFunctions
- stumpCutterSplitShapeCallback
crushSplitShape
DescriptionCrush slit shapeDefinition
crushSplitShape(integer shape)Arguments
integer | shape | shape |
233 | function StumpCutter:crushSplitShape(shape) |
234 | if self.isServer then |
235 | local range = 10 |
236 | local x, _, z = getWorldTranslation(shape) |
237 | g_densityMapHeightManager:setCollisionMapAreaDirty(x-range, z-range, x+range, z+range) |
238 | |
239 | delete(shape) |
240 | end |
241 | end |
getDirtMultiplier
DescriptionReturns current dirt multiplierDefinition
getDirtMultiplier()Return Values
float | dirtMultiplier | current dirt multiplier |
275 | function StumpCutter:getDirtMultiplier(superFunc) |
276 | local multiplier = superFunc(self) |
277 | |
278 | local spec = self.spec_stumpCutter |
279 | if spec.curSplitShape ~= nil then |
280 | multiplier = multiplier + self:getWorkDirtMultiplier() |
281 | end |
282 | |
283 | return multiplier |
284 | end |
getWearMultiplier
DescriptionReturns current wear multiplierDefinition
getWearMultiplier()Return Values
float | wearMultiplier | current wear multiplier |
289 | function StumpCutter:getWearMultiplier(superFunc) |
290 | local multiplier = superFunc(self) |
291 | |
292 | local spec = self.spec_stumpCutter |
293 | if spec.curSplitShape ~= nil then |
294 | multiplier = multiplier + self:getWorkWearMultiplier() |
295 | end |
296 | |
297 | return multiplier |
298 | end |
onDeactivate
DescriptionCalled on deactivateDefinition
onDeactivate()Code
196 | function StumpCutter:onDeactivate() |
197 | if self.isClient then |
198 | local spec = self.spec_stumpCutter |
199 | g_effectManager:stopEffects(spec.effects) |
200 | end |
201 | end |
onDelete
DescriptionCalled on deletingDefinition
onDelete()Code
91 | function StumpCutter:onDelete() |
92 | if self.isClient then |
93 | local spec = self.spec_stumpCutter |
94 | g_effectManager:deleteEffects(spec.effects) |
95 | g_soundManager:deleteSamples(spec.samples) |
96 | g_animationManager:deleteAnimations(spec.animationNodes) |
97 | end |
98 | end |
onLoad
DescriptionCalled on loadingDefinition
onLoad(table savegame)Arguments
table | savegame | savegame |
49 | function StumpCutter:onLoad(savegame) |
50 | local spec = self.spec_stumpCutter |
51 | |
52 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.turnedOnRotationNodes.turnedOnRotationNode", "vehicle.stumpCutter.animationNodes.animationNode", "stumbCutter") --FS17 to FS19 |
53 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.stumpCutterStartSound", "vehicle.stumpCutter.sounds.start") --FS17 to FS19 |
54 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.stumpCutterIdleSound", "vehicle.stumpCutter.sounds.idle") --FS17 to FS19 |
55 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.stumpCutterWorkSound", "vehicle.stumpCutter.sounds.work") --FS17 to FS19 |
56 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.stumpCutterStopSound", "vehicle.stumpCutter.sounds.stop") --FS17 to FS19 |
57 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.stumpCutter.emitterShape(0)", "vehicle.stumpCutter.effects.effectNode") --FS17 to FS19 |
58 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.stumpCutter.particleSystem(0)", "vehicle.stumpCutter.effects.effectNode") --FS17 to FS19 |
59 | |
60 | local baseKey = "vehicle.stumpCutter" |
61 | |
62 | spec.cutNode = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, baseKey .. "#cutNode"), self.i3dMappings) |
63 | spec.cutSizeY = Utils.getNoNil(getXMLFloat(self.xmlFile, baseKey .. "#cutSizeY"), 1) |
64 | spec.cutSizeZ = Utils.getNoNil(getXMLFloat(self.xmlFile, baseKey .. "#cutSizeZ"), 1) |
65 | |
66 | spec.cutFullTreeThreshold = Utils.getNoNil(getXMLFloat(self.xmlFile, baseKey .. "#cutFullTreeThreshold"), 0.4) |
67 | spec.cutPartThreshold = Utils.getNoNil(getXMLFloat(self.xmlFile, baseKey .. "#cutPartThreshold"), 0.2) |
68 | |
69 | if self.isClient then |
70 | spec.samples = {} |
71 | spec.samples.start = g_soundManager:loadSampleFromXML(self.xmlFile, baseKey .. ".sounds", "start", self.baseDirectory, self.components, 1, AudioGroup.VEHICLE, self.i3dMappings, self) |
72 | spec.samples.stop = g_soundManager:loadSampleFromXML(self.xmlFile, baseKey .. ".sounds", "stop", self.baseDirectory, self.components, 1, AudioGroup.VEHICLE, self.i3dMappings, self) |
73 | spec.samples.idle = g_soundManager:loadSampleFromXML(self.xmlFile, baseKey .. ".sounds", "idle", self.baseDirectory, self.components, 0, AudioGroup.VEHICLE, self.i3dMappings, self) |
74 | spec.samples.work = g_soundManager:loadSampleFromXML(self.xmlFile, baseKey .. ".sounds", "work", self.baseDirectory, self.components, 0, AudioGroup.VEHICLE, self.i3dMappings, self) |
75 | |
76 | spec.maxWorkFadeTime = 1000 |
77 | spec.workFadeTime = 0 |
78 | |
79 | spec.effects = g_effectManager:loadEffect(self.xmlFile, baseKey..".effects", self.components, self, self.i3dMappings) |
80 | spec.animationNodes = g_animationManager:loadAnimations(self.xmlFile, baseKey .. ".animationNodes", self.components, self, self.i3dMappings) |
81 | end |
82 | |
83 | spec.maxCutTime = Utils.getNoNil(getXMLFloat(self.xmlFile, baseKey .. "#maxCutTime"), 4000) |
84 | spec.nextCutTime = spec.maxCutTime |
85 | spec.maxResetCutTime = Utils.getNoNil(getXMLFloat(self.xmlFile, baseKey .. "#maxResetCutTime"), 1000) |
86 | spec.resetCutTime = spec.maxResetCutTime |
87 | end |
onTurnedOff
DescriptionCalled on turn offDefinition
onTurnedOff(boolean noEventSend)Arguments
boolean | noEventSend | no event send |
219 | function StumpCutter:onTurnedOff() |
220 | if self.isClient then |
221 | local spec = self.spec_stumpCutter |
222 | spec.workFadeTime = 0 |
223 | g_effectManager:stopEffects(spec.effects) |
224 | g_soundManager:stopSamples(spec.samples) |
225 | g_soundManager:playSample(spec.samples.stop) |
226 | g_animationManager:stopAnimations(spec.animationNodes) |
227 | end |
228 | end |
onTurnedOn
DescriptionCalled on turn onDefinition
onTurnedOn(boolean noEventSend)Arguments
boolean | noEventSend | no event send |
206 | function StumpCutter:onTurnedOn() |
207 | if self.isClient then |
208 | local spec = self.spec_stumpCutter |
209 | g_soundManager:stopSamples(spec.samples) |
210 | g_soundManager:playSample(spec.samples.start) |
211 | g_soundManager:playSample(spec.samples.idle, 0, spec.samples.start) |
212 | g_animationManager:startAnimations(spec.animationNodes) |
213 | end |
214 | end |
onUpdateTick
DescriptionCalled on update tickDefinition
onUpdateTick(float dt, boolean isActiveForInput, boolean isSelected)Arguments
float | dt | time since last call in ms |
boolean | isActiveForInput | true if vehicle is active for input |
boolean | isSelected | true if vehicle is selected |
105 | function StumpCutter:onUpdateTick(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected) |
106 | if self:getIsTurnedOn() then |
107 | local spec = self.spec_stumpCutter |
108 | |
109 | if spec.cutNode ~= nil then |
110 | spec.curLenAbove = 0 |
111 | |
112 | local x,y,z = getWorldTranslation(spec.cutNode) |
113 | local nx,ny,nz = localDirectionToWorld(spec.cutNode, 1,0,0) |
114 | local yx,yy,yz = localDirectionToWorld(spec.cutNode, 0,1,0) |
115 | if spec.curSplitShape ~= nil then |
116 | if testSplitShape(spec.curSplitShape, x,y,z, nx,ny,nz, yx,yy,yz, spec.cutSizeY, spec.cutSizeZ) == nil then |
117 | spec.curSplitShape = nil |
118 | end |
119 | end |
120 | if spec.curSplitShape == nil then |
121 | local shape, _, _, _, _ = findSplitShape(x,y,z, nx,ny,nz, yx,yy,yz, spec.cutSizeY, spec.cutSizeZ) |
122 | if shape ~= 0 then |
123 | spec.curSplitShape = shape |
124 | end |
125 | end |
126 | |
127 | if VehicleDebug.state == VehicleDebug.DEBUG_ATTRIBUTES then |
128 | local x1, y1, z1 = localToWorld(spec.cutNode, 0, 0, spec.cutSizeZ) |
129 | local x2, y2, z2 = localToWorld(spec.cutNode, 0, spec.cutSizeY, 0) |
130 | DebugUtil.drawDebugAreaRectangle(x, y, z, x1, y1, z1, x2, y2, z2, false, 0, 1, 0) |
131 | end |
132 | |
133 | local isWorking = false |
134 | if spec.curSplitShape ~= nil then |
135 | local lenBelow, lenAbove = getSplitShapePlaneExtents(spec.curSplitShape, x,y,z, nx,ny,nz) |
136 | isWorking = lenAbove >= spec.cutPartThreshold |
137 | |
138 | spec.workFadeTime = math.min(spec.maxWorkFadeTime, spec.workFadeTime + dt) |
139 | if self.isServer then |
140 | spec.resetCutTime = spec.maxResetCutTime |
141 | if spec.nextCutTime > 0 then |
142 | spec.nextCutTime = spec.nextCutTime - dt |
143 | if spec.nextCutTime <= 0 then |
144 | -- cut |
145 | local _,ly,_ = worldToLocal(spec.curSplitShape, x,y,z) |
146 | if (lenBelow <= spec.cutFullTreeThreshold or ly < spec.cutPartThreshold+0.01) and lenAbove < 1 then -- only delete tree if lenAbove < 1 to avoid full tree deletion |
147 | -- Delete full tree: Not much left below the cut or cutting near the ground |
148 | self:crushSplitShape(spec.curSplitShape) |
149 | spec.curSplitShape = nil |
150 | elseif lenAbove >= spec.cutPartThreshold then |
151 | -- Normal cut: Splitting 20cm below the top |
152 | spec.nextCutTime = spec.maxCutTime |
153 | local curSplitShape = spec.curSplitShape |
154 | spec.curSplitShape = nil |
155 | spec.curLenAbove = lenAbove |
156 | splitShape(curSplitShape, x,y,z, nx,ny,nz, yx,yy,yz, spec.cutSizeY, spec.cutSizeZ, "stumpCutterSplitShapeCallback", self) |
157 | else |
158 | spec.curSplitShape = nil |
159 | spec.nextCutTime = spec.maxCutTime |
160 | end |
161 | end |
162 | end |
163 | end |
164 | else |
165 | spec.workFadeTime = math.max(0, spec.workFadeTime - dt) |
166 | if self.isServer then |
167 | if spec.resetCutTime > 0 then |
168 | spec.resetCutTime = spec.resetCutTime - dt |
169 | if spec.resetCutTime <= 0 then |
170 | spec.nextCutTime = spec.maxCutTime |
171 | end |
172 | end |
173 | end |
174 | end |
175 | |
176 | if self.isClient then |
177 | if isWorking then |
178 | g_effectManager:setFillType(spec.effects, FillType.WOODCHIPS) |
179 | g_effectManager:startEffects(spec.effects) |
180 | if not g_soundManager:getIsSamplePlaying(spec.samples.work) then |
181 | g_soundManager:playSample(spec.samples.work) |
182 | end |
183 | else |
184 | g_effectManager:stopEffects(spec.effects) |
185 | if g_soundManager:getIsSamplePlaying(spec.samples.work) then |
186 | g_soundManager:stopSample(spec.samples.work) |
187 | end |
188 | end |
189 | end |
190 | end |
191 | end |
192 | end |
prerequisitesPresent
DescriptionChecks if all prerequisite specializations are loadedDefinition
prerequisitesPresent(table specializations)Arguments
table | specializations | specializations |
boolean | hasPrerequisite | true if all prerequisite specializations are loaded |
17 | function StumpCutter.prerequisitesPresent(specializations) |
18 | return SpecializationUtil.hasSpecialization(TurnOnVehicle, specializations) |
19 | end |
registerEventListeners
DescriptionDefinitionregisterEventListeners()Code
37 | function StumpCutter.registerEventListeners(vehicleType) |
38 | SpecializationUtil.registerEventListener(vehicleType, "onLoad", StumpCutter) |
39 | SpecializationUtil.registerEventListener(vehicleType, "onDelete", StumpCutter) |
40 | SpecializationUtil.registerEventListener(vehicleType, "onUpdateTick", StumpCutter) |
41 | SpecializationUtil.registerEventListener(vehicleType, "onDeactivate", StumpCutter) |
42 | SpecializationUtil.registerEventListener(vehicleType, "onTurnedOn", StumpCutter) |
43 | SpecializationUtil.registerEventListener(vehicleType, "onTurnedOff", StumpCutter) |
44 | end |
registerFunctions
DescriptionDefinitionregisterFunctions()Code
23 | function StumpCutter.registerFunctions(vehicleType) |
24 | SpecializationUtil.registerFunction(vehicleType, "crushSplitShape", StumpCutter.crushSplitShape) |
25 | SpecializationUtil.registerFunction(vehicleType, "stumpCutterSplitShapeCallback", StumpCutter.stumpCutterSplitShapeCallback) |
26 | end |
registerOverwrittenFunctions
DescriptionDefinitionregisterOverwrittenFunctions()Code
30 | function StumpCutter.registerOverwrittenFunctions(vehicleType) |
31 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "getDirtMultiplier", StumpCutter.getDirtMultiplier) |
32 | SpecializationUtil.registerOverwrittenFunction(vehicleType, "getWearMultiplier", StumpCutter.getWearMultiplier) |
33 | end |
stumpCutterSplitShapeCallback
DescriptionSplit shape callbackDefinition
stumpCutterSplitShapeCallback(integer shape, boolean isBelow, boolean isAbove, float minY, float maxY, float minZ, float maxZ)Arguments
integer | shape | shape |
boolean | isBelow | is below |
boolean | isAbove | is above |
float | minY | min y split position |
float | maxY | max y split position |
float | minZ | min z split position |
float | maxZ | max z split position |
252 | function StumpCutter:stumpCutterSplitShapeCallback(shape, isBelow, isAbove, minY, maxY, minZ, maxZ) |
253 | local spec = self.spec_stumpCutter |
254 | |
255 | if not isBelow then |
256 | if spec.curLenAbove < 1 then -- split tree if lenAbove > 1 to avoid fulltree deletion |
257 | self:crushSplitShape(shape) |
258 | end |
259 | else |
260 | local yPos = minY + (maxY-minY)/2 |
261 | local zPos = minZ + (maxZ-minZ)/2 |
262 | local _,y,_ = localToWorld(spec.cutNode, -0.05, yPos, zPos) |
263 | local height = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, getWorldTranslation(spec.cutNode)) |
264 | if y < height then |
265 | self:crushSplitShape(shape) |
266 | else |
267 | spec.curSplitShape = shape |
268 | end |
269 | end |
270 | end |