the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
1#include "stdafx.h"
2#include "net.minecraft.world.level.h"
3#include "net.minecraft.world.level.tile.h"
4#include "BasicTree.h"
5
6byte BasicTree::axisConversionArray[] = { 2, 0, 0, 1, 2, 1 };
7
8BasicTree::~BasicTree()
9{
10 delete rnd;
11
12 for( int i = 0; i < foliageCoordsLength; i++ )
13 {
14 delete [] foliageCoords[i];
15 }
16 delete [] foliageCoords;
17}
18
19BasicTree::BasicTree(bool doUpdate) : Feature(doUpdate)
20{
21 rnd = new Random();
22 origin[0] = 0;
23 origin[1] = 0;
24 origin[2] = 0;
25 // Field to hold the tree height.
26 height = 0;
27 // Other important tree information.
28 trunkHeight = 0;
29 trunkHeightScale = 0.618;
30 branchDensity = 1.0;
31 branchSlope = 0.381;
32 widthScale = 1.0;
33 foliageDensity = 1.0;
34 trunkWidth = 1;
35 heightVariance = 12;
36 foliageHeight = 4;
37 foliageCoords = NULL;
38 foliageCoordsLength = 0;
39}
40
41void BasicTree::prepare()
42{
43 // Initialize the instance variables.
44 // Populate the list of foliage cluster locations.
45 // Designed to be overridden in child classes to change basic
46 // tree properties (trunk width, branch angle, foliage density, etc..).
47 trunkHeight = (int) (height * trunkHeightScale);
48 if (trunkHeight >= height) trunkHeight = height - 1;
49 int clustersPerY = (int) (1.382 + pow(foliageDensity * height / 13.0, 2));
50 if (clustersPerY < 1) clustersPerY = 1;
51 // The foliage coordinates are a list of [x,y,z,y of branch base] values for each cluster
52 int **tempFoliageCoords = new int *[clustersPerY * height];
53 for( int i = 0; i < clustersPerY * height; i++ )
54 {
55 tempFoliageCoords[i] = new int[4];
56 }
57 int y = origin[1] + height - foliageHeight;
58 int clusterCount = 1;
59 int trunkTop = origin[1] + trunkHeight;
60 int relativeY = y - origin[1];
61
62 tempFoliageCoords[0][0] = origin[0];
63 tempFoliageCoords[0][1] = y;
64 tempFoliageCoords[0][2] = origin[2];
65 tempFoliageCoords[0][3] = trunkTop;
66 y--;
67
68 while (relativeY >= 0)
69 {
70 int num = 0;
71
72 float shapefac = treeShape(relativeY);
73 if (shapefac < 0)
74 {
75 y--;
76 relativeY--;
77 continue;
78 }
79
80 // The originOffset is to put the value in the middle of the block.
81 double originOffset = 0.5;
82 while (num < clustersPerY)
83 {
84 double radius = widthScale * (shapefac * (rnd->nextFloat() + 0.328));
85 double angle = rnd->nextFloat() * 2.0 * 3.14159;
86 int x = Mth::floor(radius * sin(angle) + origin[0] + originOffset);
87 int z = Mth::floor(radius * cos(angle) + origin[2] + originOffset);
88 int checkStart[] = { x, y, z };
89 int checkEnd[] = { x, y + foliageHeight, z };
90 // check the center column of the cluster for obstructions.
91 if (checkLine(checkStart, checkEnd) == -1) {
92 // If the cluster can be created, check the branch path
93 // for obstructions.
94 int checkBranchBase[] = { origin[0], origin[1], origin[2] };
95 double distance = sqrt(pow(abs(origin[0] - checkStart[0]), 2.0) + pow(abs(origin[2] - checkStart[2]), 2.0));
96 double branchHeight = distance * branchSlope;
97 if ((checkStart[1] - branchHeight) > trunkTop)
98 {
99 checkBranchBase[1] = trunkTop;
100 }
101 else
102 {
103 checkBranchBase[1] = (int) (checkStart[1] - branchHeight);
104 }
105 // Now check the branch path
106 if (checkLine(checkBranchBase, checkStart) == -1)
107 {
108 // If the branch path is clear, add the position to the list
109 // of foliage positions
110 tempFoliageCoords[clusterCount][0] = x;
111 tempFoliageCoords[clusterCount][1] = y;
112 tempFoliageCoords[clusterCount][2] = z;
113 tempFoliageCoords[clusterCount][3] = checkBranchBase[1];
114 clusterCount++;
115 }
116 }
117 num++;
118 }
119 y--;
120 relativeY--;
121 }
122 // 4J Stu - Rather than copying the array, we are storing the number of valid elements in the array
123 foliageCoordsLength = clusterCount;
124 foliageCoords = tempFoliageCoords;
125 // Delete the rest of the array whilst we still know how big it was
126 for( int i = clusterCount; i < clustersPerY * height; i++ )
127 {
128 delete [] tempFoliageCoords[i];
129 tempFoliageCoords[i] = NULL;
130 }
131 // 4J - original code for above is the following, it isn't obvious to me why it is doing a copy of the array, so let's not for now
132// foliageCoords = new int[clusterCount][4];
133// System.arraycopy(tempFoliageCoords, 0, foliageCoords, 0, clusterCount);
134
135}
136
137void BasicTree::crossection(int x, int y, int z, float radius, byte direction, int material)
138{
139 PIXBeginNamedEvent(0, "BasicTree crossection");
140 // Create a circular cross section.
141 //
142 // Used to nearly everything in the foliage, branches, and trunk.
143 // This is a good target for performance optimization.
144
145 // Passed values:
146 // x,y,z is the center location of the cross section
147 // radius is the radius of the section from the center
148 // direction is the direction the cross section is pointed, 0 for x, 1 for y, 2 for z
149 // material is the index number for the material to use
150 int rad = (int) (radius + 0.618);
151 byte secidx1 = axisConversionArray[direction];
152 byte secidx2 = axisConversionArray[direction + 3];
153 int center[] = { x, y, z };
154 int position[] = { 0, 0, 0 };
155 int offset1 = -rad;
156 int offset2 = -rad;
157 int thismat;
158 position[direction] = center[direction];
159 while (offset1 <= rad)
160 {
161 position[secidx1] = center[secidx1] + offset1;
162 offset2 = -rad;
163 while (offset2 <= rad)
164 {
165 double thisdistance = pow(abs(offset1) + 0.5, 2) + pow(abs(offset2) + 0.5, 2);
166 if (thisdistance > radius * radius)
167 {
168 offset2++;
169 continue;
170 }
171 position[secidx2] = center[secidx2] + offset2;
172 PIXBeginNamedEvent(0,"BasicTree getting tile");
173 thismat = thisLevel->getTile(position[0], position[1], position[2]);
174 PIXEndNamedEvent();
175 if (!((thismat == 0) || (thismat == Tile::leaves_Id)))
176 {
177 // If the material of the checked block is anything other than
178 // air or foliage, skip this tile.
179 offset2++;
180 continue;
181 }
182 PIXBeginNamedEvent(0,"BasicTree placing block");
183 placeBlock(thisLevel, position[0], position[1], position[2], material, 0);
184 PIXEndNamedEvent();
185 offset2++;
186 }
187 offset1++;
188 }
189 PIXEndNamedEvent();
190}
191
192float BasicTree::treeShape(int y)
193{
194 // Take the y position relative to the base of the tree.
195 // Return the distance the foliage should be from the trunk axis.
196 // Return a negative number if foliage should not be created at this height.
197 // This method is intended for overriding in child classes, allowing
198 // different shaped trees.
199 // This method should return a consistent value for each y (don't randomize).
200 if (y < (((float) height) * 0.3)) return (float) -1.618;
201 float radius = ((float) height) / ((float) 2.0);
202 float adjacent = (((float) height) / ((float) 2.0)) - y;
203 float distance;
204 if (adjacent == 0) distance = radius;
205 else if (abs(adjacent) >= radius) distance = (float) 0.0;
206 else distance = (float) sqrt(pow(abs(radius), 2) - pow(abs(adjacent), 2));
207 // Alter this factor to change the overall width of the tree.
208 distance *= (float) 0.5;
209 return distance;
210}
211
212float BasicTree::foliageShape(int y)
213{
214 // Take the y position relative to the base of the foliage cluster.
215 // Return the radius of the cluster at this y
216 // Return a negative number if no foliage should be created at this level
217 // this method is intended for overriding in child classes, allowing
218 // foliage of different sizes and shapes.
219 if ((y < 0) || (y >= foliageHeight)) return (float) -1;
220 else if ((y == 0) || (y == (foliageHeight - 1))) return (float) 2;
221 else return (float) 3;
222}
223
224void BasicTree::foliageCluster(int x, int y, int z)
225{
226 PIXBeginNamedEvent(0,"BasicTree foliageCluster");
227 // Generate a cluster of foliage, with the base at x, y, z.
228 // The shape of the cluster is derived from foliageShape
229 // crossection is called to make each level.
230 int topy = y + foliageHeight;
231 int cury = topy - 1;
232 float radius;
233 // 4J Stu - Generate foliage from the top down so that we don't keep recalculating heightmaps
234 while (cury >= y)
235 {
236 radius = foliageShape(cury - y);
237 crossection(x, cury, z, radius, (byte) 1, Tile::leaves_Id);
238 cury--;
239 }
240 PIXEndNamedEvent();
241}
242
243void BasicTree::limb(int *start, int *end, int material)
244{
245 // Create a limb from the start position to the end position.
246 // Used for creating the branches and trunk.
247
248 // Populate delta, the difference between start and end for all three axies.
249 // Set primidx to the index with the largest overall distance traveled.
250 int delta[] = { 0, 0, 0 };
251 byte idx = 0;
252 byte primidx = 0;
253 while (idx < 3)
254 {
255 delta[idx] = end[idx] - start[idx];
256 if (abs(delta[idx]) > abs(delta[primidx]))
257 {
258 primidx = idx;
259 }
260 idx++;
261 }
262 // If the largest distance is zero, don't bother to do anything else.
263 if (delta[primidx] == 0) return;
264 // set up the other two axis indices.
265 byte secidx1 = axisConversionArray[primidx];
266 byte secidx2 = axisConversionArray[primidx + 3];
267 // primsign is digit 1 or -1 depending on whether the limb is headed
268 // along the positive or negative primidx axis.
269 char primsign;
270 if (delta[primidx] > 0) primsign = 1;
271 else primsign = -1;
272 // Initilize the per-step movement for the non-primary axies.
273 double secfac1 = ((double) delta[secidx1]) / ((double) delta[primidx]);
274 double secfac2 = ((double) delta[secidx2]) / ((double) delta[primidx]);
275 // Initialize the coordinates.
276 int coordinate[] = { 0, 0, 0 };
277 // Loop through each crossection along the primary axis, from start to end
278 int primoffset = 0;
279 int endoffset = delta[primidx] + primsign;
280 while (primoffset != endoffset)
281 {
282 coordinate[primidx] = Mth::floor(start[primidx] + primoffset + 0.5);
283 coordinate[secidx1] = Mth::floor(start[secidx1] + (primoffset * secfac1) + 0.5);
284 coordinate[secidx2] = Mth::floor(start[secidx2] + (primoffset * secfac2) + 0.5);
285
286 int dir = TreeTile::FACING_Y;
287 int xdiff = abs(coordinate[0] - start[0]);
288 int zdiff = abs(coordinate[2] - start[2]);
289 int maxdiff = max(xdiff, zdiff);
290
291 if (maxdiff > 0)
292 {
293 if (xdiff == maxdiff)
294 {
295 dir = TreeTile::FACING_X;
296 }
297 else if (zdiff == maxdiff)
298 {
299 dir = TreeTile::FACING_Z;
300 }
301 }
302 placeBlock(thisLevel, coordinate[0], coordinate[1], coordinate[2], material, dir);
303 primoffset += primsign;
304 }
305}
306
307void BasicTree::makeFoliage()
308{
309 // Create the tree foliage.
310 // Call foliageCluster at the correct locations
311 int idx = 0;
312 int finish = foliageCoordsLength;
313 while (idx < finish)
314 {
315 int x = foliageCoords[idx][0];
316 int y = foliageCoords[idx][1];
317 int z = foliageCoords[idx][2];
318 foliageCluster(x, y, z);
319 idx++;
320 }
321}
322
323bool BasicTree::trimBranches(int localY)
324{
325 // For larger trees, randomly "prune" the branches so there
326 // aren't too many.
327 // Return true if the branch should be created.
328 // This method is intended for overriding in child classes, allowing
329 // decent amounts of branches on very large trees.
330 // Can also be used to disable branches on some tree types, or
331 // make branches more sparse.
332 if (localY < (height * 0.2)) return false;
333 else return true;
334}
335
336void BasicTree::makeTrunk()
337{
338 // Create the trunk of the tree.
339 int x = origin[0];
340 int startY = origin[1];
341 int topY = origin[1] + trunkHeight;
342 int z = origin[2];
343 int startCoord[] = { x, startY, z };
344 int endCoord[] = { x, topY, z };
345 limb(startCoord, endCoord, Tile::treeTrunk_Id);
346 if (trunkWidth == 2)
347 {
348 startCoord[0] += 1;
349 endCoord[0] += 1;
350 limb(startCoord, endCoord, Tile::treeTrunk_Id);
351 startCoord[2] += 1;
352 endCoord[2] += 1;
353 limb(startCoord, endCoord, Tile::treeTrunk_Id);
354 startCoord[0] += -1;
355 endCoord[0] += -1;
356 limb(startCoord, endCoord, Tile::treeTrunk_Id);
357 }
358}
359
360void BasicTree::makeBranches()
361{
362 // Create the tree branches.
363 // Call trimBranches for each branch to see if you should create it.
364 // Call taperedLimb to the correct locations
365 int idx = 0;
366 int finish = foliageCoordsLength;
367 int baseCoord[] = { origin[0], origin[1], origin[2] };
368 while (idx < finish)
369 {
370 int *coordValues = foliageCoords[idx];
371 int endCoord[] = { coordValues[0], coordValues[1], coordValues[2] };
372 baseCoord[1] = coordValues[3];
373 int localY = baseCoord[1] - origin[1];
374 if (trimBranches(localY))
375 {
376 limb(baseCoord, endCoord, Tile::treeTrunk_Id);
377 }
378 idx++;
379 }
380}
381
382int BasicTree::checkLine(int *start, int *end)
383{
384 // Check from coordinates start to end (both inclusive) for blocks other than air and foliage
385 // If a block other than air and foliage is found, return the number of steps taken.
386 // If no block other than air and foliage is found, return -1.
387 // Examples:
388 // If the third block searched is stone, return 2
389 // If the first block searched is lava, return 0
390
391 int delta[] = { 0, 0, 0 };
392 byte idx = 0;
393 byte primidx = 0;
394 while (idx < 3)
395 {
396 delta[idx] = end[idx] - start[idx];
397 if (abs(delta[idx]) > abs(delta[primidx]))
398 {
399 primidx = idx;
400 }
401 idx++;
402 }
403 // If the largest distance is zero, don't bother to do anything else.
404 if (delta[primidx] == 0) return -1;
405 // set up the other two axis indices.
406 byte secidx1 = axisConversionArray[primidx];
407 byte secidx2 = axisConversionArray[primidx + 3];
408 // primsign is digit 1 or -1 depending on whether the limb is headed
409 // along the positive or negative primidx axis.
410 char primsign; // 4J Stu - Was byte, but we use in a sum below and byte=unsigned char so we were setting endoffset incorrectly
411 if (delta[primidx] > 0) primsign = 1;
412 else primsign = -1;
413 // Initilize the per-step movement for the non-primary axies.
414 double secfac1 = ((double) delta[secidx1]) / ((double) delta[primidx]);
415 double secfac2 = ((double) delta[secidx2]) / ((double) delta[primidx]);
416 // Initialize the coordinates.
417 int coordinate[] = { 0, 0, 0 };
418 // Loop through each crossection along the primary axis, from start to end
419 int primoffset = 0;
420 int endoffset = delta[primidx] + primsign;
421 int thismat;
422 while (primoffset != endoffset)
423 {
424 coordinate[primidx] = start[primidx] + primoffset;
425 coordinate[secidx1] = Mth::floor(start[secidx1] + (primoffset * secfac1));
426 coordinate[secidx2] = Mth::floor(start[secidx2] + (primoffset * secfac2));
427 thismat = thisLevel->getTile(coordinate[0], coordinate[1], coordinate[2]);
428 if (!((thismat == 0) || (thismat == Tile::leaves_Id)))
429 {
430 // If the material of the checked block is anything other than
431 // air or foliage, stop looking.
432 break;
433 }
434 primoffset += primsign;
435 }
436 // If you reached the end without finding anything, return -1.
437 if (primoffset == endoffset)
438 {
439 return -1;
440 }
441 // Otherwise, return the number of steps you took.
442 else
443 {
444 return abs(primoffset);
445 }
446}
447
448bool BasicTree::checkLocation()
449{
450 // Return true if the tree can be placed here.
451 // Return false if the tree can not be placed here.
452
453 // Examine the square under the trunk. Is it grass or dirt?
454 // If not, return false
455 // Examine center column for how tall the tree can be.
456 // If the checked height is shorter than height, but taller
457 // than 4, set the tree to the maximum height allowed.
458 // If the space is too short, return false.
459 int startPosition[] = { origin[0], origin[1], origin[2] };
460 int endPosition[] = { origin[0], origin[1] + height - 1, origin[2] };
461
462 // 4J Stu Added to stop tree features generating areas previously place by game rule generation
463 if(app.getLevelGenerationOptions() != NULL)
464 {
465 LevelGenerationOptions *levelGenOptions = app.getLevelGenerationOptions();
466 bool intersects = levelGenOptions->checkIntersects(startPosition[0], startPosition[1], startPosition[2], endPosition[0], endPosition[1], endPosition[2]);
467 if(intersects)
468 {
469 //app.DebugPrintf("Skipping reeds feature generation as it overlaps a game rule structure\n");
470 return false;
471 }
472 }
473
474 // Check the location it is resting on
475 int baseMaterial = thisLevel->getTile(origin[0], origin[1] - 1, origin[2]);
476 if (!((baseMaterial == 2) || (baseMaterial == 3)))
477 {
478 return false;
479 }
480 int allowedHeight = checkLine(startPosition, endPosition);
481 // If the set height is good, go with that
482 if (allowedHeight == -1)
483 {
484 return true;
485 }
486 // If the space is too short, tell the build to abort
487 else if (allowedHeight < 6)
488 {
489 return false;
490 }
491 // If the space is shorter than the set height, but not too short
492 // shorten the height, and tell the build to continue
493 else
494 {
495 height = allowedHeight;
496 //System.out.println("Shortened the tree");
497 return true;
498 }
499}
500
501void BasicTree::init(double heightInit, double widthInit, double foliageDensityInit)
502{
503 // all of the parameters should be from 0.0 to 1.0
504 // heightInit scales the maximum overall height of the tree (still randomizes height within the possible range)
505 // widthInit scales the maximum overall width of the tree (keep this above 0.3 or so)
506 // foliageDensityInit scales how many foliage clusters are created.
507 //
508 // Note, you can call "place" without calling "init".
509 // This is the same as calling init(1.0,1.0,1.0) and then calling place.
510 heightVariance = (int) (heightInit * 12);
511 if (heightInit > 0.5) foliageHeight = 5;
512 widthScale = widthInit;
513 foliageDensity = foliageDensityInit;
514}
515
516bool BasicTree::place(Level *level, Random *random, int x, int y, int z)
517{
518 // Note to Markus.
519 // currently the following fields are set randomly. If you like, make them
520 // parameters passed into "place".
521 //
522 // height: so the map generator can intelligently set the height of the tree,
523 // and make forests with large trees in the middle and smaller ones on the edges.
524
525 // Initialize the instance fields for the level and the seed.
526 thisLevel = level;
527 __int64 seed = random->nextLong();
528 rnd->setSeed(seed);
529 // Initialize the origin of the tree trunk
530 origin[0] = x;
531 origin[1] = y;
532 origin[2] = z;
533 // Sets the height. Take out this line if height is passed as a parameter
534 if (height == 0)
535 {
536 height = 5 + rnd->nextInt(heightVariance);
537 }
538 if (!(checkLocation()))
539 {
540 //System.out.println("Tree location failed");
541 return false;
542 }
543 PIXBeginNamedEvent(0, "Placing BasicTree");
544 //System.out.println("The height is");
545 //System.out.println(height);
546 //System.out.println("Trunk Height check done");
547 PIXBeginNamedEvent(0, "Preparing tree");
548 prepare();
549 PIXEndNamedEvent();
550 //System.out.println("Prepare done");
551 PIXBeginNamedEvent(0, "Making foliage");
552 makeFoliage();
553 PIXEndNamedEvent();
554 //System.out.println("Foliage done");
555 PIXBeginNamedEvent(0, "Making trunk");
556 makeTrunk();
557 PIXEndNamedEvent();
558 //System.out.println("Trunk done");
559 PIXBeginNamedEvent(0, "Making branches");
560 makeBranches();
561 PIXEndNamedEvent();
562 //System.out.println("Branches done");
563 PIXEndNamedEvent();
564 return true;
565}