1
0

Merge pull request #1018 from mc-server/VillageGen

Village gen
This commit is contained in:
Mattes D 2014-05-31 19:11:13 +02:00
commit 6de45037c7
25 changed files with 17115 additions and 23 deletions

View File

@ -212,7 +212,7 @@ void cBioGenCache::InitializeBiomeGen(cIniFile & a_IniFile)
void cBiomeGenList::InitializeBiomes(const AString & a_Biomes)
{
AStringVector Split = StringSplit(a_Biomes, ",");
AStringVector Split = StringSplitAndTrim(a_Biomes, ",");
// Convert each string in the list into biome:
for (AStringVector::const_iterator itr = Split.begin(); itr != Split.end(); ++itr)

View File

@ -25,6 +25,7 @@
#include "Noise3DGenerator.h"
#include "POCPieceGenerator.h"
#include "Ravines.h"
#include "VillageGen.h"
@ -32,6 +33,7 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cTerrainCompositionGen:
cTerrainCompositionGen * cTerrainCompositionGen::CreateCompositionGen(cIniFile & a_IniFile, cBiomeGen & a_BiomeGen, cTerrainHeightGen & a_HeightGen, int a_Seed)
{
AString CompoGenName = a_IniFile.GetValueSet("Generator", "CompositionGen", "");
@ -404,6 +406,15 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
{
m_FinishGens.push_back(new cStructGenTrees(Seed, m_BiomeGen, m_HeightGen, m_CompositionGen));
}
else if (NoCaseCompare(*itr, "Villages") == 0)
{
int GridSize = a_IniFile.GetValueSetI("Generator", "VillageGridSize", 384);
int MaxDepth = a_IniFile.GetValueSetI("Generator", "VillageMaxDepth", 2);
int MaxSize = a_IniFile.GetValueSetI("Generator", "VillageMaxSize", 128);
int MinDensity = a_IniFile.GetValueSetI("Generator", "VillageMinDensity", 50);
int MaxDensity = a_IniFile.GetValueSetI("Generator", "VillageMaxDensity", 80);
m_FinishGens.push_back(new cVillageGen(Seed, GridSize, MaxDepth, MaxSize, MinDensity, MaxDensity, *m_BiomeGen, *m_HeightGen));
}
else if (NoCaseCompare(*itr, "WaterLakes") == 0)
{
int Probability = a_IniFile.GetValueSetI("Generator", "WaterLakesProbability", 25);

View File

@ -9,6 +9,34 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cEmptyStructure:
/** A cStructure descendant representing an empty structure.
Used when the generator descended from cGridStructGen doesn't return any structure, to keep at least the
Origin coords so that the structure isn't queried over and over again. */
class cEmptyStructure :
public cGridStructGen::cStructure
{
typedef cGridStructGen::cStructure super;
public:
cEmptyStructure(int a_OriginX, int a_OriginZ) :
super(a_OriginX, a_OriginZ)
{
}
protected:
virtual void DrawIntoChunk(cChunkDesc & a_ChunkDesc) override
{
// Do nothing
}
} ;
cGridStructGen::cGridStructGen(
int a_Seed,
int a_GridSizeX, int a_GridSizeZ,
@ -90,7 +118,12 @@ void cGridStructGen::GetStructuresForChunk(int a_ChunkX, int a_ChunkZ, cStructur
} // for itr - a_Structures[]
if (!Found)
{
a_Structures.push_back(CreateStructure(OriginX, OriginZ));
cStructurePtr Structure = CreateStructure(OriginX, OriginZ);
if (Structure.get() == NULL)
{
Structure.reset(new cEmptyStructure(OriginX, OriginZ));
}
a_Structures.push_back(Structure);
}
} // for z
} // for x

View File

@ -39,14 +39,6 @@ class cGridStructGen :
public cFinishGen
{
public:
cGridStructGen(
int a_Seed,
int a_GridSizeX, int a_GridSizeZ,
int a_MaxStructureSizeX, int a_MaxStructureSizeZ,
size_t a_MaxCacheSize
);
protected:
/** Represents a single structure that occupies the grid point. Knows how to draw itself into a chunk. */
class cStructure
{
@ -75,6 +67,14 @@ protected:
typedef std::list<cStructurePtr> cStructurePtrs;
cGridStructGen(
int a_Seed,
int a_GridSizeX, int a_GridSizeZ,
int a_MaxStructureSizeX, int a_MaxStructureSizeZ,
size_t a_MaxCacheSize
);
protected:
/** Seed for generating the semi-random grid. */
int m_Seed;

View File

@ -186,6 +186,11 @@ cPOCPieceGenerator::cPOCPieceGenerator(int a_Seed) :
cPOCPieceGenerator::~cPOCPieceGenerator()
{
cPieceGenerator::FreePieces(m_Pieces);
for (cPieces::iterator itr = m_AvailPieces.begin(), end = m_AvailPieces.end(); itr != end; ++itr)
{
delete *itr;
}
m_AvailPieces.clear();
}

View File

@ -286,7 +286,8 @@ cPlacedPiece::cPlacedPiece(const cPlacedPiece * a_Parent, const cPiece & a_Piece
m_Parent(a_Parent),
m_Piece(&a_Piece),
m_Coords(a_Coords),
m_NumCCWRotations(a_NumCCWRotations)
m_NumCCWRotations(a_NumCCWRotations),
m_HasBeenMovedToGround(false)
{
m_Depth = (m_Parent == NULL) ? 0 : (m_Parent->GetDepth() + 1);
m_HitBox = a_Piece.RotateMoveHitBox(a_NumCCWRotations, a_Coords.x, a_Coords.y, a_Coords.z);
@ -297,6 +298,36 @@ cPlacedPiece::cPlacedPiece(const cPlacedPiece * a_Parent, const cPiece & a_Piece
cPiece::cConnector cPlacedPiece::GetRotatedConnector(size_t a_Index) const
{
cPiece::cConnectors Connectors = m_Piece->GetConnectors();
ASSERT(Connectors.size() >= a_Index);
return m_Piece->RotateMoveConnector(Connectors[a_Index], m_NumCCWRotations, m_Coords.x, m_Coords.y, m_Coords.z);
}
cPiece::cConnector cPlacedPiece::GetRotatedConnector(const cPiece::cConnector & a_Connector) const
{
return m_Piece->RotateMoveConnector(a_Connector, m_NumCCWRotations, m_Coords.x, m_Coords.y, m_Coords.z);
}
void cPlacedPiece::MoveToGroundBy(int a_OffsetY)
{
m_Coords.y += a_OffsetY;
m_HasBeenMovedToGround = true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cPieceGenerator:
@ -331,7 +362,31 @@ cPlacedPiece * cPieceGenerator::PlaceStartingPiece(int a_BlockX, int a_BlockY, i
// Choose a random one of the starting pieces:
cPieces StartingPieces = m_PiecePool.GetStartingPieces();
cPiece * StartingPiece = StartingPieces[rnd % StartingPieces.size()];
int Total = 0;
for (cPieces::const_iterator itr = StartingPieces.begin(), end = StartingPieces.end(); itr != end; ++itr)
{
Total += m_PiecePool.GetStartingPieceWeight(**itr);
}
cPiece * StartingPiece;
if (Total > 0)
{
int Chosen = rnd % Total;
StartingPiece = StartingPieces.front();
for (cPieces::const_iterator itr = StartingPieces.begin(), end = StartingPieces.end(); itr != end; ++itr)
{
Chosen -= m_PiecePool.GetStartingPieceWeight(**itr);
if (Chosen <= 0)
{
StartingPiece = *itr;
break;
}
}
}
else
{
// All pieces returned zero weight, but we need one to start. Choose with equal chance:
StartingPiece = StartingPieces[rnd % StartingPieces.size()];
}
rnd = rnd >> 16;
// Choose a random supported rotation:

View File

@ -110,6 +110,7 @@ public:
virtual cPieces GetStartingPieces(void) = 0;
/** Returns the relative weight with which the a_NewPiece is to be selected for placing under a_PlacedPiece through a_ExistingConnector.
a_ExistingConnector is the original connector, before any movement or rotation is applied to it.
This allows the pool to tweak the piece's chances, based on the previous pieces in the tree and the connector used.
The higher the number returned, the higher the chance the piece will be chosen. 0 means the piece will never be chosen.
*/
@ -119,6 +120,15 @@ public:
const cPiece & a_NewPiece
) { return 1; }
/** Returns the relative weight with which the a_NewPiece is to be selected for placing as the first piece.
This allows the pool to tweak the piece's chances.
The higher the number returned, the higher the chance the piece will be chosen. 0 means the piece will not be chosen.
If all pieces return 0, a random piece is chosen, with all equal chances.
*/
virtual int GetStartingPieceWeight(
const cPiece & a_NewPiece
) { return 1; }
/** Called after a piece is placed, to notify the pool that it has been used.
The pool may adjust the pieces it will return the next time. */
virtual void PiecePlaced(const cPiece & a_Piece) = 0;
@ -138,19 +148,41 @@ class cPlacedPiece
public:
cPlacedPiece(const cPlacedPiece * a_Parent, const cPiece & a_Piece, const Vector3i & a_Coords, int a_NumCCWRotations);
const cPiece & GetPiece (void) const { return *m_Piece; }
const Vector3i & GetCoords (void) const { return m_Coords; }
int GetNumCCWRotations(void) const { return m_NumCCWRotations; }
const cCuboid & GetHitBox (void) const { return m_HitBox; }
int GetDepth (void) const { return m_Depth; }
const cPlacedPiece * GetParent (void) const { return m_Parent; }
const cPiece & GetPiece (void) const { return *m_Piece; }
const Vector3i & GetCoords (void) const { return m_Coords; }
int GetNumCCWRotations (void) const { return m_NumCCWRotations; }
const cCuboid & GetHitBox (void) const { return m_HitBox; }
int GetDepth (void) const { return m_Depth; }
bool HasBeenMovedToGround(void) const { return m_HasBeenMovedToGround; }
/** Returns the coords as a modifiable object. */
Vector3i & GetCoords(void) { return m_Coords; }
/** Returns the connector at the specified index, rotated in the actual placement.
Undefined behavior if a_Index is out of range. */
cPiece::cConnector GetRotatedConnector(size_t a_Index) const;
/** Returns a copy of the specified connector, modified to account for the translation and rotation for
this placement. */
cPiece::cConnector GetRotatedConnector(const cPiece::cConnector & a_Connector) const;
/** Moves the placed piece Y-wise by the specified offset.
Sets m_HasBeenMovedToGround to true, too.
Used eg. by village houses. */
void MoveToGroundBy(int a_OffsetY);
protected:
const cPlacedPiece * m_Parent;
const cPiece * m_Piece;
Vector3i m_Coords;
int m_NumCCWRotations;
cCuboid m_HitBox;
int m_Depth;
cCuboid m_HitBox; // Hitbox of the placed piece, in world coords
int m_Depth; // Depth in the generated piece tree
/** Set to true once the piece has been moved Y-wise.
Used eg. by village houses. */
bool m_HasBeenMovedToGround;
};
typedef std::vector<cPlacedPiece *> cPlacedPieces;

View File

@ -108,6 +108,9 @@ static const cPrefab::sDef g_TestPrefabDef =
// AddWeightIfSame:
1000,
// MoveToGround:
false,
};
static cPrefab g_TestPrefab(g_TestPrefabDef);
@ -127,7 +130,8 @@ cPrefab::cPrefab(const cPrefab::sDef & a_Def) :
m_MergeStrategy(a_Def.m_MergeStrategy),
m_ShouldExtendFloor(a_Def.m_ShouldExtendFloor),
m_DefaultWeight(a_Def.m_DefaultWeight),
m_AddWeightIfSame(a_Def.m_AddWeightIfSame)
m_AddWeightIfSame(a_Def.m_AddWeightIfSame),
m_MoveToGround(a_Def.m_MoveToGround)
{
m_BlockArea[0].Create(m_Size);
CharMap cm;
@ -136,6 +140,34 @@ cPrefab::cPrefab(const cPrefab::sDef & a_Def) :
ParseConnectors(a_Def.m_Connectors);
ParseDepthWeight(a_Def.m_DepthWeight);
AddRotatedBlockAreas();
}
cPrefab::cPrefab(const cBlockArea & a_Image, int a_AllowedRotations) :
m_Size(a_Image.GetSize()),
m_AllowedRotations(a_AllowedRotations),
m_MergeStrategy(cBlockArea::msOverwrite),
m_ShouldExtendFloor(false),
m_DefaultWeight(1),
m_AddWeightIfSame(0),
m_MoveToGround(false)
{
m_HitBox.p1.Set(0, 0, 0);
m_HitBox.p2.Set(m_Size.x - 1, m_Size.y - 1, m_Size.z - 1);
m_BlockArea[0].CopyFrom(a_Image);
AddRotatedBlockAreas();
}
void cPrefab::AddRotatedBlockAreas(void)
{
// 1 CCW rotation:
if ((m_AllowedRotations & 0x01) != 0)
{
@ -164,13 +196,21 @@ cPrefab::cPrefab(const cPrefab::sDef & a_Def) :
void cPrefab::Draw(cChunkDesc & a_Dest, const cPlacedPiece * a_Placement) const
{
Draw(a_Dest, a_Placement->GetCoords(), a_Placement->GetNumCCWRotations());
}
void cPrefab::Draw(cChunkDesc & a_Dest, const Vector3i & a_Placement, int a_NumRotations) const
{
// Draw the basic image:
Vector3i Placement = a_Placement->GetCoords();
Vector3i Placement(a_Placement);
int ChunkStartX = a_Dest.GetChunkX() * cChunkDef::Width;
int ChunkStartZ = a_Dest.GetChunkZ() * cChunkDef::Width;
Placement.Move(-ChunkStartX, 0, -ChunkStartZ);
const cBlockArea & Image = m_BlockArea[a_Placement->GetNumCCWRotations()];
const cBlockArea & Image = m_BlockArea[a_NumRotations];
a_Dest.WriteBlockArea(Image, Placement.x, Placement.y, Placement.z, m_MergeStrategy);
// If requested, draw the floor (from the bottom of the prefab down to the nearest non-air)
@ -257,6 +297,24 @@ int cPrefab::GetPieceWeight(const cPlacedPiece & a_PlacedPiece, const cPiece::cC
void cPrefab::SetDefaultWeight(int a_DefaultWeight)
{
m_DefaultWeight = a_DefaultWeight;
}
void cPrefab::AddConnector(int a_RelX, int a_RelY, int a_RelZ, eBlockFace a_Direction, int a_Type)
{
m_Connectors.push_back(cConnector(a_RelX, a_RelY, a_RelZ, a_Type, a_Direction));
}
void cPrefab::ParseCharMap(CharMap & a_CharMapOut, const char * a_CharMapDef)
{
ASSERT(a_CharMapDef != NULL);

View File

@ -82,19 +82,47 @@ public:
Can be positive or negative.
This is used e. g. to make nether bridges prefer spanning multiple segments or to penalize turrets next to each other. */
int m_AddWeightIfSame;
/** If true, the piece will be moved Y-wise so that its first connector is sitting on the terrain.
This is used e. g. for village houses. */
bool m_MoveToGround;
};
/** Creates a prefab from the provided definition. */
cPrefab(const sDef & a_Def);
/** Creates a prefab based on the given BlockArea and allowed rotations. */
cPrefab(const cBlockArea & a_Image, int a_AllowedRotations);
/** Draws the prefab into the specified chunk, according to the placement stored in the PlacedPiece. */
void Draw(cChunkDesc & a_Dest, const cPlacedPiece * a_Placement) const;
/** Draws the prefab into the specified chunks, according to the specified placement and rotations. */
void Draw(cChunkDesc & a_Dest, const Vector3i & a_Placement, int a_NumRotations) const;
/** Returns true if the prefab has any connector of the specified type. */
bool HasConnectorType(int a_ConnectorType) const;
/** Returns the weight (chance) of this prefab generating as the next piece after the specified placed piece.
PiecePool implementations can use this for their GetPieceWeight() implementations. */
int GetPieceWeight(const cPlacedPiece & a_PlacedPiece, const cPiece::cConnector & a_ExistingConnector) const;
/** Sets the (unmodified) DefaultWeight property for this piece. */
void SetDefaultWeight(int a_DefaultWeight);
/** Returns the unmodified DefaultWeight property for the piece. */
int GetDefaultWeight(void) const { return m_DefaultWeight; }
/** Sets the AddWeightIfSame member, that is used to modify the weight when the previous piece is the same prefab */
void SetAddWeightIfSame(int a_AddWeightIfSame) { m_AddWeightIfSame = a_AddWeightIfSame; }
/** Adds the specified connector to the list of connectors this piece supports. */
void AddConnector(int a_RelX, int a_RelY, int a_RelZ, eBlockFace a_Direction, int a_Type);
/** Returns whether the prefab should be moved Y-wise to ground before drawing, rather than staying
at the coords governed by the connectors. */
bool ShouldMoveToGround(void) const { return m_MoveToGround; }
protected:
/** Packs complete definition of a single block, for per-letter assignment. */
@ -149,6 +177,10 @@ protected:
Can be positive or negative.
This is used e. g. to make nether bridges prefer spanning multiple segments or to penalize turrets next to each other. */
int m_AddWeightIfSame;
/** If true, the piece will be moved Y-wise so that its first connector is sitting on the terrain.
This is used e. g. for village houses. */
bool m_MoveToGround;
// cPiece overrides:
@ -157,6 +189,10 @@ protected:
virtual cCuboid GetHitBox(void) const override;
virtual bool CanRotateCCW(int a_NumRotations) const override;
/** Based on the m_AllowedRotations, adds rotated cBlockAreas to the m_BlockArea array.
To be called only from this class's constructor! */
void AddRotatedBlockAreas(void);
/** Parses the CharMap in the definition into a CharMap binary data used for translating the definition into BlockArea. */
void ParseCharMap(CharMap & a_CharMapOut, const char * a_CharMapDef);

View File

@ -129,6 +129,15 @@ int cPrefabPiecePool::GetPieceWeight(const cPlacedPiece & a_PlacedPiece, const c
int cPrefabPiecePool::GetStartingPieceWeight(const cPiece & a_NewPiece)
{
return ((const cPrefab &)a_NewPiece).GetDefaultWeight();
}
void cPrefabPiecePool::PiecePlaced(const cPiece & a_Piece)
{
// Do nothing

View File

@ -75,6 +75,7 @@ protected:
virtual cPieces GetPiecesWithConnector(int a_ConnectorType) override;
virtual cPieces GetStartingPieces(void) override;
virtual int GetPieceWeight(const cPlacedPiece & a_PlacedPiece, const cPiece::cConnector & a_ExistingConnector, const cPiece & a_NewPiece) override;
virtual int GetStartingPieceWeight(const cPiece & a_NewPiece) override;
virtual void PiecePlaced(const cPiece & a_Piece) override;
virtual void Reset(void) override;
} ;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,15 @@
// AlchemistVillagePrefabs.h
// Declares the prefabs in the group AlchemistVillage
#include "../Prefab.h"
extern const cPrefab::sDef g_AlchemistVillagePrefabs[];
extern const cPrefab::sDef g_AlchemistVillageStartingPrefabs[];
extern const size_t g_AlchemistVillagePrefabsCount;
extern const size_t g_AlchemistVillageStartingPrefabsCount;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,15 @@
// JapaneseVillagePrefabs.h
// Declares the prefabs in the group JapaneseVillage
#include "../Prefab.h"
extern const cPrefab::sDef g_JapaneseVillagePrefabs[];
extern const cPrefab::sDef g_JapaneseVillageStartingPrefabs[];
extern const size_t g_JapaneseVillagePrefabsCount;
extern const size_t g_JapaneseVillageStartingPrefabsCount;

View File

@ -155,6 +155,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // BalconyCorridor
@ -315,6 +318,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // BalconyTee2
@ -435,6 +441,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // BlazePlatform
@ -605,6 +614,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // BlazePlatformOverhang
@ -805,6 +817,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
-1000,
// MoveToGround:
false,
}, // BridgeCircleCrossing
@ -1006,6 +1021,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // BridgeCrossing
@ -1100,6 +1118,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // BridgeCrumble1
@ -1200,6 +1221,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // BridgeCrumble2
@ -1379,6 +1403,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
1000,
// MoveToGround:
false,
}, // BridgeDoubleCrumble
@ -1619,6 +1646,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // BridgeFunnelDown
@ -1948,6 +1978,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // BridgeLevelCrossing
@ -2067,6 +2100,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
1000,
// MoveToGround:
false,
}, // BridgeSegment
@ -2227,6 +2263,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // BridgeTee
@ -2328,6 +2367,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // Corridor11
@ -2429,6 +2471,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // Corridor13
@ -2524,6 +2569,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
500,
// MoveToGround:
false,
}, // Corridor5
@ -2663,6 +2711,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // CorridorCorner5
@ -2803,6 +2854,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // CorridorCornerChest5
@ -2928,6 +2982,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
-50,
// MoveToGround:
false,
}, // CorridorCrossing
@ -3080,6 +3137,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // CorridorStairs
@ -3181,6 +3241,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // DarkCorridor
@ -3438,6 +3501,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // LavaStaircase
@ -3769,6 +3835,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
-1000,
// MoveToGround:
false,
}, // LavaStaircaseBig
@ -4047,6 +4116,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // LavaStairsBridge
@ -4235,6 +4307,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
-1000,
// MoveToGround:
false,
}, // MidStaircase
@ -4378,6 +4453,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // StairsToOpen1
@ -4521,6 +4599,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // StairsToOpen2
@ -4638,6 +4719,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // Tee2x4
@ -4767,6 +4851,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // Tee4x4
@ -4863,6 +4950,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
-50,
// MoveToGround:
false,
}, // TinyCorridorCorner
@ -4960,6 +5050,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // TinyCorridorCornerChest
@ -5059,6 +5152,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
-50,
// MoveToGround:
false,
}, // TinyCorridorCrossing
@ -5174,6 +5270,9 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// AddWeightIfSame:
-99,
// MoveToGround:
false,
}, // Turret
}; // g_NetherFortPrefabs
@ -5378,6 +5477,9 @@ const cPrefab::sDef g_NetherFortStartingPrefabs[] =
// AddWeightIfSame:
0,
// MoveToGround:
false,
}, // CentralRoom
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,15 @@
// PlainsVillagePrefabs.h
// Declares the prefabs in the group PlainsVillage
#include "../Prefab.h"
extern const cPrefab::sDef g_PlainsVillagePrefabs[];
extern const cPrefab::sDef g_PlainsVillageStartingPrefabs[];
extern const size_t g_PlainsVillagePrefabsCount;
extern const size_t g_PlainsVillageStartingPrefabsCount;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,15 @@
// SandFlatRoofVillagePrefabs.h
// Declares the prefabs in the group SandFlatRoofVillage
#include "../Prefab.h"
extern const cPrefab::sDef g_SandFlatRoofVillagePrefabs[];
extern const cPrefab::sDef g_SandFlatRoofVillageStartingPrefabs[];
extern const size_t g_SandFlatRoofVillagePrefabsCount;
extern const size_t g_SandFlatRoofVillageStartingPrefabsCount;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,15 @@
// SandVillagePrefabs.h
// Declares the prefabs in the group SandVillage
#include "../Prefab.h"
extern const cPrefab::sDef g_SandVillagePrefabs[];
extern const cPrefab::sDef g_SandVillageStartingPrefabs[];
extern const size_t g_SandVillagePrefabsCount;
extern const size_t g_SandVillageStartingPrefabsCount;

