Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Animal terrain finisher #1642

Merged
merged 22 commits into from
Dec 5, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CONTRIBUTORS
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ mgueydan
MikeHunsinger
mtilden
nesco
p-mcgowan
rs2k
SamJBarney
Sofapriester
Expand Down
6 changes: 5 additions & 1 deletion src/Generating/ComposableGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,11 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
for (AStringVector::const_iterator itr = Str.begin(); itr != Str.end(); ++itr)
{
// Finishers, alpha-sorted:
if (NoCaseCompare(*itr, "BottomLava") == 0)
if (NoCaseCompare(*itr, "Animals") == 0)
{
m_FinishGens.push_back(cFinishGenPtr(new cFinishGenPassiveMobs(Seed, a_IniFile, Dimension)));
}
else if (NoCaseCompare(*itr, "BottomLava") == 0)
{
int DefaultBottomLavaLevel = (Dimension == dimNether) ? 30 : 10;
int BottomLavaLevel = a_IniFile.GetValueSetI("Generator", "BottomLavaLevel", DefaultBottomLavaLevel);
Expand Down
236 changes: 236 additions & 0 deletions src/Generating/FinishGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
#define DEF_OVERWORLD_LAVA_SPRINGS "0, 0; 10, 5; 11, 45; 48, 2; 64, 1; 255, 0"
#define DEF_END_WATER_SPRINGS "0, 1; 255, 1"
#define DEF_END_LAVA_SPRINGS "0, 1; 255, 1"
#define DEF_ANIMAL_SPAWN_PERCENT 10
#define DEF_NO_ANIMALS 0



Expand Down Expand Up @@ -958,3 +960,237 @@ bool cFinishGenFluidSprings::TryPlaceSpring(cChunkDesc & a_ChunkDesc, int x, int




////////////////////////////////////////////////////////////////////////////////
// cFinishGenPassiveMobs:

cFinishGenPassiveMobs::cFinishGenPassiveMobs(int a_Seed, cIniFile & a_IniFile, eDimension a_Dimension) :
m_Noise(a_Seed)
{
AString SectionName = "Animals";
int DefaultAnimalSpawnChunkPercentage = DEF_ANIMAL_SPAWN_PERCENT;
switch (a_Dimension)
{
case dimOverworld:
{
DefaultAnimalSpawnChunkPercentage = DEF_ANIMAL_SPAWN_PERCENT;
break;
}
case dimNether:
case dimEnd: // No nether or end animals (currently)
{
DefaultAnimalSpawnChunkPercentage = DEF_NO_ANIMALS;
break;
}
default:
{
ASSERT(!"Unhandled world dimension");
DefaultAnimalSpawnChunkPercentage = DEF_NO_ANIMALS;
break;
}
} // switch (dimension)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indenting is wrong inside the switch, there's a needless extra level.

m_AnimalProbability = a_IniFile.GetValueSetI(SectionName, "AnimalSpawnChunkPercentage", DefaultAnimalSpawnChunkPercentage);
if ((m_AnimalProbability < 0) || (m_AnimalProbability > 100))
{
LOGWARNING("[Animals]: AnimalSpawnChunkPercentage is invalid, using the default of \"%d\".", DefaultAnimalSpawnChunkPercentage);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You say you're using the default, but I see no code doing that.

m_AnimalProbability = DefaultAnimalSpawnChunkPercentage;
}
}





void cFinishGenPassiveMobs::GenFinish(cChunkDesc & a_ChunkDesc)
{
int chunkX = a_ChunkDesc.GetChunkX();
int chunkZ = a_ChunkDesc.GetChunkZ();
int ChanceRnd = (m_Noise.IntNoise2DInt(chunkX, chunkZ) / 7) % 100;
if (ChanceRnd > m_AnimalProbability)
{
return;
}

eMonsterType RandomMob = GetRandomMob(a_ChunkDesc);
if (RandomMob == mtInvalidType)
{
LOGWARNING("Attempted to spawn invalid mob type.");
return;
}

// Try spawning a pack center 10 times, should get roughly the same probability
for (int Tries = 0; Tries < 10; Tries++)
{
int PackCenterX = (m_Noise.IntNoise2DInt(chunkX + chunkZ, Tries) / 7) % cChunkDef::Width;
int PackCenterZ = (m_Noise.IntNoise2DInt(chunkX, chunkZ + Tries) / 7) % cChunkDef::Width;
if (TrySpawnAnimals(a_ChunkDesc, PackCenterX, a_ChunkDesc.GetHeight(PackCenterX, PackCenterZ), PackCenterZ, RandomMob))
{
for (int i = 0; i < 3; i++)
{
int OffsetX = (m_Noise.IntNoise2DInt(chunkX + chunkZ + i, Tries) / 7) % cChunkDef::Width;
int OffsetZ = (m_Noise.IntNoise2DInt(chunkX, chunkZ + Tries + i) / 7) % cChunkDef::Width;
TrySpawnAnimals(a_ChunkDesc, OffsetX, a_ChunkDesc.GetHeight(OffsetX, OffsetZ), OffsetZ, RandomMob);
}
return;

} // if pack center spawn successful
} // for tries
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still not good here. The PackCenterX and PackCenterZ should use the Tries variable as well, and OffsetX and OffsetZ should use both Tries and i.

}





bool cFinishGenPassiveMobs::TrySpawnAnimals(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelY, int a_RelZ, eMonsterType AnimalToSpawn)
{
if ((a_RelY >= cChunkDef::Height - 1) || (a_RelY <= 0))
{
return false;
}

BLOCKTYPE BlockAtHead = a_ChunkDesc.GetBlockType(a_RelX, a_RelY + 1, a_RelZ);
BLOCKTYPE BlockAtFeet = a_ChunkDesc.GetBlockType(a_RelX, a_RelY, a_RelZ);
BLOCKTYPE BlockUnderFeet = a_ChunkDesc.GetBlockType(a_RelX, a_RelY - 1, a_RelZ);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will fail if the spawning is requested at a_RelY = 0 or a_RelY = 255 (it may happen for some custom terrain generator settings).


// Check block below (opaque, grass, water), and above (air)
if ((AnimalToSpawn == mtSquid) && (BlockAtFeet != E_BLOCK_WATER))
{
return false;
}
if (
(AnimalToSpawn != mtSquid) &&
(BlockAtHead != E_BLOCK_AIR) &&
(BlockAtFeet != E_BLOCK_AIR) &&
(!cBlockInfo::IsTransparent(BlockUnderFeet))
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When breaking conditions on separate lines, put the first condition on a separate line as well:

if (
    (AnimalToSpawn != mtSquid) &&
    (BlockAtHead != E_BLOCK_AIR) &&
    (BlockAtFeet != E_BLOCK_AIR) &&
    (!cBlockInfo::IsTransparent(BlockUnderFeet))
)

{
return false;
}
if (
(BlockUnderFeet != E_BLOCK_GRASS) &&
((AnimalToSpawn == mtSheep) || (AnimalToSpawn == mtChicken) || (AnimalToSpawn == mtPig))
)
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Condition formatting - separate first line, closing parenthesis on next line.

return false;
}
if ((AnimalToSpawn == mtMooshroom) && (BlockUnderFeet != E_BLOCK_MYCELIUM))
{
return false;
}

int AnimalX, AnimalY, AnimalZ;
AnimalX = (double)(a_ChunkDesc.GetChunkX()*cChunkDef::Width + a_RelX + 0.5);
AnimalY = a_RelY;
AnimalZ = (double)(a_ChunkDesc.GetChunkZ()*cChunkDef::Width + a_RelZ + 0.5);

cEntityList ChunkEntities = a_ChunkDesc.GetEntities();
cMonster * NewMob = cMonster::NewMonsterFromType(AnimalToSpawn);
NewMob->SetPosition(AnimalX, AnimalY, AnimalZ);
ChunkEntities.push_back(NewMob);
LOGD("Spawning %s #%i at {%d, %d, %d}", NewMob->GetClass(), NewMob->GetUniqueID(), AnimalX, AnimalY, AnimalZ);

return true;
}





eMonsterType cFinishGenPassiveMobs::GetRandomMob(cChunkDesc & a_ChunkDesc)
{

std::set<eMonsterType> ListOfSpawnables;
std::set<eMonsterType>::iterator MobIter = ListOfSpawnables.begin();
int chunkX = a_ChunkDesc.GetChunkX();
int chunkZ = a_ChunkDesc.GetChunkZ();
int x = (m_Noise.IntNoise2DInt(chunkX, chunkZ + 10) / 7) % cChunkDef::Width;
int z = (m_Noise.IntNoise2DInt(chunkX + chunkZ, chunkZ) / 7) % cChunkDef::Width;

/** Check biomes first to get a list of animals */
switch (a_ChunkDesc.GetBiome(x, z))
{
// No animals in deserts or non-overworld dimensions
case biNether:
case biEnd:
case biDesertHills:
case biDesert:
case biDesertM:
{
return mtInvalidType;
}
// Mooshroom only - no other mobs on mushroom islands
case biMushroomIsland:
case biMushroomShore:
{
return mtMooshroom;
}
// Add squid in ocean biomes
case biOcean:
case biFrozenOcean:
case biFrozenRiver:
case biRiver:
case biDeepOcean:
{
ListOfSpawnables.insert(MobIter, mtSquid);
break;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use braces for the case contents as well.

}
// Add ocelots in jungle biomes
case biJungle:
case biJungleHills:
case biJungleEdge:
case biJungleM:
case biJungleEdgeM:
{
ListOfSpawnables.insert(MobIter, mtOcelot);
break;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably missing a break here?

case biPlains:
case biSunflowerPlains:
case biSavanna:
case biSavannaPlateau:
case biSavannaM:
case biSavannaPlateauM:
{
ListOfSpawnables.insert(MobIter, mtHorse);
// ListOfSpawnables.insert(mtDonkey);
break;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing break?

// Add wolves in forest and spruce forests
case biForest:
case biTaiga:
case biMegaTaiga:
case biColdTaiga:
case biColdTaigaM:
{
ListOfSpawnables.insert(MobIter, mtWolf);
break;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing break?

// Nothing special about this biome
default:
{
break;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default mobs should probably added to all biomes, shouldn't they? Why not take them out of the switch?

}
ListOfSpawnables.insert(MobIter, mtChicken);
ListOfSpawnables.insert(MobIter, mtCow);
ListOfSpawnables.insert(MobIter, mtPig);
ListOfSpawnables.insert(MobIter, mtSheep);

if (ListOfSpawnables.empty())
{
return mtInvalidType;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should not log this situation, it's perfectly normal for an End world.


int RandMob = (m_Noise.IntNoise2DInt(chunkX - chunkZ + 2, chunkX + 5) / 7) % ListOfSpawnables.size();
MobIter = ListOfSpawnables.begin();
for (int i = 0; i < RandMob; i++)
{
++MobIter;
}

return *MobIter;
}




32 changes: 31 additions & 1 deletion src/Generating/FinishGen.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "ComposableGenerator.h"
#include "../Noise/Noise.h"
#include "../ProbabDistrib.h"
#include "../Mobs/Monster.h"



Expand Down Expand Up @@ -311,10 +312,39 @@ class cFinishGenFluidSprings :
// cFinishGen override:
virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;

/// Tries to place a spring at the specified coords, checks neighbors. Returns true if successful
// Tries to place a spring at the specified coords, checks neighbors. Returns true if successful
bool TryPlaceSpring(cChunkDesc & a_ChunkDesc, int x, int y, int z);
} ;





/** This class populates generated chunks with packs of biome-dependant animals
Animals: cows, sheep, pigs, mooshrooms, squid, horses, wolves, ocelots
*/
class cFinishGenPassiveMobs :
public cFinishGen
{
public:

cFinishGenPassiveMobs(int a_Seed, cIniFile & a_IniFile, eDimension a_Dimension);

protected:

cNoise m_Noise;
int m_AnimalProbability; // Chance, [0..100], that an animal pack will be generated in a chunk

// cFinishGen override:
virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not a good candidate for doxy-comment, it's just a note about where the original function comes from, it doesn't explain what it does. This comment should use regular C++ comment style (//)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To explain further: These functions should be doxy-commented in the base class, where it should be explained what they're used for and what can be achieved by overriding them. Then, the individual overrides would just repeat the description, going out of date really easily. So instead they just point to the original class. With multi-level and sometimes even multiple inheritance, this helps folks who don't have an IDE capable of searching for the parent.


// Returns false if an animal cannot spawn at given coords, else adds it to the chunk's entity list and returns true
bool TrySpawnAnimals(cChunkDesc & a_ChunkDesc, int x, int y, int z, eMonsterType AnimalToSpawn);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment is wrong, the function doesn't spawn any more animals.


// Gets a random animal from biome-dependant list
eMonsterType GetRandomMob(cChunkDesc & a_ChunkDesc);
} ;




2 changes: 1 addition & 1 deletion src/World.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -747,7 +747,7 @@ void cWorld::InitialiseGeneratorDefaults(cIniFile & a_IniFile)
a_IniFile.GetValueSet("Generator", "BiomeGen", "Grown");
a_IniFile.GetValueSet("Generator", "ShapeGen", "BiomalNoise3D");
a_IniFile.GetValueSet("Generator", "CompositionGen", "Biomal");
a_IniFile.GetValueSet("Generator", "Finishers", "Ravines, WormNestCaves, WaterLakes, WaterSprings, LavaLakes, LavaSprings, OreNests, Mineshafts, Trees, Villages, SprinkleFoliage, Ice, Snow, Lilypads, BottomLava, DeadBushes, PreSimulator");
a_IniFile.GetValueSet("Generator", "Finishers", "Ravines, WormNestCaves, WaterLakes, WaterSprings, LavaLakes, LavaSprings, OreNests, Mineshafts, Trees, Villages, SprinkleFoliage, Ice, Snow, Lilypads, BottomLava, DeadBushes, PreSimulator, Animals");
break;
}
case dimNether:
Expand Down