the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
at main 565 lines 20 kB view raw
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}