View File

@ -0,0 +1,430 @@
// VillageGen.cpp
// Implements the cVillageGen class representing the village generator
#include "Globals.h"
#include "VillageGen.h"
#include "Prefabs/AlchemistVillagePrefabs.h"
#include "Prefabs/JapaneseVillagePrefabs.h"
#include "Prefabs/PlainsVillagePrefabs.h"
#include "Prefabs/SandVillagePrefabs.h"
#include "Prefabs/SandFlatRoofVillagePrefabs.h"
#include "PieceGenerator.h"
/*
How village generating works:
By descending from a cGridStructGen, a semi-random grid is generated. A village may be generated for each of
the grid's cells. Each cell checks the biomes in an entire chunk around it, only generating a village if all
biomes are village-friendly. If yes, the entire village structure is built for that cell. If not, the cell
is left village-less.
A village is generated using the regular BFS piece generator. The well piece is used as the starting piece,
the roads and houses are then used as the following pieces. Only the houses are read from the prefabs,
though, the roads are generated by code and their content is ignored. A special subclass of the cPiecePool
class is used, so that the roads connect to each other and to the well only in predefined manners.
The well has connectors of type "2". The houses have connectors of type "-1". The roads have connectors of
both types' opposites, type "-2" at the far ends and type "1" on the long edges. Additionally, there are
type "2" connectors along the long edges of the roads as well, so that the roads create T junctions.
When the village is about to be drawn into a chunk, it queries the heights for each piece intersecting the
chunk. The pieces are shifted so that their pivot points lie on the surface, and the roads are drawn
directly by turning the surface blocks into gravel / sandstone.
The village prefabs are stored in global piecepools (one pool per village type). In order to support
per-village density setting, the cVillage class itself implements the cPiecePool interface, relaying the
calls to the underlying cVillagePiecePool, after processing the density check.
*/
class cVillagePiecePool :
public cPrefabPiecePool
{
typedef cPrefabPiecePool super;
public:
cVillagePiecePool(
const cPrefab::sDef * a_PieceDefs, size_t a_NumPieceDefs,
const cPrefab::sDef * a_StartingPieceDefs, size_t a_NumStartingPieceDefs
) :
super(a_PieceDefs, a_NumPieceDefs, a_StartingPieceDefs, a_NumStartingPieceDefs)
{
// Add the road pieces:
for (int len = 27; len < 60; len += 12)
{
cBlockArea BA;
BA.Create(len, 1, 3, cBlockArea::baTypes | cBlockArea::baMetas);
BA.Fill(cBlockArea::baTypes | cBlockArea::baMetas, E_BLOCK_GRAVEL, 0);
cPrefab * RoadPiece = new cPrefab(BA, 1);
RoadPiece->AddConnector(0, 0, 1, BLOCK_FACE_XM, -2);
RoadPiece->AddConnector(len - 1, 0, 1, BLOCK_FACE_XP, -2);
RoadPiece->SetDefaultWeight(100);
// Add the road connectors:
for (int x = 1; x < len; x += 12)
{
RoadPiece->AddConnector(x, 0, 0, BLOCK_FACE_ZM, 2);
RoadPiece->AddConnector(x, 0, 2, BLOCK_FACE_ZP, 2);
}
// Add the buildings connectors:
for (int x = 7; x < len; x += 12)
{
RoadPiece->AddConnector(x, 0, 0, BLOCK_FACE_ZM, 1);
RoadPiece->AddConnector(x, 0, 2, BLOCK_FACE_ZP, 1);
}
m_AllPieces.push_back(RoadPiece);
m_PiecesByConnector[-2].push_back(RoadPiece);
m_PiecesByConnector[1].push_back(RoadPiece);
m_PiecesByConnector[2].push_back(RoadPiece);
} // for len - roads of varying length
}
// cPrefabPiecePool overrides:
virtual int GetPieceWeight(const cPlacedPiece & a_PlacedPiece, const cPiece::cConnector & a_ExistingConnector, const cPiece & a_NewPiece) override
{
// Roads cannot branch T-wise (appending -2 connector to a +2 connector on a 1-high piece):
if ((a_ExistingConnector.m_Type == 2) && (a_PlacedPiece.GetDepth() > 0) && (a_PlacedPiece.GetPiece().GetSize().y == 1))
{
return 0;
}
return ((const cPrefab &)a_NewPiece).GetPieceWeight(a_PlacedPiece, a_ExistingConnector);
}
} ;
class cVillageGen::cVillage :
public cGridStructGen::cStructure,
protected cPiecePool
{
typedef cGridStructGen::cStructure super;
public:
cVillage(
int a_Seed,
int a_OriginX, int a_OriginZ,
int a_MaxRoadDepth,
int a_MaxSize,
int a_Density,
cPiecePool & a_Prefabs,
cTerrainHeightGen & a_HeightGen,
BLOCKTYPE a_RoadBlock
) :
super(a_OriginX, a_OriginZ),
m_Seed(a_Seed),
m_Noise(a_Seed),
m_MaxSize(a_MaxSize),
m_Density(a_Density),
m_Borders(a_OriginX - a_MaxSize, 0, a_OriginZ - a_MaxSize, a_OriginX + a_MaxSize, 255, a_OriginZ + a_MaxSize),
m_Prefabs(a_Prefabs),
m_HeightGen(a_HeightGen),
m_RoadBlock(a_RoadBlock)
{
// Generate the pieces for this village; don't care about the Y coord:
cBFSPieceGenerator pg(*this, a_Seed);
pg.PlacePieces(a_OriginX, 0, a_OriginZ, a_MaxRoadDepth + 1, m_Pieces);
if (m_Pieces.empty())
{
return;
}
// If the central piece should be moved to ground, move it, and
// check all of its dependents and move those that are strictly connector-driven based on its new Y coord:
if (((cPrefab &)m_Pieces[0]->GetPiece()).ShouldMoveToGround())
{
int OrigPosY = m_Pieces[0]->GetCoords().y;
PlacePieceOnGround(*m_Pieces[0]);
int NewPosY = m_Pieces[0]->GetCoords().y;
MoveAllDescendants(m_Pieces, 0, NewPosY - OrigPosY);
}
}
~cVillage()
{
cPieceGenerator::FreePieces(m_Pieces);
}
protected:
/** Seed for the random functions */
int m_Seed;
/** The noise used as a pseudo-random generator */
cNoise m_Noise;
/** Maximum size, in X/Z blocks, of the village (radius from the origin) */
int m_MaxSize;
/** The density for this village. Used to refrain from populating all house connectors. Range [0, 100] */
int m_Density;
/** Borders of the vilalge - no item may reach out of this cuboid. */
cCuboid m_Borders;
/** Prefabs to use for buildings */
cPiecePool & m_Prefabs;
/** The underlying height generator, used for placing the structures on top of the terrain. */
cTerrainHeightGen & m_HeightGen;
/** The village pieces, placed by the generator. */
cPlacedPieces m_Pieces;
/** The block to use for the roads. */
BLOCKTYPE m_RoadBlock;
// cGridStructGen::cStructure overrides:
virtual void DrawIntoChunk(cChunkDesc & a_Chunk) override
{
// Iterate over all items
// Each intersecting prefab is placed on ground, then drawn
// Each intersecting road is drawn by replacing top soil blocks with gravel / sandstone blocks
cChunkDef::HeightMap HeightMap; // Heightmap for this chunk, used by roads
m_HeightGen.GenHeightMap(a_Chunk.GetChunkX(), a_Chunk.GetChunkZ(), HeightMap);
for (cPlacedPieces::iterator itr = m_Pieces.begin(), end = m_Pieces.end(); itr != end; ++itr)
{
cPrefab & Prefab = (cPrefab &)((*itr)->GetPiece());
if ((*itr)->GetPiece().GetSize().y == 1)
{
// It's a road, special handling (change top terrain blocks to m_RoadBlock)
DrawRoad(a_Chunk, **itr, HeightMap);
continue;
}
if (Prefab.ShouldMoveToGround() && !(*itr)->HasBeenMovedToGround())
{
PlacePieceOnGround(**itr);
}
Prefab.Draw(a_Chunk, *itr);
} // for itr - m_PlacedPieces[]
}
/** Adjusts the Y coord of the given piece so that the piece is on the ground.
Ground level is assumed to be represented by the first connector in the piece. */
void PlacePieceOnGround(cPlacedPiece & a_Piece)
{
cPiece::cConnector FirstConnector = a_Piece.GetRotatedConnector(0);
int ChunkX, ChunkZ;
int BlockX = FirstConnector.m_Pos.x;
int BlockZ = FirstConnector.m_Pos.z;
int BlockY;
cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ);
cChunkDef::HeightMap HeightMap;
m_HeightGen.GenHeightMap(ChunkX, ChunkZ, HeightMap);
int TerrainHeight = cChunkDef::GetHeight(HeightMap, BlockX, BlockZ);
a_Piece.MoveToGroundBy(TerrainHeight - FirstConnector.m_Pos.y + 1);
}
/** Draws the road into the chunk.
The heightmap is not queried from the heightgen, but is given via parameter, so that it may be queried just
once for all roads in a chunk. */
void DrawRoad(cChunkDesc & a_Chunk, cPlacedPiece & a_Road, cChunkDef::HeightMap & a_HeightMap)
{
cCuboid RoadCoords = a_Road.GetHitBox();
RoadCoords.Sort();
int MinX = std::max(RoadCoords.p1.x - a_Chunk.GetChunkX() * cChunkDef::Width, 0);
int MaxX = std::min(RoadCoords.p2.x - a_Chunk.GetChunkX() * cChunkDef::Width, cChunkDef::Width - 1);
int MinZ = std::max(RoadCoords.p1.z - a_Chunk.GetChunkZ() * cChunkDef::Width, 0);
int MaxZ = std::min(RoadCoords.p2.z - a_Chunk.GetChunkZ() * cChunkDef::Width, cChunkDef::Width - 1);
for (int z = MinZ; z <= MaxZ; z++)
{
for (int x = MinX; x <= MaxX; x++)
{
a_Chunk.SetBlockType(x, cChunkDef::GetHeight(a_HeightMap, x, z), z, m_RoadBlock);
}
}
}
// cPiecePool overrides:
virtual cPieces GetPiecesWithConnector(int a_ConnectorType)
{
return m_Prefabs.GetPiecesWithConnector(a_ConnectorType);
}
virtual cPieces GetStartingPieces(void)
{
return m_Prefabs.GetStartingPieces();
}
virtual int GetPieceWeight(
const cPlacedPiece & a_PlacedPiece,
const cPiece::cConnector & a_ExistingConnector,
const cPiece & a_NewPiece
) override
{
// Check against the density:
if (a_ExistingConnector.m_Type == 1)
{
const Vector3i & Coords = a_PlacedPiece.GetRotatedConnector(a_ExistingConnector).m_Pos;
int rnd = (m_Noise.IntNoise3DInt(Coords.x, Coords.y, Coords.z) / 7) % 100;
if (rnd > m_Density)
{
return 0;
}
}
// Density check passed, relay to m_Prefabs:
return m_Prefabs.GetPieceWeight(a_PlacedPiece, a_ExistingConnector, a_NewPiece);
}
virtual int GetStartingPieceWeight(const cPiece & a_NewPiece) override
{
return m_Prefabs.GetStartingPieceWeight(a_NewPiece);
}
virtual void PiecePlaced(const cPiece & a_Piece) override
{
m_Prefabs.PiecePlaced(a_Piece);
}
virtual void Reset(void) override
{
m_Prefabs.Reset();
}
void MoveAllDescendants(cPlacedPieces & a_PlacedPieces, size_t a_Pivot, int a_HeightDifference)
{
size_t num = a_PlacedPieces.size();
cPlacedPiece * Pivot = a_PlacedPieces[a_Pivot];
for (size_t i = a_Pivot + 1; i < num; i++)
{
if (
(a_PlacedPieces[i]->GetParent() == Pivot) && // It is a direct dependant of the pivot
!((const cPrefab &)a_PlacedPieces[i]->GetPiece()).ShouldMoveToGround() // It attaches strictly by connectors
)
{
a_PlacedPieces[i]->MoveToGroundBy(a_HeightDifference);
MoveAllDescendants(a_PlacedPieces, i, a_HeightDifference);
}
} // for i - a_PlacedPieces[]
}
} ;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cVillageGen:
static cVillagePiecePool g_SandVillage(g_SandVillagePrefabs, g_SandVillagePrefabsCount, g_SandVillageStartingPrefabs, g_SandVillageStartingPrefabsCount);
static cVillagePiecePool g_SandFlatRoofVillage(g_SandFlatRoofVillagePrefabs, g_SandFlatRoofVillagePrefabsCount, g_SandFlatRoofVillageStartingPrefabs, g_SandFlatRoofVillageStartingPrefabsCount);
static cVillagePiecePool g_AlchemistVillage(g_AlchemistVillagePrefabs, g_AlchemistVillagePrefabsCount, g_AlchemistVillageStartingPrefabs, g_AlchemistVillageStartingPrefabsCount);
static cVillagePiecePool g_PlainsVillage(g_PlainsVillagePrefabs, g_PlainsVillagePrefabsCount, g_PlainsVillageStartingPrefabs, g_PlainsVillageStartingPrefabsCount);
static cVillagePiecePool g_JapaneseVillage(g_JapaneseVillagePrefabs, g_JapaneseVillagePrefabsCount, g_JapaneseVillageStartingPrefabs, g_JapaneseVillageStartingPrefabsCount);
static cVillagePiecePool * g_DesertVillagePools[] =
{
&g_SandVillage,
&g_SandFlatRoofVillage,
&g_AlchemistVillage,
} ;
static cVillagePiecePool * g_PlainsVillagePools[] =
{
&g_PlainsVillage,
&g_JapaneseVillage,
} ;
cVillageGen::cVillageGen(int a_Seed, int a_GridSize, int a_MaxDepth, int a_MaxSize, int a_MinDensity, int a_MaxDensity, cBiomeGen & a_BiomeGen, cTerrainHeightGen & a_HeightGen) :
super(a_Seed, a_GridSize, a_GridSize, a_MaxSize, a_MaxSize, 100),
m_Noise(a_Seed + 1000),
m_MaxDepth(a_MaxDepth),
m_MaxSize(a_MaxSize),
m_MinDensity(a_MinDensity),
m_MaxDensity(a_MaxDensity),
m_BiomeGen(a_BiomeGen),
m_HeightGen(a_HeightGen)
{
}
cGridStructGen::cStructurePtr cVillageGen::CreateStructure(int a_OriginX, int a_OriginZ)
{
// Generate the biomes for the chunk surrounding the origin:
int ChunkX, ChunkZ;
cChunkDef::BlockToChunk(a_OriginX, a_OriginZ, ChunkX, ChunkZ);
cChunkDef::BiomeMap Biomes;
m_BiomeGen.GenBiomes(ChunkX, ChunkZ, Biomes);
// Check if all the biomes are village-friendly:
// If just one is not, no village is created, because it's likely that an unfriendly biome is too close
cVillagePiecePool * VillagePrefabs = NULL;
BLOCKTYPE RoadBlock = E_BLOCK_GRAVEL;
int rnd = m_Noise.IntNoise2DInt(a_OriginX, a_OriginZ) / 11;
cVillagePiecePool * PlainsVillage = g_PlainsVillagePools[rnd % ARRAYCOUNT(g_PlainsVillagePools)];
cVillagePiecePool * DesertVillage = g_DesertVillagePools[rnd % ARRAYCOUNT(g_DesertVillagePools)];
for (size_t i = 0; i < ARRAYCOUNT(Biomes); i++)
{
switch (Biomes[i])
{
case biDesert:
case biDesertM:
{
// These biomes allow sand villages
VillagePrefabs = DesertVillage;
// RoadBlock = E_BLOCK_SANDSTONE;
break;
}
case biPlains:
case biSavanna:
case biSavannaM:
case biSunflowerPlains:
{
// These biomes allow plains-style villages
VillagePrefabs = PlainsVillage;
break;
}
default:
{
// Village-unfriendly biome, bail out with zero structure:
return cStructurePtr();
}
} // switch (Biomes[i])
} // for i - Biomes[]
// Choose density for the village, random between m_MinDensity and m_MaxDensity:
int Density;
if (m_MaxDensity > m_MinDensity)
{
Density = m_MinDensity + rnd % (m_MaxDensity - m_MinDensity);
}
else
{
Density = m_MinDensity;
}
// Create a village based on the chosen prefabs:
if (VillagePrefabs == NULL)
{
return cStructurePtr();
}
return cStructurePtr(new cVillage(m_Seed, a_OriginX, a_OriginZ, m_MaxDepth, m_MaxSize, Density, *VillagePrefabs, m_HeightGen, RoadBlock));
}

