Added new flowers on bonemeal use (#5011)
+ Added new biome-dependent flower placement * Update planter algorithm Co-authored-by: 12xx12 <12xx12100@gmail.com> Co-authored-by: Tiger Wang <ziwei.tiger@outlook.com>
This commit is contained in:
parent
ce1ceba5b3
commit
177cf041db
@ -93,7 +93,7 @@ cChunk * cChunkMap::FindChunk(int a_ChunkX, int a_ChunkZ)
|
|||||||
{
|
{
|
||||||
ASSERT(m_CSChunks.IsLockedByCurrentThread());
|
ASSERT(m_CSChunks.IsLockedByCurrentThread());
|
||||||
|
|
||||||
auto Chunk = m_Chunks.find({ a_ChunkX, a_ChunkZ });
|
const auto Chunk = m_Chunks.find({ a_ChunkX, a_ChunkZ });
|
||||||
return (Chunk == m_Chunks.end()) ? nullptr : &Chunk->second;
|
return (Chunk == m_Chunks.end()) ? nullptr : &Chunk->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,7 +100,7 @@ public:
|
|||||||
Returns true if the plant was fertilized successfully, false if not / not a plant.
|
Returns true if the plant was fertilized successfully, false if not / not a plant.
|
||||||
Note that successful fertilization doesn't mean successful growth - for blocks that have only a chance to grow,
|
Note that successful fertilization doesn't mean successful growth - for blocks that have only a chance to grow,
|
||||||
fertilization success is reported even in the case when the chance fails (bonemeal still needs to be consumed). */
|
fertilization success is reported even in the case when the chance fails (bonemeal still needs to be consumed). */
|
||||||
bool FertilizePlant(cWorld & a_World, Vector3i a_BlockPos)
|
static bool FertilizePlant(cWorld & a_World, Vector3i a_BlockPos)
|
||||||
{
|
{
|
||||||
BLOCKTYPE blockType;
|
BLOCKTYPE blockType;
|
||||||
NIBBLETYPE blockMeta;
|
NIBBLETYPE blockMeta;
|
||||||
@ -198,7 +198,7 @@ public:
|
|||||||
|
|
||||||
case E_BLOCK_GRASS:
|
case E_BLOCK_GRASS:
|
||||||
{
|
{
|
||||||
growPlantsAround(a_World, a_BlockPos);
|
GrowPlantsAround(a_World, a_BlockPos);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,51 +216,185 @@ public:
|
|||||||
|
|
||||||
/** Grows new plants around the specified block.
|
/** Grows new plants around the specified block.
|
||||||
Places up to 40 new plants, with the following probability:
|
Places up to 40 new plants, with the following probability:
|
||||||
- 20 % big grass (2-block tall grass)
|
- 0 up to 8 big grass (2-block tall grass)
|
||||||
- 60 % tall grass (1-block tall grass)
|
- 8 up tp 24 tall grass (1-block tall grass)
|
||||||
- 20 % flowers (biome dependent variants)
|
- 0 up to 8 flowers (biome dependent variants)
|
||||||
The new plants are spawned within 7 taxicab distance of a_BlockPos, on a grass block.
|
The new plants are spawned within 7 taxicab distance of a_Position, on a grass block.
|
||||||
Broadcasts a particle for each new spawned plant. */
|
Broadcasts a particle for each new spawned plant. */
|
||||||
void growPlantsAround(cWorld & a_World, Vector3i a_BlockPos)
|
static void GrowPlantsAround(cWorld & a_World, const Vector3i a_Position)
|
||||||
{
|
{
|
||||||
auto & r1 = GetRandomProvider();
|
auto & Random = GetRandomProvider();
|
||||||
for (int i = 0; i < 40; ++i)
|
|
||||||
|
auto DoubleGrassCount = Random.RandInt(8U);
|
||||||
|
auto GrassCount = Random.RandInt(8U, 24U);
|
||||||
|
auto FlowerCount = Random.RandInt(8U);
|
||||||
|
|
||||||
|
// Do a round-robin placement:
|
||||||
|
while ((DoubleGrassCount > 0) || (GrassCount > 0) || (FlowerCount > 0))
|
||||||
{
|
{
|
||||||
int ofsY = r1.RandInt(3) + r1.RandInt(3) - 3;
|
// place the big grass:
|
||||||
if (!cChunkDef::IsValidHeight(a_BlockPos.y + ofsY))
|
if (DoubleGrassCount != 0)
|
||||||
{
|
{
|
||||||
continue;
|
FindAdjacentGrassAnd<&GrowDoubleTallGrass>(a_World, a_Position);
|
||||||
|
DoubleGrassCount--;
|
||||||
}
|
}
|
||||||
int ofsX = (r1.RandInt(3) + r1.RandInt(3) + r1.RandInt(3) + r1.RandInt(3)) / 2 - 3;
|
|
||||||
int ofsZ = (r1.RandInt(3) + r1.RandInt(3) + r1.RandInt(3) + r1.RandInt(3)) / 2 - 3;
|
// place the tall grass:
|
||||||
Vector3i ofs(ofsX, ofsY, ofsZ);
|
if (GrassCount != 0)
|
||||||
auto typeGround = a_World.GetBlock(a_BlockPos + ofs);
|
|
||||||
if (typeGround != E_BLOCK_GRASS)
|
|
||||||
{
|
{
|
||||||
continue;
|
FindAdjacentGrassAnd<&GrowTallGrass>(a_World, a_Position);
|
||||||
|
GrassCount--;
|
||||||
}
|
}
|
||||||
auto pos = a_BlockPos + ofs.addedY(1);
|
|
||||||
auto typeAbove = a_World.GetBlock(pos);
|
// place the flowers
|
||||||
if (typeAbove != E_BLOCK_AIR)
|
if (FlowerCount != 0)
|
||||||
{
|
{
|
||||||
continue;
|
FindAdjacentGrassAnd<&GrowFlower>(a_World, a_Position);
|
||||||
|
FlowerCount--;
|
||||||
}
|
}
|
||||||
BLOCKTYPE spawnType;
|
}
|
||||||
NIBBLETYPE spawnMeta = 0;
|
}
|
||||||
switch (r1.RandInt(10))
|
|
||||||
|
static void GrowDoubleTallGrass(cWorld & a_World, const Vector3i a_Position)
|
||||||
|
{
|
||||||
|
a_World.SetBlock(a_Position, E_BLOCK_BIG_FLOWER, E_META_BIG_FLOWER_DOUBLE_TALL_GRASS);
|
||||||
|
a_World.BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_Position, 0);
|
||||||
|
|
||||||
|
const auto Above = a_Position.addedY(1);
|
||||||
|
a_World.SetBlock(Above, E_BLOCK_BIG_FLOWER, E_META_BIG_FLOWER_DOUBLE_TALL_GRASS | E_META_BIG_FLOWER_TOP);
|
||||||
|
a_World.BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, Above, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GrowTallGrass(cWorld & a_World, const Vector3i a_Position)
|
||||||
|
{
|
||||||
|
a_World.SetBlock(a_Position, E_BLOCK_TALL_GRASS, E_META_TALL_GRASS_GRASS);
|
||||||
|
a_World.BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, a_Position, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Grows a biome-dependent flower according to https://minecraft.gamepedia.com/Flower#Flower_biomes */
|
||||||
|
static void GrowFlower(cWorld & a_World, const Vector3i a_Position)
|
||||||
|
{
|
||||||
|
auto & Random = GetRandomProvider();
|
||||||
|
switch (a_World.GetBiomeAt(a_Position.x, a_Position.z))
|
||||||
|
{
|
||||||
|
case biPlains:
|
||||||
|
case biSunflowerPlains:
|
||||||
{
|
{
|
||||||
case 0: spawnType = E_BLOCK_YELLOW_FLOWER; break;
|
switch (Random.RandInt(8))
|
||||||
case 1: spawnType = E_BLOCK_RED_ROSE; break;
|
|
||||||
default:
|
|
||||||
{
|
{
|
||||||
spawnType = E_BLOCK_TALL_GRASS;
|
case 0: a_World.SetBlock(a_Position, E_BLOCK_DANDELION, 0); break;
|
||||||
spawnMeta = E_META_TALL_GRASS_GRASS;
|
case 1: a_World.SetBlock(a_Position, E_BLOCK_FLOWER, E_META_FLOWER_POPPY); break;
|
||||||
break;
|
case 2: a_World.SetBlock(a_Position, E_BLOCK_FLOWER, E_META_FLOWER_ALLIUM); break;
|
||||||
|
case 3: a_World.SetBlock(a_Position, E_BLOCK_RED_ROSE, 0); break; // was renamed to Azure Bluet later
|
||||||
|
case 4: a_World.SetBlock(a_Position, E_BLOCK_FLOWER, E_META_FLOWER_RED_TULIP); break;
|
||||||
|
case 5: a_World.SetBlock(a_Position, E_BLOCK_FLOWER, E_META_FLOWER_PINK_TULIP); break;
|
||||||
|
case 6: a_World.SetBlock(a_Position, E_BLOCK_FLOWER, E_META_FLOWER_WHITE_TULIP); break;
|
||||||
|
case 7: a_World.SetBlock(a_Position, E_BLOCK_FLOWER, E_META_FLOWER_ORANGE_TULIP); break;
|
||||||
|
case 8: a_World.SetBlock(a_Position, E_BLOCK_FLOWER, E_META_FLOWER_OXEYE_DAISY); break;
|
||||||
|
// TODO: Add cornflower
|
||||||
}
|
}
|
||||||
} // switch (random spawn block type)
|
break;
|
||||||
a_World.SetBlock(pos, spawnType, spawnMeta);
|
}
|
||||||
a_World.BroadcastSoundParticleEffect(EffectID::PARTICLE_HAPPY_VILLAGER, pos, 0);
|
case biSwampland:
|
||||||
} // for i - attempts
|
case biSwamplandM:
|
||||||
|
{
|
||||||
|
a_World.SetBlock(a_Position, E_BLOCK_FLOWER, E_META_FLOWER_BLUE_ORCHID);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case biFlowerForest:
|
||||||
|
{
|
||||||
|
switch (Random.RandInt(8))
|
||||||
|
{
|
||||||
|
case 0: a_World.SetBlock(a_Position, E_BLOCK_DANDELION, 0); break;
|
||||||
|
case 1: a_World.SetBlock(a_Position, E_BLOCK_FLOWER, E_META_FLOWER_POPPY); break;
|
||||||
|
case 2: a_World.SetBlock(a_Position, E_BLOCK_FLOWER, E_META_FLOWER_ALLIUM); break;
|
||||||
|
case 3: a_World.SetBlock(a_Position, E_BLOCK_RED_ROSE, 0); break; // was renamed to Azure Bluet later
|
||||||
|
case 4: a_World.SetBlock(a_Position, E_BLOCK_FLOWER, E_META_FLOWER_RED_TULIP); break;
|
||||||
|
case 5: a_World.SetBlock(a_Position, E_BLOCK_FLOWER, E_META_FLOWER_PINK_TULIP); break;
|
||||||
|
case 6: a_World.SetBlock(a_Position, E_BLOCK_FLOWER, E_META_FLOWER_WHITE_TULIP); break;
|
||||||
|
case 7: a_World.SetBlock(a_Position, E_BLOCK_FLOWER, E_META_FLOWER_ORANGE_TULIP); break;
|
||||||
|
case 8: a_World.SetBlock(a_Position, E_BLOCK_FLOWER, E_META_FLOWER_OXEYE_DAISY); break;
|
||||||
|
// TODO: Add cornflower, lily of the valley
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case biMesa:
|
||||||
|
case biMesaBryce:
|
||||||
|
case biMesaPlateau:
|
||||||
|
case biMesaPlateauF:
|
||||||
|
case biMesaPlateauM:
|
||||||
|
case biMesaPlateauFM:
|
||||||
|
case biMushroomIsland:
|
||||||
|
case biMushroomShore:
|
||||||
|
case biNether:
|
||||||
|
case biEnd:
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
switch (Random.RandInt(1))
|
||||||
|
{
|
||||||
|
case 0: a_World.SetBlock(a_Position, E_BLOCK_DANDELION, 0); break;
|
||||||
|
case 1: a_World.SetBlock(a_Position, E_BLOCK_RED_ROSE, 0); break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Walks adjacent grass blocks up to 7 taxicab distance away from a_Position and calls the Planter function on the first suitable one found.
|
||||||
|
Does nothing if no position suitable for growing was found. */
|
||||||
|
template <auto Planter>
|
||||||
|
static void FindAdjacentGrassAnd(cWorld & a_World, const Vector3i a_Position)
|
||||||
|
{
|
||||||
|
auto & Random = GetRandomProvider();
|
||||||
|
auto Position = a_Position;
|
||||||
|
|
||||||
|
// Maximum 7 taxicab distance away from centre:
|
||||||
|
for (
|
||||||
|
int Tries = 0;
|
||||||
|
Tries != 8;
|
||||||
|
Tries++,
|
||||||
|
|
||||||
|
// Get the adjacent block to visit this iteration:
|
||||||
|
Position += Vector3i(
|
||||||
|
Random.RandInt(-1, 1),
|
||||||
|
Random.RandInt(-1, 1) * (Random.RandInt(2) / 2), // Y offset, with discouragement to values that aren't zero
|
||||||
|
Random.RandInt(-1, 1)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
!cChunkDef::IsValidHeight(Position.y) ||
|
||||||
|
(a_World.GetBlock(Position) != E_BLOCK_GRASS) // Are we looking at grass?
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// Not grass or invalid height, restart random walk and bail:
|
||||||
|
Position = a_Position;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Planter == GrowDoubleTallGrass)
|
||||||
|
{
|
||||||
|
const auto TwoAbove = Position.addedY(2);
|
||||||
|
if ((TwoAbove.y >= cChunkDef::Height) || (a_World.GetBlock(TwoAbove) != E_BLOCK_AIR))
|
||||||
|
{
|
||||||
|
// Insufficient space for tall grass:
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto PlantBase = Position.addedY(1);
|
||||||
|
if ((PlantBase.y >= cChunkDef::Height) || (a_World.GetBlock(PlantBase) != E_BLOCK_AIR))
|
||||||
|
{
|
||||||
|
// Insufficient space:
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Planter(a_World, PlantBase);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} ;
|
} ;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user