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 8 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
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
213 changes: 212 additions & 1 deletion 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 @@ -400,7 +402,7 @@ void cFinishGenSoulsandRims::GenFinish(cChunkDesc & a_ChunkDesc)
int ChunkZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
HEIGHTTYPE MaxHeight = a_ChunkDesc.GetMaxHeight();

for (int x = 0; x < 16; x++)
for (int x = 0; x < 16; x++)
{
int xx = ChunkX + x;
for (int z = 0; z < 16; z++)
Expand Down Expand Up @@ -943,3 +945,212 @@ 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");
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)
Copy link
Member

Choose a reason for hiding this comment

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

Please do use parenthesis for each individual comparison. It makes the conditions more understandable.

{
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.

}
}





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) / 7) % cChunkDef::Width;
int PackCenterZ = (m_Noise.IntNoise2DInt(chunkX, chunkZ) / 7) % cChunkDef::Width;
Copy link
Member

Choose a reason for hiding this comment

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

This won't work well, the noise functions return the same number for the same input coords. You should add Tries to the equation, usually it's done by adding it to one of the coords.

if (TrySpawnAnimals(a_ChunkDesc, PackCenterX, a_ChunkDesc.GetHeight(PackCenterX, PackCenterZ), PackCenterZ, RandomMob))
{
for (int i = 0; i < 5; i++)
{
int OffsetX = (m_Noise.IntNoise2DInt(chunkX, chunkZ) / 7) % cChunkDef::Width;
int OffsetZ = (m_Noise.IntNoise2DInt(chunkX, chunkZ) / 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)
{
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)
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 - use parenthesis around each comparison.

{
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) / 7) % cChunkDef::Width;
int z = (m_Noise.IntNoise2DInt(chunkX, chunkZ) / 7) % cChunkDef::Width;
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 generate the same coords - x == z. Add a constant to the coords so that the input to IntNoise2DInt is different for each coord.


// Check biomes first to get a list of animals
switch (a_ChunkDesc.GetBiome(x, z))
{
// No animals
case biNether:
case biEnd:
return mtInvalidType;

// Squid only
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.


// Mooshroom only
case biMushroomIsland:
case biMushroomShore:
ListOfSpawnables.insert(MobIter, mtMooshroom);
break;

case biJungle:
case biJungleHills:
case biJungleEdge:
case biJungleM:
case biJungleEdgeM:
ListOfSpawnables.insert(MobIter, mtOcelot);

case biPlains:
case biSunflowerPlains:
case biSavanna:
case biSavannaPlateau:
case biSavannaM:
case biSavannaPlateauM:
ListOfSpawnables.insert(MobIter, mtHorse);
// ListOfSpawnables.insert(mtDonkey);

// Wolves only
case biForest:
case biTaiga:
case biMegaTaiga:
case biColdTaiga:
case biColdTaigaM:
ListOfSpawnables.insert(MobIter, mtWolf);

// All other mobs
default:
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) / 7) % ListOfSpawnables.size();
Copy link
Member

Choose a reason for hiding this comment

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

This random value is tightly coupled with the x and z coord. Perhaps add yet another constant to the chunk coords to make it varied.

MobIter=ListOfSpawnables.begin();
for (int i = 0; i < RandMob; i++)
{
++MobIter;
}

return *MobIter;
}




30 changes: 29 additions & 1 deletion src/Generating/FinishGen.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
#include "ComposableGenerator.h"
#include "../Noise/Noise.h"
#include "../ProbabDistrib.h"
#include "../Mobs/Monster.h"
#include "FastRandom.h"
Copy link
Member

Choose a reason for hiding this comment

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

FastRandom should not be needed anymore, is it?




Expand Down Expand Up @@ -121,7 +123,7 @@ class cFinishGenSoulsandRims :
public cFinishGen
{
public:
cFinishGenSoulsandRims(int a_Seed) :
cFinishGenSoulsandRims(int a_Seed) :
m_Noise(a_Seed)
{
}
Expand Down Expand Up @@ -315,3 +317,29 @@ class cFinishGenFluidSprings :




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.


// Tries to spawn a mob in the center of the pack. If successful, spawns 0-5 more.
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you use doxy-comments?
/** Tries to ... */

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 mob 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