View File

@ -0,0 +1,57 @@
// VillageGen.h
// Declares the cVillageGen class representing the village generator
#pragma once
#include "GridStructGen.h"
#include "PrefabPiecePool.h"
class cVillageGen :
public cGridStructGen
{
typedef cGridStructGen super;
public:
cVillageGen(int a_Seed, int a_GridSize, int a_MaxDepth, int a_MaxSize, int a_MinDensity, int a_MaxDensity, cBiomeGen & a_BiomeGen, cTerrainHeightGen & a_HeightGen);
protected:
class cVillage; // fwd: VillageGen.cpp
/** The noise used for generating random numbers */
cNoise m_Noise;
/** Maximum depth of the generator tree*/
int m_MaxDepth;
/** Maximum size, in X/Z blocks, of the village (radius from the origin) */
int m_MaxSize;
/** Minimum density - percentage of allowed house connections. Range [0, 100] */
int m_MinDensity;
/** Maximum density - percentage of allowed house connections. Range [0, 100] */
int m_MaxDensity;
/** The underlying biome generator that defines whether the village is created or not */
cBiomeGen & m_BiomeGen;
/** The underlying height generator, used to position the prefabs crossing chunk borders */
cTerrainHeightGen & m_HeightGen;
// cGridStructGen overrides:
virtual cStructurePtr CreateStructure(int a_OriginX, int a_OriginZ) override;
} ;

View File

@ -234,7 +234,8 @@ void cProtocol172::SendChat(const cCompositeChat & a_Message)
// Compose the complete Json string to send:
Json::Value msg;
msg["text"] = cClientHandle::FormatMessageType(m_Client->GetPlayer()->GetWorld()->ShouldUseChatPrefixes(), a_Message.GetMessageType(), a_Message.GetAdditionalMessageTypeData()); // The client crashes without this field being present
cWorld * World = m_Client->GetPlayer()->GetWorld();
msg["text"] = cClientHandle::FormatMessageType((World == NULL) ? false : World->ShouldUseChatPrefixes(), a_Message.GetMessageType(), a_Message.GetAdditionalMessageTypeData()); // The client crashes without this field being present
const cCompositeChat::cParts & Parts = a_Message.GetParts();
for (cCompositeChat::cParts::const_iterator itr = Parts.begin(), end = Parts.end(); itr != end; ++itr)
{