@@ -3083,6 +3083,10 @@ static ChangeInfoResult IgnoreIndustryTileProperty(int prop, ByteReader *buf)
buf->ReadWord();
break;

case 0x13:
buf->Skip(buf->ReadByte() * 2);
break;

default:
ret = CIR_UNKNOWN;
break;
@@ -3172,7 +3176,7 @@ static ChangeInfoResult IndustrytilesChangeInfo(uint indtid, int numinfo, int pr
case 0x0C: {
uint16 acctp = buf->ReadWord();
tsp->accepts_cargo[prop - 0x0A] = GetCargoTranslation(GB(acctp, 0, 8), _cur.grffile);
tsp->acceptance[prop - 0x0A] = GB(acctp, 8, 8);
tsp->acceptance[prop - 0x0A] = Clamp(GB(acctp, 8, 8), 0, 16);
break;
}

@@ -3201,6 +3205,26 @@ static ChangeInfoResult IndustrytilesChangeInfo(uint indtid, int numinfo, int pr
tsp->special_flags = (IndustryTileSpecialFlags)buf->ReadByte();
break;

case 0x13: { // variable length cargo acceptance
byte num_cargoes = buf->ReadByte();
This conversation was marked as resolved by nielsmh

This comment has been minimized.

@frosch123

frosch123 Nov 3, 2018
Member

DisableGrf(..) if num_cargoes > lengthof(tsp->acceptance)

if (num_cargoes > lengthof(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 < lengthof(tsp->acceptance); i++) {
if (i < num_cargoes) {
tsp->accepts_cargo[i] = GetCargoTranslation(buf->ReadByte(), _cur.grffile);
/* Tile acceptance can be negative to counteract the INDTILE_SPECIAL_ACCEPTS_ALL_CARGO flag */
tsp->acceptance[i] = (int8)buf->ReadByte();
} else {
tsp->accepts_cargo[i] = CT_INVALID;
tsp->acceptance[i] = 0;
}
}
break;
}

default:
ret = CIR_UNKNOWN;
break;
@@ -3280,11 +3304,17 @@ static ChangeInfoResult IgnoreIndustryProperty(int prop, ByteReader *buf)
for (byte j = 0; j < 3; j++) buf->ReadByte();
break;

case 0x15: {
byte number_of_sounds = buf->ReadByte();
for (uint8 j = 0; j < number_of_sounds; j++) {
buf->ReadByte();
}
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;
}

@@ -3642,6 +3672,77 @@ static ChangeInfoResult IndustriesChangeInfo(uint indid, int numinfo, int prop,
break;
}

case 0x25: { // variable length produced cargoes
byte num_cargoes = buf->ReadByte();
if (num_cargoes > lengthof(indsp->produced_cargo)) {
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->produced_cargo); i++) {
if (i < num_cargoes) {
CargoID cargo = GetCargoTranslation(buf->ReadByte(), _cur.grffile);
indsp->produced_cargo[i] = cargo;
} else {
indsp->produced_cargo[i] = CT_INVALID;
}
}
break;
}

case 0x26: { // variable length accepted cargoes
byte num_cargoes = buf->ReadByte();
if (num_cargoes > lengthof(indsp->accepts_cargo)) {
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->accepts_cargo); i++) {
if (i < num_cargoes) {
CargoID cargo = GetCargoTranslation(buf->ReadByte(), _cur.grffile);
indsp->accepts_cargo[i] = cargo;
} else {
indsp->accepts_cargo[i] = CT_INVALID;
}
}
break;
}

case 0x27: { // variable length production rates
byte 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
byte num_inputs = buf->ReadByte();
byte num_outputs = buf->ReadByte();
if (num_inputs > lengthof(indsp->accepts_cargo) || num_outputs > lengthof(indsp->produced_cargo)) {
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->accepts_cargo); i++) {
for (uint j = 0; j < lengthof(indsp->produced_cargo); j++) {
uint16 mult = 0;
if (i < num_inputs && j < num_outputs) mult = buf->ReadWord();
indsp->input_cargo_multiplier[i][j] = mult;
}
}
break;
}

default:
ret = CIR_UNKNOWN;
break;
@@ -4839,7 +4940,7 @@ static void NewSpriteGroup(ByteReader *buf)
}

