Add NewGRF object property/flag to use land as object ground sprite

This handles variable ground densities, snow/desert, etc.
This commit is contained in:
Jonathan G Rennison
2021-12-04 21:45:33 +00:00
parent 906fde15c7
commit 924ffb013f
11 changed files with 289 additions and 8 deletions

View File

@@ -126,6 +126,9 @@ void BuildObject(ObjectType type, TileIndex tile, CompanyID owner, Town *town, u
bool remove = IsDockingTile(t);
MakeObject(t, owner, o->index, wc, Random());
if (remove) RemoveDockingTile(t);
if ((spec->ctrl_flags & OBJECT_CTRL_FLAG_USE_LAND_GROUND) && wc == WATER_CLASS_INVALID) {
SetObjectGroundTypeDensity(t, OBJECT_GROUND_GRASS, 0);
}
MarkTileDirtyByTile(t, VMDF_NOT_MAP_MODE);
}
@@ -740,6 +743,84 @@ static void GetTileDesc_Object(TileIndex tile, TileDesc *td)
}
}
/** Convert to or from snowy tiles. */
static void TileLoopObjectGroundAlps(TileIndex tile)
{
int k;
if ((int)TileHeight(tile) < GetSnowLine() - 1) {
/* Fast path to avoid needing to check all 4 corners */
k = -1;
} else {
k = GetTileZ(tile) - GetSnowLine() + 1;
}
if (k < 0) {
/* Below the snow line, do nothing if no snow. */
if (GetObjectGroundType(tile) != OBJECT_GROUND_SNOW_DESERT) return;
} else {
/* At or above the snow line, make snow tile if needed. */
if (GetObjectGroundType(tile) != OBJECT_GROUND_SNOW_DESERT) {
SetObjectGroundTypeDensity(tile, OBJECT_GROUND_SNOW_DESERT, 0);
MarkTileDirtyByTile(tile);
return;
}
}
/* Update snow density. */
uint current_density = GetObjectGroundDensity(tile);
uint req_density = (k < 0) ? 0u : std::min<uint>(k, 3u);
if (current_density < req_density) {
SetObjectGroundDensity(tile, current_density + 1);
} else if (current_density > req_density) {
SetObjectGroundDensity(tile, current_density - 1);
} else {
/* Density at the required level. */
if (k >= 0) return;
SetObjectGroundTypeDensity(tile, OBJECT_GROUND_GRASS, 3);
}
MarkTileDirtyByTile(tile);
}
/**
* Tests if at least one surrounding tile is non-desert
* @param tile tile to check
* @return does this tile have at least one non-desert tile around?
*/
static inline bool NeighbourIsNormal(TileIndex tile)
{
for (DiagDirection dir = DIAGDIR_BEGIN; dir < DIAGDIR_END; dir++) {
TileIndex t = tile + TileOffsByDiagDir(dir);
if (!IsValidTile(t)) continue;
if (GetTropicZone(t) != TROPICZONE_DESERT) return true;
if (HasTileWaterClass(t) && GetWaterClass(t) == WATER_CLASS_SEA) return true;
}
return false;
}
static void TileLoopObjectGroundDesert(TileIndex tile)
{
/* Current desert level - 0 if it is not desert */
uint current = 0;
if (GetObjectGroundType(tile) == OBJECT_GROUND_SNOW_DESERT) current = GetObjectGroundDensity(tile);
/* Expected desert level - 0 if it shouldn't be desert */
uint expected = 0;
if (GetTropicZone(tile) == TROPICZONE_DESERT) {
expected = NeighbourIsNormal(tile) ? 1 : 3;
}
if (current == expected) return;
if (expected == 0) {
SetObjectGroundTypeDensity(tile, OBJECT_GROUND_GRASS, 3);
} else {
/* Transition from clear to desert is not smooth (after clearing desert tile) */
SetObjectGroundTypeDensity(tile, OBJECT_GROUND_SNOW_DESERT, expected);
}
MarkTileDirtyByTile(tile);
}
static void TileLoop_Object(TileIndex tile)
{
const ObjectSpec *spec = ObjectSpec::GetByTile(tile);
@@ -749,7 +830,29 @@ static void TileLoop_Object(TileIndex tile)
if (o->location.tile == tile) TriggerObjectAnimation(o, OAT_256_TICKS, spec);
}
if (IsTileOnWater(tile)) TileLoop_Water(tile);
if (IsTileOnWater(tile)) {
TileLoop_Water(tile);
} else if (spec->ctrl_flags & OBJECT_CTRL_FLAG_USE_LAND_GROUND) {
switch (_settings_game.game_creation.landscape) {
case LT_TROPIC: TileLoopObjectGroundDesert(tile); break;
case LT_ARCTIC: TileLoopObjectGroundAlps(tile); break;
}
if (GetObjectGroundType(tile) == OBJECT_GROUND_GRASS && GetObjectGroundDensity(tile) != 3) {
if (_game_mode != GM_EDITOR) {
if (GetObjectGroundCounter(tile) < 7) {
AddObjectGroundCounter(tile, 1);
} else {
SetObjectGroundCounter(tile, 0);
SetObjectGroundDensity(tile, GetObjectGroundDensity(tile) + 1);
MarkTileDirtyByTile(tile, VMDF_NOT_MAP_MODE);
}
} else {
SetObjectGroundTypeDensity(tile, OBJECT_GROUND_GRASS, 3);
MarkTileDirtyByTile(tile, VMDF_NOT_MAP_MODE);
}
}
}
if (!IsObjectType(tile, OBJECT_HQ)) return;