the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
at main 496 lines 19 kB view raw
1#include "stdafx.h" 2#include "net.minecraft.h" 3#include "net.minecraft.world.entity.h" 4#include "net.minecraft.world.entity.animal.h" 5#include "net.minecraft.world.entity.monster.h" 6#include "net.minecraft.world.entity.player.h" 7#include "net.minecraft.world.level.h" 8#include "net.minecraft.world.level.biome.h" 9#include "net.minecraft.world.level.material.h" 10#include "net.minecraft.world.level.pathfinder.h" 11#include "net.minecraft.world.level.tile.h" 12#include "Difficulty.h" 13#include "WeighedRandom.h" 14#include "Level.h" 15#include "ChunkPos.h" 16#include "TilePos.h" 17#include "..\Minecraft.Client\ServerLevel.h" 18#include "MobSpawner.h" 19#include "Dimension.h" 20 21const int MobSpawner::MIN_SPAWN_DISTANCE = 24; 22 23TilePos MobSpawner::getRandomPosWithin(Level *level, int cx, int cz) 24{ 25 // 4J Stu - Added 1.2.3 but we don't need it as it was only used to access sections 26 // Leaving here though to help explain why chunk coords are not passed in rather than full coords 27 //LevelChunk *chunk = level->getChunk(cx, cz); 28 int x = cx * 16 + level->random->nextInt(16); 29 int y = level->random->nextInt(level->getHeight()); 30 int z = cz * 16 + level->random->nextInt(16); 31 32 return TilePos(x, y, z); 33} 34 35#ifdef __PSVITA__ 36 // AP - See CustomMap.h for an explanation of this 37 CustomMap MobSpawner::chunksToPoll; 38#else 39 unordered_map<ChunkPos,bool,ChunkPosKeyHash,ChunkPosKeyEq> MobSpawner::chunksToPoll; 40#endif 41 42const int MobSpawner::tick(ServerLevel *level, bool spawnEnemies, bool spawnFriendlies, bool spawnPersistent) 43{ 44#ifndef _CONTENT_PACKAGE 45 46#if 0 47 // PIX output for mob counters - generally disabling as Entity::countFlagsForPIX is reasonably expensive 48 if( level->dimension->id == 0 ) 49 { 50 Entity::countFlagsForPIX(); 51 PIXAddNamedCounter( level->countInstanceOf(eTYPE_WATERANIMAL ,false), "eTYPE_WATERANIMAL"); 52 PIXAddNamedCounter( level->countInstanceOf(eTYPE_ANIMALS_SPAWN_LIMIT_CHECK ,false), "eTYPE_ANIMAL"); 53 PIXAddNamedCounter( level->countInstanceOf(eTYPE_MONSTER ,false), "eTYPE_MONSTER"); 54 PIXAddNamedCounter( level->countInstanceOf(eTYPE_SQUID ,true ), "eTYPE_SQUID"); 55 PIXAddNamedCounter( level->countInstanceOf(eTYPE_VILLAGER ,true ), "eTYPE_VILLAGER"); 56 57 unsigned int totalCount[4]; 58 unsigned int protectedCount[4]; 59 unsigned int unprotectedCount[4]; 60 unsigned int couldWanderCount[4]; 61 62 totalCount[0] = level->countInstanceOf(eTYPE_COW ,true, &protectedCount[0], &couldWanderCount[0] ); 63 totalCount[1] = level->countInstanceOf(eTYPE_SHEEP ,true, &protectedCount[1], &couldWanderCount[1] ); 64 totalCount[2] = level->countInstanceOf(eTYPE_CHICKEN ,true, &protectedCount[2], &couldWanderCount[2] ); 65 totalCount[3] = level->countInstanceOf(eTYPE_PIG ,true, &protectedCount[3], &couldWanderCount[3] ); 66 67 for( int i = 0; i < 4; i++ ) unprotectedCount[i] = totalCount[i] - protectedCount[i]; 68 69 PIXAddNamedCounter( unprotectedCount[0], "eTYPE_COW (unprotected)"); 70 PIXAddNamedCounter( unprotectedCount[1], "eTYPE_SHEEP (unprotected)"); 71 PIXAddNamedCounter( unprotectedCount[2], "eTYPE_CHICKEN (unprotected)"); 72 PIXAddNamedCounter( unprotectedCount[3], "eTYPE_PIG (unprotected)"); 73 74 PIXAddNamedCounter( protectedCount[0], "eTYPE_COW (protected)"); 75 PIXAddNamedCounter( protectedCount[1], "eTYPE_SHEEP (protected)"); 76 PIXAddNamedCounter( protectedCount[2], "eTYPE_CHICKEN (protected)"); 77 PIXAddNamedCounter( protectedCount[3], "eTYPE_PIG (protected)"); 78 79 PIXAddNamedCounter( couldWanderCount[0], "eTYPE_COW (could wander)"); 80 PIXAddNamedCounter( couldWanderCount[1], "eTYPE_SHEEP (could wander)"); 81 PIXAddNamedCounter( couldWanderCount[2], "eTYPE_CHICKEN (could wander)"); 82 PIXAddNamedCounter( couldWanderCount[3], "eTYPE_PIG (could wander)"); 83 84 PIXAddNamedCounter( level->countInstanceOf(eTYPE_WOLF ,true ), "eTYPE_WOLF"); 85 PIXAddNamedCounter( level->countInstanceOf(eTYPE_CREEPER ,true ), "eTYPE_CREEPER"); 86 PIXAddNamedCounter( level->countInstanceOf(eTYPE_GIANT ,true ), "eTYPE_GIANT"); 87 PIXAddNamedCounter( level->countInstanceOf(eTYPE_SKELETON ,true ), "eTYPE_SKELETON"); 88 PIXAddNamedCounter( level->countInstanceOf(eTYPE_SPIDER ,true ), "eTYPE_SPIDER"); 89 PIXAddNamedCounter( level->countInstanceOf(eTYPE_ZOMBIE ,true ), "eTYPE_ZOMBIE"); 90 PIXAddNamedCounter( level->countInstanceOf(eTYPE_PIGZOMBIE ,true ), "eTYPE_PIGZOMBIE"); 91 PIXAddNamedCounter( level->countInstanceOf(eTYPE_SLIME ,true ), "eTYPE_SLIME"); 92 PIXAddNamedCounter( level->countInstanceOf(eTYPE_GHAST ,true ), "eTYPE_GHAST"); 93 } 94#endif 95#endif 96 97 if (!spawnEnemies && !spawnFriendlies && !spawnPersistent) 98 { 99 return 0; 100 } 101 MemSect(20); 102 chunksToPoll.clear(); 103 104#if 0 105 AUTO_VAR(itEnd, level->players.end()); 106 for (AUTO_VAR(it, level->players.begin()); it != itEnd; it++) 107 { 108 shared_ptr<Player> player = *it; //level->players.at(i); 109 int xx = Mth::floor(player->x / 16); 110 int zz = Mth::floor(player->z / 16); 111 112 int r = 128 / 16; 113 for (int x = -r; x <= r; x++) 114 for (int z = -r; z <= r; z++) 115 { 116 chunksToPoll.insert(ChunkPos(x + xx, z + zz)); 117 } 118 } 119#else 120 // 4J - rewritten to add chunks interleaved by player, and to add them from the centre outwards. We're going to be 121 // potentially adding less creatures than the original so that our count stays consistent with number of players added, so 122 // we want to make sure as best we can that the ones we do add are near the active players 123 int playerCount = (int)level->players.size(); 124 int *xx = new int[playerCount]; 125 int *zz = new int[playerCount]; 126 for (int i = 0; i < playerCount; i++) 127 { 128 shared_ptr<Player> player = level->players[i]; 129 xx[i] = Mth::floor(player->x / 16); 130 zz[i] = Mth::floor(player->z / 16); 131#ifdef __PSVITA__ 132 chunksToPoll.insert(ChunkPos(xx[i], zz[i] ),false); 133#else 134 chunksToPoll.insert(std::pair<ChunkPos,bool>(ChunkPos(xx[i], zz[i] ),false)); 135#endif 136 } 137 138 for( int r = 1; r <= 8; r++ ) 139 { 140 for( int l = 0; l < ( r * 2 ) ; l++ ) 141 { 142 for( int i = 0; i < playerCount; i++ ) 143 { 144 bool edgeChunk = ( r == 8 ); 145 146 // If this chunk isn't at the edge of the region for this player, then always store with a flag of false 147 // so that if it was at the edge of another player, then this will remove that 148 if( !edgeChunk ) 149 { 150#ifdef __PSVITA__ 151 chunksToPoll.insert(ChunkPos( ( xx[i] - r ) + l , ( zz[i] - r ) ), false); 152 chunksToPoll.insert(ChunkPos( ( xx[i] + r ) , ( zz[i] - r ) + l ), false); 153 chunksToPoll.insert(ChunkPos( ( xx[i] + r ) - l , ( zz[i] + r ) ), false); 154 chunksToPoll.insert(ChunkPos( ( xx[i] - r ) , ( zz[i] + r ) - l ), false); 155#else 156 chunksToPoll.insert(std::pair<ChunkPos,bool>(ChunkPos( ( xx[i] - r ) + l , ( zz[i] - r ) ), false)); 157 chunksToPoll.insert(std::pair<ChunkPos,bool>(ChunkPos( ( xx[i] + r ) , ( zz[i] - r ) + l ), false)); 158 chunksToPoll.insert(std::pair<ChunkPos,bool>(ChunkPos( ( xx[i] + r ) - l , ( zz[i] + r ) ), false)); 159 chunksToPoll.insert(std::pair<ChunkPos,bool>(ChunkPos( ( xx[i] - r ) , ( zz[i] + r ) - l ), false)); 160#endif 161 } 162 else 163 { 164#ifdef __PSVITA__ 165 ChunkPos cp = ChunkPos( ( xx[i] - r ) + l , ( zz[i] - r )); 166 if( chunksToPoll.find( cp ) ) chunksToPoll.insert(cp, true); 167 cp = ChunkPos( ( xx[i] + r ), ( zz[i] - r ) + l ); 168 if( chunksToPoll.find( cp ) ) chunksToPoll.insert(cp, true); 169 cp = ChunkPos( ( xx[i] + r ) - l , ( zz[i] + r )); 170 if( chunksToPoll.find( cp ) ) chunksToPoll.insert(cp, true); 171 cp = ChunkPos( ( xx[i] - r ), ( zz[i] + r ) - l); 172 if( chunksToPoll.find( cp ) ) chunksToPoll.insert(cp, true); 173#else 174 ChunkPos cp = ChunkPos( ( xx[i] - r ) + l , ( zz[i] - r )); 175 if( chunksToPoll.find( cp ) == chunksToPoll.end() ) chunksToPoll.insert(std::pair<ChunkPos,bool>(cp, true)); 176 cp = ChunkPos( ( xx[i] + r ), ( zz[i] - r ) + l ); 177 if( chunksToPoll.find( cp ) == chunksToPoll.end() ) chunksToPoll.insert(std::pair<ChunkPos,bool>(cp, true)); 178 cp = ChunkPos( ( xx[i] + r ) - l , ( zz[i] + r )); 179 if( chunksToPoll.find( cp ) == chunksToPoll.end() ) chunksToPoll.insert(std::pair<ChunkPos,bool>(cp, true)); 180 cp = ChunkPos( ( xx[i] - r ), ( zz[i] + r ) - l); 181 if( chunksToPoll.find( cp ) == chunksToPoll.end() ) chunksToPoll.insert(std::pair<ChunkPos,bool>(cp, true)); 182#endif 183 184 } 185 } 186 } 187 } 188 delete [] xx; 189 delete [] zz; 190#endif 191 MemSect(0); 192 int count = 0; 193 MemSect(31); 194 Pos *spawnPos = level->getSharedSpawnPos(); 195 MemSect(0); 196 197 for (unsigned int i = 0; i < MobCategory::values.length; i++) 198 { 199 MobCategory *mobCategory = MobCategory::values[i]; 200 if ((mobCategory->isFriendly() && !spawnFriendlies) || (!mobCategory->isFriendly() && !spawnEnemies) || (mobCategory->isPersistent() && !spawnPersistent)) 201 { 202 continue; 203 } 204 205 // 4J - early out for non-main dimensions, if spawning anything friendly 206 if( mobCategory->isFriendly() ) 207 { 208 if( level->dimension->id != 0 ) 209 { 210 continue; 211 } 212 } 213 214 // 4J - this is now quite different to the java version. We just have global max counts for the level whereas the original has a max per chunk that 215 // scales with the number of chunks to be polled. 216 int categoryCount = level->countInstanceOf( mobCategory->getEnumBaseClass(), mobCategory->isSingleType()); 217 if( categoryCount >= mobCategory->getMaxInstancesPerLevel()) 218 { 219 continue; 220 } 221 222#ifdef __PSVITA__ 223 for( int i = 0;i < chunksToPoll.end();i += 1 ) 224 { 225 SCustomMapNode *it = chunksToPoll.get(i); 226#else 227 AUTO_VAR(itEndCTP, chunksToPoll.end()); 228 for (AUTO_VAR(it, chunksToPoll.begin()); it != itEndCTP; it++) 229 { 230#endif 231 if( it->second ) 232 { 233 // don't add mobs to edge chunks, to prevent adding mobs "outside" of the active playground 234 continue; 235 } 236 ChunkPos *cp = (ChunkPos *) (&it->first); 237 238 // 4J - don't let this actually create/load a chunk that isn't here already - we'll let the normal updateDirtyChunks etc. processes do that, so it can happen on another thread 239 if( !level->hasChunk(cp->x,cp->z) ) continue; 240 241 TilePos start = getRandomPosWithin(level, cp->x, cp->z); 242 int xStart = start.x; 243 int yStart = start.y; 244 int zStart = start.z; 245 246 if (level->isSolidBlockingTile(xStart, yStart, zStart)) continue; 247 if (level->getMaterial(xStart, yStart, zStart) != mobCategory->getSpawnPositionMaterial()) continue; 248 int clusterSize = 0; 249 250 for (int dd = 0; dd < 3; dd++) 251 { 252 int x = xStart; 253 int y = yStart; 254 int z = zStart; 255 int ss = 6; 256 257 Biome::MobSpawnerData *currentMobType = NULL; 258 MobGroupData *groupData = NULL; 259 260 for (int ll = 0; ll < 4; ll++) 261 { 262 x += level->random->nextInt(ss) - level->random->nextInt(ss); 263 y += level->random->nextInt(1) - level->random->nextInt(1); 264 z += level->random->nextInt(ss) - level->random->nextInt(ss); 265 // int y = heightMap[x + z * w] + 1; 266 267 // 4J - don't let this actually create/load a chunk that isn't here already - we'll let the normal updateDirtyChunks etc. processes do that, so it can happen on another thread 268 if( !level->hasChunkAt( x, y, z ) ) continue; 269 270 if (isSpawnPositionOk(mobCategory, level, x, y, z)) 271 { 272 float xx = x + 0.5f; 273 float yy = (float) y; 274 float zz = z + 0.5f; 275 if (level->getNearestPlayer(xx, yy, zz, MIN_SPAWN_DISTANCE) != NULL) 276 { 277 continue; 278 } 279 else 280 { 281 float xd = xx - spawnPos->x; 282 float yd = yy - spawnPos->y; 283 float zd = zz - spawnPos->z; 284 float sd = xd * xd + yd * yd + zd * zd; 285 if (sd < MIN_SPAWN_DISTANCE * MIN_SPAWN_DISTANCE) 286 { 287 continue; 288 } 289 } 290 291 if (currentMobType == NULL) 292 { 293 currentMobType = level->getRandomMobSpawnAt(mobCategory, x, y, z); 294 if (currentMobType == NULL) 295 { 296 break; 297 } 298 } 299 300 shared_ptr<Mob> mob; 301 // 4J - removed try/catch 302// try 303// { 304 MemSect(29); 305 //mob = type.mobClass.getConstructor(Level.class).newInstance(level); 306 mob = dynamic_pointer_cast<Mob>(EntityIO::newByEnumType(currentMobType->mobClass, level)); 307 MemSect(0); 308// } 309// catch (exception e) 310// { 311// // TODO 4J We can't print a stack trace, and the newInstance function doesn't throw an exception just now anyway 312// //e.printStackTrace(); 313// return count; 314// } 315 316 // 4J - If it is an animal or a monster, don't let any one type of mob represent more than 50% of the total amount of these things. This 317 // was added initially to stop flat lands being totally populated with slimes but seems like a generally good rule. 318 eINSTANCEOF mobType = mob->GetType(); 319 320 if( ( mobType & eTYPE_ANIMALS_SPAWN_LIMIT_CHECK ) || ( mobType & eTYPE_MONSTER ) ) 321 { 322 // even more special rule for ghasts, because filling up the nether with 25 of them is a bit unpleasant. In the java version they are 323 // only limited by the fact that the world fills up with pig zombies (the only other type of enemy mob in the nether) before them - they 324 // aren't actually even counted properly themselves 325 if( mobType == eTYPE_GHAST ) 326 { 327 if( level->countInstanceOf(mobType, true) >= 4 ) continue; 328 } 329 else if( mobType == eTYPE_ENDERMAN && level->dimension->id == 1 ) 330 { 331 // Special rule for the end, as we only have Endermen (plus the dragon). Increase the spawnable counts based on level difficulty 332 int maxEndermen = mobCategory->getMaxInstancesPerLevel(); 333 334 if( level->difficulty == Difficulty::NORMAL ) 335 { 336 maxEndermen -= mobCategory->getMaxInstancesPerLevel()/4; 337 } 338 else if( level->difficulty <= Difficulty::EASY) 339 { 340 maxEndermen -= mobCategory->getMaxInstancesPerLevel()/2; 341 } 342 343 if( level->countInstanceOf(mobType, true) >= maxEndermen ) continue; 344 } 345 else if( level->countInstanceOf(mobType, true) >= ( mobCategory->getMaxInstancesPerLevel() / 2 ) ) continue; 346 } 347 348 mob->moveTo(xx, yy, zz, level->random->nextFloat() * 360, 0); 349 350 if (mob->canSpawn()) 351 { 352 // 4J - check if we are going to despawn straight away too, and don't add if we will - otherwise we'll be sending 353 // network packets for adding & removal that we don't need 354 mob->checkDespawn(); 355 if( !mob->removed ) 356 { 357 clusterSize++; 358 categoryCount++; 359 mob->setDespawnProtected(); // 4J added - default to protected against despawning 360 level->addEntity(mob); 361 groupData = mob->finalizeMobSpawn(groupData); 362 // 4J - change here so that we can't ever make more than the desired amount of entities in each priority. In the original java version 363 // depending on the random spawn positions being considered the only limit as to the number of entities created per category is the number 364 // of chunks to poll. 365 if (categoryCount >= mobCategory->getMaxInstancesPerLevel() ) goto categoryLoop; 366 if (clusterSize >= mob->getMaxSpawnClusterSize()) goto chunkLoop; 367 } 368 } 369 count += clusterSize; 370 } 371 } 372 } 373 chunkLoop: continue; 374 } 375 categoryLoop: continue; 376 } 377 delete spawnPos; 378 379 return count; 380} 381 382bool MobSpawner::isSpawnPositionOk(MobCategory *category, Level *level, int x, int y, int z) 383{ 384 // 4J - don't let this actually create/load a chunk that isn't here already - we'll let the normal updateDirtyChunks etc. processes do that, so it can happen on another thread 385 if( !level->hasChunkAt(x, y, z ) ) return false; 386 387#ifdef __PSVITA__ 388 // AP - added this for Vita. Make sure a new spawn point has 2 chunks around it. This will make sure monsters don't keep getting spawned on the edge preventing other new monsters 389 // from being spawned 390 int r = 32; 391 if( !level->hasChunksAt(x - r, 0, z - r, x + r, 0, z + r)) 392 { 393 return false; 394 } 395#endif 396 397 if (category->getSpawnPositionMaterial() == Material::water) 398 { 399 // 4J - changed to spawn water things only in deep water 400 int yo = 0; 401 int liquidCount = 0; 402 403 while( ( y - yo ) >= 0 && ( yo < 5 ) ) 404 { 405 if( level->getMaterial(x, y - yo, z)->isLiquid() ) liquidCount++; 406 yo++; 407 } 408 409 // 4J - Sometimes deep water could be just a waterfall, so check that it's wide as well 410 bool inEnoughWater = false; 411 if( liquidCount == 5 ) 412 { 413 if( level->getMaterial(x+5, y, z)->isLiquid() && 414 level->getMaterial(x-5, y, z)->isLiquid() && 415 level->getMaterial(x, y, z+5)->isLiquid() && 416 level->getMaterial(x, y, z-5)->isLiquid() 417 ) 418 { 419 inEnoughWater = true; 420 } 421 } 422 423 return inEnoughWater && !level->isSolidBlockingTile(x, y + 1, z); 424 } 425 else 426 { 427 if (!level->isTopSolidBlocking(x, y - 1, z)) return false; 428 int tt = level->getTile(x, y - 1, z); 429 return tt != Tile::unbreakable_Id && !level->isSolidBlockingTile(x, y, z) && !level->getMaterial(x, y, z)->isLiquid() && !level->isSolidBlockingTile(x, y + 1, z); 430 } 431 } 432 433void MobSpawner::postProcessSpawnMobs(Level *level, Biome *biome, int xo, int zo, int cellWidth, int cellHeight, Random *random) 434{ 435 // 4J - not for our version. Creates a few too many mobs. 436#if 0 437 vector<Biome::MobSpawnerData *> *mobs = biome->getMobs(MobCategory::creature); 438 if (mobs->empty()) 439 { 440 return; 441 } 442 443 while (random->nextFloat() < biome->getCreatureProbability()) 444 { 445 Biome::MobSpawnerData *type = (Biome::MobSpawnerData *) WeighedRandom::getRandomItem(level->random, ((vector<WeighedRandomItem *> *)mobs)); 446 MobGroupData *groupData = NULL; 447 int count = type->minCount + random->nextInt(1 + type->maxCount - type->minCount); 448 449 int x = xo + random->nextInt(cellWidth); 450 int z = zo + random->nextInt(cellHeight); 451 int startX = x, startZ = z; 452 453 for (int c = 0; c < count; c++) 454 { 455 bool success = false; 456 for (int attempts = 0; !success && attempts < 4; attempts++) 457 { 458 // these mobs always spawn at the topmost position 459 int y = level->getTopSolidBlock(x, z); 460 if (isSpawnPositionOk(MobCategory::creature, level, x, y, z)) 461 { 462 463 float xx = x + 0.5f; 464 float yy = (float)y; 465 float zz = z + 0.5f; 466 467 shared_ptr<Mob> mob; 468 //try { 469 mob = dynamic_pointer_cast<Mob>( EntityIO::newByEnumType(type->mobClass, level ) ); 470 //} catch (Exception e) { 471 // e.printStackTrace(); 472 // continue; 473 //} 474 475 // System.out.println("Placing night mob"); 476 mob->moveTo(xx, yy, zz, random->nextFloat() * 360, 0); 477 478 mob->setDespawnProtected(); 479 480 level->addEntity(mob); 481 groupData = mob->finalizeMobSpawn(groupData); 482 success = true; 483 } 484 485 x += random->nextInt(5) - random->nextInt(5); 486 z += random->nextInt(5) - random->nextInt(5); 487 while (x < xo || x >= (xo + cellWidth) || z < zo || z >= (zo + cellWidth)) 488 { 489 x = startX + random->nextInt(5) - random->nextInt(5); 490 z = startZ + random->nextInt(5) - random->nextInt(5); 491 } 492 } 493 } 494 } 495#endif 496}