Building Reference¶
TL;DR
buildings/<name>.json describes a vertical stack: cellars, ground floor, upper floors, top. Each level picks from a list of candidate parts, filtered by conditions. Every level that can generate must have at least one matching part, or that chunk fails to generate. See Floor coverage.
Some keys here do not exist on every version
overrideFloors and the part reference key belowpart were added in 7.4.12,
and are absent in 8.2.2. allowDoors and allowFillers are absent in 6.0.3
only. See Key availability.
Keys¶
| Key | Required | Default | Meaning code review |
|---|---|---|---|
filler |
yes | One palette character. The mod uses it to seat the building into the terrain. See Filler. | |
rubble |
no | One palette character, used for rubble when this building is ruined. If the character is not defined in the palette, the mod uses filler instead. |
|
refpalette |
no | Name of a shared palette. | |
palette |
no | An embedded palette, used instead of refpalette. It is a whole palette asset, so the entry list nests under a second palette key, exactly as on a Part. Written as a bare list it decodes to nothing and is not an error. |
|
minfloors / maxfloors |
no | -1 |
Bounds on the number of floors above ground. By default these only narrow the count the profile already chose. They do not replace it. See Floor counts. |
mincellars / maxcellars |
no | -1 |
The same, for levels below ground. |
allowDoors |
no | true |
If true, the mod cuts doorways through this building's walls to adjacent city chunks. If false, the walls are left exactly as the part draws them and the building is sealed. The top floor never gets doors either way. Measured on 7.4.12: the same three-storey part placed 2240 wall blocks with doors allowed and 2256 with allowDoors: false. |
allowFillers |
no | true |
If true, the mod generates the filler skirt around a building that has cellars. If false, it does not. On a building with no cellars this key changes nothing. See Filler. |
overrideFloors |
no | false |
If false, this building's floor bounds clamp the profile's count. If true, they replace it. See Floor counts. |
preferslonely |
no | 0 |
The chance, from 0 to 1, that this building type suppresses a building in each neighbouring chunk. 0 disables it. See preferslonely. |
parts |
yes | The list of part references, one entry per candidate part. | |
parts2 |
no | A second, independent list, generated as an overlay. See parts2. |
Unset is -1, not 0
The four floor and cellar bounds default to -1, and -1 is the value the mod checks for "not set". So "minfloors": 0 is not the same as omitting minfloors. 0 is a real bound that takes part in the clamping. Omitting the key lets the profile's value pass through untouched.
No bound is enforced on these numbers
minfloors, maxfloors, mincellars and maxcellars are plain integers in the codec. Nothing rejects a negative, an absurd value, or a minimum above its maximum.
The 0 to 60 floor range and 0 to 20 cellar range you may have seen belong to the profile, not to a building, and even there they only drive the config screen's sliders. Configuration.getInt returns the stored value without clamping it, so a hand-edited profile is not validated either. See Profile. code review
The casing is not consistent
allowDoors, allowFillers and overrideFloors are camelCase. filler, rubble, preferslonely, minfloors, maxfloors, mincellars and maxcellars are lowercase, in the same file. That is how the mod names them. Copy the exact key. Do not guess from the pattern.
Building your first one?
Your First Custom City walks through a complete working building and palette, and links to the finished files.
Filler: what it is, and why it is required¶
filler is one palette character. With parts, it is one of only two required keys. It is not part of your building's design. It is what makes the building sit correctly in the ground. The mod uses it in two places. game test
1. The foundation slab. The mod clears space before a building generates. At the building's lowest level, any column that would otherwise be open air gets the filler block. Natural terrain is uneven, so without this a building on a slope generates with holes in its lowest floor where the ground falls away. code review
2. The skirt around cellars. On a building with one or more cellars, the mod fills the outermost ring of the chunk with the filler block. The skirt runs from the building's bottom up to the ground level of whichever is lower, this chunk or the neighbour on that side. This hides the exposed outside face of the cellars, so the building reads as buried rather than as a box in a pit. code review
allowFillers: false disables step 2 only. On a building with no cellars it changes nothing. code review
{ "filler": "#" }
Choose something structural that matches the building's walls, such as stone bricks or concrete. The filler is visible. It is the underside and the buried exterior.
filler and rubble resolve against the building's palette, not the part's
The building's palette is the Style's palettes, plus this building's own refpalette or palette. A refpalette on a part is not included.
So a building whose filler character is defined only in a palette that its parts reference will pass every load check, generate its parts correctly, and then throw as soon as the mod places a door: game test
java.lang.NullPointerException: Cannot invoke "...BlockState.m_60734_()" because "state" is null
at mcjty.lostcities.worldgen.ChunkDriver.correct(ChunkDriver.java:253)
at mcjty.lostcities.worldgen.gen.Doors.generateDoors(Doors.java:60)
Give the building a refpalette as well as the parts. See Error Messages. game test
The building palette is built the same way in 7.4.12, 7.5.1 and 10.0.1. code review
How floor and cellar counts are decided¶
Your building does not decide how tall it is. The Profile rolls a number, and the bounds then pull it up or down. game test
The mod rolls the count first: code review
floors = buildingMinFloors
+ random( buildingMinFloorsChance
+ (cityFactor + 0.1) x (buildingMaxFloorsChance - buildingMinFloorsChance) )
+ 1
The trailing + 1 is in the mod and is easy to miss. It is applied before either
bound, so it cannot push a building past maxfloors, but it does mean the roll
never produces buildingMinFloors exactly. code review
cityFactor is how strong the city is at that point, so buildings are taller near
the centre. See How a Chunk Becomes a City. code review
Then it applies the two bounds, in this order: game test
if floors > maximum: floors = maximumif floors < minimum: floors = minimumgame test
Each bound is resolved from three sources: code review
| Bound | overrideFloors: false (default) |
overrideFloors: true game test |
|---|---|---|
| maximum | The smallest of the profile's buildingMaxFloors, the building's maxfloors, and the city style's maxfloors |
The building's maxfloors, alone |
| minimum | The largest of the profile's buildingMinFloors, the building's minfloors, and the city style's minfloors |
The building's minfloors, alone |
minfloors is applied last, so it can push a building past every maximum
The minimum is a max(), and it runs after the maximum has already been
applied. A building with minfloors: 6 gets 6 floors even when the profile,
the city style and its own maxfloors all say 3.
This happens with overrideFloors absent. The key is not required to exceed
the profile, and setting it changes nothing in that case, because the building's
own value was already going to win the max(). game test
Tested in game on 7.4.12: two buildings both declaring minfloors: 6 and
maxfloors: 6, one with overrideFloors and one without, generate at the same
height under a profile allowing 2 to 3 floors. Both are 6. game test
The minimum is a max() in 7.4.12, 7.5.1 and 10.0.1 alike. code review
So what is overrideFloors actually for? Making a building shorter or looser
than the profile permits, which is the case the min and max cannot express: game test
| Goal | What to write game test |
|---|---|
| Never taller than 2, whatever the profile says | maxfloors: 2. No override needed, min() already wins. |
| Never shorter than 6, whatever the profile says | minfloors: 6. No override needed, max() already wins. |
Exactly 2, in a profile whose buildingMinFloors is 4 |
minfloors: 2, maxfloors: 2, and overrideFloors: true. Without it the profile's minimum of 4 wins the max(). |
Cellar counts work the same way, with one addition. The mod adds the chunk's city level to the profile's cellar maximum, so a building on higher terrain is allowed deeper cellars. code review game test
buildingMaxCellars: 0 does not mean no cellars
The maximum is a base, not a cap. Because the chunk's city level is added
to it, a profile set to 0 still builds cellars on every chunk above level 0.
Measured: 0 produced 2352 cellar blocks over sixteen chunks, and the same
profile with every chunk pinned to level 0 produced none. game test
Floor coverage: the most common failure¶
Floor numbering:
| Level | Index game test |
|---|---|
| Deepest cellar | -cellars |
| Ground floor | 0 |
| Top floor | floors |
maxfloors: 3 is a four-storey building
The count is the top index, not the number of levels. Levels run from -cellars up to floors inclusive, and 0 is the ground floor. So floors: 3 gives indices 0, 1, 2 and 3, which is the ground floor plus three above it. Cellars work the same way. maxcellars: 1 adds one level, at index -1.
top: true does not add a level. It is a test that passes on whichever index is currently highest. With floors: 3 it matches index 3, the same level "floor": 3 matches. code review
That has a consequence. If you write both a "floor": 3 part and a top: true part, both match at index 3, and the mod picks one at random with equal probability. If you want the top part to win there, either remove the "floor": 3 entry or narrow it to "floor": 3, "top": false. code review
You can combine top with a number. The mod chains tests with AND, so {"part": "roof", "top": true, "floor": 3} means "the top level, but only when the building is exactly four storeys tall". That is useful for a roof that suits only one height. game test
The mod fills every level from -cellars up to and including floors in one pass. For each level it collects every entry in parts whose conditions match, then picks one at random. If nothing matches, the mod throws: game test
Misconfiguration! Floor were generated for a building where no part condition matches!
The wording, including the missing word, is the mod's own. This kills the chunk that was generating. game test
The real rule is coverage, not minfloors and maxfloors. You do not have to declare bounds. You have to guarantee that every level that can generate has a matching part. Declaring bounds is the most direct way to make that guarantee, because otherwise the profile decides the height and will eventually roll higher than the parts you wrote. game test
This is why writing "floor": 0, "floor": 1 and "floor": 2 and nothing else crashes. As soon as the profile rolls a four-floor building, level 3 has no match. game test
There are two ways to fix it.
{
"filler": "#",
"minfloors": 0,
"maxfloors": 2,
"overrideFloors": true,
"parts": [
{ "part": "shop_ground", "floor": 0 },
{ "part": "shop_mid", "floor": 1 },
{ "part": "shop_top", "floor": 2 }
]
}
overrideFloors: true matters here. Without it, a city style with a higher minimum can still push this building past floor 2. game test
{
"filler": "#",
"parts": [
{ "part": "shop_ground", "floor": 0 },
{ "part": "shop_top", "top": true },
{ "part": "shop_generic" }
]
}
Use the catch-all as your default, especially if your building may be used under a profile you did not write.
The same rule applies below ground. If cellars generate and no part matches a negative index, you get the same crash. game test
parts2 never crashes
Only parts has to match. If nothing in parts2 matches a level, that level simply gets no overlay.
parts2 sits on the same level, it does not stack above it
For each level the mod places the parts entry, then places the parts2 entry
at the same origin if one matched. So the overlay writes into the space the
main part just filled, and anything it sets replaces what is already there.
Leave the positions you want kept as air in the overlay part.
Confirmed in game on 7.4.12: a base part of 1504 blocks with an 8-block overlay generated all 1504 and all 8. game test
Part references¶
Each entry in parts is a part name plus any of 13 optional test keys. They are the same set a Condition entry uses, so anything valid there is valid here. code review
| Key | Type | Matches when code review |
|---|---|---|
part |
string | Required. Not a test. It names the Building Part this entry places when every test below passes. An entry without it fails to load. |
floor |
int | The level index equals this number. 0 is ground, negatives are cellars. |
range |
string | The level index falls between two comma-separated integers, including both ends. |
top |
bool | If true, this is the building's topmost level. If false, it is any other level. |
ground |
bool | If true, the level index is 0. If false, it is any other level. |
cellar |
bool | If true, the level index is below 0. If false, it is 0 or above. |
isbuilding |
bool | If true, a building stands in this chunk. If false, none does. |
issphere |
bool | If true, this chunk is inside a city sphere. If false, it is outside one. |
chunkx / chunkz |
int | The absolute chunk coordinate equals this number. |
inpart |
string or list | The current part name is in this set. |
belowpart |
string or list | The part directly below is in this set. |
inbuilding |
string or list | The current building name is in this set. |
inbiome |
string | The current biome is in this set. Avoid it here on Minecraft 1.21 and later, see below. |
inbiome on a part reference fails every chunk on 1.21 and later
Reading a biome here means reading it out of a neighbouring chunk while that chunk is still generating, which Minecraft 1.21 refuses. Measured on 8.2.2: one part reference carrying inbiome failed 335 chunks with Exception generating new chunk. The same pack runs clean on 7.4.12 and 7.5.1. game test
The same key on a Condition is safe on every version, because a condition is evaluated later. Put the biome test there instead. game test
The accepted shape also moved. 7.5.1 takes a list or a string, 8.2.2 takes only a string, and 7.4.12 accepted an object and quietly did nothing with it. A bare string is the only form every version accepts. game test
{ "part": "apartment_floor", "floor": 2 }
{ "part": "apartment_mid", "range": "9,12" }
When several test keys are set on one entry, all of them must pass. The mod chains them with AND, never with OR. An entry with no test keys matches every level, which is what makes the catch-all pattern work. game test
Among all matching entries the mod picks one at random with equal probability. There is no factor key here, unlike a Condition entry. code review
range, for a run of identical floors¶
range is the compact way to say "floors 9 through 12 all use this part" instead of writing four separate floor entries. game test
{
"filler": "~",
"parts": [
{ "part": "building001_floor4", "range": "9,12" },
{ "part": "building001_floor5", "range": "9,12" },
{ "part": "building001_top", "top": true }
]
}
| game test | |
|---|---|
| Format | A string holding two integers separated by a comma. Write "9,12", not [9,12] and not 9,12. |
| Bounds | Inclusive at both ends. "9,12" matches 9, 10, 11 and 12. |
| Negatives | Work normally. range tests the same index floor does, so "-2,-1" matches the two deepest cellars. |
A third number is accepted and silently ignored
The mod splits the string on commas and reads only the first two pieces. "1,2,3" does not throw. It produces the range 1 to 2 and discards the 3, with no error and no log line.
These forms do throw Bad range specification: <l1>,<l2>!: code review
| You write | Why it throws code review |
|---|---|
"9" |
There is no second number. |
"9, 12" |
The space makes " 12" a non-number. |
"abc,def" |
Neither piece is a number. |
range does not require minfloors or maxfloors
range filters the level index and does nothing else. It shares the coverage rule with floor: every level that can generate still needs something to match it.
Declaring minfloors and maxfloors is one way to keep the generated range inside what your parts cover, and it is a reasonable habit. Remember that those bounds only clamp the profile unless you also set overrideFloors: true. Under a profile whose buildingMaxFloors is 8, maxfloors: 13 gives you 8, not 13. A top: true entry is what safely caps the stack however tall it ends up. game test
Example: two candidates for the same floor¶
{
"filler": "#",
"parts": [
{ "part": "apartment_floor_a", "floor": 2 },
{ "part": "apartment_floor_b", "floor": 2 }
]
}
Floor 2 becomes apartment_floor_a or apartment_floor_b, with equal probability. code review
parts2¶
parts2 is a second, independent pass over the same levels. The mod checks it separately for each level. If something matches, it generates that part on top of the one from parts, at the same height. game test
Use it for decoration or variation layered over a structural base. Put one plain floor shell in parts, and an optional furniture, damage or signage overlay in parts2. That avoids authoring every combination as its own part.
preferslonely¶
preferslonely is a probability from 0 to 1, default 0, which disables it. It does not affect the building it is set on. code review
When the mod decides whether a chunk gets a building, it looks at the building type of the four orthogonally adjacent chunks, west, east, north and south. It rolls once against each neighbour's preferslonely. If any roll succeeds, this chunk gets no building. code review
So preferslonely: 0.8 on a cathedral means chunks next to a cathedral are usually left empty, which gives the cathedral open space. This applies only to normal single-chunk buildings. A multi-building ignores it. code review
1.0 does not empty every neighbour. In a city whose only building type carries 1.0, about a quarter of the buildings a 0.0 control builds still generate: 4560 blocks against 18028 over the same sixteen chunks. The effect is large and it is not total, and the reason for the surviving quarter has not been traced. Treat the value as a strong preference rather than a guarantee. game test
See also¶
- Building Part Reference for what a part contains
- Condition Reference for the full test key table
- City Style Reference for the settings that override yours
- Profile Reference for where floor counts come from