case GSF_INDUSTRIES: {
if (type > 1) {
if (type > 2) {
grfmsg(1, "NewSpriteGroup: Unsupported industry production version %d, skipping", type);
break;
}
@@ -4849,21 +4950,63 @@ static void NewSpriteGroup(ByteReader *buf)
act_group = group;
group->version = type;
if (type == 0) {
group->num_input = 3;
for (uint i = 0; i < 3; i++) {
group->subtract_input[i] = (int16)buf->ReadWord(); // signed
}
group->num_output = 2;
for (uint i = 0; i < 2; i++) {
group->add_output[i] = buf->ReadWord(); // unsigned
}
group->again = buf->ReadByte();
} else {
} else if (type == 1) {
group->num_input = 3;
for (uint i = 0; i < 3; i++) {
group->subtract_input[i] = buf->ReadByte();
}
group->num_output = 2;
for (uint i = 0; i < 2; i++) {
group->add_output[i] = buf->ReadByte();
}
group->again = buf->ReadByte();
} else if (type == 2) {
group->num_input = buf->ReadByte();
if (group->num_input > lengthof(group->subtract_input)) {
GRFError *error = DisableGrf(STR_NEWGRF_ERROR_INDPROD_CALLBACK);
error->data = stredup("too many inputs (max 16)");
return;
}
for (uint i = 0; i < group->num_input; i++) {
byte rawcargo = buf->ReadByte();
CargoID cargo = GetCargoTranslation(rawcargo, _cur.grffile);
if (std::find(group->cargo_input, group->cargo_input + i, cargo) != group->cargo_input + i) {
GRFError *error = DisableGrf(STR_NEWGRF_ERROR_INDPROD_CALLBACK);
error->data = stredup("duplicate input cargo");
return;
}
group->cargo_input[i] = cargo;
group->subtract_input[i] = buf->ReadByte();
}
group->num_output = buf->ReadByte();
if (group->num_output > lengthof(group->add_output)) {
GRFError *error = DisableGrf(STR_NEWGRF_ERROR_INDPROD_CALLBACK);
error->data = stredup("too many outputs (max 16)");
return;
}
for (uint i = 0; i < group->num_output; i++) {
byte rawcargo = buf->ReadByte();
CargoID cargo = GetCargoTranslation(rawcargo, _cur.grffile);
if (std::find(group->cargo_output, group->cargo_output + i, cargo) != group->cargo_output + i) {
GRFError *error = DisableGrf(STR_NEWGRF_ERROR_INDPROD_CALLBACK);
error->data = stredup("duplicate output cargo");
return;
}
group->cargo_output[i] = cargo;
group->add_output[i] = buf->ReadByte();
}
group->again = buf->ReadByte();
} else {
NOT_REACHED();
}
break;
}
@@ -304,6 +304,33 @@ static uint32 GetCountAndDistanceOfClosestInstance(byte param_setID, byte layout
return GetCountAndDistanceOfClosestInstance(parameter, layout_filter, town_filter, this->industry);
}

case 0x69:
case 0x6A:
case 0x6B:
case 0x6C:
case 0x6D: {
CargoID cargo = GetCargoTranslation(parameter, this->ro.grffile);
int index = this->industry->GetCargoProducedIndex(cargo);
if (index < 0) return 0; // invalid cargo
if (variable == 0x69) return this->industry->produced_cargo_waiting[index];
if (variable == 0x6A) return this->industry->this_month_production[index];
if (variable == 0x6B) return this->industry->this_month_transported[index];
if (variable == 0x6C) return this->industry->last_month_production[index];
if (variable == 0x6D) return this->industry->last_month_transported[index];
NOT_REACHED();
}


case 0x6E:
case 0x6F: {
CargoID cargo = GetCargoTranslation(parameter, this->ro.grffile);
int index = this->industry->GetCargoAcceptedIndex(cargo);
if (index < 0) return 0; // invalid cargo
if (variable == 0x6E) return this->industry->last_cargo_accepted_at[index];
if (variable == 0x6F) return this->industry->incoming_cargo_waiting[index];
NOT_REACHED();
}

/* Get a variable from the persistent storage */
case 0x7C: return (this->industry->psa != NULL) ? this->industry->psa->GetValue(parameter) : 0;

@@ -364,7 +391,10 @@ static uint32 GetCountAndDistanceOfClosestInstance(byte param_setID, byte layout

case 0xB0: return Clamp(this->industry->construction_date - DAYS_TILL_ORIGINAL_BASE_YEAR, 0, 65535); // Date when built since 1920 (in days)
case 0xB3: return this->industry->construction_type; // Construction type
case 0xB4: return Clamp(this->industry->last_cargo_accepted_at - DAYS_TILL_ORIGINAL_BASE_YEAR, 0, 65535); // Date last cargo accepted since 1920 (in days)
case 0xB4: {
Date *latest = std::max_element(this->industry->last_cargo_accepted_at, endof(this->industry->last_cargo_accepted_at));
return Clamp((*latest) - DAYS_TILL_ORIGINAL_BASE_YEAR, 0, 65535); // Date last cargo accepted since 1920 (in days)
}
}

DEBUG(grf, 1, "Unhandled industry variable 0x%X", variable);
@@ -575,13 +605,28 @@ void IndustryProductionCallback(Industry *ind, int reason)
if (tgroup == NULL || tgroup->type != SGT_INDUSTRY_PRODUCTION) break;
const IndustryProductionSpriteGroup *group = (const IndustryProductionSpriteGroup *)tgroup;

bool deref = (group->version == 1);
bool deref = (group->version >= 1);

for (uint i = 0; i < 3; i++) {
ind->incoming_cargo_waiting[i] = Clamp(ind->incoming_cargo_waiting[i] - DerefIndProd(group->subtract_input[i], deref) * multiplier, 0, 0xFFFF);
}
for (uint i = 0; i < 2; i++) {
ind->produced_cargo_waiting[i] = Clamp(ind->produced_cargo_waiting[i] + max(DerefIndProd(group->add_output[i], deref), 0) * multiplier, 0, 0xFFFF);
if (group->version < 2) {
This conversation was marked as resolved by nielsmh

This comment has been minimized.

@frosch123

frosch123 Jul 27, 2018
Member

The comments here are misleading.
This "if" is about whether group->cargo_input/output is valid and shall be used.
Even though the number of cargos are known for version < 2, I would still use num_input/output there, since they are set in newgrf.cpp.

This conversation was marked as resolved by nielsmh

This comment has been minimized.

@nielsmh

nielsmh Oct 7, 2018
Author Contributor

Thinking about this again, maybe there should still be a choice between direct/indirect cargo types specification here. I think @andythenorth talked about the indirection being a bother and not useful?

One idea would be to let the lowest bit specify whether indirection is used, and the next whether long cargo lists are used, such that:

  • v0 = direct cargo spec, short lists
  • v1 = indirect cargo spec, short lists
  • v2 = direct cargo spec, long lists
  • v3 = indirect cargo spec, long lists.

This comment has been minimized.

@nielsmh

nielsmh Oct 27, 2018
Author Contributor

Talked to Andy about this, no change to the spec/code is needed here.

/* Callback parameters map directly to industry cargo slot indices */
for (uint i = 0; i < group->num_input; i++) {
ind->incoming_cargo_waiting[i] = Clamp(ind->incoming_cargo_waiting[i] - DerefIndProd(group->subtract_input[i], deref) * multiplier, 0, 0xFFFF);
}
for (uint i = 0; i < group->num_output; i++) {
ind->produced_cargo_waiting[i] = Clamp(ind->produced_cargo_waiting[i] + max(DerefIndProd(group->add_output[i], deref), 0) * multiplier, 0, 0xFFFF);
}
} else {
/* Callback receives list of cargos to apply for, which need to have their cargo slots in industry looked up */
for (uint i = 0; i < group->num_input; i++) {
int cargo_index = ind->GetCargoAcceptedIndex(group->cargo_input[i]);
if (cargo_index < 0) continue;
ind->incoming_cargo_waiting[cargo_index] = Clamp(ind->incoming_cargo_waiting[cargo_index] - DerefIndProd(group->subtract_input[i], deref) * multiplier, 0, 0xFFFF);
}
for (uint i = 0; i < group->num_output; i++) {
int cargo_index = ind->GetCargoProducedIndex(group->cargo_output[i]);
if (cargo_index < 0) continue;
ind->produced_cargo_waiting[cargo_index] = Clamp(ind->produced_cargo_waiting[cargo_index] + max(DerefIndProd(group->add_output[i], deref), 0) * multiplier, 0, 0xFFFF);
}
}

int32 again = DerefIndProd(group->again, deref);
@@ -602,12 +647,7 @@ void IndustryProductionCallback(Industry *ind, int reason)
*/
bool IndustryTemporarilyRefusesCargo(Industry *ind, CargoID cargo_type)
{
assert(
cargo_type == ind->accepts_cargo[0] || cargo_type == ind->accepts_cargo[1] || cargo_type == ind->accepts_cargo[2] || cargo_type == ind->accepts_cargo[3] ||
cargo_type == ind->accepts_cargo[4] || cargo_type == ind->accepts_cargo[5] || cargo_type == ind->accepts_cargo[6] || cargo_type == ind->accepts_cargo[7] ||
cargo_type == ind->accepts_cargo[8] || cargo_type == ind->accepts_cargo[9] || cargo_type == ind->accepts_cargo[10] || cargo_type == ind->accepts_cargo[11] ||
cargo_type == ind->accepts_cargo[12] || cargo_type == ind->accepts_cargo[13] || cargo_type == ind->accepts_cargo[14] || cargo_type == ind->accepts_cargo[15]
);
assert(std::find(ind->accepts_cargo, endof(ind->accepts_cargo), cargo_type) != endof(ind->accepts_cargo));

const IndustrySpec *indspec = GetIndustrySpec(ind->type);
if (HasBit(indspec->callback_mask, CBM_IND_REFUSE_CARGO)) {
@@ -15,6 +15,7 @@
#include "town_type.h"
#include "engine_type.h"
#include "house_type.h"
#include "industry_type.h"

#include "newgrf_callbacks.h"
#include "newgrf_generic.h"
@@ -277,9 +278,14 @@ struct IndustryProductionSpriteGroup : SpriteGroup {
IndustryProductionSpriteGroup() : SpriteGroup(SGT_INDUSTRY_PRODUCTION) {}

uint8 version;
int16 subtract_input[3]; // signed
uint16 add_output[2]; // unsigned
uint8 num_input; ///< How many subtract_input values are valid
int16 subtract_input[INDUSTRY_NUM_INPUTS]; ///< Take this much of the input cargo (can be negative, is indirect in cb version 1+)
CargoID cargo_input[INDUSTRY_NUM_INPUTS]; ///< Which input cargoes to take from (only cb version 2)
uint8 num_output; ///< How many add_output values are valid
uint16 add_output[INDUSTRY_NUM_OUTPUTS]; ///< Add this much output cargo when successful (unsigned, is indirect in cb version 1+)
CargoID cargo_output[INDUSTRY_NUM_OUTPUTS]; ///< Which output cargoes to add to (only cb version 2)
uint8 again;

};

/**
@@ -3033,6 +3033,15 @@ bool AfterLoadGame()
i->accepts_cargo[ci] = CT_INVALID;
i->incoming_cargo_waiting[ci] = 0;
}
/* Make sure last_cargo_accepted_at is copied to elements for every valid input cargo.
* The loading routine should put the original singular value into the first array element. */
for (size_t ci = 0; ci < lengthof(i->accepts_cargo); ci++) {
if (i->accepts_cargo[ci] != CT_INVALID) {
i->last_cargo_accepted_at[ci] = i->last_cargo_accepted_at[0];
} else {
i->last_cargo_accepted_at[ci] = 0;
}
}
}
}

@@ -61,7 +61,8 @@ static const SaveLoad _industry_desc[] = {
SLE_CONDVAR(Industry, founder, SLE_UINT8, 70, SL_MAX_VERSION),
SLE_CONDVAR(Industry, construction_date, SLE_INT32, 70, SL_MAX_VERSION),
SLE_CONDVAR(Industry, construction_type, SLE_UINT8, 70, SL_MAX_VERSION),
SLE_CONDVAR(Industry, last_cargo_accepted_at, SLE_INT32, 70, SL_MAX_VERSION),
SLE_CONDVAR(Industry, last_cargo_accepted_at[0], SLE_INT32, 70, 201),
SLE_CONDARR(Industry, last_cargo_accepted_at, SLE_INT32, 16, 202, SL_MAX_VERSION),
SLE_CONDVAR(Industry, selected_layout, SLE_UINT8, 73, SL_MAX_VERSION),

SLEG_CONDARR(_old_ind_persistent_storage.storage, SLE_UINT32, 16, 76, 160),
@@ -384,12 +384,12 @@ bool FindSubsidyIndustryCargoRoute()

/* Randomize cargo type */
int num_cargos = 0;
for (size_t ci = 0; ci < lengthof(src_ind->produced_cargo); ci++) {
if (src_ind->produced_cargo[ci] != CT_INVALID) num_cargos++;
uint cargo_index;
for (cargo_index = 0; cargo_index < lengthof(src_ind->produced_cargo); cargo_index++) {
if (src_ind->produced_cargo[cargo_index] != CT_INVALID) num_cargos++;

This comment has been minimized.

@nielsmh

nielsmh Oct 7, 2018
Author Contributor

Why such a weird algorithm to select a random cargo type. Could as well add valid types to a std::vector and select an element from that.

}
if (num_cargos == 0) return false; // industry produces nothing
int cargo_num = RandomRange(num_cargos) + 1;
int cargo_index;
for (cargo_index = 0; cargo_index < lengthof(src_ind->produced_cargo); cargo_index++) {

This comment has been minimized.

@nielsmh

nielsmh Jul 25, 2018
Author Contributor

This algorithm feels overly complicated. Does it seem right?

This comment has been minimized.

@frosch123

frosch123 Jul 25, 2018
Member

"ci" and "cargo_index" for the same thing is a bit weird.
But otherwise I see nothing wrong.

This comment has been minimized.

@frosch123

frosch123 Jul 27, 2018
Member

warning: comparison between signed and unsigned integer expressions [-Wsign-compare]

if (src_ind->produced_cargo[cargo_index] != CT_INVALID) cargo_num--;
if (cargo_num == 0) break;
@@ -272,11 +272,38 @@ static const NIFeature _nif_industrytile = {
/*** NewGRF industries ***/

static const NIProperty _nip_industries[] = {
NIP(0x10, Industry, produced_cargo[0], NIT_CARGO, "produced cargo 0"),
NIP(0x10, Industry, produced_cargo[1], NIT_CARGO, "produced cargo 1"),
NIP(0x11, Industry, accepts_cargo[0], NIT_CARGO, "accepted cargo 0"),
NIP(0x11, Industry, accepts_cargo[1], NIT_CARGO, "accepted cargo 1"),
NIP(0x11, Industry, accepts_cargo[2], NIT_CARGO, "accepted cargo 2"),
NIP(0x25, Industry, produced_cargo[ 0], NIT_CARGO, "produced cargo 0"),
NIP(0x25, Industry, produced_cargo[ 1], NIT_CARGO, "produced cargo 1"),
NIP(0x25, Industry, produced_cargo[ 2], NIT_CARGO, "produced cargo 2"),
NIP(0x25, Industry, produced_cargo[ 3], NIT_CARGO, "produced cargo 3"),
NIP(0x25, Industry, produced_cargo[ 4], NIT_CARGO, "produced cargo 4"),
NIP(0x25, Industry, produced_cargo[ 5], NIT_CARGO, "produced cargo 5"),
NIP(0x25, Industry, produced_cargo[ 6], NIT_CARGO, "produced cargo 6"),
NIP(0x25, Industry, produced_cargo[ 7], NIT_CARGO, "produced cargo 7"),
NIP(0x25, Industry, produced_cargo[ 8], NIT_CARGO, "produced cargo 8"),
NIP(0x25, Industry, produced_cargo[ 9], NIT_CARGO, "produced cargo 9"),
NIP(0x25, Industry, produced_cargo[10], NIT_CARGO, "produced cargo 10"),
NIP(0x25, Industry, produced_cargo[11], NIT_CARGO, "produced cargo 11"),
NIP(0x25, Industry, produced_cargo[12], NIT_CARGO, "produced cargo 12"),
NIP(0x25, Industry, produced_cargo[13], NIT_CARGO, "produced cargo 13"),
NIP(0x25, Industry, produced_cargo[14], NIT_CARGO, "produced cargo 14"),
NIP(0x25, Industry, produced_cargo[15], NIT_CARGO, "produced cargo 15"),
NIP(0x26, Industry, accepts_cargo[ 0], NIT_CARGO, "accepted cargo 0"),
NIP(0x26, Industry, accepts_cargo[ 1], NIT_CARGO, "accepted cargo 1"),
NIP(0x26, Industry, accepts_cargo[ 2], NIT_CARGO, "accepted cargo 2"),
NIP(0x26, Industry, accepts_cargo[ 3], NIT_CARGO, "accepted cargo 3"),
NIP(0x26, Industry, accepts_cargo[ 4], NIT_CARGO, "accepted cargo 4"),
NIP(0x26, Industry, accepts_cargo[ 5], NIT_CARGO, "accepted cargo 5"),
NIP(0x26, Industry, accepts_cargo[ 6], NIT_CARGO, "accepted cargo 6"),
NIP(0x26, Industry, accepts_cargo[ 7], NIT_CARGO, "accepted cargo 7"),
NIP(0x26, Industry, accepts_cargo[ 8], NIT_CARGO, "accepted cargo 8"),
NIP(0x26, Industry, accepts_cargo[ 9], NIT_CARGO, "accepted cargo 9"),
NIP(0x26, Industry, accepts_cargo[10], NIT_CARGO, "accepted cargo 10"),
NIP(0x26, Industry, accepts_cargo[11], NIT_CARGO, "accepted cargo 11"),
NIP(0x26, Industry, accepts_cargo[12], NIT_CARGO, "accepted cargo 12"),
NIP(0x26, Industry, accepts_cargo[13], NIT_CARGO, "accepted cargo 13"),
NIP(0x26, Industry, accepts_cargo[14], NIT_CARGO, "accepted cargo 14"),
NIP(0x26, Industry, accepts_cargo[15], NIT_CARGO, "accepted cargo 15"),
NIP_END()
};