mirror of https://github.com/OpenTTD/OpenTTD
709 lines
21 KiB
C++
709 lines
21 KiB
C++
/*
|
|
* 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_act0_industries.cpp NewGRF Action 0x00 handler for industries and industrytiles. */
|
|
|
|
#include "../stdafx.h"
|
|
#include "../debug.h"
|
|
#include "../newgrf_cargo.h"
|
|
#include "../industry.h"
|
|
#include "../industrytype.h"
|
|
#include "../industry_map.h"
|
|
#include "../newgrf_industries.h"
|
|
#include "newgrf_bytereader.h"
|
|
#include "newgrf_internal.h"
|
|
#include "newgrf_stringmapping.h"
|
|
|
|
#include "table/strings.h"
|
|
#include "../table/build_industry.h"
|
|
|
|
#include "../safeguards.h"
|
|
|
|
/**
|
|
* Ignore an industry tile property
|
|
* @param prop The property to ignore.
|
|
* @param buf The property value.
|
|
* @return ChangeInfoResult.
|
|
*/
|
|
static ChangeInfoResult IgnoreIndustryTileProperty(int prop, ByteReader &buf)
|
|
{
|
|
ChangeInfoResult ret = CIR_SUCCESS;
|
|
|
|
switch (prop) {
|
|
case 0x09:
|
|
case 0x0D:
|
|
case 0x0E:
|
|
case 0x10:
|
|
case 0x11:
|
|
case 0x12:
|
|
buf.ReadByte();
|
|
break;
|
|
|
|
case 0x0A:
|
|
case 0x0B:
|
|
case 0x0C:
|
|
case 0x0F:
|
|
buf.ReadWord();
|
|
break;
|
|
|
|
case 0x13:
|
|
buf.Skip(buf.ReadByte() * 2);
|
|
break;
|
|
|
|
default:
|
|
ret = CIR_UNKNOWN;
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Define properties for industry tiles
|
|
* @param first Local ID of the first industry tile.
|
|
* @param last Local ID of the last industry tile.
|
|
* @param prop The property to change.
|
|
* @param buf The property value.
|
|
* @return ChangeInfoResult.
|
|
*/
|
|
static ChangeInfoResult IndustrytilesChangeInfo(uint first, uint last, int prop, ByteReader &buf)
|
|
{
|
|
ChangeInfoResult ret = CIR_SUCCESS;
|
|
|
|
if (last > NUM_INDUSTRYTILES_PER_GRF) {
|
|
GrfMsg(1, "IndustryTilesChangeInfo: Too many industry tiles loaded ({}), max ({}). Ignoring.", last, NUM_INDUSTRYTILES_PER_GRF);
|
|
return CIR_INVALID_ID;
|
|
}
|
|
|
|
/* Allocate industry tile specs if they haven't been allocated already. */
|
|
if (_cur.grffile->indtspec.size() < last) _cur.grffile->indtspec.resize(last);
|
|
|
|
for (uint id = first; id < last; ++id) {
|
|
auto &tsp = _cur.grffile->indtspec[id];
|
|
|
|
if (prop != 0x08 && tsp == nullptr) {
|
|
ChangeInfoResult cir = IgnoreIndustryTileProperty(prop, buf);
|
|
if (cir > ret) ret = cir;
|
|
continue;
|
|
}
|
|
|
|
switch (prop) {
|
|
case 0x08: { // Substitute industry tile type
|
|
uint8_t subs_id = buf.ReadByte();
|
|
if (subs_id >= NEW_INDUSTRYTILEOFFSET) {
|
|
/* The substitute id must be one of the original industry tile. */
|
|
GrfMsg(2, "IndustryTilesChangeInfo: Attempt to use new industry tile {} as substitute industry tile for {}. Ignoring.", subs_id, id);
|
|
continue;
|
|
}
|
|
|
|
/* Allocate space for this industry. */
|
|
if (tsp == nullptr) {
|
|
tsp = std::make_unique<IndustryTileSpec>(_industry_tile_specs[subs_id]);
|
|
|
|
tsp->enabled = true;
|
|
|
|
/* A copied tile should not have the animation infos copied too.
|
|
* The anim_state should be left untouched, though
|
|
* It is up to the author to animate them */
|
|
tsp->anim_production = INDUSTRYTILE_NOANIM;
|
|
tsp->anim_next = INDUSTRYTILE_NOANIM;
|
|
|
|
tsp->grf_prop.local_id = id;
|
|
tsp->grf_prop.subst_id = subs_id;
|
|
tsp->grf_prop.SetGRFFile(_cur.grffile);
|
|
_industile_mngr.AddEntityID(id, _cur.grffile->grfid, subs_id); // pre-reserve the tile slot
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 0x09: { // Industry tile override
|
|
uint8_t ovrid = buf.ReadByte();
|
|
|
|
/* The industry being overridden must be an original industry. */
|
|
if (ovrid >= NEW_INDUSTRYTILEOFFSET) {
|
|
GrfMsg(2, "IndustryTilesChangeInfo: Attempt to override new industry tile {} with industry tile id {}. Ignoring.", ovrid, id);
|
|
continue;
|
|
}
|
|
|
|
_industile_mngr.Add(id, _cur.grffile->grfid, ovrid);
|
|
break;
|
|
}
|
|
|
|
case 0x0A: // Tile acceptance
|
|
case 0x0B:
|
|
case 0x0C: {
|
|
uint16_t acctp = buf.ReadWord();
|
|
tsp->accepts_cargo[prop - 0x0A] = GetCargoTranslation(GB(acctp, 0, 8), _cur.grffile);
|
|
tsp->acceptance[prop - 0x0A] = Clamp(GB(acctp, 8, 8), 0, 16);
|
|
tsp->accepts_cargo_label[prop - 0x0A] = CT_INVALID;
|
|
break;
|
|
}
|
|
|
|
case 0x0D: // Land shape flags
|
|
tsp->slopes_refused = (Slope)buf.ReadByte();
|
|
break;
|
|
|
|
case 0x0E: // Callback mask
|
|
tsp->callback_mask = static_cast<IndustryTileCallbackMasks>(buf.ReadByte());
|
|
break;
|
|
|
|
case 0x0F: // Animation information
|
|
tsp->animation.frames = buf.ReadByte();
|
|
tsp->animation.status = buf.ReadByte();
|
|
break;
|
|
|
|
case 0x10: // Animation speed
|
|
tsp->animation.speed = buf.ReadByte();
|
|
break;
|
|
|
|
case 0x11: // Triggers for callback 25
|
|
tsp->animation.triggers = buf.ReadByte();
|
|
break;
|
|
|
|
case 0x12: // Special flags
|
|
tsp->special_flags = IndustryTileSpecialFlags{buf.ReadByte()};
|
|
break;
|
|
|
|
case 0x13: { // variable length cargo acceptance
|
|
uint8_t num_cargoes = buf.ReadByte();
|
|
if (num_cargoes > std::size(tsp->acceptance)) {
|
|
GRFError *error = DisableGrf(STR_NEWGRF_ERROR_LIST_PROPERTY_TOO_LONG);
|
|
error->param_value[1] = prop;
|
|
return CIR_DISABLED;
|
|
}
|
|
for (uint i = 0; i < std::size(tsp->acceptance); i++) {
|
|
if (i < num_cargoes) {
|
|
tsp->accepts_cargo[i] = GetCargoTranslation(buf.ReadByte(), _cur.grffile);
|
|
/* Tile acceptance can be negative to counteract the IndustryTileSpecialFlag::AcceptsAllCargo flag */
|
|
tsp->acceptance[i] = (int8_t)buf.ReadByte();
|
|
} else {
|
|
tsp->accepts_cargo[i] = INVALID_CARGO;
|
|
tsp->acceptance[i] = 0;
|
|
}
|
|
if (i < std::size(tsp->accepts_cargo_label)) tsp->accepts_cargo_label[i] = CT_INVALID;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 0x14: // Badge list
|
|
tsp->badges = ReadBadgeList(buf, GSF_INDUSTRYTILES);
|
|
break;
|
|
|
|
default:
|
|
ret = CIR_UNKNOWN;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Ignore an industry property
|
|
* @param prop The property to ignore.
|
|
* @param buf The property value.
|
|
* @return ChangeInfoResult.
|
|
*/
|
|
static ChangeInfoResult IgnoreIndustryProperty(int prop, ByteReader &buf)
|
|
{
|
|
ChangeInfoResult ret = CIR_SUCCESS;
|
|
|
|
switch (prop) {
|
|
case 0x09:
|
|
case 0x0B:
|
|
case 0x0F:
|
|
case 0x12:
|
|
case 0x13:
|
|
case 0x14:
|
|
case 0x17:
|
|
case 0x18:
|
|
case 0x19:
|
|
case 0x21:
|
|
case 0x22:
|
|
buf.ReadByte();
|
|
break;
|
|
|
|
case 0x0C:
|
|
case 0x0D:
|
|
case 0x0E:
|
|
case 0x10: // INDUSTRY_ORIGINAL_NUM_OUTPUTS bytes
|
|
case 0x1B:
|
|
case 0x1F:
|
|
case 0x24:
|
|
buf.ReadWord();
|
|
break;
|
|
|
|
case 0x11: // INDUSTRY_ORIGINAL_NUM_INPUTS bytes + 1
|
|
case 0x1A:
|
|
case 0x1C:
|
|
case 0x1D:
|
|
case 0x1E:
|
|
case 0x20:
|
|
case 0x23:
|
|
buf.ReadDWord();
|
|
break;
|
|
|
|
case 0x0A: {
|
|
uint8_t num_table = buf.ReadByte();
|
|
for (uint8_t j = 0; j < num_table; j++) {
|
|
for (uint k = 0;; k++) {
|
|
uint8_t x = buf.ReadByte();
|
|
if (x == 0xFE && k == 0) {
|
|
buf.ReadByte();
|
|
buf.ReadByte();
|
|
break;
|
|
}
|
|
|
|
uint8_t y = buf.ReadByte();
|
|
if (x == 0 && y == 0x80) break;
|
|
|
|
uint8_t gfx = buf.ReadByte();
|
|
if (gfx == 0xFE) buf.ReadWord();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 0x16:
|
|
for (uint8_t j = 0; j < INDUSTRY_ORIGINAL_NUM_INPUTS; j++) buf.ReadByte();
|
|
break;
|
|
|
|
case 0x15:
|
|
case 0x25:
|
|
case 0x26:
|
|
case 0x27:
|
|
buf.Skip(buf.ReadByte());
|
|
break;
|
|
|
|
case 0x28: {
|
|
int num_inputs = buf.ReadByte();
|
|
int num_outputs = buf.ReadByte();
|
|
buf.Skip(num_inputs * num_outputs * 2);
|
|
break;
|
|
}
|
|
|
|
case 0x29: // Badge list
|
|
SkipBadgeList(buf);
|
|
break;
|
|
|
|
default:
|
|
ret = CIR_UNKNOWN;
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Validate the industry layout; e.g. to prevent duplicate tiles.
|
|
* @param layout The layout to check.
|
|
* @return True if the layout is deemed valid.
|
|
*/
|
|
static bool ValidateIndustryLayout(const IndustryTileLayout &layout)
|
|
{
|
|
const size_t size = layout.size();
|
|
if (size == 0) return false;
|
|
|
|
for (size_t i = 0; i < size - 1; i++) {
|
|
for (size_t j = i + 1; j < size; j++) {
|
|
if (layout[i].ti.x == layout[j].ti.x &&
|
|
layout[i].ti.y == layout[j].ti.y) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool have_regular_tile = false;
|
|
for (const auto &tilelayout : layout) {
|
|
if (tilelayout.gfx != GFX_WATERTILE_SPECIALCHECK) {
|
|
have_regular_tile = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return have_regular_tile;
|
|
}
|
|
|
|
/**
|
|
* Define properties for industries
|
|
* @param first Local ID of the first industry.
|
|
* @param last Local ID of the last industry.
|
|
* @param prop The property to change.
|
|
* @param buf The property value.
|
|
* @return ChangeInfoResult.
|
|
*/
|
|
static ChangeInfoResult IndustriesChangeInfo(uint first, uint last, int prop, ByteReader &buf)
|
|
{
|
|
ChangeInfoResult ret = CIR_SUCCESS;
|
|
|
|
if (last > NUM_INDUSTRYTYPES_PER_GRF) {
|
|
GrfMsg(1, "IndustriesChangeInfo: Too many industries loaded ({}), max ({}). Ignoring.", last, NUM_INDUSTRYTYPES_PER_GRF);
|
|
return CIR_INVALID_ID;
|
|
}
|
|
|
|
/* Allocate industry specs if they haven't been allocated already. */
|
|
if (_cur.grffile->industryspec.size() < last) _cur.grffile->industryspec.resize(last);
|
|
|
|
for (uint id = first; id < last; ++id) {
|
|
auto &indsp = _cur.grffile->industryspec[id];
|
|
|
|
if (prop != 0x08 && indsp == nullptr) {
|
|
ChangeInfoResult cir = IgnoreIndustryProperty(prop, buf);
|
|
if (cir > ret) ret = cir;
|
|
continue;
|
|
}
|
|
|
|
switch (prop) {
|
|
case 0x08: { // Substitute industry type
|
|
uint8_t subs_id = buf.ReadByte();
|
|
if (subs_id == 0xFF) {
|
|
/* Instead of defining a new industry, a substitute industry id
|
|
* of 0xFF disables the old industry with the current id. */
|
|
_industry_specs[id].enabled = false;
|
|
continue;
|
|
} else if (subs_id >= NEW_INDUSTRYOFFSET) {
|
|
/* The substitute id must be one of the original industry. */
|
|
GrfMsg(2, "_industry_specs: Attempt to use new industry {} as substitute industry for {}. Ignoring.", subs_id, id);
|
|
continue;
|
|
}
|
|
|
|
/* Allocate space for this industry.
|
|
* Only need to do it once. If ever it is called again, it should not
|
|
* do anything */
|
|
if (indsp == nullptr) {
|
|
indsp = std::make_unique<IndustrySpec>(_origin_industry_specs[subs_id]);
|
|
|
|
indsp->enabled = true;
|
|
indsp->grf_prop.local_id = id;
|
|
indsp->grf_prop.subst_id = subs_id;
|
|
indsp->grf_prop.SetGRFFile(_cur.grffile);
|
|
/* If the grf industry needs to check its surrounding upon creation, it should
|
|
* rely on callbacks, not on the original placement functions */
|
|
indsp->check_proc = CHECK_NOTHING;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 0x09: { // Industry type override
|
|
uint8_t ovrid = buf.ReadByte();
|
|
|
|
/* The industry being overridden must be an original industry. */
|
|
if (ovrid >= NEW_INDUSTRYOFFSET) {
|
|
GrfMsg(2, "IndustriesChangeInfo: Attempt to override new industry {} with industry id {}. Ignoring.", ovrid, id);
|
|
continue;
|
|
}
|
|
indsp->grf_prop.override_id = ovrid;
|
|
_industry_mngr.Add(id, _cur.grffile->grfid, ovrid);
|
|
break;
|
|
}
|
|
|
|
case 0x0A: { // Set industry layout(s)
|
|
uint8_t new_num_layouts = buf.ReadByte();
|
|
uint32_t definition_size = buf.ReadDWord();
|
|
uint32_t bytes_read = 0;
|
|
std::vector<IndustryTileLayout> new_layouts;
|
|
IndustryTileLayout layout;
|
|
|
|
for (uint8_t j = 0; j < new_num_layouts; j++) {
|
|
layout.clear();
|
|
|
|
for (uint k = 0;; k++) {
|
|
if (bytes_read >= definition_size) {
|
|
GrfMsg(3, "IndustriesChangeInfo: Incorrect size for industry tile layout definition for industry {}.", id);
|
|
/* Avoid warning twice */
|
|
definition_size = UINT32_MAX;
|
|
}
|
|
|
|
IndustryTileLayoutTile &it = layout.emplace_back();
|
|
|
|
it.ti.x = buf.ReadByte(); // Offsets from northermost tile
|
|
++bytes_read;
|
|
|
|
if (it.ti.x == 0xFE && k == 0) {
|
|
/* This means we have to borrow the layout from an old industry */
|
|
IndustryType type = buf.ReadByte();
|
|
uint8_t laynbr = buf.ReadByte();
|
|
bytes_read += 2;
|
|
|
|
if (type >= lengthof(_origin_industry_specs)) {
|
|
GrfMsg(1, "IndustriesChangeInfo: Invalid original industry number for layout import, industry {}", id);
|
|
DisableGrf(STR_NEWGRF_ERROR_INVALID_ID);
|
|
return CIR_DISABLED;
|
|
}
|
|
if (laynbr >= _origin_industry_specs[type].layouts.size()) {
|
|
GrfMsg(1, "IndustriesChangeInfo: Invalid original industry layout index for layout import, industry {}", id);
|
|
DisableGrf(STR_NEWGRF_ERROR_INVALID_ID);
|
|
return CIR_DISABLED;
|
|
}
|
|
layout = _origin_industry_specs[type].layouts[laynbr];
|
|
break;
|
|
}
|
|
|
|
it.ti.y = buf.ReadByte(); // Or table definition finalisation
|
|
++bytes_read;
|
|
|
|
if (it.ti.x == 0 && it.ti.y == 0x80) {
|
|
/* Terminator, remove and finish up */
|
|
layout.pop_back();
|
|
break;
|
|
}
|
|
|
|
it.gfx = buf.ReadByte();
|
|
++bytes_read;
|
|
|
|
if (it.gfx == 0xFE) {
|
|
/* Use a new tile from this GRF */
|
|
int local_tile_id = buf.ReadWord();
|
|
bytes_read += 2;
|
|
|
|
/* Read the ID from the _industile_mngr. */
|
|
int tempid = _industile_mngr.GetID(local_tile_id, _cur.grffile->grfid);
|
|
|
|
if (tempid == INVALID_INDUSTRYTILE) {
|
|
GrfMsg(2, "IndustriesChangeInfo: Attempt to use industry tile {} with industry id {}, not yet defined. Ignoring.", local_tile_id, id);
|
|
} else {
|
|
/* Declared as been valid, can be used */
|
|
it.gfx = tempid;
|
|
}
|
|
} else if (it.gfx == GFX_WATERTILE_SPECIALCHECK) {
|
|
it.ti.x = (int8_t)GB(it.ti.x, 0, 8);
|
|
it.ti.y = (int8_t)GB(it.ti.y, 0, 8);
|
|
|
|
/* When there were only 256x256 maps, TileIndex was a uint16_t and
|
|
* it.ti was just a TileIndexDiff that was added to it.
|
|
* As such negative "x" values were shifted into the "y" position.
|
|
* x = -1, y = 1 -> x = 255, y = 0
|
|
* Since GRF version 8 the position is interpreted as pair of independent int8.
|
|
* For GRF version < 8 we need to emulate the old shifting behaviour.
|
|
*/
|
|
if (_cur.grffile->grf_version < 8 && it.ti.x < 0) it.ti.y += 1;
|
|
}
|
|
}
|
|
|
|
if (!ValidateIndustryLayout(layout)) {
|
|
/* The industry layout was not valid, so skip this one. */
|
|
GrfMsg(1, "IndustriesChangeInfo: Invalid industry layout for industry id {}. Ignoring", id);
|
|
} else {
|
|
new_layouts.push_back(layout);
|
|
}
|
|
}
|
|
|
|
/* Install final layout construction in the industry spec */
|
|
indsp->layouts = std::move(new_layouts);
|
|
break;
|
|
}
|
|
|
|
case 0x0B: // Industry production flags
|
|
indsp->life_type = IndustryLifeTypes{buf.ReadByte()};
|
|
break;
|
|
|
|
case 0x0C: // Industry closure message
|
|
AddStringForMapping(GRFStringID{buf.ReadWord()}, &indsp->closure_text);
|
|
break;
|
|
|
|
case 0x0D: // Production increase message
|
|
AddStringForMapping(GRFStringID{buf.ReadWord()}, &indsp->production_up_text);
|
|
break;
|
|
|
|
case 0x0E: // Production decrease message
|
|
AddStringForMapping(GRFStringID{buf.ReadWord()}, &indsp->production_down_text);
|
|
break;
|
|
|
|
case 0x0F: // Fund cost multiplier
|
|
indsp->cost_multiplier = buf.ReadByte();
|
|
break;
|
|
|
|
case 0x10: // Production cargo types
|
|
for (uint8_t j = 0; j < INDUSTRY_ORIGINAL_NUM_OUTPUTS; j++) {
|
|
indsp->produced_cargo[j] = GetCargoTranslation(buf.ReadByte(), _cur.grffile);
|
|
indsp->produced_cargo_label[j] = CT_INVALID;
|
|
}
|
|
break;
|
|
|
|
case 0x11: // Acceptance cargo types
|
|
for (uint8_t j = 0; j < INDUSTRY_ORIGINAL_NUM_INPUTS; j++) {
|
|
indsp->accepts_cargo[j] = GetCargoTranslation(buf.ReadByte(), _cur.grffile);
|
|
indsp->accepts_cargo_label[j] = CT_INVALID;
|
|
}
|
|
buf.ReadByte(); // Unnused, eat it up
|
|
break;
|
|
|
|
case 0x12: // Production multipliers
|
|
case 0x13:
|
|
indsp->production_rate[prop - 0x12] = buf.ReadByte();
|
|
break;
|
|
|
|
case 0x14: // Minimal amount of cargo distributed
|
|
indsp->minimal_cargo = buf.ReadByte();
|
|
break;
|
|
|
|
case 0x15: { // Random sound effects
|
|
uint8_t num_sounds = buf.ReadByte();
|
|
|
|
std::vector<uint8_t> sounds;
|
|
sounds.reserve(num_sounds);
|
|
for (uint8_t j = 0; j < num_sounds; ++j) {
|
|
sounds.push_back(buf.ReadByte());
|
|
}
|
|
|
|
indsp->random_sounds = std::move(sounds);
|
|
break;
|
|
}
|
|
|
|
case 0x16: // Conflicting industry types
|
|
for (uint8_t j = 0; j < 3; j++) indsp->conflicting[j] = buf.ReadByte();
|
|
break;
|
|
|
|
case 0x17: // Probability in random game
|
|
indsp->appear_creation[to_underlying(_settings_game.game_creation.landscape)] = buf.ReadByte();
|
|
break;
|
|
|
|
case 0x18: // Probability during gameplay
|
|
indsp->appear_ingame[to_underlying(_settings_game.game_creation.landscape)] = buf.ReadByte();
|
|
break;
|
|
|
|
case 0x19: // Map colour
|
|
indsp->map_colour = buf.ReadByte();
|
|
break;
|
|
|
|
case 0x1A: // Special industry flags to define special behavior
|
|
indsp->behaviour = IndustryBehaviours{buf.ReadDWord()};
|
|
break;
|
|
|
|
case 0x1B: // New industry text ID
|
|
AddStringForMapping(GRFStringID{buf.ReadWord()}, &indsp->new_industry_text);
|
|
break;
|
|
|
|
case 0x1C: // Input cargo multipliers for the three input cargo types
|
|
case 0x1D:
|
|
case 0x1E: {
|
|
uint32_t multiples = buf.ReadDWord();
|
|
indsp->input_cargo_multiplier[prop - 0x1C][0] = GB(multiples, 0, 16);
|
|
indsp->input_cargo_multiplier[prop - 0x1C][1] = GB(multiples, 16, 16);
|
|
break;
|
|
}
|
|
|
|
case 0x1F: // Industry name
|
|
AddStringForMapping(GRFStringID{buf.ReadWord()}, &indsp->name);
|
|
break;
|
|
|
|
case 0x20: // Prospecting success chance
|
|
indsp->prospecting_chance = buf.ReadDWord();
|
|
break;
|
|
|
|
case 0x21: // Callback mask
|
|
case 0x22: { // Callback additional mask
|
|
auto mask = indsp->callback_mask.base();
|
|
SB(mask, (prop - 0x21) * 8, 8, buf.ReadByte());
|
|
indsp->callback_mask = IndustryCallbackMasks{mask};
|
|
break;
|
|
}
|
|
|
|
case 0x23: // removal cost multiplier
|
|
indsp->removal_cost_multiplier = buf.ReadDWord();
|
|
break;
|
|
|
|
case 0x24: { // name for nearby station
|
|
GRFStringID str{buf.ReadWord()};
|
|
if (str == 0) {
|
|
indsp->station_name = STR_NULL;
|
|
} else {
|
|
AddStringForMapping(str, &indsp->station_name);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 0x25: { // variable length produced cargoes
|
|
uint8_t num_cargoes = buf.ReadByte();
|
|
if (num_cargoes > std::size(indsp->produced_cargo)) {
|
|
GRFError *error = DisableGrf(STR_NEWGRF_ERROR_LIST_PROPERTY_TOO_LONG);
|
|
error->param_value[1] = prop;
|
|
return CIR_DISABLED;
|
|
}
|
|
for (size_t i = 0; i < std::size(indsp->produced_cargo); i++) {
|
|
if (i < num_cargoes) {
|
|
CargoType cargo = GetCargoTranslation(buf.ReadByte(), _cur.grffile);
|
|
indsp->produced_cargo[i] = cargo;
|
|
} else {
|
|
indsp->produced_cargo[i] = INVALID_CARGO;
|
|
}
|
|
if (i < std::size(indsp->produced_cargo_label)) indsp->produced_cargo_label[i] = CT_INVALID;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 0x26: { // variable length accepted cargoes
|
|
uint8_t num_cargoes = buf.ReadByte();
|
|
if (num_cargoes > std::size(indsp->accepts_cargo)) {
|
|
GRFError *error = DisableGrf(STR_NEWGRF_ERROR_LIST_PROPERTY_TOO_LONG);
|
|
error->param_value[1] = prop;
|
|
return CIR_DISABLED;
|
|
}
|
|
for (size_t i = 0; i < std::size(indsp->accepts_cargo); i++) {
|
|
if (i < num_cargoes) {
|
|
CargoType cargo = GetCargoTranslation(buf.ReadByte(), _cur.grffile);
|
|
indsp->accepts_cargo[i] = cargo;
|
|
} else {
|
|
indsp->accepts_cargo[i] = INVALID_CARGO;
|
|
}
|
|
if (i < std::size(indsp->accepts_cargo_label)) indsp->accepts_cargo_label[i] = CT_INVALID;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 0x27: { // variable length production rates
|
|
uint8_t num_cargoes = buf.ReadByte();
|
|
if (num_cargoes > lengthof(indsp->production_rate)) {
|
|
GRFError *error = DisableGrf(STR_NEWGRF_ERROR_LIST_PROPERTY_TOO_LONG);
|
|
error->param_value[1] = prop;
|
|
return CIR_DISABLED;
|
|
}
|
|
for (uint i = 0; i < lengthof(indsp->production_rate); i++) {
|
|
if (i < num_cargoes) {
|
|
indsp->production_rate[i] = buf.ReadByte();
|
|
} else {
|
|
indsp->production_rate[i] = 0;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 0x28: { // variable size input/output production multiplier table
|
|
uint8_t num_inputs = buf.ReadByte();
|
|
uint8_t num_outputs = buf.ReadByte();
|
|
if (num_inputs > std::size(indsp->accepts_cargo) || num_outputs > std::size(indsp->produced_cargo)) {
|
|
GRFError *error = DisableGrf(STR_NEWGRF_ERROR_LIST_PROPERTY_TOO_LONG);
|
|
error->param_value[1] = prop;
|
|
return CIR_DISABLED;
|
|
}
|
|
for (size_t i = 0; i < std::size(indsp->accepts_cargo); i++) {
|
|
for (size_t j = 0; j < std::size(indsp->produced_cargo); j++) {
|
|
uint16_t mult = 0;
|
|
if (i < num_inputs && j < num_outputs) mult = buf.ReadWord();
|
|
indsp->input_cargo_multiplier[i][j] = mult;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 0x29: // Badge list
|
|
indsp->badges = ReadBadgeList(buf, GSF_INDUSTRIES);
|
|
break;
|
|
|
|
default:
|
|
ret = CIR_UNKNOWN;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
template <> ChangeInfoResult GrfChangeInfoHandler<GSF_INDUSTRYTILES>::Reserve(uint, uint, int, ByteReader &) { return CIR_UNHANDLED; }
|
|
template <> ChangeInfoResult GrfChangeInfoHandler<GSF_INDUSTRYTILES>::Activation(uint first, uint last, int prop, ByteReader &buf) { return IndustrytilesChangeInfo(first, last, prop, buf); }
|
|
|
|
template <> ChangeInfoResult GrfChangeInfoHandler<GSF_INDUSTRIES>::Reserve(uint, uint, int, ByteReader &) { return CIR_UNHANDLED; }
|
|
template <> ChangeInfoResult GrfChangeInfoHandler<GSF_INDUSTRIES>::Activation(uint first, uint last, int prop, ByteReader &buf) { return IndustriesChangeInfo(first, last, prop, buf); }
|