diff --git a/src/newgrf.cpp b/src/newgrf.cpp index e835a3e834..8de6063645 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -41,6 +41,7 @@ #include "cargotype.h" #include "industry.h" #include "newgrf_canal.h" +#include "table/build_industry.h" #include "newgrf_commons.h" #include "newgrf_townname.h" @@ -1677,6 +1678,363 @@ static bool SoundEffectChangeInfo(uint sid, int numinfo, int prop, byte **bufp, return ret; } +static bool IndustrytilesChangeInfo(uint indtid, int numinfo, int prop, byte **bufp, int len) +{ + byte *buf = *bufp; + bool ret = false; + + if (indtid + numinfo > NUM_INDUSTRYTILES) { + grfmsg(1, "IndustryTilesChangeInfo: Too many industry tiles loaded (%u), max (%u). Ignoring.", indtid + numinfo, NUM_INDUSTRYTILES); + return false; + } + + /* Allocate industry tile specs if they haven't been allocated already. */ + if (_cur_grffile->indtspec == NULL) { + _cur_grffile->indtspec = CallocT(NUM_INDUSTRYTILES); + + /* Reset any overrides that have been set. */ + _industile_mngr.ResetOverride(); + } + + for (int i = 0; i < numinfo; i++) { + IndustryTileSpec *tsp = _cur_grffile->indtspec[indtid + i]; + + if (prop != 0x08 && tsp == NULL) { + grfmsg(2, "IndustryTilesChangeInfo: Attempt to modify undefined industry tile %u. Ignoring.", indtid + i); + continue; + } + + switch (prop) { + case 0x08: { // Substitute industry tile type + IndustryTileSpec **tilespec = &_cur_grffile->indtspec[indtid + i]; + byte subs_id = grf_load_byte(&buf); + + if (subs_id == 0xFF) { + /* Instead of defining a new industry, a substitute industry id + * of 0xFF disables the old industry with the current id. */ + tsp->enabled = false; + continue; + } else 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 %u as substitute industry tile for %u. Ignoring.", subs_id, indtid + i); + return false; + } + + /* Allocate space for this industry. */ + if (*tilespec == NULL) { + int tempid; + *tilespec = CallocT(1); + tsp = *tilespec; + + memcpy(tsp, &_industry_tile_specs[subs_id], sizeof(_industry_tile_specs[subs_id])); + tsp->enabled = true; + tsp->grf_prop.local_id = indtid + i; + tsp->grf_prop.subst_id = subs_id; + tsp->grf_prop.grffile = _cur_grffile; + tempid = _industile_mngr.AddEntityID(indtid + i, _cur_grffile->grfid, subs_id); // pre-reserve the tile slot + } + } break; + + case 0x09: { // Industry tile override + byte ovrid = grf_load_byte(&buf); + + /* The industry being overridden must be an original industry. */ + if (ovrid >= NEW_INDUSTRYTILEOFFSET) { + grfmsg(2, "IndustryTilesChangeInfo: Attempt to override new industry tile %u with industry tile id %u. Ignoring.", ovrid, indtid + i); + return false; + } + + tsp->grf_prop.override = ovrid; + _industile_mngr.Add(indtid + i, ovrid); + } break; + + case 0x0A: // Tile acceptance + case 0x0B: + case 0x0C: { + uint16 acctp = grf_load_word(&buf); + tsp->accepts_cargo[prop - 0x0A] = GetCargoTranslation(GB(acctp, 0, 8), _cur_grffile); + tsp->acceptance[prop - 0x0A] = GetCargoTranslation(GB(acctp, 8, 8), _cur_grffile); + } break; + + case 0x0D: // Land shape flags + tsp->slopes_refused = (Slope)grf_load_byte(&buf); + break; + + case 0x0E: // Callback flags + tsp->callback_flags = grf_load_byte(&buf); + break; + + case 0x0F: // Animation information + grf_load_word(&buf); // TODO + break; + + case 0x10: // Animation speed + grf_load_byte(&buf); // TODO + break; + + case 0x11: // Triggers for callback 25 + grf_load_byte(&buf); // TODO + break; + + case 0x12: // Special flags + grf_load_byte(&buf); // TODO + break; + + default: + ret = true; + break; + } + } + + *bufp = buf; + return ret; +} + +static bool IndustriesChangeInfo(uint indid, int numinfo, int prop, byte **bufp, int len) +{ + byte *buf = *bufp; + bool ret = false; + + if (indid + numinfo > NUM_INDUSTRYTYPES) { + grfmsg(1, "IndustriesChangeInfo: Too many industries loaded (%u), max (%u). Ignoring.", indid + numinfo, NUM_INDUSTRYTYPES); + return false; + } + + grfmsg(1, "IndustriesChangeInfo: newid %u", indid); + + /* Allocate industry specs if they haven't been allocated already. */ + if (_cur_grffile->industryspec == NULL) { + _cur_grffile->industryspec = CallocT(NUM_INDUSTRYTYPES); + + /* Reset any overrides that have been set. */ + _industry_mngr.ResetOverride(); + } + + for (int i = 0; i < numinfo; i++) { + IndustrySpec *indsp = _cur_grffile->industryspec[indid + i]; + + if (prop != 0x08 && indsp == NULL) { + grfmsg(2, "IndustriesChangeInfo: Attempt to modify undefined industry %u. Ignoring.", indid + i); + continue; + } + + switch (prop) { + case 0x08: { // Substitute industry type + IndustrySpec **indspec = &_cur_grffile->industryspec[indid + i]; + byte subs_id = grf_load_byte(&buf); + + 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[indid + i].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 %u as substitute industry for %u. Ignoring.", subs_id, indid + i); + return false; + } + + /* Allocate space for this industry. + * Only need to do it once. If ever it is called again, it should not + * do anything */ + if (*indspec == NULL) { + *indspec = CallocT(1); + indsp = *indspec; + + memcpy(indsp, &_origin_industry_specs[subs_id], sizeof(_industry_specs[subs_id])); + indsp->enabled = true; + indsp->grf_prop.local_id = indid + i; + indsp->grf_prop.subst_id = subs_id; + indsp->grf_prop.grffile = _cur_grffile; + } + } break; + + case 0x09: { // Industry type override + byte ovrid = grf_load_byte(&buf); + + /* The industry being overridden must be an original industry. */ + if (ovrid >= NEW_INDUSTRYOFFSET) { + grfmsg(2, "IndustriesChangeInfo: Attempt to override new industry %u with industry id %u. Ignoring.", ovrid, indid + i); + return false; + } + indsp->grf_prop.override = ovrid; + _industry_mngr.Add(indid + i, ovrid); + } break; + + case 0x0A: { // Set industry layout(s) + indsp->num_table = grf_load_byte(&buf); // Number of layaouts + uint32 defsize = grf_load_dword(&buf); // Total size of the definition + IndustryTileTable **tile_table = CallocT(indsp->num_table); // Table with tiles to compose an industry + IndustryTileTable *itt = CallocT(defsize); // Temporary array to read the tile layouts from the GRF + int size; + IndustryTileTable *copy_from; + + for (byte j = 0; j < indsp->num_table; j++) { + for (int k = 0;; k++) { + itt[k].ti.x = grf_load_byte(&buf); // Offsets from northermost tile + + if (itt[k].ti.x == 0xFE && k == 0) { + /* This means we have to borrow the layout from an old industry */ + IndustryType type = grf_load_byte(&buf); //industry holding required layout + byte laynbr = grf_load_byte(&buf); //layout number to borrow + + copy_from = (IndustryTileTable*)_origin_industry_specs[type].table[laynbr]; + for (size = 1;; size++) { + if (_origin_industry_specs[type].table[laynbr + (size - 1)]->ti.x == -0x80 && + _origin_industry_specs[type].table[laynbr + (size - 1)]->ti.y == 0) break; + } + break; + } + + itt[k].ti.y = grf_load_byte(&buf); // Or table definition finalisation + + if (itt[k].ti.x == 0 && itt[k].ti.y == 0x80) { + /* Not the same terminator. The one we are using is rather + x= -80, y = x . So, adjust it. */ + itt[k].ti.x = -0x80; + itt[k].ti.y = 0; + itt[k].gfx = 0; + + size = k + 1; + copy_from = itt; + break; + } + + itt[k].gfx = grf_load_byte(&buf); + + if (itt[k].gfx == 0xFE) { + /* Use a new tile from this GRF */ + int local_tile_id = grf_load_word(&buf); + + /* 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 %u with industry id %u, not yet defined. Ignoring.", local_tile_id, indid); + } else { + /* Declared as been valid, can be used */ + itt[k].gfx = tempid; + size = k + 1; + copy_from = itt; + } + } + } + tile_table[j] = CallocT(size); + memcpy(tile_table[j], copy_from, sizeof(*copy_from) * size); + } + /* Install final layout construction in the industry spec */ + indsp->table = tile_table; + SETBIT(indsp->cleanup_flag, 1); + free(itt); + } break; + + case 0x0B: // Industry production flags + indsp->life_type = (IndustryLifeType)grf_load_byte(&buf); + break; + + case 0x0C: // Industry closure message + indsp->closure_text = MapGRFStringID(_cur_grffile->grfid, grf_load_word(&buf)); + break; + + case 0x0D: // Production increase message + indsp->production_up_text = MapGRFStringID(_cur_grffile->grfid, grf_load_word(&buf)); + break; + + case 0x0E: // Production decrease message + indsp->production_down_text = MapGRFStringID(_cur_grffile->grfid, grf_load_word(&buf)); + break; + + case 0x0F: // Fund cost multiplier + indsp->cost_multiplier = grf_load_byte(&buf); + break; + + case 0x10: // Production cargo types + for (byte j = 0; j < 2; j++) { + indsp->produced_cargo[j] = GetCargoTranslation(grf_load_byte(&buf), _cur_grffile); + } + break; + + case 0x11: // Acceptance cargo types + for (byte j = 0; j < 3; j++) { + indsp->accepts_cargo[j] = GetCargoTranslation(grf_load_byte(&buf), _cur_grffile); + } + grf_load_byte(&buf); // Unnused, eat it up + break; + + case 0x12: // Production multipliers + case 0x13: + indsp->production_rate[prop - 0x12] = grf_load_byte(&buf); + break; + + case 0x14: // Minimal amount of cargo distributed + indsp->minimal_cargo = grf_load_byte(&buf); + break; + + case 0x15: { // Random sound effects + indsp->number_of_sounds = grf_load_byte(&buf); + uint8 *sounds = MallocT(indsp->number_of_sounds); + + for (uint8 j = 0; j < indsp->number_of_sounds; j++) sounds[j] = grf_load_byte(&buf); + indsp->random_sounds = sounds; + SETBIT(indsp->cleanup_flag, 0); + } break; + + case 0x16: // Conflicting industry types + for (byte j = 0; j < 3; j++) indsp->conflicting[j] = grf_load_byte(&buf); + break; + + case 0x17: // Probability in random game + indsp->appear_ingame[_opt.landscape] = grf_load_byte(&buf); + break; + + case 0x18: // Probability during gameplay + indsp->appear_creation[_opt.landscape] = grf_load_byte(&buf); + break; + + case 0x19: // Map color + indsp->map_colour = MapDOSColour(grf_load_byte(&buf)); + break; + + case 0x1A: // Special industry flags to define special behavior + indsp->behaviour = (IndustyBehaviour)grf_load_dword(&buf); + break; + + case 0x1B: // New industry text ID + indsp->new_industry_text = MapGRFStringID(_cur_grffile->grfid, grf_load_word(&buf)); + break; + + case 0x1C: // Input cargo multipliers for the three input cargo types + case 0x1D: + case 0x1E: { + uint32 multiples = grf_load_dword(&buf); + indsp->input_cargo_multiplier[prop - 0x1C][0] = GB(multiples, 0,15); + indsp->input_cargo_multiplier[prop - 0x1C][1] = GB(multiples, 15,15); + } break; + + case 0x1F: // Industry name + indsp->name = MapGRFStringID(_cur_grffile->grfid, grf_load_word(&buf)); + break; + + case 0x20: // Prospecting success chance + indsp->prospecting_chance = grf_load_dword(&buf); + break; + + case 0x21: // Callback flags + case 0x22: { // Callback additional flags + byte aflag = grf_load_byte(&buf); + SB(indsp->callback_flags, (prop - 0x21) * 8, 8, aflag); + } break; + + default: + ret = true; + break; + } + } + + *bufp = buf; + return ret; +} + /* Action 0x00 */ static void FeatureChangeInfo(byte *buf, int len) { @@ -1705,8 +2063,8 @@ static void FeatureChangeInfo(byte *buf, int len) /* GSF_BRIDGE */ BridgeChangeInfo, /* GSF_TOWNHOUSE */ TownHouseChangeInfo, /* GSF_GLOBALVAR */ GlobalVarChangeInfo, - /* GSF_INDUSTRYTILES */NULL, - /* GSF_INDUSTRIES */ NULL, + /* GSF_INDUSTRYTILES */IndustrytilesChangeInfo, + /* GSF_INDUSTRIES */ IndustriesChangeInfo, /* GSF_CARGOS */ NULL, /* Cargo is handled during reservation */ /* GSF_SOUNDFX */ SoundEffectChangeInfo, }; @@ -2562,6 +2920,53 @@ static void TownHouseMapSpriteGroup(byte *buf, uint8 idcount, uint8 cidcount) } } +static void IndustryMapSpriteGroup(byte *buf, uint8 idcount, uint8 cidcount) +{ + byte *bp = &buf[4 + idcount + cidcount * 3]; + uint16 groupid = grf_load_word(&bp); + + if (groupid >= _cur_grffile->spritegroups_count || _cur_grffile->spritegroups[groupid] == NULL) { + grfmsg(1, "IndustryMapSpriteGroup: Spriteset 0x%04X out of range 0x%X or empty, skipping.", + groupid, _cur_grffile->spritegroups_count); + return; + } + + for (uint i = 0; i < idcount; i++) { + uint8 id = buf[3 + i]; + IndustrySpec *indsp = _cur_grffile->industryspec[id]; + + if (indsp == NULL) { + grfmsg(1, "IndustryMapSpriteGroup: Too many industries defined, skipping"); + return; + } + + indsp->grf_prop.spritegroup = _cur_grffile->spritegroups[groupid]; + } +} + +static void IndustrytileMapSpriteGroup(byte *buf, uint8 idcount, uint8 cidcount) +{ + byte *bp = &buf[4 + idcount + cidcount * 3]; + uint16 groupid = grf_load_word(&bp); + + if (groupid >= _cur_grffile->spritegroups_count || _cur_grffile->spritegroups[groupid] == NULL) { + grfmsg(1, "IndustrytileMapSpriteGroup: Spriteset 0x%04X out of range 0x%X or empty, skipping.", + groupid, _cur_grffile->spritegroups_count); + return; + } + + for (uint i = 0; i < idcount; i++) { + uint8 id = buf[3 + i]; + IndustryTileSpec *indtsp = _cur_grffile->indtspec[id]; + + if (indtsp == NULL) { + grfmsg(1, "IndustrytileMapSpriteGroup: Too many industry tiles defined, skipping"); + return; + } + + indtsp->grf_prop.spritegroup = _cur_grffile->spritegroups[groupid]; + } +} static void CargoMapSpriteGroup(byte *buf, uint8 idcount, uint8 cidcount) { @@ -2651,6 +3056,14 @@ static void FeatureMapSpriteGroup(byte *buf, int len) TownHouseMapSpriteGroup(buf, idcount, cidcount); return; + case GSF_INDUSTRIES: + IndustryMapSpriteGroup(buf, idcount, cidcount); + return; + + case GSF_INDUSTRYTILES: + IndustrytileMapSpriteGroup(buf, idcount, cidcount); + return; + case GSF_CARGOS: CargoMapSpriteGroup(buf, idcount, cidcount); return; @@ -4780,7 +5193,26 @@ static void FinaliseIndustriesArray() for (int i = 0; i < NUM_INDUSTRYTYPES; i++) { IndustrySpec *indsp = file->industryspec[i]; - if (indsp != NULL && indsp->enabled) { + if (indsp != NULL && indsp->enabled) { + StringID strid; + /* process the conversion of text at the end, so to be sure everything will be fine + * and available. Check if it does not return undefind marker, which is a very good sign of a + * substitute industry who has not changed the string been examined, thus using it as such */ + strid = GetGRFStringID(indsp->grf_prop.grffile->grfid, indsp->name); + if (strid != STR_UNDEFINED) indsp->name = strid; + + strid = GetGRFStringID(indsp->grf_prop.grffile->grfid, indsp->closure_text); + if (strid != STR_UNDEFINED) indsp->closure_text = strid; + + strid = GetGRFStringID(indsp->grf_prop.grffile->grfid, indsp->production_up_text); + if (strid != STR_UNDEFINED) indsp->production_up_text = strid; + + strid = GetGRFStringID(indsp->grf_prop.grffile->grfid, indsp->production_down_text); + if (strid != STR_UNDEFINED) indsp->production_down_text = strid; + + strid = GetGRFStringID(indsp->grf_prop.grffile->grfid, indsp->new_industry_text); + if (strid != STR_UNDEFINED) indsp->new_industry_text = strid; + _industry_mngr.SetEntitySpec(indsp); _loaded_newgrf_features.has_newindustries = true; }