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:
@@ -1853,6 +1853,52 @@
|
||||
<li>m3: random bits</li>
|
||||
<li>m5: index into the array of objects, bits 16 to 23 (lower bits in m2)</li>
|
||||
<li>m7: animation counter</li>
|
||||
<li style="color: blue">m4 bits 7..5: update counter (for objects using land ground sprites), incremented on every periodic processing.<BR>
|
||||
For snow and desert, these bits are not used, tile is updated on every periodic processing.</li>
|
||||
<li style="color: blue">m4 bits 3..2: ground type (for objects using land ground sprites):
|
||||
<table style="color: blue">
|
||||
<tr>
|
||||
<td nowrap valign=top><tt>0</tt> </td>
|
||||
<td align=left>bare land / grass</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td nowrap valign=top><tt>1</tt> </td>
|
||||
<td align=left>snow or desert</td>
|
||||
</tr>
|
||||
</table>
|
||||
</li>
|
||||
<li style="color: blue">m4 bits 1..0: density (for objects using land ground sprites):
|
||||
<table style="color: blue">
|
||||
<tr>
|
||||
<td nowrap valign=top><tt>0</tt> </td>
|
||||
<td>bare land</td>
|
||||
<td>1/4 snow</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td nowrap valign=top><tt>1</tt> </td>
|
||||
<td>1/3 grass</td>
|
||||
<td>2/4 snow; </td>
|
||||
<td>1/2 desert</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td nowrap valign=top><tt>2</tt> </td>
|
||||
<td>2/3 grass</td>
|
||||
<td>3/4 snow</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td nowrap valign=top><tt>3</tt> </td>
|
||||
<td>full grass; </td>
|
||||
<td>full snow; </td>
|
||||
<td>full desert</td>
|
||||
</tr>
|
||||
</table>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
|
@@ -324,7 +324,7 @@ the array so you can quickly see what is used and what is not.
|
||||
<td class="bits"><span class="free">O</span><span class="used" title="Water class">XX</span> <span class="used" title="Owner">XXXXX</span></td>
|
||||
<td class="bits"><span class="pool" title="Object index on pool (m2 + m5)">XXXX XXXX XXXX XXXX</span></td>
|
||||
<td class="bits"><span class="used" title="Random bits">XXXX XXXX</span></td>
|
||||
<td class="bits"><span class="free">OOOO OOOO</span></td>
|
||||
<td class="bits"><span class="patch" title="Ground update counter">PPP</span><span class="free">O</span> <span class="patch" title="Ground type: grass/bare, snow/desert">PP</span> <span class="patch" title="Ground density">PP</span></td>
|
||||
<td class="bits"><span class="pool" title="Object index on pool (m2 + m5)">XXXX XXXX</span></td>
|
||||
<td class="bits"><span class="free">OOOO OOOO</span></td>
|
||||
<td class="bits"><span class="used" title="Animation counter">XXXX XXXX</span></td>
|
||||
|
@@ -4296,6 +4296,11 @@ static ChangeInfoResult ObjectChangeInfo(uint id, int numinfo, int prop, const G
|
||||
spec->generate_amount = buf->ReadByte();
|
||||
break;
|
||||
|
||||
case A0RPI_OBJECT_USE_LAND_GROUND:
|
||||
if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
|
||||
SB(spec->ctrl_flags, OBJECT_CTRL_FLAG_USE_LAND_GROUND, 1, (buf->ReadByte() != 0 ? 1 : 0));
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = HandleAction0PropertyDefault(buf, prop);
|
||||
break;
|
||||
|
@@ -42,6 +42,7 @@ extern const GRFFeatureInfo _grf_feature_list[] = {
|
||||
GRFFeatureInfo("action0_signals_recolour", 1),
|
||||
GRFFeatureInfo("action0_signals_extra_aspects", 1),
|
||||
GRFFeatureInfo("action3_signals_custom_signal_sprites", 1),
|
||||
GRFFeatureInfo("action0_object_use_land_ground", 1),
|
||||
GRFFeatureInfo(),
|
||||
};
|
||||
|
||||
@@ -66,6 +67,7 @@ extern const GRFPropertyMapDefinition _grf_action0_remappable_properties[] = {
|
||||
GRFPropertyMapDefinition(GSF_SIGNALS, A0RPI_SIGNALS_ENABLE_RESTRICTED_SIGNALS, "signals_enable_restricted_signals"),
|
||||
GRFPropertyMapDefinition(GSF_SIGNALS, A0RPI_SIGNALS_ENABLE_SIGNAL_RECOLOUR, "signals_enable_signal_recolour"),
|
||||
GRFPropertyMapDefinition(GSF_SIGNALS, A0RPI_SIGNALS_EXTRA_ASPECTS, "signals_extra_aspects"),
|
||||
GRFPropertyMapDefinition(GSF_OBJECTS, A0RPI_OBJECT_USE_LAND_GROUND, "object_use_land_ground"),
|
||||
GRFPropertyMapDefinition(),
|
||||
};
|
||||
|
||||
|
@@ -32,6 +32,7 @@ enum Action0RemapPropertyIds {
|
||||
A0RPI_SIGNALS_ENABLE_RESTRICTED_SIGNALS,
|
||||
A0RPI_SIGNALS_ENABLE_SIGNAL_RECOLOUR,
|
||||
A0RPI_SIGNALS_EXTRA_ASPECTS,
|
||||
A0RPI_OBJECT_USE_LAND_GROUND,
|
||||
};
|
||||
|
||||
|
||||
|
@@ -20,6 +20,7 @@
|
||||
#include "tile_cmd.h"
|
||||
#include "town.h"
|
||||
#include "water.h"
|
||||
#include "clear_func.h"
|
||||
#include "newgrf_animation_base.h"
|
||||
|
||||
#include "safeguards.h"
|
||||
@@ -425,7 +426,26 @@ static void DrawTileLayout(const TileInfo *ti, const TileLayoutSpriteGroup *grou
|
||||
SpriteID image = dts->ground.sprite;
|
||||
PaletteID pal = dts->ground.pal;
|
||||
|
||||
if (GB(image, 0, SPRITE_WIDTH) != 0) {
|
||||
if (spec->ctrl_flags & OBJECT_CTRL_FLAG_USE_LAND_GROUND) {
|
||||
if (IsTileOnWater(ti->tile)) {
|
||||
DrawWaterClassGround(ti);
|
||||
} else {
|
||||
switch (GetObjectGroundType(ti->tile)) {
|
||||
case OBJECT_GROUND_GRASS:
|
||||
DrawClearLandTile(ti, GetObjectGroundDensity(ti->tile));
|
||||
break;
|
||||
|
||||
case OBJECT_GROUND_SNOW_DESERT:
|
||||
DrawGroundSprite(GetSpriteIDForSnowDesert(ti->tileh, GetObjectGroundDensity(ti->tile)), PAL_NONE);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* This should never be reached, just draw a black sprite to make the problem clear without being unnecessarily punitive */
|
||||
DrawGroundSprite(SPR_FLAT_BARE_LAND + SlopeToSpriteOffset(ti->tileh), PALETTE_ALL_BLACK);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (GB(image, 0, SPRITE_WIDTH) != 0) {
|
||||
/* If the ground sprite is the default flat water sprite, draw also canal/river borders
|
||||
* Do not do this if the tile's WaterClass is 'land'. */
|
||||
if ((image == SPR_FLAT_WATER_TILE || spec->flags & OBJECT_FLAG_DRAW_WATER) && IsTileOnWater(ti->tile)) {
|
||||
|
@@ -40,6 +40,12 @@ enum ObjectFlags {
|
||||
};
|
||||
DECLARE_ENUM_AS_BIT_SET(ObjectFlags)
|
||||
|
||||
enum ObjectCtrlFlags {
|
||||
OBJECT_CTRL_FLAG_NONE = 0, ///< Just nothing.
|
||||
OBJECT_CTRL_FLAG_USE_LAND_GROUND = 1 << 0, ///< Use land for ground sprite.
|
||||
};
|
||||
DECLARE_ENUM_AS_BIT_SET(ObjectCtrlFlags)
|
||||
|
||||
void ResetObjects();
|
||||
|
||||
/** Class IDs for objects. */
|
||||
@@ -68,6 +74,7 @@ struct ObjectSpec {
|
||||
Date introduction_date; ///< From when can this object be built.
|
||||
Date end_of_life_date; ///< When can't this object be built anymore.
|
||||
ObjectFlags flags; ///< Flags/settings related to the object.
|
||||
ObjectCtrlFlags ctrl_flags; ///< Extra control flags.
|
||||
AnimationInfo animation; ///< Information about the animation.
|
||||
uint16 callback_mask; ///< Bitmask of requested/allowed callbacks.
|
||||
uint8 height; ///< The height of this structure, in heightlevels; max MAX_TILE_HEIGHT.
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -13,6 +13,11 @@
|
||||
#include "water_map.h"
|
||||
#include "object_type.h"
|
||||
|
||||
enum ObjectGround {
|
||||
OBJECT_GROUND_GRASS = 0, ///< Grass or bare
|
||||
OBJECT_GROUND_SNOW_DESERT = 1, ///< Snow or desert
|
||||
};
|
||||
|
||||
ObjectType GetObjectType(TileIndex t);
|
||||
|
||||
/**
|
||||
@@ -62,6 +67,93 @@ static inline byte GetObjectRandomBits(TileIndex t)
|
||||
return _m[t].m3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ground type of ths tile.
|
||||
* @param t The tile to get the ground type of.
|
||||
* @pre IsTileType(t, MP_OBJECT)
|
||||
* @return The ground type.
|
||||
*/
|
||||
static inline ObjectGround GetObjectGroundType(TileIndex t)
|
||||
{
|
||||
assert_tile(IsTileType(t, MP_OBJECT), t);
|
||||
return (ObjectGround)GB(_m[t].m4, 2, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ground density of this tile.
|
||||
* Only meaningful for some ground types.
|
||||
* @param t The tile to get the density of.
|
||||
* @pre IsTileType(t, MP_OBJECT)
|
||||
* @return the density
|
||||
*/
|
||||
static inline uint GetObjectGroundDensity(TileIndex t)
|
||||
{
|
||||
assert_tile(IsTileType(t, MP_OBJECT), t);
|
||||
return GB(_m[t].m4, 0, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ground density of this tile.
|
||||
* Only meaningful for some ground types.
|
||||
* @param t The tile to set the density of.
|
||||
* @param d the new density
|
||||
* @pre IsTileType(t, MP_OBJECT)
|
||||
*/
|
||||
static inline void SetObjectGroundDensity(TileIndex t, uint d)
|
||||
{
|
||||
assert_tile(IsTileType(t, MP_OBJECT), t);
|
||||
SB(_m[t].m4, 0, 2, d);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the counter used to advance to the next ground density type.
|
||||
* @param t The tile to get the counter of.
|
||||
* @pre IsTileType(t, MP_OBJECT)
|
||||
* @return The value of the counter
|
||||
*/
|
||||
static inline uint GetObjectGroundCounter(TileIndex t)
|
||||
{
|
||||
assert_tile(IsTileType(t, MP_OBJECT), t);
|
||||
return GB(_m[t].m4, 5, 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the counter used to advance to the next ground density type.
|
||||
* @param t the tile to increment the counter of
|
||||
* @param c the amount to increment the counter with
|
||||
* @pre IsTileType(t, MP_OBJECT)
|
||||
*/
|
||||
static inline void AddObjectGroundCounter(TileIndex t, int c)
|
||||
{
|
||||
assert_tile(IsTileType(t, MP_OBJECT), t);
|
||||
_m[t].m4 += c << 5;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the counter used to advance to the next ground density type.
|
||||
* @param t The tile to set the counter of.
|
||||
* @param c The amount to set the counter to.
|
||||
* @pre IsTileType(t, MP_OBJECT)
|
||||
*/
|
||||
static inline void SetObjectGroundCounter(TileIndex t, uint c)
|
||||
{
|
||||
assert_tile(IsTileType(t, MP_OBJECT), t);
|
||||
SB(_m[t].m4, 5, 3, c);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets ground type and density in one go, also sets the counter to 0
|
||||
* @param t the tile to set the ground type and density for
|
||||
* @param type the new ground type of the tile
|
||||
* @param density the density of the ground tile
|
||||
* @pre IsTileType(t, MP_OBJECT)
|
||||
*/
|
||||
static inline void SetObjectGroundTypeDensity(TileIndex t, ObjectGround type, uint density)
|
||||
{
|
||||
assert_tile(IsTileType(t, MP_OBJECT), t);
|
||||
_m[t].m4 = 0 << 5 | type << 2 | density;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an Object tile.
|
||||
|
@@ -868,11 +868,15 @@ class NIHObject : public NIHelper {
|
||||
seprintf(buffer, lastof(buffer), " [%c] flags: 0x%X", output.flags & 1 ? '-' : '+', spec->flags);
|
||||
output.print(buffer);
|
||||
if (output.flags & 1) {
|
||||
auto print = [&](const char *name) {
|
||||
seprintf(buffer, lastof(buffer), " %s", name);
|
||||
output.print(buffer);
|
||||
};
|
||||
auto check_flag = [&](ObjectFlags flag, const char *name) {
|
||||
if (spec->flags & flag) {
|
||||
seprintf(buffer, lastof(buffer), " %s", name);
|
||||
output.print(buffer);
|
||||
}
|
||||
if (spec->flags & flag) print(name);
|
||||
};
|
||||
auto check_ctrl_flag = [&](ObjectCtrlFlags flag, const char *name) {
|
||||
if (spec->ctrl_flags & flag) print(name);
|
||||
};
|
||||
check_flag(OBJECT_FLAG_ONLY_IN_SCENEDIT, "OBJECT_FLAG_ONLY_IN_SCENEDIT");
|
||||
check_flag(OBJECT_FLAG_CANNOT_REMOVE, "OBJECT_FLAG_CANNOT_REMOVE");
|
||||
@@ -888,6 +892,7 @@ class NIHObject : public NIHelper {
|
||||
check_flag(OBJECT_FLAG_ALLOW_UNDER_BRIDGE, "OBJECT_FLAG_ALLOW_UNDER_BRIDGE");
|
||||
check_flag(OBJECT_FLAG_ANIM_RANDOM_BITS, "OBJECT_FLAG_ANIM_RANDOM_BITS");
|
||||
check_flag(OBJECT_FLAG_SCALE_BY_WATER, "OBJECT_FLAG_SCALE_BY_WATER");
|
||||
check_ctrl_flag(OBJECT_CTRL_FLAG_USE_LAND_GROUND, "OBJECT_CTRL_FLAG_USE_LAND_GROUND");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -121,7 +121,7 @@ static const DrawTileSprites _object_hq[] = {
|
||||
|
||||
#undef TILE_SPRITE_LINE
|
||||
|
||||
#define M(name, size, build_cost_multiplier, clear_cost_multiplier, height, climate, gen_amount, flags) { GRFFilePropsBase<2>(), INVALID_OBJECT_CLASS, name, climate, size, build_cost_multiplier, clear_cost_multiplier, 0, MAX_DAY + 1, flags, {0, 0, 0, 0}, 0, height, 1, gen_amount, true }
|
||||
#define M(name, size, build_cost_multiplier, clear_cost_multiplier, height, climate, gen_amount, flags) { GRFFilePropsBase<2>(), INVALID_OBJECT_CLASS, name, climate, size, build_cost_multiplier, clear_cost_multiplier, 0, MAX_DAY + 1, flags, OBJECT_CTRL_FLAG_NONE, {0, 0, 0, 0}, 0, height, 1, gen_amount, true }
|
||||
|
||||
/* Climates
|
||||
* T = Temperate
|
||||
|
Reference in New Issue
Block a user