Add GRF feature: New Landscape

Custom graphics using actions 1/2/3.

Currently only for rock tiles
pull/397/head^2
Jonathan G Rennison 2 years ago
parent 80281daa23
commit 7bb0de247d

@ -29,6 +29,7 @@
<h3>Features with separate pages</h3>
<ul>
<li><a href="newgrf-roadstops-nml.html">Road stops (FEAT_ROADSTOPS)</a></li>
<li><a href="newgrf-newlandscape-nml.html">New landscape (FEAT_NEWLANDSCAPE)</a></li>
</ul>
<h3><a href="https://newgrf-specs.tt-wiki.net/wiki/NML:Builtin_functions">Builtin functions</a></h3>

@ -29,6 +29,7 @@
<h3 id="separate_features">Features with separate pages</h3>
<ul>
<li><a href="newgrf-roadstops.html">Road stops</a></li>
<li><a href="newgrf-newlandscape.html">New landscape (custom graphics)</a></li>
</ul>
<br />
@ -740,6 +741,7 @@
<table>
<tr><th>Feature name</th><th>Description</th></tr>
<tr><td><font face="monospace">road_stops</font></td><td><a href="newgrf-roadstops.html">Custom road stops (bus stops, lorry stops and road waypoints)</a></d></tr>
<tr><td><font face="monospace">new_landscape</font></td><td><a href="newgrf-newlandscape.html">Custom landscape graphics (currently rocky tiles only)</a></d></tr>
</table>
</body>
</html>

