Add GRF feature: New Landscape

Custom graphics using actions 1/2/3.

Currently only for rock tiles
This commit is contained in:
Jonathan G Rennison
2022-06-13 02:53:27 +01:00
parent 80281daa23
commit 7bb0de247d
13 changed files with 543 additions and 2 deletions

View File

@@ -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

View File

@@ -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:

View File

@@ -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;

View File

@@ -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

View File

@@ -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(),
};

View File

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

102
src/newgrf_newlandscape.cpp Normal file
View File

@@ -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;
}
}

55
src/newgrf_newlandscape.h Normal file
View File

@@ -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 */

View File

@@ -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
};