diff options
Diffstat (limited to 'src/newgrf.cpp')
-rw-r--r-- | src/newgrf.cpp | 426 |
1 files changed, 416 insertions, 10 deletions
diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 09e63d6ee..7323a0107 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -18,6 +18,7 @@ #include "string.h" #include "table/strings.h" #include "bridge.h" +#include "town.h" #include "economy.h" #include "newgrf_engine.h" #include "vehicle.h" @@ -28,9 +29,11 @@ #include "currency.h" #include "sound.h" #include "newgrf_config.h" +#include "newgrf_house.h" #include "newgrf_sound.h" #include "newgrf_spritegroup.h" #include "helpers.hpp" +#include "table/town_land.h" #include "cargotype.h" /* TTDPatch extended GRF format codec @@ -68,6 +71,9 @@ static byte *_preload_sprite = NULL; /* Set if any vehicle is loaded which uses 2cc (two company colours) */ bool _have_2cc = false; +/* Set if there are any newhouses loaded. */ +bool _have_newhouses = false; + /* Default cargo translation table. By default there are 27 possible cargo types */ static const uint _default_cargo_max = 27; static CargoLabel _default_cargo_list[_default_cargo_max]; @@ -227,6 +233,32 @@ static GRFFile *GetFileByFilename(const char *filename) } +/** Used when setting an object's property to map to the GRF's strings + * while taking in consideration the "drift" between TTDPatch string system and OpenTTD's one + * @param str StringID that we want to have the equivalent in OoenTTD + * @return the properly adjusted StringID + */ +static StringID MapGRFStringID(StringID str) +{ + /* 0xD0 and 0xDC stand for all the TextIDs in the range + * of 0xD000 (misc graphics texts) and 0xDC00 (misc persistent texts). + * These strings are unique to each grf file, and thus require to be used with the + * grfid in which they are declared */ + if (GB(str, 8, 8) == 0xD0 || GB(str, 8, 8) == 0xDC) { + return GetGRFStringID(_cur_grffile->grfid, str); + } + + /* Map building names according to our lang file changes + * 0x200F = Tall Office Block, first house name in the original data, the one that TTDPatch stil uses + * 0x201F = Old houses is the last house name. + * OpenTTD does not have exactly the same order aymore, so, the code below allows + * to compensate for the difference */ + if (str >= 0x200F && str <= 0x201F) return str + (STR_200F_TALL_OFFICE_BLOCK - 0x200F); + + return str; +} + + typedef bool (*VCI_Handler)(uint engine, int numinfo, int prop, byte **buf, int len); #define FOR_EACH_OBJECT for (i = 0; i < numinfo; i++) @@ -1168,6 +1200,206 @@ static bool BridgeChangeInfo(uint brid, int numinfo, int prop, byte **bufp, int return ret; } +static bool TownHouseChangeInfo(uint hid, int numinfo, int prop, byte **bufp, int len) +{ + HouseSpec **housespec; + byte *buf = *bufp; + int i; + bool ret = false; + + if (hid + numinfo >= HOUSE_MAX) { + grfmsg(1, "TownHouseChangeInfo: Too many houses loaded (%u), max (%u). Ignoring.", hid + numinfo, HOUSE_MAX-1); + return false; + } + + /* Allocate house specs if they haven't been allocated already. */ + if (_cur_grffile->housespec == NULL) { + _cur_grffile->housespec = CallocT<HouseSpec*>(HOUSE_MAX); + + /* Reset any overrides that have been set. */ + ResetHouseOverrides(); + } + + housespec = &_cur_grffile->housespec[hid]; + + if (prop != 0x08) { + /* Check that all the houses being modified have been defined. */ + FOR_EACH_OBJECT { + if (housespec[i] == NULL) { + grfmsg(2, "TownHouseChangeInfo: Attempt to modify undefined house %u. Ignoring.", hid + i); + return false; + } + } + } + + switch (prop) { + case 0x08: // Substitute building type, and definition of a new house + FOR_EACH_OBJECT { + byte subs_id = grf_load_byte(&buf); + + if (subs_id == 0xFF) { + /* Instead of defining a new house, a substitute house id + * of 0xFF disables the old house with the current id. */ + _house_specs[hid + i].enabled = false; + continue; + } else if (subs_id >= NEW_HOUSE_OFFSET) { + /* The substitute id must be one of the original houses. */ + grfmsg(2, "TownHouseChangeInfo: Attempt to use new house %u as substitute house for %u. Ignoring.", subs_id, hid + i); + return false; + } + + /* Allocate space for this house. */ + if (housespec[i] == NULL) housespec[i] = CallocT<HouseSpec>(1); + + memcpy(housespec[i], &_house_specs[subs_id], sizeof(_house_specs[subs_id])); + + housespec[i]->enabled = true; + housespec[i]->local_id = hid + i; + housespec[i]->substitute_id = subs_id; + housespec[i]->random_colour[0] = 0x04; // those 4 random colours are the base colour + housespec[i]->random_colour[1] = 0x08; // for all new houses + housespec[i]->random_colour[2] = 0x0C; // they stand for red, blue, orange and green + housespec[i]->random_colour[3] = 0x06; + + /* New houses do not (currently) expect to have a default start + * date before 1930, as this breaks the build date stuff. See + * FinaliseHouseArray() for more details. */ + if (housespec[i]->min_date < 1930) housespec[i]->min_date = 1930; + } + _have_newhouses = true; + break; + + case 0x09: // Building flags + FOR_EACH_OBJECT { + byte state = grf_load_byte(&buf); + housespec[i]->building_flags = (BuildingFlags)state; + } + break; + + case 0x0A: // Availability years + FOR_EACH_OBJECT { + uint16 years = grf_load_word(&buf); + housespec[i]->min_date = GB(years, 0, 8) > 150 ? MAX_YEAR : ORIGINAL_BASE_YEAR + GB(years, 0, 8); + housespec[i]->max_date = GB(years, 8, 8) > 150 ? MAX_YEAR : ORIGINAL_BASE_YEAR + GB(years, 8, 8); + } + break; + + case 0x0B: // Population + FOR_EACH_OBJECT housespec[i]->population = grf_load_byte(&buf); + break; + + case 0x0C: // Mail generation multiplier + FOR_EACH_OBJECT housespec[i]->mail_generation = grf_load_byte(&buf); + break; + + case 0x0D: // Passenger acceptance + FOR_EACH_OBJECT housespec[i]->passenger_acceptance = grf_load_byte(&buf); + break; + + case 0x0E: // Mail acceptance + FOR_EACH_OBJECT housespec[i]->mail_acceptance = grf_load_byte(&buf); + break; + + case 0x0F: // Goods, food or fizzy drinks acceptance + FOR_EACH_OBJECT { + int8 goods = grf_load_byte(&buf); + if (goods > 0) { + housespec[i]->goods_acceptance = goods; + } else { + housespec[i]->food_acceptance = -goods; + } + } + break; + + case 0x10: // Local authority rating decrease on removal + FOR_EACH_OBJECT housespec[i]->remove_rating_decrease = grf_load_word(&buf); + break; + + case 0x11: // Removal cost multiplier + FOR_EACH_OBJECT housespec[i]->removal_cost = grf_load_byte(&buf); + break; + + case 0x12: // Building name ID + FOR_EACH_OBJECT housespec[i]->building_name = MapGRFStringID(grf_load_word(&buf)); + break; + + case 0x13: // Building availability mask + FOR_EACH_OBJECT { + uint16 avail = grf_load_word(&buf); + housespec[i]->building_availability = (HouseZones)avail; + } + break; + + case 0x14: // House callback flags + FOR_EACH_OBJECT housespec[i]->callback_mask = grf_load_byte(&buf); + break; + + case 0x15: // House override byte + FOR_EACH_OBJECT { + byte override = grf_load_byte(&buf); + + /* The house being overridden must be an original house. */ + if (override >= NEW_HOUSE_OFFSET) { + grfmsg(2, "TownHouseChangeInfo: Attempt to override new house %u with house id %u. Ignoring.", override, hid); + return false; + } + + AddHouseOverride(hid, override); + } + break; + + case 0x16: // Periodic refresh multiplier + FOR_EACH_OBJECT housespec[i]->processing_time = grf_load_byte(&buf); + break; + + case 0x17: // Four random colours to use + FOR_EACH_OBJECT { + uint j; + for (j = 0; j < 4; j++) housespec[i]->random_colour[j] = grf_load_byte(&buf); + } + break; + + case 0x18: // Relative probability of appearing + FOR_EACH_OBJECT housespec[i]->probability = grf_load_byte(&buf); + break; + + case 0x19: // Extra flags + FOR_EACH_OBJECT { + byte flags = grf_load_byte(&buf); + housespec[i]->extra_flags = (HouseExtraFlags)flags; + } + break; + + case 0x1A: // Animation frames + FOR_EACH_OBJECT housespec[i]->animation_frames = grf_load_byte(&buf); + break; + + case 0x1B: // Animation speed + FOR_EACH_OBJECT housespec[i]->animation_speed = clamp(grf_load_byte(&buf), 2, 16); + break; + + case 0x1C: // Class of the building type + FOR_EACH_OBJECT housespec[i]->class_id = AllocateHouseClassID(grf_load_byte(&buf), _cur_grffile->grfid); + break; + + case 0x1D: // Callback flags 2 + FOR_EACH_OBJECT housespec[i]->callback_mask |= (grf_load_byte(&buf) << 8); + break; + + case 0x1E: // Accepted cargo types + FOR_EACH_OBJECT grf_load_dword(&buf); + ret = true; + break; + + default: + ret = true; + break; + } + + *bufp = buf; + return ret; +} + static bool GlobalVarChangeInfo(uint gvid, int numinfo, int prop, byte **bufp, int len) { byte *buf = *bufp; @@ -1375,7 +1607,7 @@ static void FeatureChangeInfo(byte *buf, int len) /* GSF_STATION */ StationChangeInfo, /* GSF_CANAL */ NULL, /* GSF_BRIDGE */ BridgeChangeInfo, - /* GSF_TOWNHOUSE */ NULL, + /* GSF_TOWNHOUSE */ TownHouseChangeInfo, /* GSF_GLOBALVAR */ GlobalVarChangeInfo, /* GSF_INDUSTRYTILES */NULL, /* GSF_INDUSTRIES */ NULL, @@ -1853,6 +2085,86 @@ static void NewSpriteGroup(byte *buf, int len) break; } + case GSF_TOWNHOUSE: { + byte sprites = _cur_grffile->spriteset_numents; + byte num_sprites = max((uint8)1, type); + uint i; + + group = AllocateSpriteGroup(); + group->type = SGT_TILELAYOUT; + group->g.layout.num_sprites = sprites; + group->g.layout.dts = CallocT<DrawTileSprites>(1); + + /* Groundsprite */ + group->g.layout.dts->ground_sprite = grf_load_word(&buf); + group->g.layout.dts->ground_pal = grf_load_word(&buf); + /* Remap transparent/colour modifier bits */ + if (HASBIT(group->g.layout.dts->ground_sprite, 14)) { + CLRBIT(group->g.layout.dts->ground_sprite, 14); + SETBIT(group->g.layout.dts->ground_sprite, PALETTE_MODIFIER_TRANSPARENT); + } + if (HASBIT(group->g.layout.dts->ground_sprite, 15)) { + CLRBIT(group->g.layout.dts->ground_sprite, 15); + SETBIT(group->g.layout.dts->ground_sprite, PALETTE_MODIFIER_COLOR); + } + if (HASBIT(group->g.layout.dts->ground_pal, 14)) { + CLRBIT(group->g.layout.dts->ground_pal, 14); + SETBIT(group->g.layout.dts->ground_sprite, SPRITE_MODIFIER_OPAQUE); + } + if (HASBIT(group->g.layout.dts->ground_pal, 15)) { + /* Bit 31 set means this is a custom sprite, so rewrite it to the + * last spriteset defined. */ + SpriteID sprite = _cur_grffile->spriteset_start + GB(group->g.layout.dts->ground_sprite, 0, 14) * sprites; + SB(group->g.layout.dts->ground_sprite, 0, SPRITE_WIDTH, sprite); + CLRBIT(group->g.layout.dts->ground_pal, 15); + } + + group->g.layout.dts->seq = CallocT<DrawTileSeqStruct>(num_sprites + 1); + + for (i = 0; i < num_sprites; i++) { + DrawTileSeqStruct *seq = (DrawTileSeqStruct*)&group->g.layout.dts->seq[i]; + + seq->image = grf_load_word(&buf); + seq->pal = grf_load_word(&buf); + seq->delta_x = grf_load_byte(&buf); + seq->delta_y = grf_load_byte(&buf); + + if (HASBIT(seq->image, 14)) { + CLRBIT(seq->image, 14); + SETBIT(seq->image, PALETTE_MODIFIER_TRANSPARENT); + } + if (HASBIT(seq->image, 15)) { + CLRBIT(seq->image, 15); + SETBIT(seq->image, PALETTE_MODIFIER_COLOR); + } + if (HASBIT(seq->pal, 14)) { + CLRBIT(seq->pal, 14); + SETBIT(seq->image, SPRITE_MODIFIER_OPAQUE); + } + if (HASBIT(seq->pal, 15)) { + /* Bit 31 set means this is a custom sprite, so rewrite it to the + * last spriteset defined. */ + SpriteID sprite = _cur_grffile->spriteset_start + GB(seq->image, 0, 14) * sprites; + SB(seq->image, 0, SPRITE_WIDTH, sprite); + CLRBIT(seq->pal, 15); + } + + if (type > 0) { + seq->delta_z = grf_load_byte(&buf); + if ((byte)seq->delta_z == 0x80) continue; + } + + seq->size_x = grf_load_byte(&buf); + seq->size_y = grf_load_byte(&buf); + seq->size_z = grf_load_byte(&buf); + } + + /* Set the terminator value. */ + ((DrawTileSeqStruct*)group->g.layout.dts->seq)[i].delta_x = (byte)0x80; + + break; + } + /* Loading of Tile Layout and Production Callback groups would happen here */ default: grfmsg(1, "NewSpriteGroup: Unsupported feature %d, skipping", feature); } @@ -1934,7 +2246,7 @@ static void FeatureMapSpriteGroup(byte *buf, int len) grfmsg(6, "FeatureMapSpriteGroup: Feature %d, %d ids, %d cids, wagon override %d", feature, idcount, cidcount, wagover); - if (feature > GSF_STATION) { + if (feature > GSF_STATION && feature != GSF_TOWNHOUSE) { grfmsg(1, "FeatureMapSpriteGroup: Unsupported feature %d, skipping", feature); return; } @@ -1986,6 +2298,29 @@ static void FeatureMapSpriteGroup(byte *buf, int len) } } return; + } else if (feature == GSF_TOWNHOUSE) { + 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, "FeatureMapSpriteGroup: 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 hid = buf[3 + i]; + HouseSpec *hs = _cur_grffile->housespec[hid]; + + if (hs == NULL) { + grfmsg(1, "FeatureMapSpriteGroup: Too many houses defined, skipping"); + return; + } + + hs->spritegroup = _cur_grffile->spritegroups[groupid]; + hs->grffile = _cur_grffile; + } + return; } // FIXME: Tropicset contains things like: @@ -2148,6 +2483,7 @@ static void FeatureNewName(byte *buf, int len) break; } + case GSF_TOWNHOUSE: default: switch (GB(id, 8, 8)) { case 0xC4: /* Station class name */ @@ -2167,7 +2503,15 @@ static void FeatureNewName(byte *buf, int len) } break; - case 0xC9: + case 0xC9: { /* House name */ + if (_cur_grffile->housespec == NULL || _cur_grffile->housespec[GB(id, 0, 8)] == NULL) { + grfmsg(1, "FeatureNewName: Attempt to name undefined house 0x%X, ignoring.", GB(id, 0, 8)); + } else { + _cur_grffile->housespec[GB(id, 0, 8)]->building_name = AddGRFString(_cur_grffile->grfid, id, lang, new_scheme, name, STR_UNDEFINED); + } + break; + } + case 0xD0: case 0xDC: AddGRFString(_cur_grffile->grfid, id, lang, new_scheme, name, STR_UNDEFINED); @@ -2182,7 +2526,6 @@ static void FeatureNewName(byte *buf, int len) #if 0 case GSF_CANAL : case GSF_BRIDGE : - case GSF_TOWNHOUSE : AddGRFString(_cur_spriteid, id, lang, name); switch (GB(id, 8,8)) { case 0xC9: /* House name */ @@ -3478,7 +3821,7 @@ static void InitializeGRFSpecial() | (1 << 0x16) // canals | (1 << 0x17) // newstartyear | (0 << 0x18) // freighttrains - | (0 << 0x19) // newhouses + | (1 << 0x19) // newhouses | (1 << 0x1A) // newbridges | (0 << 0x1B) // newtownnames | (0 << 0x1C) // moreanimations @@ -3550,6 +3893,20 @@ static void ResetCustomStations() } } +static void ResetCustomHouses() +{ + GRFFile *file; + uint i; + + for (file = _first_grffile; file != NULL; file = file->next) { + if (file->housespec == NULL) continue; + for (i = 0; i < HOUSE_MAX; i++) free(file->housespec[i]); + + free(file->housespec); + file->housespec = NULL; + } +} + static void ResetNewGRF() { GRFFile *next; @@ -3610,6 +3967,10 @@ static void ResetNewGRFData() /* Reset the curencies array */ ResetCurrencies(); + /* Reset the house array */ + ResetCustomHouses(); + ResetHouses(); + // Reset station classes ResetStationClasses(); ResetCustomStations(); @@ -3635,6 +3996,7 @@ static void ResetNewGRFData() _traininfo_vehicle_pitch = 0; _traininfo_vehicle_width = 29; _have_2cc = false; + _have_newhouses = false; _signal_base = 0; _coast_base = 0; @@ -3838,6 +4200,41 @@ static void CalculateRefitMasks() } } +/** Add all new houses to the house array. House properties can be set at any + * time in the GRF file, so we can only add a house spec to the house array + * after the file has finished loading. We also need to check the dates, due to + * the TTDPatch behaviour described below that we need to emulate. */ +static void FinaliseHouseArray() +{ + /* If there are no houses with start dates before 1930, then all houses + * with start dates of 1930 have them reset to 0. This is in order to be + * compatible with TTDPatch, where if no houses have start dates before + * 1930 and the date is before 1930, the game pretends that this is 1930. + * If there have been any houses defined with start dates before 1930 then + * the dates are left alone. */ + bool reset_dates = true; + + for (GRFFile *file = _first_grffile; file != NULL; file = file->next) { + if (file->housespec == NULL) continue; + + for (int i = 0; i < HOUSE_MAX; i++) { + HouseSpec *hs = file->housespec[i]; + if (hs != NULL) { + SetHouseSpec(hs); + if (hs->min_date < 1930) reset_dates = false; + } + } + } + + if (reset_dates) { + for (int i = NEW_HOUSE_OFFSET; i < HOUSE_MAX; i++) { + HouseSpec *hs = GetHouseSpecs(i); + + if (hs->enabled && hs->min_date == 1930) hs->min_date = 0; + } + } +} + /* Here we perform initial decoding of some special sprites (as are they * described at http://www.ttdpatch.net/src/newgrf.txt, but this is only a very * partial implementation yet). */ @@ -4003,6 +4400,18 @@ void LoadNewGRFFile(GRFConfig *config, uint file_index, GrfLoadingStage stage) void InitDepotWindowBlockSizes(); +static void AfterLoadGRFs() +{ + /* Pre-calculate all refit masks after loading GRF files. */ + CalculateRefitMasks(); + + /* Set the block size in the depot windows based on vehicle sprite sizes */ + InitDepotWindowBlockSizes(); + + /* Add all new houses to the house array. */ + FinaliseHouseArray(); +} + void LoadNewGRF(uint load_index, uint file_index) { InitializeGRFSpecial(); @@ -4033,9 +4442,6 @@ void LoadNewGRF(uint load_index, uint file_index) } } - // Pre-calculate all refit masks after loading GRF files - CalculateRefitMasks(); - - /* Set the block size in the depot windows based on vehicle sprite sizes */ - InitDepotWindowBlockSizes(); + /* Call any functions that should be run after GRFs have been loaded. */ + AfterLoadGRFs(); } |