Copyright (c) 2008-2015 GIANTS Software GmbH, Confidential, All Rights Reserved.
This document is to be published solely by ls-mods.de
1 | -- Copyright (C) GIANTS Software GmbH, Confidential, All Rights Reserved. |
2 | |
3 | TreePlantUtil = {}; |
4 | |
5 | TreePlantUtil.TREETYPE_UNKNOWN = 0; |
6 | TreePlantUtil.NUM_TREETYPES = 0; |
7 | |
8 | TreePlantUtil.treeTypes = {}; |
9 | TreePlantUtil.treeTypeIndexToDesc = {}; |
10 | |
11 | function TreePlantUtil.registerTreeType(name, nameI18N, treeFilenames, growthTimeHours) |
12 | local key = "TREETYPE_"..string.upper(name); |
13 | if TreePlantUtil[key] == nil then |
14 | TreePlantUtil.NUM_TREETYPES = TreePlantUtil.NUM_TREETYPES + 1; |
15 | TreePlantUtil[key] = TreePlantUtil.NUM_TREETYPES; |
16 | |
17 | local desc = {name = name, nameI18N=nameI18N, index = TreePlantUtil.NUM_TREETYPES}; |
18 | desc.treeFilenames = treeFilenames; |
19 | desc.growthTimeHours = growthTimeHours; |
20 | TreePlantUtil.treeTypes[name] = desc; |
21 | TreePlantUtil.treeTypeIndexToDesc[TreePlantUtil.NUM_TREETYPES] = desc; |
22 | end |
23 | end |
24 | |
25 | function TreePlantUtil.initTreesData(treesData) |
26 | local rootNode = createTransformGroup("trees"); |
27 | link(getRootNode(), rootNode); |
28 | treesData.rootNode = rootNode; |
29 | treesData.growingTrees = {}; |
30 | treesData.splitTrees = {}; |
31 | treesData.clientTrees = {}; |
32 | treesData.updateDtGame = 0; |
33 | treesData.treeCutJoints = {}; |
34 | end |
35 | |
36 | function TreePlantUtil.deleteTreesData(treesData) |
37 | delete(treesData.rootNode); |
38 | end |
39 | |
40 | function TreePlantUtil.plantTree(treesData, treeType, x,y,z, rx,ry,rz, growthState, growthStateI, isGrowing, splitShapeFileId) |
41 | local treeTypeDesc = TreePlantUtil.treeTypeIndexToDesc[treeType]; |
42 | if treeTypeDesc ~= nil then |
43 | growthState = Utils.clamp(growthState, 0, 1); |
44 | if growthStateI == nil then |
45 | growthStateI = math.floor(growthState*(table.getn(treeTypeDesc.treeFilenames)-1))+1; |
46 | end |
47 | local treeId, splitShapeFileId = TreePlantUtil.loadTreeNode(treesData, treeTypeDesc, x,y,z, rx,ry,rz, growthStateI, splitShapeFileId) |
48 | |
49 | local tree = {}; |
50 | tree.node = treeId; |
51 | local isGrowing = Utils.getNoNil(isGrowing, true); |
52 | if table.getn(treeTypeDesc.treeFilenames) <= 1 then |
53 | tree.growthState = 1; |
54 | isGrowing = false; |
55 | else |
56 | tree.growthState = growthState; |
57 | end |
58 | tree.x, tree.y, tree.z = x,y,z; |
59 | tree.rx, tree.ry, tree.rz = rx,ry,rz; |
60 | tree.treeType = treeType; |
61 | tree.splitShapeFileId = splitShapeFileId; |
62 | if isGrowing then |
63 | tree.origSplitShape = getChildAt(treeId, 0); |
64 | table.insert(treesData.growingTrees, tree); |
65 | else |
66 | table.insert(treesData.splitTrees, tree); |
67 | end |
68 | g_server:broadcastEvent(TreePlantEvent:new(treeType, x,y,z, rx,ry,rz, growthState, splitShapeFileId)); |
69 | end |
70 | end |
71 | |
72 | function TreePlantUtil.loadTreeNode(treesData, treeTypeDesc, x,y,z, rx,ry,rz, growthStateI, splitShapeFileId) |
73 | growthStateI = math.min(growthStateI, table.getn(treeTypeDesc.treeFilenames)); |
74 | local i3dFilename = treeTypeDesc.treeFilenames[growthStateI]; |
75 | |
76 | -- make sure the i3d is loaded, so that the file id will not be used by the i3d clone source |
77 | setSplitShapesLoadingFileId(-1); |
78 | setSplitShapesNextFileId(true); |
79 | Utils.fillSharedI3DFileCache(i3dFilename, nil); |
80 | |
81 | setSplitShapesLoadingFileId(Utils.getNoNil(splitShapeFileId, -1)); |
82 | local splitShapeFileId = setSplitShapesNextFileId(); |
83 | |
84 | local treeId = Utils.loadSharedI3DFile(i3dFilename, nil, false, false); |
85 | link(treesData.rootNode, treeId); |
86 | |
87 | setTranslation(treeId, x,y,z); |
88 | setRotation(treeId, rx,ry,rz); |
89 | addToPhysics(treeId); |
90 | |
91 | return treeId, splitShapeFileId; |
92 | end |
93 | |
94 | function TreePlantUtil.updateTrees(treesData, dt, dtGame) |
95 | treesData.updateDtGame = treesData.updateDtGame + dtGame; |
96 | |
97 | -- update all 10 ingame minutes |
98 | if treesData.updateDtGame > 1000*60*10 then |
99 | local dtHours = treesData.updateDtGame / (1000*60*60); |
100 | treesData.updateDtGame = 0; |
101 | local numGrowingTrees = #treesData.growingTrees; |
102 | local i = 1; |
103 | while i<=numGrowingTrees do |
104 | local tree = treesData.growingTrees[i]; |
105 | -- Check if the tree has been cut in the mean time |
106 | if getNumOfChildren(tree.node) == 0 then |
107 | -- The tree has been removed completely, remove from list |
108 | table.remove(treesData.growingTrees, i); |
109 | numGrowingTrees = numGrowingTrees-1; |
110 | delete(tree.node); |
111 | elseif getChildAt(tree.node, 0) ~= tree.origSplitShape then |
112 | -- The tree has been cut, it will not grow anymore |
113 | table.remove(treesData.growingTrees, i); |
114 | numGrowingTrees = numGrowingTrees-1; |
115 | tree.origSplitShape = nil; |
116 | table.insert(treesData.splitTrees, tree); |
117 | else |
118 | local treeTypeDesc = TreePlantUtil.treeTypeIndexToDesc[tree.treeType]; |
119 | local numTreeFiles = table.getn(treeTypeDesc.treeFilenames); |
120 | local growthState = tree.growthState; |
121 | -- TODO check for collisions |
122 | local oldGrowthStateI = math.floor(growthState*(numTreeFiles-1))+1; |
123 | growthState = math.min(growthState+dtHours/treeTypeDesc.growthTimeHours, 1); |
124 | local growthStateI = math.floor(growthState*(numTreeFiles-1))+1; |
125 | |
126 | tree.growthState = growthState; |
127 | if oldGrowthStateI ~= growthStateI and treeTypeDesc.treeFilenames[oldGrowthStateI] ~= treeTypeDesc.treeFilenames[growthStateI] then |
128 | local i3dFilename = treeTypeDesc.treeFilenames[growthStateI]; |
129 | |
130 | -- make sure the i3d is loaded, so that the file id will not be used by the i3d clone source |
131 | setSplitShapesLoadingFileId(-1); |
132 | setSplitShapesNextFileId(true); |
133 | Utils.fillSharedI3DFileCache(i3dFilename, nil); |
134 | |
135 | setSplitShapesLoadingFileId(-1); -- growing always creates a fresh/unsaved tree |
136 | local splitShapeFileId = setSplitShapesNextFileId(); |
137 | |
138 | local treeId = Utils.loadSharedI3DFile(i3dFilename, nil, false, false); |
139 | link(treesData.rootNode, treeId); |
140 | setTranslation(treeId, tree.x, tree.y, tree.z); |
141 | setRotation(treeId, tree.rx, tree.ry, tree.rz); |
142 | delete(tree.node); |
143 | |
144 | addToPhysics(treeId); |
145 | local oldSplitShapeFileId = tree.splitShapeFileId; |
146 | |
147 | tree.origSplitShape = getChildAt(treeId, 0); |
148 | tree.splitShapeFileId = splitShapeFileId; |
149 | tree.node = treeId; |
150 | |
151 | g_server:broadcastEvent(TreeGrowEvent:new(tree.treeType, tree.x, tree.y, tree.z, tree.rx, tree.ry, tree.rz, tree.growthState, splitShapeFileId, oldSplitShapeFileId)); |
152 | end |
153 | if growthStateI >= numTreeFiles then |
154 | -- Reached max grow level, can't grow anymore |
155 | table.remove(treesData.growingTrees, i); |
156 | numGrowingTrees = numGrowingTrees-1; |
157 | tree.origSplitShape = nil; |
158 | table.insert(treesData.splitTrees, tree); |
159 | else |
160 | i = i+1; |
161 | end |
162 | end |
163 | end |
164 | end |
165 | |
166 | local curTime = g_currentMission.time; |
167 | for joint in pairs(treesData.treeCutJoints) do |
168 | if joint.destroyTime <= curTime or not entityExists(joint.shape) then |
169 | removeJoint(joint.jointIndex); |
170 | treesData.treeCutJoints[joint] = nil; |
171 | else |
172 | local x1,y1,z1 = localDirectionToWorld(joint.shape, joint.lnx, joint.lny, joint.lnz); |
173 | if x1*joint.nx + y1*joint.ny + z1*joint.nz < joint.maxCosAngle then |
174 | removeJoint(joint.jointIndex); |
175 | treesData.treeCutJoints[joint] = nil; |
176 | end |
177 | end |
178 | end |
179 | end |
180 | |
181 | function TreePlantUtil.addTreeCutJoint(treesData, jointIndex, shape, nx,ny,nz, maxAngle, maxLifetime) |
182 | local lnx,lny,lnz = worldDirectionToLocal(shape, nx,ny,nz); |
183 | local joint = {jointIndex=jointIndex, shape=shape, nx=nx,ny=ny,nz=nz, lnx=lnx,lny=lny,lnz=lnz, maxCosAngle=math.cos(maxAngle), destroyTime=g_currentMission.time+maxLifetime}; |
184 | treesData.treeCutJoints[joint] = joint; |
185 | end |
186 | |
187 | function TreePlantUtil.loadFromAttributesAndNodes(treesData, xmlFile, baseKey) |
188 | |
189 | local i=0; |
190 | while true do |
191 | local key = string.format("%s.tree(%d)", baseKey, i); |
192 | if not hasXMLProperty(xmlFile, key) then |
193 | break |
194 | end |
195 | |
196 | local x,y,z = Utils.getVectorFromString(getXMLString(xmlFile, key.."#position")); |
197 | local rx,ry,rz = Utils.getVectorFromString(getXMLString(xmlFile, key.."#rotation")); |
198 | |
199 | local treeTypeName = getXMLString(xmlFile, key.."#treeType"); |
200 | local treeType; |
201 | if treeTypeName ~= nil then |
202 | treeType = TreePlantUtil.treeTypes[treeTypeName]; |
203 | end |
204 | if x ~= nil and y ~= nil and z ~= nil and rx ~= nil and ry ~= nil and rz ~= nil and treeType ~= nil then |
205 | local growthState = Utils.getNoNil(getXMLFloat(xmlFile, key.."#growthState"), 0.0); |
206 | local isGrowing = Utils.getNoNil(getXMLBool(xmlFile, key.."#isGrowing"), true); |
207 | local growthStateI = getXMLInt(xmlFile, key.."#growthStateI"); -- note: might be nil, plantTree will use default behaviour (calculate from float growthState) |
208 | local splitShapeFileId = getXMLInt(xmlFile, key.."#splitShapeFileId"); -- note: might be nil if not available |
209 | TreePlantUtil.plantTree(treesData, treeType.index, x,y,z, rx,ry,rz, growthState, growthStateI, isGrowing, splitShapeFileId); |
210 | end |
211 | |
212 | i=i+1; |
213 | end |
214 | end |
215 | |
216 | function TreePlantUtil.getSaveAttributesAndNodes(treesData, nodeIdent) |
217 | local attributes = ''; |
218 | local nodes = ""; |
219 | |
220 | local firstTreeWritten = false; |
221 | for _, tree in pairs(treesData.growingTrees) do |
222 | -- only save trees which have not been removed in the meantime |
223 | if getNumOfChildren(tree.node) > 0 then |
224 | local isGrowing = (getChildAt(tree.node, 0) == tree.origSplitShape); |
225 | nodes = nodes .. TreePlantUtil.getTreeSaveAttributesAndNodes(treesData, nodeIdent, tree, isGrowing, firstTreeWritten); |
226 | firstTreeWritten = true; |
227 | end |
228 | end |
229 | for _, tree in pairs(treesData.splitTrees) do |
230 | -- only save trees which have not been removed in the meantime |
231 | if getNumOfChildren(tree.node) > 0 then |
232 | nodes = nodes .. TreePlantUtil.getTreeSaveAttributesAndNodes(treesData, nodeIdent, tree, false, firstTreeWritten); |
233 | firstTreeWritten = true; |
234 | end |
235 | end |
236 | return attributes,nodes; |
237 | end |
238 | |
239 | function TreePlantUtil.getTreeSaveAttributesAndNodes(treesData, nodeIdent, tree, isGrowing, firstTreeWritten) |
240 | local treeTypeDesc = TreePlantUtil.treeTypeIndexToDesc[tree.treeType]; |
241 | local treeTypeName = treeTypeDesc.name; |
242 | local growthStateI = math.floor(tree.growthState*(table.getn(treeTypeDesc.treeFilenames)-1))+1; |
243 | |
244 | if firstTreeWritten then |
245 | nodeIdent = "\n"..nodeIdent; |
246 | end |
247 | |
248 | local splitShapeFileId = Utils.getNoNil(tree.splitShapeFileId, -1); |
249 | |
250 | -- Note: we also save growthStateI so that we don't have issues with precision and load a different i3d when loading the savegame |
251 | return string.format('%s<tree treeType="%s" position="%.4f %.4f %.4f" rotation="%.4f %.4f %.4f" growthState="%.4f" growthStateI="%d" isGrowing="%s" splitShapeFileId="%d"/>', |
252 | nodeIdent, treeTypeName, tree.x,tree.y,tree.z, tree.rx,tree.ry,tree.rz, tree.growthState,growthStateI, tostring(isGrowing), splitShapeFileId); |
253 | end |
254 | |
255 | function TreePlantUtil.readFromServerStream(treesData, streamId) |
256 | local numTrees = streamReadInt32(streamId); |
257 | for i=1, numTrees do |
258 | local treeType = streamReadInt32(streamId); |
259 | local x = streamReadFloat32(streamId); |
260 | local y = streamReadFloat32(streamId); |
261 | local z = streamReadFloat32(streamId); |
262 | local rx = streamReadFloat32(streamId); |
263 | local ry = streamReadFloat32(streamId); |
264 | local rz = streamReadFloat32(streamId); |
265 | local growthStateI = streamReadInt8(streamId); |
266 | local serverSplitShapeFileId = streamReadInt32(streamId); |
267 | |
268 | local treeTypeDesc = TreePlantUtil.treeTypeIndexToDesc[treeType]; |
269 | if treeTypeDesc ~= nil then |
270 | local nodeId, splitShapeFileId = TreePlantUtil.loadTreeNode(treesData, treeTypeDesc, x,y,z, rx,ry,rz, growthStateI, -1); |
271 | setSplitShapesFileIdMapping(splitShapeFileId, serverSplitShapeFileId); |
272 | treesData.clientTrees[serverSplitShapeFileId] = nodeId; |
273 | end |
274 | end |
275 | end |
276 | |
277 | function TreePlantUtil.writeToClientStream(treesData, streamId) |
278 | local numTrees = 0; |
279 | for _, tree in pairs(treesData.growingTrees) do |
280 | if getNumOfChildren(tree.node) > 0 then |
281 | numTrees = numTrees+1; |
282 | end |
283 | end |
284 | for _, tree in pairs(treesData.splitTrees) do |
285 | if getNumOfChildren(tree.node) > 0 then |
286 | numTrees = numTrees+1; |
287 | end |
288 | end |
289 | |
290 | streamWriteInt32(streamId, numTrees); |
291 | for _, tree in pairs(treesData.growingTrees) do |
292 | -- only save trees which have not been removed in the meantime |
293 | if getNumOfChildren(tree.node) > 0 then |
294 | streamWriteInt32(streamId, tree.treeType); |
295 | streamWriteFloat32(streamId, tree.x); |
296 | streamWriteFloat32(streamId, tree.y); |
297 | streamWriteFloat32(streamId, tree.z); |
298 | streamWriteFloat32(streamId, tree.rx); |
299 | streamWriteFloat32(streamId, tree.ry); |
300 | streamWriteFloat32(streamId, tree.rz); |
301 | local treeTypeDesc = TreePlantUtil.treeTypeIndexToDesc[tree.treeType]; |
302 | local growthStateI = math.floor(tree.growthState*(table.getn(treeTypeDesc.treeFilenames)-1))+1; |
303 | streamWriteInt8(streamId, growthStateI); |
304 | streamWriteInt32(streamId, tree.splitShapeFileId); |
305 | end |
306 | end |
307 | for _, tree in pairs(treesData.splitTrees) do |
308 | -- only save trees which have not been removed in the meantime |
309 | if getNumOfChildren(tree.node) > 0 then |
310 | streamWriteInt32(streamId, tree.treeType); |
311 | streamWriteFloat32(streamId, tree.x); |
312 | streamWriteFloat32(streamId, tree.y); |
313 | streamWriteFloat32(streamId, tree.z); |
314 | streamWriteFloat32(streamId, tree.rx); |
315 | streamWriteFloat32(streamId, tree.ry); |
316 | streamWriteFloat32(streamId, tree.rz); |
317 | local treeTypeDesc = TreePlantUtil.treeTypeIndexToDesc[tree.treeType]; |
318 | local growthStateI = math.floor(tree.growthState*(table.getn(treeTypeDesc.treeFilenames)-1))+1; |
319 | streamWriteInt8(streamId, growthStateI); |
320 | streamWriteInt32(streamId, tree.splitShapeFileId); |
321 | end |
322 | end |
323 | end
|
Copyright (c) 2008-2015 GIANTS Software GmbH, Confidential, All Rights Reserved.
This document is to be published solely by ls-mods.de