Initial abstraction around NewGRF feature ID bytes

This commit is contained in:
Jonathan G Rennison
2022-01-27 21:09:45 +00:00
parent c69ed1711d
commit ef3916928c
4 changed files with 129 additions and 61 deletions

View File

@@ -1032,8 +1032,8 @@ static bool MappedPropertyLengthMismatch(ByteReader *buf, uint expected_size, co
uint length = buf->ReadExtendedByte();
if (length != expected_size) {
if (mapping_entry != nullptr) {
grfmsg(2, "Ignoring use of mapped property: %s, feature: %X, mapped to: %X, with incorrect data size: %u instead of %u",
mapping_entry->name, mapping_entry->feature, mapping_entry->property_id, length, expected_size);
grfmsg(2, "Ignoring use of mapped property: %s, feature: %s, mapped to: %X, with incorrect data size: %u instead of %u",
mapping_entry->name, GetFeatureString(mapping_entry->feature), mapping_entry->property_id, length, expected_size);
}
buf->Skip(length);
return true;
@@ -4901,7 +4901,7 @@ static ChangeInfoResult AirportTilesChangeInfo(uint airtid, int numinfo, int pro
return ret;
}
static bool HandleChangeInfoResult(const char *caller, ChangeInfoResult cir, uint8 feature, int property)
static bool HandleChangeInfoResult(const char *caller, ChangeInfoResult cir, GrfSpecFeature feature, int property)
{
switch (cir) {
default: NOT_REACHED();
@@ -4914,11 +4914,11 @@ static bool HandleChangeInfoResult(const char *caller, ChangeInfoResult cir, uin
return false;
case CIR_UNHANDLED:
grfmsg(1, "%s: Ignoring property 0x%02X of feature 0x%02X (not implemented)", caller, property, feature);
grfmsg(1, "%s: Ignoring property 0x%02X of feature %s (not implemented)", caller, property, GetFeatureString(feature));
return false;
case CIR_UNKNOWN:
grfmsg(0, "%s: Unknown property 0x%02X of feature 0x%02X, disabling", caller, property, feature);
grfmsg(0, "%s: Unknown property 0x%02X of feature %s, disabling", caller, property, GetFeatureString(feature));
FALLTHROUGH;
case CIR_INVALID_ID: {
@@ -4930,6 +4930,57 @@ static bool HandleChangeInfoResult(const char *caller, ChangeInfoResult cir, uin
}
}
static GrfSpecFeatureRef ReadFeature(uint8 raw_byte, bool allow_48 = false)
{
GrfSpecFeature feature;
if (raw_byte >= GSF_REAL_FEATURE_END && !(allow_48 && raw_byte == 0x48)) {
feature = GSF_INVALID;
} else {
feature = static_cast<GrfSpecFeature>(raw_byte);
}
return { feature, raw_byte };
}
static const char *_feature_names[] = {
"TRAINS",
"ROADVEHICLES",
"SHIPS",
"AIRCRAFT",
"STATIONS",
"CANALS",
"BRIDGES",
"HOUSES",
"GLOBALVAR",
"INDUSTRYTILES",
"INDUSTRIES",
"CARGOES",
"SOUNDFX",
"AIRPORTS",
"SIGNALS",
"OBJECTS",
"RAILTYPES",
"AIRPORTTILES",
"ROADTYPES",
"TRAMTYPES",
};
static_assert(lengthof(_feature_names) == GSF_END);
const char *GetFeatureString(GrfSpecFeatureRef feature)
{
static char buffer[32];
if (feature.id < GSF_END) {
seprintf(buffer, lastof(buffer), "0x%02X (%s)", feature.raw_byte, _feature_names[feature.id]);
} else {
seprintf(buffer, lastof(buffer), "0x%02X", feature.raw_byte);
}
return buffer;
}
const char *GetFeatureString(GrfSpecFeature feature)
{
return GetFeatureString(GrfSpecFeatureRef{ feature, feature });
}
struct GRFFilePropertyDescriptor {
int prop;
const GRFFilePropertyRemapEntry *entry;
@@ -4948,13 +4999,13 @@ static GRFFilePropertyDescriptor ReadAction0PropertyID(ByteReader *buf, uint8 fe
const GRFFilePropertyRemapEntry &def = iter->second;
int prop = def.id;
if (prop == A0RPI_UNKNOWN_ERROR) {
grfmsg(0, "Error: Unimplemented mapped property: %s, feature: %X, mapped to: %X", def.name, def.feature, raw_prop);
grfmsg(0, "Error: Unimplemented mapped property: %s, feature: %s, mapped to: %X", def.name, GetFeatureString(def.feature), raw_prop);
GRFError *error = DisableGrf(STR_NEWGRF_ERROR_UNIMPLEMETED_MAPPED_PROPERTY);
error->data = stredup(def.name);
error->param_value[1] = def.feature;
error->param_value[2] = raw_prop;
} else if (prop == A0RPI_UNKNOWN_IGNORE) {
grfmsg(2, "Ignoring unimplemented mapped property: %s, feature: %X, mapped to: %X", def.name, def.feature, raw_prop);
grfmsg(2, "Ignoring unimplemented mapped property: %s, feature: %s, mapped to: %X", def.name, GetFeatureString(def.feature), raw_prop);
}
return GRFFilePropertyDescriptor(prop, &def);
} else {
@@ -5001,21 +5052,22 @@ static void FeatureChangeInfo(ByteReader *buf)
static_assert(GSF_END == lengthof(handler));
static_assert(lengthof(handler) == lengthof(_cur.grffile->action0_property_remaps), "Action 0 feature list length mismatch");
uint8 feature = buf->ReadByte();
GrfSpecFeatureRef feature_ref = ReadFeature(buf->ReadByte());
GrfSpecFeature feature = feature_ref.id;
uint8 numprops = buf->ReadByte();
uint numinfo = buf->ReadByte();
uint engine = buf->ReadExtendedByte();
if (feature >= GSF_END) {
grfmsg(1, "FeatureChangeInfo: Unsupported feature 0x%02X, skipping", feature);
grfmsg(1, "FeatureChangeInfo: Unsupported feature %s skipping", GetFeatureString(feature));
return;
}
grfmsg(6, "FeatureChangeInfo: Feature 0x%02X, %d properties, to apply to %d+%d",
feature, numprops, engine, numinfo);
grfmsg(6, "FeatureChangeInfo: Feature %s, %d properties, to apply to %d+%d",
GetFeatureString(feature_ref), numprops, engine, numinfo);
if (handler[feature] == nullptr) {
if (feature != GSF_CARGOES) grfmsg(1, "FeatureChangeInfo: Unsupported feature 0x%02X, skipping", feature);
if (feature != GSF_CARGOES) grfmsg(1, "FeatureChangeInfo: Unsupported feature %s, skipping", GetFeatureString(feature_ref));
return;
}
@@ -5033,18 +5085,18 @@ static void FeatureChangeInfo(ByteReader *buf)
/* Action 0x00 (GLS_SAFETYSCAN) */
static void SafeChangeInfo(ByteReader *buf)
{
uint8 feature = buf->ReadByte();
GrfSpecFeatureRef feature = ReadFeature(buf->ReadByte());
uint8 numprops = buf->ReadByte();
uint numinfo = buf->ReadByte();
buf->ReadExtendedByte(); // id
if (feature == GSF_BRIDGES && numprops == 1) {
GRFFilePropertyDescriptor desc = ReadAction0PropertyID(buf, feature);
if (feature.id == GSF_BRIDGES && numprops == 1) {
GRFFilePropertyDescriptor desc = ReadAction0PropertyID(buf, feature.id);
/* Bridge property 0x0D is redefinition of sprite layout tables, which
* is considered safe. */
if (desc.prop == 0x0D) return;
} else if (feature == GSF_GLOBALVAR && numprops == 1) {
GRFFilePropertyDescriptor desc = ReadAction0PropertyID(buf, feature);
} else if (feature.id == GSF_GLOBALVAR && numprops == 1) {
GRFFilePropertyDescriptor desc = ReadAction0PropertyID(buf, feature.id);
/* Engine ID Mappings are safe, if the source is static */
if (desc.prop == 0x11) {
bool is_safe = true;
@@ -5070,7 +5122,8 @@ static void SafeChangeInfo(ByteReader *buf)
/* Action 0x00 (GLS_RESERVE) */
static void ReserveChangeInfo(ByteReader *buf)
{
uint8 feature = buf->ReadByte();
GrfSpecFeatureRef feature_ref = ReadFeature(buf->ReadByte());
GrfSpecFeature feature = feature_ref.id;
if (feature != GSF_CARGOES && feature != GSF_GLOBALVAR && feature != GSF_RAILTYPES && feature != GSF_ROADTYPES && feature != GSF_TRAMTYPES) return;
@@ -5126,7 +5179,8 @@ static void NewSpriteSet(ByteReader *buf)
* In that case, use num-dirs=4.
*/
uint8 feature = buf->ReadByte();
GrfSpecFeatureRef feature_ref = ReadFeature(buf->ReadByte());
GrfSpecFeature feature = feature_ref.id;
uint16 num_sets = buf->ReadByte();
uint16 first_set = 0;
@@ -5140,14 +5194,14 @@ static void NewSpriteSet(ByteReader *buf)
if (feature >= GSF_END) {
_cur.skip_sprites = num_sets * num_ents;
grfmsg(1, "NewSpriteSet: Unsupported feature 0x%02X, skipping %d sprites", feature, _cur.skip_sprites);
grfmsg(1, "NewSpriteSet: Unsupported feature %s, skipping %d sprites", GetFeatureString(feature), _cur.skip_sprites);
return;
}
_cur.AddSpriteSets(feature, _cur.spriteid, first_set, num_sets, num_ents);
grfmsg(7, "New sprite set at %d of feature 0x%02X, consisting of %d sets with %d views each (total %d)",
_cur.spriteid, feature, num_sets, num_ents, num_sets * num_ents
grfmsg(7, "New sprite set at %d of feature %s, consisting of %d sets with %d views each (total %d)",
_cur.spriteid, GetFeatureString(feature), num_sets, num_ents, num_sets * num_ents
);
for (int i = 0; i < num_sets * num_ents; i++) {
@@ -5237,9 +5291,10 @@ static void NewSpriteGroup(ByteReader *buf)
* V feature-specific-data (huge mess, don't even look it up --pasky) */
const SpriteGroup *act_group = nullptr;
uint8 feature = buf->ReadByte();
GrfSpecFeatureRef feature_ref = ReadFeature(buf->ReadByte());
GrfSpecFeature feature = feature_ref.id;
if (feature >= GSF_END) {
grfmsg(1, "NewSpriteGroup: Unsupported feature 0x%02X, skipping", feature);
grfmsg(1, "NewSpriteGroup: Unsupported feature %s, skipping", GetFeatureString(feature_ref));
return;
}
@@ -5588,7 +5643,7 @@ static void NewSpriteGroup(ByteReader *buf)
}
/* Loading of Tile Layout and Production Callback groups would happen here */
default: grfmsg(1, "NewSpriteGroup: Unsupported feature 0x%02X, skipping", feature);
default: grfmsg(1, "NewSpriteGroup: Unsupported feature %s, skipping", GetFeatureString(feature));
}
}
}
@@ -5696,7 +5751,7 @@ static void VehicleMapSpriteGroup(ByteReader *buf, byte feature, uint8 idcount)
/* No engine could be allocated?!? Deal with it. Okay,
* this might look bad. Also make sure this NewGRF
* gets disabled, as a half loaded one is bad. */
HandleChangeInfoResult("VehicleMapSpriteGroup", CIR_INVALID_ID, 0, 0);
HandleChangeInfoResult("VehicleMapSpriteGroup", CIR_INVALID_ID, (GrfSpecFeature)0, 0);
return;
}
@@ -6178,11 +6233,12 @@ static void FeatureMapSpriteGroup(ByteReader *buf)
* W cid cargo ID (sprite group ID) for this type of cargo
* W def-cid default cargo ID (sprite group ID) */
uint8 feature = buf->ReadByte();
GrfSpecFeatureRef feature_ref = ReadFeature(buf->ReadByte());
GrfSpecFeature feature = feature_ref.id;
uint8 idcount = buf->ReadByte();
if (feature >= GSF_END) {
grfmsg(1, "FeatureMapSpriteGroup: Unsupported feature 0x%02X, skipping", feature);
grfmsg(1, "FeatureMapSpriteGroup: Unsupported feature %s, skipping", GetFeatureString(feature_ref));
return;
}
@@ -6193,7 +6249,7 @@ static void FeatureMapSpriteGroup(ByteReader *buf)
uint16 groupid = buf->ReadWord();
if (!IsValidGroupID(groupid, "FeatureMapSpriteGroup")) return;
grfmsg(6, "FeatureMapSpriteGroup: Adding generic feature callback for feature 0x%02X", feature);
grfmsg(6, "FeatureMapSpriteGroup: Adding generic feature callback for feature %s", GetFeatureString(feature_ref));
AddGenericCallback(feature, _cur.grffile, _cur.spritegroups[groupid]);
return;
@@ -6202,7 +6258,7 @@ static void FeatureMapSpriteGroup(ByteReader *buf)
/* Mark the feature as used by the grf (generic callbacks do not count) */
SetBit(_cur.grffile->grf_features, feature);
grfmsg(6, "FeatureMapSpriteGroup: Feature 0x%02X, %d ids", feature, idcount);
grfmsg(6, "FeatureMapSpriteGroup: Feature %s, %d ids", GetFeatureString(feature_ref), idcount);
switch (feature) {
case GSF_TRAINS:
@@ -6265,7 +6321,7 @@ static void FeatureMapSpriteGroup(ByteReader *buf)
return;
default:
grfmsg(1, "FeatureMapSpriteGroup: Unsupported feature 0x%02X, skipping", feature);
grfmsg(1, "FeatureMapSpriteGroup: Unsupported feature %s, skipping", GetFeatureString(feature_ref));
return;
}
}
@@ -6291,9 +6347,10 @@ static void FeatureNewName(ByteReader *buf)
bool new_scheme = _cur.grffile->grf_version >= 7;
uint8 feature = buf->ReadByte();
GrfSpecFeatureRef feature_ref = ReadFeature(buf->ReadByte(), true);
GrfSpecFeature feature = feature_ref.id;
if (feature >= GSF_END && feature != 0x48) {
grfmsg(1, "FeatureNewName: Unsupported feature 0x%02X, skipping", feature);
grfmsg(1, "FeatureNewName: Unsupported feature %s, skipping", GetFeatureString(feature_ref));
return;
}
@@ -6313,8 +6370,8 @@ static void FeatureNewName(ByteReader *buf)
uint16 endid = id + num;
grfmsg(6, "FeatureNewName: About to rename engines %d..%d (feature 0x%02X) in language 0x%02X",
id, endid, feature, lang);
grfmsg(6, "FeatureNewName: About to rename engines %d..%d (feature %s) in language 0x%02X",
id, endid, GetFeatureString(feature), lang);
for (; id < endid && buf->HasData(); id++) {
const char *name = buf->ReadString();
@@ -7492,7 +7549,8 @@ static void ParamSet(ByteReader *buf)
} else {
/* GRF Resource Management */
uint8 op = src1;
uint8 feature = GB(data, 8, 8);
GrfSpecFeatureRef feature_ref = ReadFeature(GB(data, 8, 8));
GrfSpecFeature feature = feature_ref.id;
uint16 count = GB(data, 16, 16);
if (_cur.stage == GLS_RESERVE) {
@@ -7562,7 +7620,7 @@ static void ParamSet(ByteReader *buf)
if (_cur.skip_sprites == -1) return;
break;
default: grfmsg(1, "ParamSet: GRM: Unsupported feature 0x%X", feature); return;
default: grfmsg(1, "ParamSet: GRM: Unsupported feature %s", GetFeatureString(feature_ref)); return;
}
} else {
/* Ignore GRM during initialization */
@@ -8674,7 +8732,7 @@ struct GRFPropertyMapAction {
const char *tag_name = nullptr;
const char *descriptor = nullptr;
int feature;
GrfSpecFeature feature;
int prop_id;
std::string name;
GRFPropertyMapFallbackMode fallback_mode;
@@ -8689,7 +8747,7 @@ struct GRFPropertyMapAction {
this->tag_name = tag;
this->descriptor = desc;
this->feature = -1;
this->feature = GSF_INVALID;
this->prop_id = -1;
this->name.clear();
this->fallback_mode = GPMFM_IGNORE;
@@ -8702,7 +8760,7 @@ struct GRFPropertyMapAction {
void ExecutePropertyRemapping()
{
if (this->feature < 0) {
if (this->feature == GSF_INVALID) {
grfmsg(2, "Action 14 %s remapping: no feature defined, doing nothing", this->descriptor);
return;
}
@@ -8733,15 +8791,15 @@ struct GRFPropertyMapAction {
}
if (!success) {
if (this->fallback_mode == GPMFM_ERROR_ON_DEFINITION) {
grfmsg(0, "Error: Unimplemented mapped %s: %s, feature: %X, mapped to: %X", this->descriptor, str, this->feature, this->prop_id);
grfmsg(0, "Error: Unimplemented mapped %s: %s, feature: %s, mapped to: %X", this->descriptor, str, GetFeatureString(this->feature), this->prop_id);
GRFError *error = DisableGrf(STR_NEWGRF_ERROR_UNIMPLEMETED_MAPPED_PROPERTY);
error->data = stredup(str);
error->param_value[1] = this->feature;
error->param_value[2] = this->prop_id;
} else {
const char *str_store = stredup(str);
grfmsg(2, "Unimplemented mapped %s: %s, feature: %X, mapped to: %X, %s on use",
this->descriptor, str, this->feature, this->prop_id, (this->fallback_mode == GPMFM_IGNORE) ? "ignoring" : "error");
grfmsg(2, "Unimplemented mapped %s: %s, feature: %s, mapped to: %X, %s on use",
this->descriptor, str, GetFeatureString(this->feature), this->prop_id, (this->fallback_mode == GPMFM_IGNORE) ? "ignoring" : "error");
_cur.grffile->remap_unknown_property_names.emplace_back(str_store);
GRFFilePropertyRemapEntry &entry = _cur.grffile->action0_property_remaps[this->feature].Entry(this->prop_id);
entry.name = str_store;
@@ -8754,7 +8812,7 @@ struct GRFPropertyMapAction {
void ExecuteVariableRemapping()
{
if (this->feature < 0) {
if (this->feature == GSF_INVALID) {
grfmsg(2, "Action 14 %s remapping: no feature defined, doing nothing", this->descriptor);
return;
}
@@ -8776,7 +8834,7 @@ struct GRFPropertyMapAction {
SB(_cur.grffile->var8D_overlay, this->ttd_ver_var_bit, 1, success ? 1 : 0);
}
if (!success) {
grfmsg(2, "Unimplemented mapped %s: %s, feature: %X, mapped to 0", this->descriptor, str, this->feature);
grfmsg(2, "Unimplemented mapped %s: %s, feature: %s, mapped to 0", this->descriptor, str, GetFeatureString(this->feature));
}
}
@@ -8844,11 +8902,11 @@ static bool ChangePropertyRemapFeature(size_t len, ByteReader *buf)
grfmsg(2, "Action 14 %s mapping: expected 1 byte for '%s'->'FEAT' but got " PRINTF_SIZE ", ignoring this field", action.descriptor, action.tag_name, len);
buf->Skip(len);
} else {
uint8 feature = buf->ReadByte();
if (feature >= GSF_END) {
grfmsg(2, "Action 14 %s mapping: invalid feature ID: %u, in '%s'->'FEAT', ignoring this field", action.descriptor, feature, action.tag_name);
GrfSpecFeatureRef feature = ReadFeature(buf->ReadByte());
if (feature.id >= GSF_END) {
grfmsg(2, "Action 14 %s mapping: invalid feature ID: %s, in '%s'->'FEAT', ignoring this field", action.descriptor, GetFeatureString(feature), action.tag_name);
} else {
action.feature = feature;
action.feature = feature.id;
}
}
return true;

View File

@@ -89,6 +89,8 @@ enum GrfSpecFeature {
GSF_TRAMTYPES,
GSF_END,
GSF_REAL_FEATURE_END = GSF_END,
GSF_FAKE_TOWNS = GSF_END, ///< Fake town GrfSpecFeature for NewGRF debugging (parent scope)
GSF_FAKE_STATION_STRUCT, ///< Fake station struct GrfSpecFeature for NewGRF debugging
GSF_FAKE_END, ///< End of the fake features
@@ -115,16 +117,16 @@ enum GRFPropertyMapFallbackMode {
struct GRFPropertyMapDefinition {
const char *name; // nullptr indicates the end of the list
int id;
uint8 feature;
GrfSpecFeature feature;
/** Create empty object used to identify the end of a list. */
GRFPropertyMapDefinition() :
name(nullptr),
id(0),
feature(0)
feature((GrfSpecFeature)0)
{}
GRFPropertyMapDefinition(uint8 feature, int id, const char *name) :
GRFPropertyMapDefinition(GrfSpecFeature feature, int id, const char *name) :
name(name),
id(id),
feature(feature)
@@ -134,7 +136,7 @@ struct GRFPropertyMapDefinition {
struct GRFFilePropertyRemapEntry {
const char *name = nullptr;
int id = 0;
uint8 feature = 0;
GrfSpecFeature feature = (GrfSpecFeature)0;
uint8 property_id = 0;
};
@@ -152,16 +154,16 @@ struct GRFFilePropertyRemapSet {
struct GRFVariableMapDefinition {
const char *name; // nullptr indicates the end of the list
int id;
uint8 feature;
GrfSpecFeature feature;
/** Create empty object used to identify the end of a list. */
GRFVariableMapDefinition() :
name(nullptr),
id(0),
feature(0)
feature((GrfSpecFeature)0)
{}
GRFVariableMapDefinition(uint8 feature, int id, const char *name) :
GRFVariableMapDefinition(GrfSpecFeature feature, int id, const char *name) :
name(name),
id(id),
feature(feature)
@@ -364,4 +366,12 @@ uint CountSelectedGRFs(GRFConfig *grfconf);
struct TemplateVehicle;
struct GrfSpecFeatureRef {
GrfSpecFeature id;
uint8 raw_byte;
};
const char *GetFeatureString(GrfSpecFeatureRef feature);
const char *GetFeatureString(GrfSpecFeature feature);
#endif /* NEWGRF_H */

View File

@@ -106,10 +106,10 @@ void ResetGenericCallbacks()
* @param file The GRF of the callback.
* @param group The sprite group of the callback.
*/
void AddGenericCallback(uint8 feature, const GRFFile *file, const SpriteGroup *group)
void AddGenericCallback(GrfSpecFeature feature, const GRFFile *file, const SpriteGroup *group)
{
if (feature >= lengthof(_gcl)) {
grfmsg(5, "AddGenericCallback: Unsupported feature 0x%02X", feature);
grfmsg(5, "AddGenericCallback: Unsupported feature %s", GetFeatureString(feature));
return;
}
@@ -204,7 +204,7 @@ static uint16 GetGenericCallbackResult(uint8 feature, ResolverObject &object, ui
* @param[out] file Optionally returns the GRFFile which made the final decision for the callback result. May be nullptr if not required.
* @return callback value if successful or CALLBACK_FAILED
*/
uint16 GetAiPurchaseCallbackResult(uint8 feature, CargoID cargo_type, uint8 default_selection, IndustryType src_industry, IndustryType dst_industry, uint8 distance, AIConstructionEvent event, uint8 count, uint8 station_size, const GRFFile **file)
uint16 GetAiPurchaseCallbackResult(GrfSpecFeature feature, CargoID cargo_type, uint8 default_selection, IndustryType src_industry, IndustryType dst_industry, uint8 distance, AIConstructionEvent event, uint8 count, uint8 station_size, const GRFFile **file)
{
GenericResolverObject object(true, CBID_GENERIC_AI_PURCHASE_SELECTION);

View File

@@ -45,9 +45,9 @@ static const IndustryType IT_AI_UNKNOWN = 0xFE; ///< The AI has no specific indu
static const IndustryType IT_AI_TOWN = 0xFF; ///< The AI actually wants to transport to/from a town, not an industry.
void ResetGenericCallbacks();
void AddGenericCallback(uint8 feature, const GRFFile *file, const SpriteGroup *group);
void AddGenericCallback(GrfSpecFeature feature, const GRFFile *file, const SpriteGroup *group);
uint16 GetAiPurchaseCallbackResult(uint8 feature, CargoID cargo_type, uint8 default_selection, IndustryType src_industry, IndustryType dst_industry, uint8 distance, AIConstructionEvent event, uint8 count, uint8 station_size, const GRFFile **file);
uint16 GetAiPurchaseCallbackResult(GrfSpecFeature feature, CargoID cargo_type, uint8 default_selection, IndustryType src_industry, IndustryType dst_industry, uint8 distance, AIConstructionEvent event, uint8 count, uint8 station_size, const GRFFile **file);
/** Play an ambient sound effect for an empty tile. */
static inline void AmbientSoundEffect(TileIndex tile)