@ -0,0 +1,126 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>JGR's Patchpack - NewGRF New Landscape Addition to NewGRF Specifications in NML</title>
<style type="text/css">
td li { white-space: nowrap; text-align: left; }
th { white-space: nowrap; text-align: center; }
td, th { border: 1px solid #CCCCCC; padding: 0px 5px; }
table { border-collapse: collapse; empty-cells: show; }
.code { white-space: pre; font-family: "Courier New", Courier, mono; }
.indent { margin-left: 2em; }
</style>
</head>
<body>
<h2>NewGRF New Landscape Addition to NewGRF Specifications in JGR's Patchpack in NML</h2>
<p>This document describes the non-standard addition of the NewGRF new landscape feature to the <a href="https://newgrf-specs.tt-wiki.net/wiki/NML:Main">Official OpenTTD NML Specifications</a>, as implemented in this patchpack, and the associated <a href="https://github.com/JGRennison/nml">NML fork</a></p>
<p>This feature allows NewGRF custom graphics using switches and spritesets for landscape tiles. Currently this includes: rock tiles.</p>
<p>This feature may not necessarily match implementations of additional landscape graphics features in other patches, branches, etc.<br />
This feature as implemented here MAY also be present in other patchpacks.</p>
<p>The feature identifier is <span class="code">FEAT_NEWLANDSCAPE</span>.<br />
There is no permanent storage associated with this feature.</p>
<p>See the <a href="newgrf-additions-nml.html">NewGRF additions (NML)</a> document for background information on additions to NML.</p>
<p>See the associated <a href="newgrf-newlandscape.html">non-NML document</a> for more details on the NewGRF new landscape feature.</p>
<p>This feature will be automatically skipped when loaded into a version of OpenTTD which does not support this feature.<br />
If this feature is the only significant thing in this GRF, then <span class="code">extended_feature_test("new_landscape")</span> SHOULD be called and some message, error or other form of
signalling to the user used to inform the user that this version of OpenTTD does not support the feature, if the return value is false.<br />
Otherwise the GRF could silently do nothing instead of the expected functionality, creating confusion for end users.</p>
<p><b>Sections:</b>
<ul>
<li><a href="#newlandscape_ids">IDs</a></li>
<li><a href="#newlandscape_properties">Properties</a></li>
<li><a href="#newlandscape_variables">Variables</a></li>
<li><a href="#newlandscape_example">Syntax example</a></li>
</ul></p>
<h3 id="newlandscape_ids">New Landscape IDs</h3>
<p>
The ID field for an item must be set to one of the IDs in the table below (further IDs may be allocated for other purposes in future).
<table>
<tr><th>ID</th><th>Landscape type</th></tr>
<tr><td>NEW_LANDSCAPE_ID_ROCKS</td><td>Rocky tiles</td></tr>
</table>
</p>
</p>
<h3 id="newlandscape_properties">New Landscape Properties</h3>
<table>
<tr><th>Property</th><th>Value range</th><th>Comment</th></tr>
<tr><td>enable_recolour</td><td>0 or 1</td>
<td>
Enable recolouring of graphics.<br />
When enabled, in addition to returning a sprite, register 0x100 may be set to the following using STORE_TEMP:
<table>
<tr><th>Bits</th><th>Meaning</th></tr>
<tr><td>0 - 23</td><td>Recolour sprite to use. Set to 0 for no recolouring.</td></tr>
<tr><td>24 - 31</td><td>Reserved, set to zero.</td></tr>
</table>
</td>
</tr>
</table>
<h3 id="newlandscape_variables">New Landscape Variables</h3>
<p>A number of variables are shared between new landscape and stations. These are listed on the <a href="https://newgrf-specs.tt-wiki.net/wiki/NML:Stations#Base_station_variables">stations</a> page.</p>
<table>
<tr><th>Name</th><th>Value range</th><th>Comment</th></tr>
<tr><td>terrain_type</td><td>TILETYPE_XXX</td><td>XXX = NORMAL | DESERT | RAIN_FOREST | SNOW</td></tr>
<tr><td>tile_slope</td><td>SLOPE_XXX</td><td>See <a href="https://newgrf-specs.tt-wiki.net/wiki/NML:List_of_tile_slopes">tile slopes</a> for an overview of possible values</td></tr>
<tr><td>tile_height</td><td>0..255</td><td>Height of the lowest corner of the tile</td></tr>
<tr><td>tile_hash</td><td>0..4294967295</td><td>Hash value derived from the coordinates of the tile, suitable for pseudo-randomising graphics</td></tr>
<tr><td>landscape_type</td><td>NEW_LANDSCAPE_TYPE_XXX</td><td>Landscape type<br />XXX = ROCKS</td></tr>
</table>
<br />
Variables that require one or more parameters:
<table>
<tr><th>Name</th><th>Arguments</th><th>Value range</th><th>Comment</th></tr>
<tr><td>nearby_tile_slope</td><td>x, y offset (-8..7)</td><td>SLOPE_XXX</td><td>Slope of a nearby tile</td></tr>
<tr><td>nearby_tile_is_same_type</td><td>x, y offset (-8..7)</td><td>0 | 1</td><td>Is nearby tile the same landscape type as this one?</td></tr>
<tr><td>nearby_tile_is_water</td><td>x, y offset (-8..7)</td><td>0 | 1</td><td>Is nearby tile a water tile?</td></tr>
<tr><td>nearby_tile_terrain_type</td><td>x, y offset (-8..7)</td><td></td><td>See terrain_type</td></tr>
<tr><td>nearby_tile_water_class</td><td>x, y offset (-8..7)</td><td>WATER_CLASS_XXX</td><td>XXX = [NONE | SEA | CANAL | RIVER]<br />Note that tiles for which nearby_tile_is_water is 0 may still have a water class, e.g. industry tiles with water beneath them. </td></tr>
<tr><td>nearby_tile_height</td><td>x, y offset (-8..7)</td><td></td><td>The minimum height of the given tile in height level units</td></tr>
<tr><td>nearby_tile_class</td><td>x, y offset (-8..7)</td><td><a href="https://newgrf-specs.tt-wiki.net/wiki/NML:List_of_tile_classes">tile class</a></td><td></td></tr>
<tr><td>nearby_tile_info</td><td>x, y offset (-8..7)</td><td></td><td>Above nearby tile variables in one variable (all of variable 0x60)</td></tr>
</table>
<h3 id="newlandscape_example">Syntax example</h3>
<p>
<pre class="code">
grf {
...
}
if (!extended_feature_test("new_landscape")) {
error(FATAL, string(STR_UNSUPPORTED_VERSION));
}
spriteset spriteset_rocks {
/* 19 tile sprites in the standard order */
}
switch (FEAT_NEWLANDSCAPE, SELF, switch_rocks, ...) {
...
}
item (FEAT_NEWLANDSCAPE, item_rocks, NEW_LANDSCAPE_ID_ROCKS) {
property {
enable_recolour: 0;
}
graphics {
default: switch_rocks;
}
}
</pre>
</p>
</body>
</html>

@ -0,0 +1,124 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>JGR's Patchpack - NewGRF New Landscape Addition to NewGRF Specifications</title>
<style type="text/css">
td li { white-space: nowrap; text-align: left; }
th { white-space: nowrap; text-align: center; }
td, th { border: 1px solid #CCCCCC; padding: 0px 5px; }
table { border-collapse: collapse; empty-cells: show; }
</style>
</head>
<body>
<h2>NewGRF New Landscape Addition to NewGRF Specifications in JGR's Patchpack</h2>
<p>This document describes the non-standard addition of the NewGRF new landscape feature to the <a href="https://newgrf-specs.tt-wiki.net/wiki/Main_Page">Official OpenTTD NewGRF Specifications</a>, as implemented in this patchpack.</p>
<p>This feature allows NewGRF custom graphics using Action 1/2/3 for landscape tiles. Currently this includes: rock tiles.</p>
<p>This feature may not necessarily match implementations of additional landscape graphics features in other patches, branches, etc.<br />
This feature as implemented here MAY also be present in other patchpacks.</p>
<p>See the <a href="newgrf-additions.html">NewGRF additions</a> document for background information on additions to the NewGRF specifications.</p>
<p>The functionality listed below is also supported in a fork of NML, see the associated <a href="newgrf-newlandscape-nml.html">NML new landscape</a> and <a href="newgrf-additions-nml.html">NML additions</a> documents for more details.</p>
<p>NewGRFs which use this feature SHOULD use the <a href="newgrf-additions.html#feature-test">feature testing</a> mechanism to check whether the new landscape feature and/or feature ID mapping is supported.</p>
<p>NewGRFs which use this feature MUST use the <a href="newgrf-additions.html#feature-id-mapping">feature ID mapping</a> mechanism to map the non-standard NewGRF road stop feature to a local feature ID.</p>
<p>This feature is indicated by the feature name: <font face="monospace">new_landscape</font>, version 1.<br />
The feature name to use for feature ID mapping is <font face="monospace">new_landscape</font>.<br />
Features/properties/variables which require a higher version will indicate the required version. Unless indicated otherwise these will fall back to doing nothing on versions which do not support them.</p>
<p><h3 id="ids">IDs:</h3>
The ID field for Actions 0 and 3 must be set to one of the IDs in the table below (further IDs may be allocated for other purposes in future).
<table>
<tr><th>ID</th><th>Landscape type</th></tr>
<tr><td>0</td><td>Rocky tiles</td></tr>
</table>
</p>
<p><h3 id="actions">Actions:</h3>
<ul>
<li><a href="#a0roadstops">Action 0</a></li>
<li><a href="#a2roadstops">Action 2</a></li>
<li><a href="#varaction2roadstops">Variational Action 2</a></li>
<li><a href="#a3roadstops">Action 3</a></li>
</ul></p>
<h3 id="a0roadstops">Action 0 - New Landscape</h3>
<p>See the <a href="https://newgrf-specs.tt-wiki.net/wiki/Action0">Action 0 Specification</a> for background information.</p>
<b>Properties:</b>
<table>
<tr><th>Mappable name</th><th>Size in bytes</th><th>Description</th></tr>
<tr><td><a href="#newlandscape_enable_recolour">newlandscape_enable_recolour</a></td><td>1</td><td>Enable recolour</td></tr>
</table>
<h4 id="newlandscape_enable_recolour">Enable recolouring for new landscape graphics (mappable property: newlandscape_enable_recolour)</h4>
<p>When enabled, in addition to returning a sprite, register 0x100 may be set to the following:
<table>
<tr><th>Bits</th><th>Meaning</th></tr>
<tr><td>0 - 23</td><td>Recolour sprite to use. Set to 0 for no recolouring.</td></tr>
<tr><td>24 - 31</td><td>Reserved, set to zero. </td></tr>
</table></p>
<p>The property length is 1 byte. 0 is disabled (default). 1 is enabled.</p>
<h3 id="a2roadstops">Action 2 - New Landscape</h3>
<p>See the <a href="https://newgrf-specs.tt-wiki.net/wiki/Action2">Action 2 Specification</a> for background information.</p>
<p>New landscape <b>does not</b> use the special sprite layout format.</p>
<br />
<h3 id="varaction2roadstops">Variational Action 2 - New Landscape</h3>
<p>See the <a href="https://newgrf-specs.tt-wiki.net/wiki/VariationalAction2">Variational Action 2 Specification</a> for background information.</p>
<b>Variables:</b>
<table>
<tr><th>Number</th><th>Mappable name</th><th>Description</th></tr>
<tr><td><a href="#newlandscape_terrain_type">40</a></td><td><a href="#newlandscape_terrain_type">newlandscape_terrain_type</a></td><td>Terrain type</td></tr>
<tr><td><a href="#newlandscape_tile_slope">41</a></td><td><a href="#newlandscape_tile_slope">newlandscape_tile_slope</a></td><td>Tile Slope</td></tr>
<tr><td><a href="#newlandscape_tile_height">42</a></td><td><a href="#newlandscape_tile_height">newlandscape_tile_height</a></td><td>Tile Height</td></tr>
<tr><td><a href="#newlandscape_tile_hash">43</a></td><td><a href="#newlandscape_tile_hash">newlandscape_tile_hash</a></td><td>Tile Hash</td></tr>
<tr><td><a href="#newlandscape_landscape_type">44</a></td><td><a href="#newlandscape_landscape_type">newlandscape_landscape_type</a></td><td>Landscape Type</td></tr>
<tr><td><a href="#newlandscape_land_info_nearby_tiles">60</a></td><td><a href="#newlandscape_land_info_nearby_tiles">newlandscape_land_info_nearby_tiles</a></td><td>Land info of nearby tiles</td></tr>
</table>
<h4 id="newlandscape_terrain_type">Tile terrain type (40, or mappable variable: newlandscape_terrain_type)</h4>
<p>This has the same value as bits 0 - 7 of <a href="https://newgrf-specs.tt-wiki.net/wiki/VariationalAction2/Objects#Tile_information_.2841.29">object (feature F) variable 41</a>.
<table>
<tr><th>Value</th><th>Meaning</th></tr>
<tr><td>0</td><td>Normal</td></tr>
<tr><td>1</td><td>Desert</td></tr>
<tr><td>2</td><td>Rainforest</td></tr>
<tr><td>4</td><td>On or above snowline</td></tr>
</table>
</p>
<h4 id="newlandscape_tile_slope">Tile slope (41, or mappable variable: newlandscape_tile_slope)</h4>
<p>This has the same value as bits 0 - 7 of <a href="https://newgrf-specs.tt-wiki.net/wiki/VariationalAction2/Industry_Tiles#Land_info_of_nearby_tiles_.2860.29">industry tile (feature 9) variable 60</a> for this tile.</p>
<h4 id="newlandscape_tile_height">Tile height (42, or mappable variable: newlandscape_tile_height)</h4>
<p>The height of the lowest corner of the tile (in units of 1).</p>
<h4 id="newlandscape_tile_hash">Tile hash (43, or mappable variable: newlandscape_tile_hash)</h4>
<p>Hash value derived from the coordinates of the tile, suitable for pseudo-randomising graphics.</p>
<h4 id="roadstop_land_info_nearby_tiles">Land info of nearby tile (60, or mappable variable: newlandscape_land_info_nearby_tiles)</h4>
<p>This has the same value as <a href="https://newgrf-specs.tt-wiki.net/wiki/VariationalAction2/Industry_Tiles#Land_info_of_nearby_tiles_.2860.29">industry tile (feature 9) variable 60</a>.</p>
<br />
<h3 id="a3roadstops">Action 3 - New Landscape</h3>
<p>See the <a href="https://newgrf-specs.tt-wiki.net/wiki/Action3">Action 3 Specification</a> for background information.</p>
<p>Note that this is not a generic callback, the sprite group must be assigned to an ID in the <a href="#ids">IDs table</a>.
<table>
<tr><th>Action 3 ID</th><th>Expected number of sprites</th><th>Landscape type</th></tr>
<tr><td>0</td><td>19</td><td>Rocky tiles</td></tr>
</table>
Cargo type definitions are not used. Only the default set ID is used.
</p>
<p>This is indicated by the feature name: <font face="monospace">new_landscape</font>, version 1</p>
</body>
</html>

@ -272,6 +272,8 @@ add_files(
newgrf_industrytiles.cpp
newgrf_industrytiles.h
newgrf_industrytiles_analysis.h
newgrf_newlandscape.cpp
newgrf_newlandscape.h
newgrf_newsignals.cpp
newgrf_newsignals.h
newgrf_object.cpp

@ -16,6 +16,7 @@
#include "water.h"
#include "core/random_func.hpp"
#include "newgrf_generic.h"
#include "newgrf_newlandscape.h"
#include "table/strings.h"
#include "table/sprites.h"
@ -73,6 +74,29 @@ SpriteID GetSpriteIDForRocks(const Slope slope, const uint tile_hash)
return ((HasGrfMiscBit(GMB_SECOND_ROCKY_TILE_SET) && (tile_hash & 1)) ? SPR_FLAT_ROCKY_LAND_2 : SPR_FLAT_ROCKY_LAND_1) + SlopeToSpriteOffset(slope);
}
inline SpriteID GetSpriteIDForRocksUsingOffset(const uint slope_to_sprite_offset, const uint x, const uint y)
{
return ((HasGrfMiscBit(GMB_SECOND_ROCKY_TILE_SET) && (TileHash(x, y) & 1)) ? SPR_FLAT_ROCKY_LAND_2 : SPR_FLAT_ROCKY_LAND_1) + slope_to_sprite_offset;
}
void DrawCustomSpriteIDForRocks(const TileInfo *ti)
{
uint8 slope_to_sprite_offset = SlopeToSpriteOffset(ti->tileh);
for (const GRFFile *grf : _new_landscape_rocks_grfs) {
NewLandscapeResolverObject object(grf, ti, NEW_LANDSCAPE_ROCKS);
const SpriteGroup *group = object.Resolve();
if (group != nullptr && group->GetNumResults() > slope_to_sprite_offset) {
PaletteID pal = HasBit(grf->new_landscape_ctrl_flags, NLCF_ROCKS_RECOLOUR_ENABLED) ? GB(GetRegister(0x100), 0, 24) : PAL_NONE;
DrawGroundSprite(group->GetResult() + slope_to_sprite_offset, pal);
return;
}
}
DrawGroundSprite(GetSpriteIDForRocksUsingOffset(slope_to_sprite_offset, ti->x, ti->y), PAL_NONE);
}
SpriteID GetSpriteIDForFields(const Slope slope, const uint field_type)
{
return _clear_land_sprites_farmland[field_type] + SlopeToSpriteOffset(slope);
@ -135,7 +159,9 @@ static void DrawTile_Clear(TileInfo *ti, DrawTileProcParams params)
break;
case CLEAR_ROCKS:
if (!params.no_ground_tiles) DrawGroundSprite(GetSpriteIDForRocks(ti->tileh, TileHash(ti->x, ti->y)), PAL_NONE);
if (!params.no_ground_tiles) {
DrawCustomSpriteIDForRocks(ti);
}
break;
case CLEAR_FIELDS:

@ -35,6 +35,7 @@
#include "newgrf_airport.h"
#include "newgrf_object.h"
#include "newgrf_newsignals.h"
#include "newgrf_newlandscape.h"
#include "newgrf_extension.h"
#include "rev.h"
#include "fios.h"
@ -5168,6 +5169,38 @@ static ChangeInfoResult RoadStopChangeInfo(uint id, int numinfo, int prop, const
return ret;
}
/**
* Define properties for new landscape
* @param id Landscape type.
* @param numinfo Number of subsequent IDs to change the property for.
* @param prop The property to change.
* @param buf The property value.
* @return ChangeInfoResult.
*/
static ChangeInfoResult NewLandscapeChangeInfo(uint id, int numinfo, int prop, const GRFFilePropertyRemapEntry *mapping_entry, ByteReader *buf)
{
/* Properties which are handled per item */
ChangeInfoResult ret = CIR_SUCCESS;
for (int i = 0; i < numinfo; i++) {
switch (prop) {
case A0RPI_NEWLANDSCAPE_ENABLE_RECOLOUR: {
if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
bool enabled = (buf->ReadByte() != 0 ? 1 : 0);
if (id == NLA3ID_CUSTOM_ROCKS) {
SB(_cur.grffile->new_landscape_ctrl_flags, NLCF_ROCKS_RECOLOUR_ENABLED, 1, enabled);
}
break;
}
default:
ret = HandleAction0PropertyDefault(buf, prop);
break;
}
}
return ret;
}
static bool HandleChangeInfoResult(const char *caller, ChangeInfoResult cir, GrfSpecFeature feature, int property)
{
switch (cir) {
@ -5248,6 +5281,7 @@ static const char *_feature_names[] = {
"ROADTYPES",
"TRAMTYPES",
"ROADSTOPS",
"NEWLANDSCAPE",
};
static_assert(lengthof(_feature_names) == GSF_END);
@ -5353,6 +5387,7 @@ static void FeatureChangeInfo(ByteReader *buf)
/* GSF_ROADTYPES */ RoadTypeChangeInfo,
/* GSF_TRAMTYPES */ TramTypeChangeInfo,
/* GSF_ROADSTOPS */ RoadStopChangeInfo,
/* GSF_NEWLANDSCAPE */ NewLandscapeChangeInfo,
};
static_assert(GSF_END == lengthof(handler));
static_assert(lengthof(handler) == lengthof(_cur.grffile->action0_property_remaps), "Action 0 feature list length mismatch");
@ -8060,6 +8095,7 @@ static void NewSpriteGroup(ByteReader *buf)
case GSF_ROADTYPES:
case GSF_TRAMTYPES:
case GSF_SIGNALS:
case GSF_NEWLANDSCAPE:
{
byte num_loaded = type;
byte num_loading = buf->ReadByte();
@ -8861,6 +8897,39 @@ static void RoadStopMapSpriteGroup(ByteReader *buf, uint8 idcount)
}
}
static void NewLandscapeMapSpriteGroup(ByteReader *buf, uint8 idcount)
{
uint8 *ids = AllocaM(uint8, idcount);
for (uint i = 0; i < idcount; i++) {
ids[i] = buf->ReadByte();
}
/* Skip the cargo type section, we only care about the default group */
uint8 cidcount = buf->ReadByte();
buf->Skip(cidcount * 3);
uint16 groupid = buf->ReadWord();
if (!IsValidGroupID(groupid, "NewLandscapeMapSpriteGroup")) return;
for (uint i = 0; i < idcount; i++) {
uint8 id = ids[i];
switch (id) {
case NLA3ID_CUSTOM_ROCKS:
_cur.grffile->new_rocks_group = GetGroupByID(groupid);
if (!HasBit(_cur.grffile->new_landscape_ctrl_flags, NLCF_ROCKS_SET)) {
SetBit(_cur.grffile->new_landscape_ctrl_flags, NLCF_ROCKS_SET);
_new_landscape_rocks_grfs.push_back(_cur.grffile);
}
break;
default:
grfmsg(1, "NewLandscapeMapSpriteGroup: ID not implemented: %d", id);
break;
}
}
}
/* Action 0x03 */
static void FeatureMapSpriteGroup(ByteReader *buf)
{
@ -8969,6 +9038,10 @@ static void FeatureMapSpriteGroup(ByteReader *buf)
RoadStopMapSpriteGroup(buf, idcount);
return;
case GSF_NEWLANDSCAPE:
NewLandscapeMapSpriteGroup(buf, idcount);
return;
default:
grfmsg(1, "FeatureMapSpriteGroup: Unsupported feature %s, skipping", GetFeatureString(feature_ref));
return;
@ -12229,6 +12302,7 @@ static void ResetNewGRF()
_grf_files.clear();
_cur.grffile = nullptr;
_new_signals_grfs.clear();
_new_landscape_rocks_grfs.clear();
}
/** Clear all NewGRF errors */
@ -12415,6 +12489,9 @@ GRFFile::GRFFile(const GRFConfig *config)
this->new_signal_ctrl_flags = 0;
this->new_signal_extra_aspects = 0;
this->new_rocks_group = nullptr;
this->new_landscape_ctrl_flags = 0;
/* Mark price_base_multipliers as 'not set' */
for (Price i = PR_BEGIN; i < PR_END; i++) {
this->price_base_multipliers[i] = INVALID_PRICE_MODIFIER;

@ -89,6 +89,7 @@ enum GrfSpecFeature : uint8 {
GSF_TRAMTYPES,
GSF_ROADSTOPS,
GSF_NEWLANDSCAPE,
GSF_END,
GSF_REAL_FEATURE_END = GSF_ROADSTOPS,
@ -278,11 +279,22 @@ enum {
NEW_SIGNALS_MAX_EXTRA_ASPECT = 6,
};
/** New signal control flags. */
/** New signal action 3 IDs. */
enum NewSignalAction3ID {
NSA3ID_CUSTOM_SIGNALS = 0, ///< Action 3 ID for custom signal sprites
};
/** New landscape control flags. */
enum NewLandscapeCtrlFlags {
NLCF_ROCKS_SET = 0, ///< Custom landscape rocks sprites group set.
NLCF_ROCKS_RECOLOUR_ENABLED = 1, ///< Recolour sprites enabled for rocks
};
/** New landscape action 3 IDs. */
enum NewLandscapeAction3ID {
NLA3ID_CUSTOM_ROCKS = 0, ///< Action 3 ID for custom landscape sprites
};
/** GRFFile control flags. */
enum GRFFileCtrlFlags {
GFCF_HAVE_FEATURE_ID_REMAP = 0, ///< This GRF has one or more feature ID mappings
@ -346,6 +358,9 @@ struct GRFFile : ZeroedMemoryAllocator {
byte new_signal_ctrl_flags; ///< Ctrl flags for new signals
byte new_signal_extra_aspects; ///< Number of extra aspects for new signals
const SpriteGroup *new_rocks_group; ///< New landscape rocks group
byte new_landscape_ctrl_flags; ///< Ctrl flags for new landscape
byte ctrl_flags; ///< General GRF control flags
btree::btree_map<uint16, uint> string_map; ///< Map of local GRF string ID to string ID

@ -52,12 +52,14 @@ extern const GRFFeatureInfo _grf_feature_list[] = {
GRFFeatureInfo("action0_object_flood_resistant", 1),
GRFFeatureInfo("action0_object_viewport_map_tile_type", 1),
GRFFeatureInfo("road_stops", 2),
GRFFeatureInfo("new_landscape", 1),
GRFFeatureInfo(),
};
/** Action14 remappable feature list */
extern const GRFFeatureMapDefinition _grf_remappable_features[] = {
GRFFeatureMapDefinition(GSF_ROADSTOPS, "road_stops"),
GRFFeatureMapDefinition(GSF_NEWLANDSCAPE, "new_landscape"),
GRFFeatureMapDefinition(),
};
@ -105,6 +107,7 @@ extern const GRFPropertyMapDefinition _grf_action0_remappable_properties[] = {
GRFPropertyMapDefinition(GSF_ROADSTOPS, A0RPI_ROADSTOP_MIN_BRIDGE_HEIGHT, "roadstop_min_bridge_height"),
GRFPropertyMapDefinition(GSF_ROADSTOPS, A0RPI_ROADSTOP_DISALLOWED_BRIDGE_PILLARS, "roadstop_disallowed_bridge_pillars"),
GRFPropertyMapDefinition(GSF_ROADSTOPS, A0RPI_ROADSTOP_COST_MULTIPLIERS, "roadstop_cost_multipliers"),
GRFPropertyMapDefinition(GSF_NEWLANDSCAPE, A0RPI_NEWLANDSCAPE_ENABLE_RECOLOUR, "newlandscape_enable_recolour"),
GRFPropertyMapDefinition(),
};
@ -128,6 +131,12 @@ extern const GRFVariableMapDefinition _grf_action2_remappable_variables[] = {
GRFVariableMapDefinition(GSF_ROADSTOPS, 0x6A, "roadstop_road_stop_grfid_nearby_tiles"),
GRFVariableMapDefinition(GSF_RAILTYPES, A2VRI_RAILTYPE_SIGNAL_RESTRICTION_INFO, "railtype_signal_restriction_info"),
GRFVariableMapDefinition(GSF_SIGNALS, A2VRI_SIGNALS_SIGNAL_RESTRICTION_INFO, "signals_signal_restriction_info"),
GRFVariableMapDefinition(GSF_NEWLANDSCAPE, 0x40, "newlandscape_terrain_type"),
GRFVariableMapDefinition(GSF_NEWLANDSCAPE, 0x41, "newlandscape_tile_slope"),
GRFVariableMapDefinition(GSF_NEWLANDSCAPE, 0x42, "newlandscape_tile_height"),
GRFVariableMapDefinition(GSF_NEWLANDSCAPE, 0x43, "newlandscape_tile_hash"),
GRFVariableMapDefinition(GSF_NEWLANDSCAPE, 0x44, "newlandscape_landscape_type"),
GRFVariableMapDefinition(GSF_NEWLANDSCAPE, 0x60, "newlandscape_land_info_nearby_tiles"),
GRFVariableMapDefinition(),
};

@ -54,6 +54,7 @@ enum Action0RemapPropertyIds {
A0RPI_ROADSTOP_MIN_BRIDGE_HEIGHT,
A0RPI_ROADSTOP_DISALLOWED_BRIDGE_PILLARS,
A0RPI_ROADSTOP_COST_MULTIPLIERS,
A0RPI_NEWLANDSCAPE_ENABLE_RECOLOUR,
};

@ -0,0 +1,102 @@
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file newgrf_newlandscape.cpp NewGRF handling of new landscape. */
#include "stdafx.h"
#include "debug.h"
#include "newgrf_newlandscape.h"
#include "newgrf_extension.h"
#include "map_func.h"
#include "clear_map.h"
#include "core/hash_func.hpp"
#include "safeguards.h"
std::vector<const GRFFile *> _new_landscape_rocks_grfs;
/* virtual */ uint32 NewLandscapeScopeResolver::GetVariable(uint16 variable, uint32 parameter, GetVariableExtra *extra) const
{
if (unlikely(this->ti->tile == INVALID_TILE)) {
switch (variable) {
case 0x40: return 0;
case 0x41: return 0;
case 0x42: return 0;
case 0x43: return 0;
case 0x44: return this->landscape_type;
case 0x60: return 0;
}
}
switch (variable) {
case 0x40:
return GetTerrainType(this->ti->tile, TCX_NORMAL);
case 0x41:
return this->ti->tileh;
case 0x42:
return this->ti->z / TILE_HEIGHT;
case 0x43:
/* Knuth hash */
return SimpleHash32(this->ti->tile);
case 0x44:
return this->landscape_type;
case 0x60: {
TileIndex tile = this->ti->tile;
if (parameter != 0) tile = GetNearbyTile(parameter, tile); // only perform if it is required
uint32 result = 0;
if (extra->mask & ~0x100) result |= GetNearbyTileInformation(tile, this->ro.grffile->grf_version >= 8, extra->mask);
if (extra->mask & 0x100) {
switch (this->landscape_type) {
case NEW_LANDSCAPE_ROCKS:
if (IsTileType(tile, MP_CLEAR) && IsClearGround(tile, CLEAR_ROCKS)) result |= 0x100;
break;
}
}
return result;
}
}
DEBUG(grf, 1, "Unhandled new landscape tile variable 0x%X", variable);
extra->available = false;
return UINT_MAX;
}
/* virtual */ const SpriteGroup *NewLandscapeResolverObject::ResolveReal(const RealSpriteGroup *group) const
{
if (!group->loading.empty()) return group->loading[0];
if (!group->loaded.empty()) return group->loaded[0];
return nullptr;
}
GrfSpecFeature NewLandscapeResolverObject::GetFeature() const
{
return GSF_NEWLANDSCAPE;
}
NewLandscapeResolverObject::NewLandscapeResolverObject(const GRFFile *grffile, const TileInfo *ti, NewLandscapeType landscape_type, uint32 param1, uint32 param2)
: ResolverObject(grffile, CBID_NO_CALLBACK, param1, param2), newlandscape_scope(*this, ti, landscape_type)
{
if (grffile != nullptr) {
switch (landscape_type) {
case NEW_LANDSCAPE_ROCKS:
this->root_spritegroup = grffile->new_rocks_group;
break;
default:
this->root_spritegroup = nullptr;
break;
}
} else {
this->root_spritegroup = nullptr;
}
}

@ -0,0 +1,55 @@
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file newgrf_newlandscape.h NewGRF handling of new landscape. */
#ifndef NEWGRF_NEWLANDSCAPE_H
#define NEWGRF_NEWLANDSCAPE_H
#include "newgrf_commons.h"
#include "newgrf_spritegroup.h"
extern std::vector<const GRFFile *> _new_landscape_rocks_grfs;
enum NewLandscapeType : uint8 {
NEW_LANDSCAPE_ROCKS,
};
struct TileInfo;
/** Resolver for the new landscape scope. */
struct NewLandscapeScopeResolver : public ScopeResolver {
const TileInfo *ti;
NewLandscapeType landscape_type;
NewLandscapeScopeResolver(ResolverObject &ro, const TileInfo *ti, NewLandscapeType landscape_type)
: ScopeResolver(ro), ti(ti), landscape_type(landscape_type)
{
}
uint32 GetVariable(uint16 variable, uint32 parameter, GetVariableExtra *extra) const override;
};
struct NewLandscapeResolverObject : public ResolverObject {
NewLandscapeScopeResolver newlandscape_scope;
NewLandscapeResolverObject(const GRFFile *grffile, const TileInfo *ti, NewLandscapeType landscape_type, uint32 param1 = 0, uint32 param2 = 0);
ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, byte relative = 0) override
{
switch (scope) {
case VSG_SCOPE_SELF: return &this->newlandscape_scope;
default: return ResolverObject::GetScope(scope, relative);
}
}
const SpriteGroup *ResolveReal(const RealSpriteGroup *group) const override;
GrfSpecFeature GetFeature() const override;
};
#endif /* NEWGRF_NEWLANDSCAPE_H */

@ -1534,6 +1534,7 @@ static const NIFeature * const _nifeatures[] = {
&_nif_roadtype, // GSF_ROADTYPES
&_nif_roadtype, // GSF_TRAMTYPES
&_nif_roadstop, // GSF_ROADSTOPS
nullptr, // GSF_NEWLANDSCAPE
&_nif_town, // GSF_FAKE_TOWNS
&_nif_station_struct, // GSF_FAKE_STATION_STRUCT
};

Loading…
Cancel
Save