# HG changeset patch # User maedhros # Date 2007-03-19 11:27:30 # Node ID 7393965b53229e39f86911db446d5a649d101531 # Parent 007d161f5c7746a13a3a2f3ed170951d7f495733 (svn r9315) -Merge: The newhouses branch. With this merge comes almost complete support for the newhouses grf specs, so all newhouses grfs will be playable in the game. Many thanks to everyone who contributed code and ideas, and all the testers who found things we missed. diff --git a/docs/landscape.html b/docs/landscape.html --- a/docs/landscape.html +++ b/docs/landscape.html @@ -568,621 +568,67 @@   + Newhouses is the name englobing a newGRF feature developped by TTDPatch devs (mainly Csaboka).
+ It allows the replacement of the properties as well as the graphics of houses in the game.
+ To distinguish between the standard behaviour and the newGRF one, HouseID (m4 + m3[6]) is tested for anything above 110.
+ 110 is the count of standard houses. So above 110 means there is a new definition of at least one house
diff --git a/docs/landscape_externals.html b/docs/landscape_externals.html new file mode 100644 --- /dev/null +++ b/docs/landscape_externals.html @@ -0,0 +1,616 @@ + + + + + + + + OpenTTD Landscape externals + + + +

Landscape

+

+ These are the different house types available on standard game.
+ Note: In the climate list, 'sub-arctic' means below the snow line, and 'snow' means above the snow line in the sub-arctic climate. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Type Size Climates Description
00  1×1temperatetall office block
01  1×1temperateoffice block
02  1×1temperatesmall block of flats
03  1×1temperatechurch
04  1×1temperate, sub-arctic, sub-tropicallarge office block
05  1×1snowlarge office block
06  1×1temperatetown houses
07..08  1×2temperatehotel
09  1×1temperate, sub-arctic, sub-tropical  statue
0A  1×1temperate, sub-arctic, sub-tropicalfountain
0B  1×1temperatepark (with a pond)
0C  1×1temperatepark (with an alley)
0D  1×1temperateoffice block
0E..10  1×1temperatevarious types of shops and offices
11  1×1temperate, sub-arctic, sub-tropicalmodern office building
12  1×1temperatewarehouse
13  1×1temperateoffice block (with spiral stairway on the side)
14..17  2×2temperatestadium
18  1×1temperateold houses
19  1×1temperatecottages
1A  1×1temperatehouses
1B  1×1temperateflats
1C  1×1temperatetall office block
1D  1×1temperateshops and offices
1E  1×1temperate, sub-tropicalshops and offices
1F  1×1temperatetheatre
20..23  2×2temperate, sub-arctic, sub-tropicalstadium (modern style)
24  1×1temperate, sub-arctic, sub-tropicaloffices (the modern 'vertical tube' style)
25  1×1sub-arctichouses
26  1×1snowhouses
27  1×1temperatecinema
28..2B  2×2temperateshopping mall
2C  1×1sub-arcticflats
2D  1×1snowflats
2E  1×1sub-arctichouses
2F  1×1snowhouses
30  1×1sub-arctichouses
31  1×1snowhouses
32  1×1sub-arctic, sub-tropicaltall office block
33  1×1snowtall office block
34  1×1sub-arctictall office block
35  1×1snowtall office block
36  1×1sub-arctic, sub-tropicaltall office block
37  1×1snowtall office block
38  1×1sub-arctichouses
39  1×1snowhouses
3A  1×1sub-arcticshops and offices
3B  1×1snowshops and offices
3C  1×1sub-arcticchurch
3D  1×1snowchurch
3E  1×1sub-arctichouses
3F  1×1snowhouses
40  1×1sub-arcticshops and offices
41  1×1snowshops and offices
42..43  1×2sub-arctichotel
44..45  1×2snowhotel
46  1×1sub-arctic, sub-tropicalshops and offices
47  1×1snowshops and offices
48  1×1sub-arctictall office block
49  1×1snowtall office block
4A..4B  2×1sub-arctictall office block
4C..4D  2×1snowtall office block
4E  1×1sub-tropicalhouses (with a tree in a corner)
4F, 50  1×1sub-tropicalhouses
51  1×1sub-tropicalhouses (suburb-type)
52  1×1sub-tropicalflats
53  1×1sub-tropicalchurch
54  1×1sub-tropicalhouses (with two trees in front)
55, 56  1×1sub-tropicalflats
57..58  2×1sub-tropicaltall office block
59  1×1sub-tropicalflats
5A  1×1sub-tropicaltall office block
5B  1×1toylandchurch
5C..61  1×1toylandvarious types of toyland houses
62  1×1toylandtall office block
63..64  1×2toylandhouses ('shoe' style)
65  1×1toylandtall office block
66  1×1toylandigloo
67  1×1toylandtepees
68, 69  1×1toylandshops and offices
6A  1×1toylandtall office block
6B  1×1toylandstatue
6C  1×1toylandteapot-house
6D  1×1toylandpiggy-bank
+ + + \ No newline at end of file diff --git a/docs/landscape_grid.html b/docs/landscape_grid.html --- a/docs/landscape_grid.html +++ b/docs/landscape_grid.html @@ -33,6 +33,7 @@ the array so you can quickly see what is
  • m4 - 8 bits in size, is used for general storage
  • m5 - 8 bits in size, is used for general storage
  • m6 - 8 bits in size, special meaning : lower 2 bits only valid in tropic climate, upper 2 bits for bridges
  • +
  • m7 - 8 bits in size, is used for general storage
  • @@ -46,6 +47,7 @@ the array so you can quickly see what is + @@ -56,6 +58,7 @@ the array so you can quickly see what is + @@ -67,6 +70,7 @@ the array so you can quickly see what is + @@ -77,6 +81,7 @@ the array so you can quickly see what is + @@ -88,6 +93,7 @@ the array so you can quickly see what is + @@ -98,6 +104,7 @@ the array so you can quickly see what is + @@ -108,6 +115,7 @@ the array so you can quickly see what is + @@ -119,6 +127,7 @@ the array so you can quickly see what is + @@ -129,6 +138,7 @@ the array so you can quickly see what is + @@ -139,17 +149,19 @@ the array so you can quickly see what is + - + - + - + - - + + + @@ -161,6 +173,7 @@ the array so you can quickly see what is + @@ -172,6 +185,7 @@ the array so you can quickly see what is + @@ -183,20 +197,25 @@ the array so you can quickly see what is + - + + - + @@ -204,6 +223,7 @@ the array so you can quickly see what is + @@ -214,6 +234,7 @@ the array so you can quickly see what is + @@ -225,16 +246,19 @@ the array so you can quickly see what is + - + + @@ -246,6 +270,7 @@ the array so you can quickly see what is +
    m4 (8) m5 (8) m6 (8)m7 (8)
    bits7654 3210 7654 3210 7654 32107654 3210
    0XXXX XXOO XXXX XXXX XXOO OOXXOOOO OOOO
    farmland-inherit- OOOX XXXX XXOO OOXXOOOO OOOO
    1OOOO OXXX XXXX XXXX XXOO OOXXOOOO OOOO
    depotOOOO XXXX XXOO OXXX XXOO OOXXOOOO OOOO
    waypointOOOO XXXX XXOO OXXX XXOO OOXXOOOO OOOO
    2XXXX XXXX XXXX XXXX XXOO OOXXOOOO OOOO
    level crossingXXXX XXXX XXXX XXOO XXOO OOXXOOOO OOOO
    road depotOOOO OOOO XXXX XXXX XXOO OOXXOOOO OOOO
    3townhouse XXXX XXXXXXXX XXXXXXXX XXXX XXXX XXXX XXXX XXXXXXOO OOOOXXOX XXXX XXXX XXXXXOXX XXXXOOOO OOXXXXXX XXXXXXXX XXXXXXXX XXXX
    4XXXX XXOO XXOO OXXX OOOO OOXXOOOO OOOO
    5XXXX XXXX XXXX XXXX OOOO OXXXOOOO OOOO
    6OOOO OOOO OOOO OOXX XXOO OOXXOOOO OOOO
    8 industry XXXX XXXXXOOX XXXXXOOX + XXXX XXXX XXXX XXXX XXXX OOOO OOOO OOOO OOOO XXXX XXXX OOOO OOXXOOOO OOOO
    bubble/sugar/toffee,
    gold/copper/coal,
    oil wells, power station
    bubble/sugar/toffee,
    + gold/copper/coal,
    + oil wells, power station
    -inherit- XOOO OOOO -inherit-OOOO OOOO -inherit- -inherit-OOOO OOOO
    toy factoryXXXX XXXX -inherit- -inherit-OOOO OOOO
    9XOOO OOOO XOOO XXXX XXOO OOXXOOOO OOOO
    bridge ramp XXXX XXXX XXXX XXXXOOOO OOOO XXXX OOOOOOOO OOOO XXXX + OOOO OOOO XXXX XOOO OOOO XOOO XXXX XXOO OOXXOOOO OOOO
    AOOOO OOOO XXXX XXXX XXOO OOXXOOOO OOOO
    diff --git a/projects/openttd.vcproj b/projects/openttd.vcproj --- a/projects/openttd.vcproj +++ b/projects/openttd.vcproj @@ -519,6 +519,9 @@ RelativePath=".\..\src\newgrf_engine.h"> + + + + + + + + + + @@ -936,6 +940,10 @@ > + + @@ -1456,6 +1464,10 @@ > + + @@ -1471,6 +1483,10 @@ RelativePath=".\..\src\newgrf_text.cpp" > + + (_map_size); + _me = CallocT(_map_size); - /* XXX TODO handle memory shortage more gracefully */ - if (_m == NULL) error("Failed to allocate memory for the map"); + /* XXX @todo handle memory shortage more gracefully + * Maybe some attemps could be made to try with smaller maps down to 64x64 + * Maybe check for available memory before doing the calls, after all, we know how big + * the map is */ + if ((_m == NULL) || (_me == NULL)) error("Failed to allocate memory for the map"); } diff --git a/src/map.h b/src/map.h --- a/src/map.h +++ b/src/map.h @@ -28,7 +28,12 @@ struct Tile { byte m6; }; -extern Tile* _m; +struct TileExtended { + byte m7; +}; + +extern Tile *_m; +extern TileExtended *_me; void AllocateMap(uint size_x, uint size_y); diff --git a/src/misc.cpp b/src/misc.cpp --- a/src/misc.cpp +++ b/src/misc.cpp @@ -18,6 +18,7 @@ #include "vehicle_gui.h" #include "variables.h" #include "ai/ai.h" +#include "newgrf_house.h" #include "date.h" #include "cargotype.h" @@ -132,6 +133,7 @@ void InitializeGame(int mode, uint size_ InitializeSigns(); InitializeStations(); InitializeIndustries(); + InitializeBuildingCounts(); InitializeMainGui(); InitializeNameMgr(); @@ -578,6 +580,34 @@ static void Save_MAP6() } } +static void Load_MAP7() +{ + uint size = MapSize(); + uint i; + + for (i = 0; i != size;) { + uint8 buf[4096]; + uint j; + + SlArray(buf, lengthof(buf), SLE_UINT8); + for (j = 0; j != lengthof(buf); j++) _me[i++].m7 = buf[j]; + } +} + +static void Save_MAP7() +{ + uint size = MapSize(); + uint i; + + SlSetLength(size); + for (i = 0; i != size;) { + uint8 buf[4096]; + uint j; + + for (j = 0; j != lengthof(buf); j++) buf[j] = _me[i++].m7; + SlArray(buf, lengthof(buf), SLE_UINT8); + } +} static void Save_CHTS() { @@ -614,6 +644,7 @@ extern const ChunkHandler _misc_chunk_ha { 'M3HI', Save_MAP4, Load_MAP4, CH_RIFF }, { 'MAP5', Save_MAP5, Load_MAP5, CH_RIFF }, { 'MAPE', Save_MAP6, Load_MAP6, CH_RIFF }, + { 'MAP7', Save_MAP7, Load_MAP7, CH_RIFF }, { 'NAME', Save_NAME, Load_NAME, CH_ARRAY}, { 'DATE', SaveLoad_DATE, SaveLoad_DATE, CH_RIFF}, diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp --- a/src/misc_gui.cpp +++ b/src/misc_gui.cpp @@ -188,6 +188,7 @@ static void Place_LandInfo(TileIndex til DEBUG(misc, LANDINFOD_LEVEL, "m4 = %#x", _m[tile].m4); DEBUG(misc, LANDINFOD_LEVEL, "m5 = %#x", _m[tile].m5); DEBUG(misc, LANDINFOD_LEVEL, "m6 = %#x", _m[tile].m6); + DEBUG(misc, LANDINFOD_LEVEL, "m7 = %#x", _me[tile].m7); #undef LANDINFOD_LEVEL } diff --git a/src/newgrf.cpp b/src/newgrf.cpp --- 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 } +/** 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, 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(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(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, /* 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, in 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(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(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 * 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 * } } 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, in break; } + case GSF_TOWNHOUSE: default: switch (GB(id, 8, 8)) { case 0xC4: /* Station class name */ @@ -2167,7 +2503,15 @@ static void FeatureNewName(byte *buf, in } 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, in #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, u 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 fi } } - // 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(); } diff --git a/src/newgrf.h b/src/newgrf.h --- a/src/newgrf.h +++ b/src/newgrf.h @@ -4,6 +4,7 @@ #define NEWGRF_H #include "station.h" +#include "town.h" #include "newgrf_config.h" #include "helpers.hpp" #include "cargotype.h" @@ -56,6 +57,7 @@ struct GRFFile { uint sound_offset; StationSpec **stations; + HouseSpec **housespec; uint32 param[0x80]; uint param_end; /// one more than the highest set parameter @@ -72,6 +74,7 @@ extern GRFFile *_first_grffile; extern SpriteID _signal_base; extern SpriteID _coast_base; extern bool _have_2cc; +extern bool _have_newhouses; void LoadNewGRFFile(GRFConfig *config, uint file_index, GrfLoadingStage stage); void LoadNewGRF(uint load_index, uint file_index); diff --git a/src/newgrf_callbacks.h b/src/newgrf_callbacks.h --- a/src/newgrf_callbacks.h +++ b/src/newgrf_callbacks.h @@ -37,10 +37,42 @@ enum CallbackID { CBID_TRAIN_ARTIC_ENGINE = 0x16, + /* Called (if appropriate bit in callback mask is set) to determine whether + * the house can be built on the specified tile. */ + CBID_HOUSE_ALLOW_CONSTRUCTION = 0x17, + CBID_VEHICLE_CARGO_SUFFIX = 0x19, + /* Called (if appropriate bit in callback mask is set) to determine + * the next animation frame. */ + CBID_HOUSE_ANIMATION_NEXT_FRAME = 0x1A, + + /* Called (if appropriate bit in callback mask is set) for periodically + * starting or stopping the animation. */ + CBID_HOUSE_ANIMATION_START_STOP = 0x1B, + + /* Called (if appropriate bit in callback mask is set) whenever the + * construction state of a house changes. */ + CBID_CONSTRUCTION_STATE_CHANGE = 0x1C, + CBID_TRAIN_ALLOW_WAGON_ATTACH = 0x1D, + /* Called (if appropriate bit in callback mask is set) to determine the + * colour of a town building. */ + CBID_BUILDING_COLOUR = 0x1E, + + /* Called (if appropriate bit in callback mask is set) to decide how much + * cargo a town building can accept. */ + CBID_HOUSE_CARGO_ACCEPTANCE = 0x1F, // not yet implemented + + /* Called (if appropriate bit in callback mask is set) to indicate + * how long the current animation frame should last. */ + CBID_HOUSE_ANIMATION_SPEED = 0x20, + + /* Called (if appropriate bit in callback mask is set) periodically to + * determine if a house should be destroyed. */ + CBID_HOUSE_DESTRUCTION = 0x21, + /* This callback is called from vehicle purchase lists. It returns a value to be * used as a custom string ID in the 0xD000 range. */ CBID_VEHICLE_ADDITIONAL_TEXT = 0x23, @@ -48,16 +80,28 @@ enum CallbackID { /* Called when building a station to customize the tile layout */ CBID_STATION_TILE_LAYOUT = 0x24, + /* Called (if appropriate bit in callback mask is set) to determine which + * cargoes a town building should accept. */ + CBID_HOUSE_ACCEPT_CARGO = 0x2A, // not yet implemented + /* Called to determine if a specific colour map should be used for a vehicle * instead of the default livery */ CBID_VEHICLE_COLOUR_MAPPING = 0x2D, + /* Called (if appropriate bit in callback mask is set) to determine how much + * cargo a town building produces. */ + CBID_HOUSE_PRODUCE_CARGO = 0x2E, // not yet implemented + /* Called when the player (or AI) tries to start or stop a vehicle. Mainly * used for preventing a vehicle from leaving the depot. */ CBID_VEHICLE_START_STOP_CHECK = 0x31, /* Called to play a special sound effect */ CBID_VEHICLE_SOUND_EFFECT = 0x33, + + /* Called (if appropriate bit in callback mask set) to determine whether a + * town building can be destroyed. */ + CBID_HOUSE_DENY_DESTRUCTION = 0x143, }; /** @@ -84,6 +128,23 @@ enum StationCallbackMask { }; /** + * Callback masks for houses. + */ +enum HouseCallbackMask { + CBM_HOUSE_ALLOW_CONSTRUCTION = 0, + CBM_ANIMATION_NEXT_FRAME = 1, + CBM_ANIMATION_START_STOP = 2, + CBM_CONSTRUCTION_STATE_CHANGE = 3, + CBM_BUILDING_COLOUR = 4, + CBM_CARGO_ACCEPTANCE = 5, + CBM_ANIMATION_SPEED = 6, + CBM_HOUSE_DESTRUCTION = 7, + CBM_HOUSE_ACCEPT_CARGO = 8, + CBM_HOUSE_PRODUCE_CARGO = 9, + CBM_HOUSE_DENY_DESTRUCTION = 10, +}; + +/** * Result of a failed callback. */ enum { diff --git a/src/newgrf_house.cpp b/src/newgrf_house.cpp new file mode 100644 --- /dev/null +++ b/src/newgrf_house.cpp @@ -0,0 +1,609 @@ +/* $Id$ */ + +/** @file newgrf_house.cpp */ + +#include "stdafx.h" +#include "openttd.h" +#include "functions.h" +#include "variables.h" +#include "debug.h" +#include "viewport.h" +#include "date.h" +#include "town.h" +#include "town_map.h" +#include "sound.h" +#include "sprite.h" +#include "strings.h" +#include "table/strings.h" +#include "table/sprites.h" +#include "table/town_land.h" +#include "newgrf.h" +#include "newgrf_house.h" +#include "newgrf_spritegroup.h" +#include "newgrf_callbacks.h" +#include "newgrf_town.h" +#include "newgrf_sound.h" + +static BuildingCounts _building_counts; +static HouseClassMapping _class_mapping[HOUSE_CLASS_MAX]; +HouseIDMapping _house_id_mapping[HOUSE_MAX]; + +/* Since the house IDs defined by the GRF file don't necessarily correlate + * to those used by the game, the IDs used for overriding old houses must be + * translated when the house spec is set. */ +static uint16 _house_overrides[NEW_HOUSE_OFFSET]; + +void AddHouseOverride(uint8 local_id, uint house_type) +{ + assert(house_type < NEW_HOUSE_OFFSET); + _house_overrides[house_type] = local_id; +} + +void ResetHouseOverrides() +{ + for (int i = 0; i != lengthof(_house_overrides); i++) { + _house_overrides[i] = INVALID_HOUSE_ID; + } +} + +static HouseID GetHouseID(byte grf_local_id, uint32 grfid) +{ + const HouseIDMapping *map; + + for (HouseID house_id = NEW_HOUSE_OFFSET; house_id != lengthof(_house_id_mapping); house_id++) { + map = &_house_id_mapping[house_id]; + if (map->house_id == grf_local_id && map->grfid == grfid) return house_id; + } + return INVALID_HOUSE_ID; +} + +static HouseID AddHouseID(byte grf_local_id, uint32 grfid, byte substitute_id) +{ + HouseID house_id; + HouseIDMapping *map; + + /* Look to see if this house has already been added. This is done + * separately from the loop below in case a GRF has been deleted, and there + * are any gaps in the array. */ + house_id = GetHouseID(grf_local_id, grfid); + if (house_id != INVALID_HOUSE_ID) return house_id; + + /* This house hasn't been defined before, so give it an ID now. */ + for (house_id = NEW_HOUSE_OFFSET; house_id != lengthof(_house_id_mapping); house_id++) { + map = &_house_id_mapping[house_id]; + + if (map->house_id == 0 && map->grfid == 0) { + map->house_id = grf_local_id; + map->grfid = grfid; + map->substitute_id = substitute_id; + return house_id; + } + } + + return INVALID_HOUSE_ID; +} + +void SetHouseSpec(const HouseSpec *hs) +{ + HouseID house_id = AddHouseID(hs->local_id, hs->grffile->grfid, hs->substitute_id); + + if (house_id == INVALID_HOUSE_ID) { + grfmsg(1, "SetHouseSpec: Too many houses allocated. Ignoring."); + return; + } + + memcpy(&_house_specs[house_id], hs, sizeof(*hs)); + + /* Now add the overrides. */ + for (int i = 0; i != lengthof(_house_overrides); i++) { + HouseSpec *overridden_hs = GetHouseSpecs(i); + + if (_house_overrides[i] != hs->local_id) continue; + + overridden_hs->override = house_id; + _house_overrides[i] = INVALID_HOUSE_ID; + } +} + +void ResetHouseIDMapping() +{ + memset(&_house_id_mapping, 0, sizeof(_house_id_mapping)); +} + +void CheckHouseIDs() +{ + for (TileIndex t = 0; t < MapSize(); t++) { + HouseID house_id; + + if (!IsTileType(t, MP_HOUSE)) continue; + + house_id = GetHouseType(t); + if (!GetHouseSpecs(house_id)->enabled && house_id >= NEW_HOUSE_OFFSET) { + /* The specs for this type of house are not available any more, so + * replace it with the substitute original house type. */ + SetHouseType(t, _house_id_mapping[house_id].substitute_id); + } + } + + InitializeBuildingCounts(); + AfterLoadCountBuildings(); + +} + +HouseClassID AllocateHouseClassID(byte grf_class_id, uint32 grfid) +{ + /* Start from 1 because 0 means that no class has been assigned. */ + for (int i = 1; i != lengthof(_class_mapping); i++) { + HouseClassMapping *map = &_class_mapping[i]; + + if (map->class_id == grf_class_id && map->grfid == grfid) return (HouseClassID)i; + + if (map->class_id == 0 && map->grfid == 0) { + map->class_id = grf_class_id; + map->grfid = grfid; + return (HouseClassID)i; + } + } + return HOUSE_NO_CLASS; +} + +void InitializeBuildingCounts() +{ + memset(&_building_counts, 0, sizeof(_building_counts)); +} + +/** + * IncreaseBuildingCount() + * Increase the count of a building when it has been added by a town. + * @param t The town that the building is being built in + * @param house_id The id of the house being added + */ +void IncreaseBuildingCount(Town *t, HouseID house_id) +{ + HouseClassID class_id = GetHouseSpecs(house_id)->class_id; + + if (!_have_newhouses) return; + + /* If there are 255 buildings of this type in this town, there are also + * at least that many houses of the same class in the town, and + * therefore on the map as well. */ + if (t->building_counts.id_count[house_id] == 255) return; + + t->building_counts.id_count[house_id]++; + if (_building_counts.id_count[house_id] < 255) _building_counts.id_count[house_id]++; + + /* Similarly, if there are 255 houses of this class in this town, there + * must be at least that number on the map too. */ + if (class_id == HOUSE_NO_CLASS || t->building_counts.class_count[class_id] == 255) return; + + t->building_counts.class_count[class_id]++; + if (_building_counts.class_count[class_id] < 255) _building_counts.class_count[class_id]++; +} + +/** + * DecreaseBuildingCount() + * Decrease the number of a building when it is deleted. + * @param t The town that the building was built in + * @param house_id The id of the house being removed + */ +void DecreaseBuildingCount(Town *t, HouseID house_id) +{ + HouseClassID class_id = GetHouseSpecs(house_id)->class_id; + + if (!_have_newhouses) return; + + if (t->building_counts.id_count[house_id] > 0) t->building_counts.id_count[house_id]--; + if (_building_counts.id_count[house_id] > 0) _building_counts.id_count[house_id]--; + + if (class_id == HOUSE_NO_CLASS) return; + + if (t->building_counts.class_count[class_id] > 0) t->building_counts.class_count[class_id]--; + if (_building_counts.class_count[class_id] > 0) _building_counts.class_count[class_id]--; +} + +/** + * AfterLoadCountBuildings() + * + * After a savegame has been loaded, count the number of buildings on the map. + */ +void AfterLoadCountBuildings() +{ + if (!_have_newhouses) return; + + for (TileIndex t = 0; t < MapSize(); t++) { + if (!IsTileType(t, MP_HOUSE)) continue; + IncreaseBuildingCount(GetTownByTile(t), GetHouseType(t)); + } +} + + +static uint32 HouseGetRandomBits(const ResolverObject *object) +{ + const TileIndex tile = object->u.house.tile; + return (tile == INVALID_TILE || !IsTileType(tile, MP_HOUSE)) ? 0 : GetHouseRandomBits(tile); +} + +static uint32 HouseGetTriggers(const ResolverObject *object) +{ + const TileIndex tile = object->u.house.tile; + return (tile == INVALID_TILE || !IsTileType(tile, MP_HOUSE)) ? 0 : GetHouseTriggers(tile); +} + +static void HouseSetTriggers(const ResolverObject *object, int triggers) +{ + const TileIndex tile = object->u.house.tile; + if (IsTileType(tile, MP_HOUSE)) SetHouseTriggers(tile, triggers); +} + +static uint32 GetNumHouses(HouseID house_id, const Town *town) +{ + uint8 map_id_count, town_id_count, map_class_count, town_class_count; + HouseClassID class_id = GetHouseSpecs(house_id)->class_id; + + map_id_count = _building_counts.id_count[house_id]; + map_class_count = _building_counts.class_count[class_id]; + town_id_count = town->building_counts.id_count[house_id]; + town_class_count = town->building_counts.class_count[class_id]; + + return map_class_count << 24 | town_class_count << 16 | map_id_count << 8 | town_id_count; +} + +static uint32 GetTerrainType(TileIndex tile) +{ + switch (_opt.landscape) { + case LT_DESERT: return GetTropicZone(tile) == TROPICZONE_DESERT ? 1 : 2; + case LT_HILLY: return GetTileZ(tile) >= _opt.snow_line ? 4 : 0; + default: return 0; + } +} + +static uint32 GetGRFParameter(HouseID house_id, byte parameter) +{ + const HouseSpec *hs = GetHouseSpecs(house_id); + const GRFFile *file = hs->grffile; + + if (parameter >= file->param_end) return 0; + return file->param[parameter]; +} + +/** + * HouseGetVariable(): + * + * Used by the resolver to get values for feature 07 deterministic spritegroups. + */ +static uint32 HouseGetVariable(const ResolverObject *object, byte variable, byte parameter, bool *available) +{ + const Town *town = object->u.house.town; + TileIndex tile = object->u.house.tile; + HouseID house_id = object->u.house.house_id; + + if (object->scope == VSG_SCOPE_PARENT) { + return TownGetVariable(variable, parameter, available, town); + } + + switch (variable) { + /* Construction stage. */ + case 0x40: return (IsTileType(tile, MP_HOUSE) ? GetHouseBuildingStage(tile) : 0) | OriginalTileRandomiser(TileX(tile), TileY(tile)) << 2; + + /* Building age. */ + case 0x41: return clamp(_cur_year - GetHouseConstructionYear(tile), 0, 0xFF); + + /* Town zone */ + case 0x42: return GetTownRadiusGroup(town, tile); + + /* Terrain type */ + case 0x43: return GetTerrainType(tile); + + /* Number of this type of building on the map. */ + case 0x44: return GetNumHouses(house_id, town); + + /* Whether the town is being created or just expanded. */ + case 0x45: return _generating_world ? 1 : 0; + + /* Current animation frame. */ + case 0x46: return IsTileType(tile, MP_HOUSE) ? GetHouseAnimationFrame(tile) : 0; + + + /* Building counts for old houses with id = parameter. */ + case 0x60: return GetNumHouses(parameter, town); + + /* Building counts for new houses with id = parameter. */ + case 0x61: { + const HouseSpec *hs = GetHouseSpecs(house_id); + if (hs->grffile == NULL) return 0; + + HouseID new_house = GetHouseID(parameter, hs->grffile->grfid); + return new_house == INVALID_HOUSE_ID ? 0 : GetNumHouses(new_house, town); + } + + /* Land info for nearby tiles. */ + case 0x62: { + int8 x = GB(parameter, 0, 4); + int8 y = GB(parameter, 4, 4); + byte tile_type; + + if (x >= 8) x -= 16; + if (y >= 8) y -= 16; + + tile += TileDiffXY(x, y); + + tile_type = GetTerrainType(tile) << 2 | (IsTileType(tile, MP_WATER) ? 1 : 0) << 1; + + return GetTileType(tile) << 24 | (TileHeight(tile) * 8) << 16 | tile_type << 8 | GetTileSlope(tile, NULL); + } + + /* Read GRF parameter */ + case 0x7F: return GetGRFParameter(object->u.house.house_id, parameter); + } + + DEBUG(grf, 1, "Unhandled house property 0x%X", variable); + + *available = false; + return UINT_MAX; +} + +static const SpriteGroup *HouseResolveReal(const ResolverObject *object, const SpriteGroup *group) +{ + /* Houses do not have 'real' groups */ + return NULL; +} + +/** + * NewHouseResolver(): + * + * Returns a resolver object to be used with feature 07 spritegroups. + */ +static void NewHouseResolver(ResolverObject *res, HouseID house_id, TileIndex tile, Town *town) +{ + res->GetRandomBits = HouseGetRandomBits; + res->GetTriggers = HouseGetTriggers; + res->SetTriggers = HouseSetTriggers; + res->GetVariable = HouseGetVariable; + res->ResolveReal = HouseResolveReal; + + res->u.house.tile = tile; + res->u.house.town = town; + res->u.house.house_id = house_id; + + res->callback = 0; + res->callback_param1 = 0; + res->callback_param2 = 0; + res->last_value = 0; + res->trigger = 0; + res->reseed = 0; +} + +uint16 GetHouseCallback(uint16 callback, uint32 param1, HouseID house_id, Town *town, TileIndex tile) +{ + ResolverObject object; + const SpriteGroup *group; + + NewHouseResolver(&object, house_id, tile, town); + object.callback = callback; + object.callback_param1 = param1; + object.callback_param2 = 0; + + group = Resolve(GetHouseSpecs(house_id)->spritegroup, &object); + if (group == NULL || group->type != SGT_CALLBACK) return CALLBACK_FAILED; + + return group->g.callback.result; +} + +void DrawTileLayout(const TileInfo *ti, const SpriteGroup *group, byte stage, HouseID house_id) +{ + const DrawTileSprites *dts = group->g.layout.dts; + const DrawTileSeqStruct *dtss; + + SpriteID image = dts->ground_sprite; + SpriteID pal = dts->ground_pal; + + if (GB(image, 0, SPRITE_WIDTH) != 0) DrawGroundSprite(image, pal); + + foreach_draw_tile_seq(dtss, dts->seq) { + if (GB(dtss->image, 0, SPRITE_WIDTH) == 0) continue; + + image = dtss->image + stage; + pal = dtss->pal; + + if (!HASBIT(image, SPRITE_MODIFIER_OPAQUE) && ((_display_opt & DO_TRANS_BUILDINGS))) { + SETBIT(image, PALETTE_MODIFIER_TRANSPARENT); + pal = PALETTE_TO_TRANSPARENT; + } else if (HASBIT(image, PALETTE_MODIFIER_COLOR)) { + if (pal == 0) { + const HouseSpec *hs = GetHouseSpecs(house_id); + if (HASBIT(hs->callback_mask, CBM_BUILDING_COLOUR)) { + uint16 callback = GetHouseCallback(CBID_BUILDING_COLOUR, 0, house_id, GetTownByTile(ti->tile), ti->tile); + if (callback != CALLBACK_FAILED) { + /* If bit 14 is set, we should use a 2cc colour map, else use the callback value. */ + pal = HASBIT(callback, 14) ? GB(callback, 0, 8) + SPR_2CCMAP_BASE : callback; + } + } else { + pal = hs->random_colour[OriginalTileRandomiser(ti->x, ti->y)] + PALETTE_RECOLOR_START; + } + } + } else { + pal = PAL_NONE; + } + + if ((byte)dtss->delta_z != 0x80) { + AddSortableSpriteToDraw( + image, pal, + ti->x + dtss->delta_x, ti->y + dtss->delta_y, + dtss->size_x, dtss->size_y, + dtss->size_z, ti->z + dtss->delta_z + ); + } else { + AddChildSpriteScreen(image, pal, dtss->delta_x, dtss->delta_y); + } + } +} + +void DrawNewHouseTile(TileInfo *ti, HouseID house_id) +{ + const HouseSpec *hs = GetHouseSpecs(house_id); + const SpriteGroup *group; + ResolverObject object; + + if (ti->tileh != SLOPE_FLAT) DrawFoundation(ti, ti->tileh); + + NewHouseResolver(&object, house_id, ti->tile, GetTownByTile(ti->tile)); + + group = Resolve(hs->spritegroup, &object); + if (group == NULL || group->type != SGT_TILELAYOUT) { + /* XXX: This is for debugging purposes really, and shouldn't stay. */ + DrawGroundSprite(SPR_SHADOW_CELL, PAL_NONE); + } else { + /* Limit the building stage to the number of stages supplied. */ + byte stage = GetHouseBuildingStage(ti->tile); + stage = clamp(stage - 4 + group->g.layout.num_sprites, 0, group->g.layout.num_sprites - 1); + DrawTileLayout(ti, group, stage, house_id); + } +} + +void AnimateNewHouseTile(TileIndex tile) +{ + const HouseSpec *hs = GetHouseSpecs(GetHouseType(tile)); + byte animation_speed = hs->animation_speed; + bool frame_set_by_callback = false; + + if (HASBIT(hs->callback_mask, CBM_ANIMATION_SPEED)) { + uint16 callback_res = GetHouseCallback(CBID_HOUSE_ANIMATION_SPEED, 0, GetHouseType(tile), GetTownByTile(tile), tile); + if (callback_res != CALLBACK_FAILED) animation_speed = clamp(callback_res & 0xFF, 2, 16); + } + + /* An animation speed of 2 means the animation frame changes 4 ticks, and + * increasing this value by one doubles the wait. 2 is the minimum value + * allowed for animation_speed, which corresponds to 120ms, and 16 is the + * maximum, corresponding to around 33 minutes. */ + if (_tick_counter % (1 << animation_speed) != 0) return; + + byte frame = GetHouseAnimationFrame(tile); + byte num_frames = GB(hs->animation_frames, 0, 7); + + if (HASBIT(hs->callback_mask, CBM_ANIMATION_NEXT_FRAME)) { + uint32 param = (hs->extra_flags & CALLBACK_1A_RANDOM_BITS) ? Random() : 0; + uint16 callback_res = GetHouseCallback(CBID_HOUSE_ANIMATION_NEXT_FRAME, param, GetHouseType(tile), GetTownByTile(tile), tile); + + if (callback_res != CALLBACK_FAILED) { + frame_set_by_callback = true; + + switch (callback_res & 0xFF) { + case 0xFF: + DeleteAnimatedTile(tile); + break; + case 0xFE: + /* Carry on as normal. */ + frame_set_by_callback = false; + break; + default: + frame = callback_res & 0xFF; + break; + } + + /* If the lower 7 bits of the upper byte of the callback + * result are not empty, it is a sound effect. */ + if (GB(callback_res, 8, 7) != 0) PlayHouseSound(GB(callback_res, 8, 7), tile); + } + } + + if (!frame_set_by_callback) { + if (frame < num_frames) { + frame++; + } else if (frame == num_frames && HASBIT(hs->animation_frames, 7)) { + /* This animation loops, so start again from the beginning */ + frame = 0; + } else { + /* This animation doesn't loop, so stay here */ + DeleteAnimatedTile(tile); + } + } + + SetHouseAnimationFrame(tile, frame); + MarkTileDirtyByTile(tile); +} + +void ChangeHouseAnimationFrame(TileIndex tile, uint16 callback_result) +{ + switch (callback_result & 0xFF) { + case 0xFD: /* Do nothing. */ break; + case 0xFE: AddAnimatedTile(tile); break; + case 0xFF: DeleteAnimatedTile(tile); break; + default: + SetHouseAnimationFrame(tile, callback_result & 0xFF); + AddAnimatedTile(tile); + break; + } + /* If the lower 7 bits of the upper byte of the callback + * result are not empty, it is a sound effect. */ + if (GB(callback_result, 8, 7) != 0) PlayHouseSound(GB(callback_result, 8, 7), tile); +} + +bool CanDeleteHouse(TileIndex tile) +{ + const HouseSpec *hs = GetHouseSpecs(GetHouseType(tile)); + + /* Human players are always allowed to remove buildings, as is water and + * anyone using the scenario editor. */ + if ((IsValidPlayer(_current_player) && IsHumanPlayer(_current_player)) + || _current_player == OWNER_WATER || _current_player == OWNER_NONE) return true; + + if (HASBIT(hs->callback_mask, CBM_HOUSE_DENY_DESTRUCTION)) { + uint16 callback_res = GetHouseCallback(CBID_HOUSE_DENY_DESTRUCTION, 0, GetHouseType(tile), GetTownByTile(tile), tile); + return (callback_res == CALLBACK_FAILED || callback_res == 0); + } else { + return !(hs->extra_flags & BUILDING_IS_PROTECTED); + } +} + +static void AnimationControl(TileIndex tile, uint16 random_bits) +{ + const HouseSpec *hs = GetHouseSpecs(GetHouseType(tile)); + + if (HASBIT(hs->callback_mask, CBM_ANIMATION_START_STOP)) { + uint32 param = (hs->extra_flags & SYNCHRONISED_CALLBACK_1B) ? (GB(Random(), 0, 16) | random_bits << 16) : Random(); + uint16 callback_res = GetHouseCallback(CBID_HOUSE_ANIMATION_START_STOP, param, GetHouseType(tile), GetTownByTile(tile), tile); + + if (callback_res != CALLBACK_FAILED) ChangeHouseAnimationFrame(tile, callback_res); + } +} + +bool NewHouseTileLoop(TileIndex tile) +{ + const HouseSpec *hs = GetHouseSpecs(GetHouseType(tile)); + + if (GetHouseProcessingTime(tile) > 0) { + DecHouseProcessingTime(tile); + return true; + } + + /* @todo: Magic with triggers goes here. Got to implement that, one day. .. */ + + if (HASBIT(hs->callback_mask, CBM_ANIMATION_START_STOP)) { + /* If this house is marked as having a synchronised callback, all the + * tiles will have the callback called at once, rather than when the + * tile loop reaches them. This should only be enabled for the northern + * tile, or strange things will happen (here, and in TTDPatch). */ + if (hs->extra_flags & SYNCHRONISED_CALLBACK_1B) { + uint16 random = GB(Random(), 0, 16); + + if (hs->building_flags & BUILDING_HAS_1_TILE) AnimationControl(tile, random); + if (hs->building_flags & BUILDING_2_TILES_Y) AnimationControl(TILE_ADDXY(tile, 0, 1), random); + if (hs->building_flags & BUILDING_2_TILES_X) AnimationControl(TILE_ADDXY(tile, 1, 0), random); + if (hs->building_flags & BUILDING_HAS_4_TILES) AnimationControl(TILE_ADDXY(tile, 1, 1), random); + } else { + AnimationControl(tile, 0); + } + } + + /* Check callback 21, which determines if a house should be destroyed. */ + if (HASBIT(hs->callback_mask, CBM_HOUSE_DESTRUCTION)) { + uint16 callback_res = GetHouseCallback(CBID_HOUSE_DESTRUCTION, 0, GetHouseType(tile), GetTownByTile(tile), tile); + if (callback_res != CALLBACK_FAILED && callback_res > 0) { + ClearTownHouse(GetTownByTile(tile), tile); + return false; + } + } + + SetHouseProcessingTime(tile, hs->processing_time); + return true; +} diff --git a/src/newgrf_house.h b/src/newgrf_house.h new file mode 100644 --- /dev/null +++ b/src/newgrf_house.h @@ -0,0 +1,71 @@ +/* $Id$ */ + +/** @file newgrf_house.h */ + +#ifndef NEWGRF_HOUSE_H +#define NEWGRF_HOUSE_H + +#include "town.h" + +/** + * Maps a house id stored on the map to a GRF file. + * House IDs are stored on the map, so there needs to be a way to tie them to + * GRF files. An array of HouseIDMapping structs is saved with the savegame so + * that house GRFs can be loaded in a different order, or removed safely. The + * index in the array is the house ID stored on the map. + * + * The substitute ID is the ID of an original house that should be used instead + * if the GRF containing the new house is not available. + */ +struct HouseIDMapping { + uint32 grfid; ///< The GRF ID of the file this house belongs to + uint8 house_id; ///< The house ID within the GRF file + uint8 substitute_id; ///< The (original) house ID to use if this GRF is not available +}; + +/** + * Makes class IDs unique to each GRF file. + * Houses can be assigned class IDs which are only comparable within the GRF + * file they were defined in. This mapping ensures that if two houses have the + * same class as defined by the GRF file, the classes are different within the + * game. An array of HouseClassMapping structs is created, and the array index + * of the struct that matches both the GRF ID and the class ID is the class ID + * used in the game. + * + * Although similar to the HouseIDMapping struct above, this serves a different + * purpose. Since the class ID is not saved anywhere, this mapping does not + * need to be persistent; it just needs to keep class ids unique. + */ +struct HouseClassMapping { + uint32 grfid; ////< The GRF ID of the file this class belongs to + uint8 class_id; ////< The class id within the grf file +}; + +extern HouseIDMapping _house_id_mapping[HOUSE_MAX]; ///< Declared in newgrf_house.cpp + +void AddHouseOverride(uint8 local_id, uint house_type); +void ResetHouseOverrides(); + +void SetHouseSpec(const HouseSpec *hs); + +void CheckHouseIDs(); +void ResetHouseIDMapping(); + +HouseClassID AllocateHouseClassID(byte grf_class_id, uint32 grfid); + +void InitializeBuildingCounts(); +void IncreaseBuildingCount(Town *t, HouseID house_id); +void DecreaseBuildingCount(Town *t, HouseID house_id); +void AfterLoadCountBuildings(); + +void DrawNewHouseTile(TileInfo *ti, HouseID house_id); +void AnimateNewHouseTile(TileIndex tile); +void ChangeHouseAnimationFrame(TileIndex tile, uint16 callback_result); + +uint16 GetHouseCallback(uint16 callback, uint32 param1, HouseID house_id, Town *town, TileIndex tile); + +bool CanDeleteHouse(TileIndex tile); + +bool NewHouseTileLoop(TileIndex tile); + +#endif /* NEWGRF_HOUSE_H */ diff --git a/src/newgrf_sound.cpp b/src/newgrf_sound.cpp --- a/src/newgrf_sound.cpp +++ b/src/newgrf_sound.cpp @@ -66,3 +66,12 @@ bool PlayVehicleSound(const Vehicle *v, if (callback < GetNumSounds()) SndPlayVehicleFx((SoundFx)callback, v); return true; } + +bool PlayHouseSound(uint16 sound_id, TileIndex tile) +{ + if (sound_id < GetNumOriginalSounds()) { + SndPlayTileFx((SoundFx)sound_id, tile); + return true; + } + return false; +} diff --git a/src/newgrf_sound.h b/src/newgrf_sound.h --- a/src/newgrf_sound.h +++ b/src/newgrf_sound.h @@ -21,5 +21,6 @@ void InitializeSoundPool(); FileEntry *GetSound(uint index); uint GetNumSounds(); bool PlayVehicleSound(const Vehicle *v, VehicleSoundEvent event); +bool PlayHouseSound(uint16 sound_id, TileIndex tile); #endif /* NEWGRF_SOUND_H */ diff --git a/src/newgrf_spritegroup.cpp b/src/newgrf_spritegroup.cpp --- a/src/newgrf_spritegroup.cpp +++ b/src/newgrf_spritegroup.cpp @@ -8,6 +8,7 @@ #include "newgrf_callbacks.h" #include "newgrf_spritegroup.h" #include "date.h" +#include "sprite.h" static void SpriteGroupPoolCleanBlock(uint start_item, uint end_item); @@ -33,6 +34,11 @@ static void DestroySpriteGroup(SpriteGro free((SpriteGroup**)group->g.random.groups); break; + case SGT_TILELAYOUT: + free((void*)group->g.layout.dts->seq); + free(group->g.layout.dts); + break; + default: break; } diff --git a/src/newgrf_spritegroup.h b/src/newgrf_spritegroup.h --- a/src/newgrf_spritegroup.h +++ b/src/newgrf_spritegroup.h @@ -3,6 +3,7 @@ #ifndef NEWGRF_SPRITEGROUP_H #define NEWGRF_SPRITEGROUP_H +#include "town.h" struct SpriteGroup; @@ -128,6 +129,11 @@ struct ResultSpriteGroup { byte num_sprites; }; +struct TileLayoutSpriteGroup { + byte num_sprites; /* Number of sprites in the spriteset, used for loading stages */ + struct DrawTileSprites *dts; +}; + /* List of different sprite group types */ enum SpriteGroupType { SGT_INVALID, @@ -136,6 +142,7 @@ enum SpriteGroupType { SGT_RANDOMIZED, SGT_CALLBACK, SGT_RESULT, + SGT_TILELAYOUT, }; /* Common wrapper for all the different sprite group types */ @@ -148,6 +155,7 @@ struct SpriteGroup { RandomizedSpriteGroup random; CallbackResultSpriteGroup callback; ResultSpriteGroup result; + TileLayoutSpriteGroup layout; } g; }; @@ -180,6 +188,11 @@ struct ResolverObject { const struct StationSpec *statspec; CargoID cargo_type; } station; + struct { + TileIndex tile; + Town *town; + HouseID house_id; + } house; } u; uint32 (*GetRandomBits)(const struct ResolverObject*); diff --git a/src/newgrf_town.cpp b/src/newgrf_town.cpp new file mode 100644 --- /dev/null +++ b/src/newgrf_town.cpp @@ -0,0 +1,91 @@ +/* $Id$ */ + +/** @file newgrf_town.cpp */ + +#include "stdafx.h" +#include "openttd.h" +#include "debug.h" +#include "functions.h" +#include "town.h" + +/** This function implements the town variables that newGRF defines. + * @param variable that is queried + * @param parameter unused + * @param available will return false if ever the variable asked for does not exist + * @param t is of course the town we are inquiring + * @return the value stored in the corresponding variable*/ +uint32 TownGetVariable(byte variable, byte parameter, bool *available, const Town *t) +{ + switch (variable) { + /* Larger towns */ + case 0x40: return 1; + + /* Town index */ + case 0x41: return t->index; + + /* Town properties */ + case 0x80: return t->xy; + case 0x81: return GB(t->xy, 8, 8); + case 0x82: return t->population; + case 0x83: return GB(t->population, 8, 8); + case 0x8A: return t->grow_counter; + case 0x92: return t->flags12; // In original game, 0x92 and 0x93 are really one word. Since flags12 is a byte, this is to adjust + case 0x93: return 0; + case 0x94: return t->radius[0]; + case 0x95: return GB(t->radius[0], 8, 8); + case 0x96: return t->radius[1]; + case 0x97: return GB(t->radius[1], 8, 8); + case 0x98: return t->radius[2]; + case 0x99: return GB(t->radius[2], 8, 8); + case 0x9A: return t->radius[3]; + case 0x9B: return GB(t->radius[3], 8, 8); + case 0x9C: return t->radius[4]; + case 0x9D: return GB(t->radius[4], 8, 8); + case 0x9E: return t->ratings[0]; + case 0x9F: return t->ratings[1]; + case 0xA0: return t->ratings[2]; + case 0xA1: return t->ratings[3]; + case 0xA2: return t->ratings[4]; + case 0xA3: return t->ratings[5]; + case 0xA4: return t->ratings[6]; + case 0xA5: return t->ratings[7]; + case 0xA6: return t->ratings[8]; + case 0xAE: return t->have_ratings; + case 0xB2: return t->statues; + case 0xB6: return t->num_houses; + case 0xB9: return t->growth_rate; + case 0xBA: return t->new_max_pass; + case 0xBB: return GB(t->new_max_pass, 8, 8); + case 0xBC: return t->new_max_mail; + case 0xBD: return GB(t->new_max_mail, 8, 8); + case 0xBE: return t->new_act_pass; + case 0xBF: return GB(t->new_act_pass, 8, 8); + case 0xC0: return t->new_act_mail; + case 0xC1: return GB(t->new_act_mail, 8, 8); + case 0xC2: return t->max_pass; + case 0xC3: return GB(t->max_pass, 8, 8); + case 0xC4: return t->max_mail; + case 0xC5: return GB(t->max_mail, 8, 8); + case 0xC6: return t->act_pass; + case 0xC7: return GB(t->act_pass, 8, 8); + case 0xC8: return t->act_mail; + case 0xC9: return GB(t->act_mail, 8, 8); + case 0xCA: return t->pct_pass_transported; + case 0xCB: return t->pct_mail_transported; + case 0xCC: return t->new_act_food; + case 0xCD: return GB(t->new_act_food, 8, 8); + case 0xCE: return t->new_act_water; + case 0xCF: return GB(t->new_act_water, 8, 8); + case 0xD0: return t->act_food; + case 0xD1: return GB(t->act_food, 8, 8); + case 0xD2: return t->act_water; + case 0xD3: return GB(t->act_water, 8, 8); + case 0xD4: return t->road_build_months; + case 0xD5: return t->fund_buildings_months; + } + + DEBUG(grf, 1, "Unhandled town property 0x%X", variable); + + *available = false; + return (uint32)-1; +} diff --git a/src/newgrf_town.h b/src/newgrf_town.h new file mode 100644 --- /dev/null +++ b/src/newgrf_town.h @@ -0,0 +1,13 @@ +/* $Id$ */ + +/** @file newgrf_town.h */ + +#ifndef NEWGRF_TOWN_H +#define NEWGRF_TOWN_H + +/* Currently there is no direct town resolver; we only need to get town + * variable results from inside stations, house tiles and industry tiles. */ + +uint32 TownGetVariable(byte variable, byte parameter, bool *available, const Town *t); + +#endif /* NEWGRF_TOWN_H */ diff --git a/src/openttd.cpp b/src/openttd.cpp --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -56,6 +56,7 @@ #include "clear_map.h" #include "fontcache.h" #include "newgrf_config.h" +#include "newgrf_house.h" #include "player_face.h" #include "bridge_map.h" @@ -677,6 +678,7 @@ static void MakeNewGame(bool from_height _game_mode = GM_NORMAL; ResetGRFConfig(true); + ResetHouseIDMapping(); GenerateWorldSetCallback(&MakeNewGameDone); GenerateWorld(from_heightmap ? GW_HEIGHTMAP : GW_NEWGAME, 1 << _patches.map_x, 1 << _patches.map_y); @@ -1756,6 +1758,47 @@ bool AfterLoadGame() /* do the same as when elrails were enabled/disabled manually just now */ SettingsDisableElrail(_patches.disable_elrails); + /* From version 52, the map array was changed for house tiles to allow + * space for newhouses grf features. A new byte, m7, was also added. */ + if (CheckSavegameVersion(52)) { + for (TileIndex t = 0; t < map_size; t++) { + _me[t].m7 = 0; + + if (IsTileType(t, MP_HOUSE)) { + if (GB(_m[t].m3, 6, 2) != TOWN_HOUSE_COMPLETED) { + /* Move the construction stage from m3[7..6] to m5[5..4]. + * The construction counter does not have to move. */ + SB(_m[t].m5, 3, 2, GB(_m[t].m3, 6, 2)); + SB(_m[t].m3, 6, 2, 0); + + /* The "house is completed" bit is now in m6[2]. */ + SetHouseCompleted(t, false); + } else { + /* The "lift has destination" bit has been moved from + * m5[7] to m7[0]. */ + SB(_me[t].m7, 0, 1, HASBIT(_m[t].m5, 7)); + CLRBIT(_m[t].m5, 7); + + /* The "lift is moving" bit has been removed, as it does + * the same job as the "lift has destination" bit. */ + CLRBIT(_m[t].m1, 7); + + /* The position of the lift goes from m1[7..0] to m6[7..2], + * making m1 totally free, now. The lift position does not + * have to be a full byte since the maximum value is 36. */ + SetLiftPosition(t, GB(_m[t].m1, 0, 6 )); + + _m[t].m1 = 0; + _m[t].m3 = 0; + SetHouseCompleted(t, true); + } + } + } + } + + /* Count the buildings after updating the map array. */ + AfterLoadCountBuildings(); + if (CheckSavegameVersion(43)) { for (TileIndex t = 0; t < map_size; t++) { if (IsTileType(t, MP_INDUSTRY)) { @@ -1886,6 +1929,8 @@ void ReloadNewGRFData() /* update station and waypoint graphics */ AfterLoadWaypoints(); AfterLoadStations(); + /* check that house ids are still valid */ + CheckHouseIDs(); /* redraw the whole screen */ MarkWholeScreenDirty(); } diff --git a/src/table/sprites.h b/src/table/sprites.h --- a/src/table/sprites.h +++ b/src/table/sprites.h @@ -35,7 +35,6 @@ * @todo Split the "Sprites" enum into smaller chunks and document them */ - enum Sprites { SPR_SELECT_TILE = 752, SPR_DOT = 774, // corner marker for lower/raise land @@ -1324,6 +1323,7 @@ enum SpriteSetup { */ enum Modifiers { SPRITE_MODIFIER_USE_OFFSET = OFFSET_BIT, + SPRITE_MODIFIER_OPAQUE = OFFSET_BIT, ///when a sprite is to be displayed transparently, this bit needs to be set. PALETTE_MODIFIER_TRANSPARENT = TRANSPARENT_BIT, ///this bit is set when a recoloring process is in action @@ -1418,6 +1418,5 @@ enum PaletteSprites { PALETTE_59E = 0x59E, PALETTE_59F = 0x59F, }; -#undef PALETTE_RECOLOR_SPRITE #endif /* SPRITES_H */ diff --git a/src/table/town_land.h b/src/table/town_land.h --- a/src/table/town_land.h +++ b/src/table/town_land.h @@ -2,17 +2,6 @@ /** @file town_land.h */ -enum { - HOUSE_TEMP_CHURCH = 0x03, - HOUSE_STADIUM = 0x14, - HOUSE_MODERN_STADIUM = 0x20, - HOUSE_ARCT_CHURCH = 0x3c, - HOUSE_SNOW_CHURCH = 0x3d, - HOUSE_TROP_CHURCH = 0x53, - HOUSE_TOY_CHURCH = 0x5b, - - HOUSE_MAX = 110 -}; /** Writes the data into the Town Tile Drawing Struct * @param s1 The first sprite of the building, mostly the ground sprite @@ -1790,437 +1779,372 @@ static const DrawBuildingsTileStruct _to M(0x1244, PAL_NONE, 0x125a, PAL_NONE, 0, 0, 16, 16, 50, 0), }; #undef M -// 4 variants * 4 build stages -assert_compile(lengthof(_town_draw_tile_data) == HOUSE_MAX * 4 * 4); - - -static const StringID _town_tile_names[] = { - STR_200F_TALL_OFFICE_BLOCK, - STR_2010_OFFICE_BLOCK, - STR_2011_SMALL_BLOCK_OF_FLATS, - STR_2012_CHURCH, - STR_2013_LARGE_OFFICE_BLOCK, - STR_2013_LARGE_OFFICE_BLOCK, - STR_2014_TOWN_HOUSES, - STR_2015_HOTEL, - STR_2015_HOTEL, - STR_2016_STATUE, - STR_2017_FOUNTAIN, - STR_2018_PARK, - STR_2018_PARK, - STR_2019_OFFICE_BLOCK, - STR_201A_SHOPS_AND_OFFICES, - STR_201A_SHOPS_AND_OFFICES, - STR_201A_SHOPS_AND_OFFICES, - STR_201B_MODERN_OFFICE_BUILDING, - STR_201C_WAREHOUSE, - STR_201D_OFFICE_BLOCK, - STR_201E_STADIUM, - STR_201E_STADIUM, - STR_201E_STADIUM, - STR_201E_STADIUM, - STR_201F_OLD_HOUSES, - STR_2036_COTTAGES, - STR_2037_HOUSES, - STR_2038_FLATS, - STR_2039_TALL_OFFICE_BLOCK, - STR_203A_SHOPS_AND_OFFICES, - STR_203B_SHOPS_AND_OFFICES, - STR_203C_THEATER, - STR_203D_STADIUM, - STR_203D_STADIUM, - STR_203D_STADIUM, - STR_203D_STADIUM, - STR_203E_OFFICES, - STR_203F_HOUSES, - STR_203F_HOUSES, - STR_2040_CINEMA, - STR_2041_SHOPPING_MALL, - STR_2041_SHOPPING_MALL, - STR_2041_SHOPPING_MALL, - STR_2041_SHOPPING_MALL, - STR_2038_FLATS, - STR_2038_FLATS, - STR_203F_HOUSES, - STR_203F_HOUSES, - STR_203F_HOUSES, - STR_203F_HOUSES, - STR_200F_TALL_OFFICE_BLOCK, - STR_200F_TALL_OFFICE_BLOCK, - STR_200F_TALL_OFFICE_BLOCK, - STR_200F_TALL_OFFICE_BLOCK, - STR_200F_TALL_OFFICE_BLOCK, - STR_200F_TALL_OFFICE_BLOCK, - STR_203F_HOUSES, - STR_203F_HOUSES, - STR_201A_SHOPS_AND_OFFICES, - STR_201A_SHOPS_AND_OFFICES, - STR_2012_CHURCH, - STR_2012_CHURCH, - STR_203F_HOUSES, - STR_203F_HOUSES, - STR_201A_SHOPS_AND_OFFICES, - STR_201A_SHOPS_AND_OFFICES, - STR_2015_HOTEL, - STR_2015_HOTEL, - STR_2015_HOTEL, - STR_2015_HOTEL, - STR_201A_SHOPS_AND_OFFICES, - STR_201A_SHOPS_AND_OFFICES, - STR_200F_TALL_OFFICE_BLOCK, - STR_200F_TALL_OFFICE_BLOCK, - STR_200F_TALL_OFFICE_BLOCK, - STR_200F_TALL_OFFICE_BLOCK, - STR_200F_TALL_OFFICE_BLOCK, - STR_200F_TALL_OFFICE_BLOCK, - STR_203F_HOUSES, - STR_203F_HOUSES, - STR_203F_HOUSES, - STR_203F_HOUSES, - STR_2038_FLATS, - STR_2012_CHURCH, - STR_203F_HOUSES, - STR_2038_FLATS, - STR_2038_FLATS, - STR_200F_TALL_OFFICE_BLOCK, - STR_200F_TALL_OFFICE_BLOCK, - STR_2038_FLATS, - STR_200F_TALL_OFFICE_BLOCK, - STR_2012_CHURCH, - STR_203F_HOUSES, - STR_203F_HOUSES, - STR_203F_HOUSES, - STR_203F_HOUSES, - STR_203F_HOUSES, - STR_203F_HOUSES, - STR_200F_TALL_OFFICE_BLOCK, - STR_203F_HOUSES, - STR_203F_HOUSES, - STR_200F_TALL_OFFICE_BLOCK, - STR_2059_IGLOO, - STR_205A_TEPEES, - STR_201A_SHOPS_AND_OFFICES, - STR_201A_SHOPS_AND_OFFICES, - STR_200F_TALL_OFFICE_BLOCK, - STR_2016_STATUE, - STR_205B_TEAPOT_HOUSE, - STR_205C_PIGGY_BANK, -}; -assert_compile(lengthof(_town_tile_names) == HOUSE_MAX); - -static const uint16 _housetype_flags[] = { - 0x1010, 0x1018, 0x100E, 0x100F, 0x7010, 0x0810, 0x100F, 0x1014, - 0x0000, 0x7018, 0x7010, 0x1004, 0x1008, 0x1018, 0x101C, 0x101C, - 0x101C, 0x7010, 0x1010, 0x1010, 0x100F, 0x0000, 0x0000, 0x0000, - 0x1003, 0x1001, 0x100F, 0x101C, 0x101C, 0x101C, 0x5014, 0x1018, - 0x700F, 0x0000, 0x0000, 0x0000, 0x7018, 0x2003, 0x0803, 0x101C, - 0x101E, 0x0000, 0x0000, 0x0000, 0x201C, 0x081C, 0x200F, 0x080F, - 0x2007, 0x0807, 0x6018, 0x0818, 0x2018, 0x0818, 0x6018, 0x0818, - 0x2001, 0x0801, 0x201E, 0x081E, 0x200F, 0x080F, 0x2007, 0x0807, - 0x201C, 0x081C, 0x201C, 0x0000, 0x081C, 0x0000, 0x601C, 0x081C, - 0x2018, 0x0818, 0x201C, 0x0000, 0x081C, 0x0000, 0x401E, 0x401E, - 0x401E, 0x4001, 0x401C, 0x400E, 0x401E, 0x401C, 0x401C, 0x4018, - 0x4000, 0x401C, 0x4018, 0x801F, 0x801F, 0x8003, 0x800F, 0x800F, - 0x800F, 0x800F, 0x801C, 0x801F, 0x0000, 0x801C, 0x8001, 0x8001, - 0x801C, 0x801C, 0x801C, 0x801C, 0x801F, 0x801F, -}; -assert_compile(lengthof(_housetype_flags) == HOUSE_MAX); - -static const byte _housetype_extra_flags[] = { - 0, 0, 0, 0, 32, 32, 0, 8, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 16, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 16, 0, 0, 0, 0, 0, 0, 0, - 16, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 8, 0, 8, 0, 0, 0, - 0, 0, 4, 0, 4, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 4, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 8, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, -}; -assert_compile(lengthof(_housetype_extra_flags) == HOUSE_MAX); - -static const byte _housetype_population[] = { - 187, 85, 40, 5, 220, 220, 30, 140, - 0, 0, 0, 0, 0, 150, 95, 95, - 95, 130, 6, 110, 65, 0, 0, 0, - 15, 12, 13, 100, 170, 100, 180, 35, - 65, 0, 0, 0, 140, 15, 15, 35, - 180, 0, 0, 0, 80, 80, 16, 16, - 14, 14, 135, 135, 170, 170, 210, 210, - 10, 10, 25, 25, 6, 6, 17, 17, - 90, 90, 140, 0, 140, 0, 105, 105, - 190, 190, 250, 0, 250, 0, 16, 16, - 16, 7, 45, 8, 18, 90, 120, 250, - 0, 80, 180, 8, 18, 7, 15, 17, - 19, 21, 75, 35, 0, 85, 11, 10, - 67, 86, 95, 30, 25, 18, -}; -assert_compile(lengthof(_housetype_population) == HOUSE_MAX); - -static const byte _housetype_mailamount[] = { - 70, 55, 20, 2, 85, 85, 12, 22, - 22, 0, 0, 0, 0, 65, 48, 48, - 48, 50, 10, 55, 5, 5, 5, 5, - 6, 7, 8, 35, 50, 40, 64, 23, - 5, 5, 5, 5, 65, 6, 6, 23, - 5, 5, 5, 5, 20, 20, 6, 6, - 6, 6, 60, 60, 70, 70, 80, 80, - 5, 5, 20, 20, 2, 2, 7, 7, - 45, 45, 25, 25, 25, 25, 50, 50, - 75, 75, 60, 60, 60, 60, 6, 6, - 5, 4, 15, 3, 7, 24, 25, 80, - 80, 23, 90, 3, 5, 3, 6, 6, - 6, 6, 20, 9, 0, 18, 3, 3, - 22, 23, 28, 10, 8, 7, -}; -assert_compile(lengthof(_housetype_mailamount) == HOUSE_MAX); - -static const byte _housetype_remove_cost[] = { - 150, 140, 100, 90, 160, 160, 80, 180, - 180, 65, 65, 60, 60, 130, 110, 105, - 107, 200, 145, 155, 250, 250, 250, 250, - 70, 75, 71, 135, 145, 132, 155, 220, - 250, 250, 250, 250, 170, 70, 70, 210, - 250, 250, 250, 250, 100, 100, 70, 70, - 80, 80, 150, 150, 170, 170, 200, 200, - 60, 60, 100, 100, 85, 85, 80, 80, - 140, 140, 160, 160, 160, 160, 130, 130, - 190, 190, 140, 140, 140, 140, 80, 80, - 80, 30, 130, 90, 80, 110, 120, 190, - 190, 110, 180, 90, 90, 70, 80, 80, - 80, 80, 160, 90, 90, 150, 60, 60, - 140, 145, 165, 90, 75, 85, -}; -assert_compile(lengthof(_housetype_remove_cost) == HOUSE_MAX); +/* 4 variants * 4 build stages */ +assert_compile(lengthof(_town_draw_tile_data) == (NEW_HOUSE_OFFSET) * 4 * 4); -static const uint16 _housetype_remove_ratingmod[] = { - 140, 130, 90, 230, 160, 160, 80, 150, - 150, 40, 40, 75, 75, 110, 100, 100, - 100, 150, 110, 110, 300, 300, 300, 300, - 75, 75, 75, 100, 170, 135, 180, 230, - 300, 300, 300, 300, 250, 75, 75, 230, - 300, 300, 300, 300, 90, 90, 70, 70, - 70, 70, 120, 120, 130, 130, 140, 140, - 60, 60, 80, 80, 230, 230, 80, 80, - 110, 110, 160, 160, 160, 160, 105, 105, - 135, 135, 200, 200, 200, 200, 80, 80, - 80, 30, 95, 200, 80, 95, 95, 140, - 140, 95, 150, 200, 90, 50, 75, 75, - 75, 75, 130, 80, 80, 130, 45, 45, - 130, 130, 130, 70, 65, 95, -}; -assert_compile(lengthof(_housetype_remove_ratingmod) == HOUSE_MAX); - - -struct HousetypeYear { - Year min, max; +/** Describes the data that defines each house in the game + * @param mnd introduction year of the house + * @param mxd last year it can be built + * @param p population + * @param rc cost multiplier for removing it + * @param bn building name + * @param rr rating decrease if removed + * @param mg mail generation multiplier + * @param pa passenger acceptance + * @param ma mail acceptance + * @param ga goods acceptance + * @param fa food acceptance + * @param bf building flags (size, stadium etc...) + * @param ba building availability (zone, climate...) + * @see HouseSpec + */ +#define M(mnd, mxd, p, rc, bn, rr, mg, pa, ma, ga, fa, bf, ba) \ + {mnd, mxd, p, rc, bn, rr, mg, pa, ma, ga, fa, bf, ba, true, \ + 0, NULL, 0, 0, {0, 0, 0, 0}, 16, NO_EXTRA_FLAG, HOUSE_NO_CLASS, 0, 2, 0, 0, NULL} +static const HouseSpec _original_house_specs[] = { + /** + * remove_rating_decrease + * | mail_generation + * min_date | | passenger_acceptance + * | max_date | | | mail_acceptance + * | | population | | | | goods_acceptance + * | | | removal_cost | | | | | food_acceptance + * | | | | building_name | | | | | | + * | | | | | | | | | | | + * | | | | | | | | | | | + * +-building_flags | | | | | | | | | + * +-building_availability | | | | | | | | + * | | | | | | | | | | | |*/ + M( 1963, MAX_YEAR, 187, 150, STR_200F_TALL_OFFICE_BLOCK, 140, 70, 8, 3, 4, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_ZON5), + M( 1957, MAX_YEAR, 85, 140, STR_2010_OFFICE_BLOCK, 130, 55, 8, 3, 4, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_ZON5 | HZ_ZON4), + M( 1968, MAX_YEAR, 40, 100, STR_2011_SMALL_BLOCK_OF_FLATS, 90, 20, 8, 3, 1, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_ZON4 | HZ_ZON3 | HZ_ZON2), + M( 0, MAX_YEAR, 5, 90, STR_2012_CHURCH, 230, 2, 2, 0, 0, 0, + BUILDING_IS_CHURCH | TILE_SIZE_1x1, + HZ_TEMP | HZ_ZON4 | HZ_ZON3 | HZ_ZON2 | HZ_ZON1), + M( 1975, MAX_YEAR, 220, 160, STR_2013_LARGE_OFFICE_BLOCK, 160, 85, 10, 4, 6, 0, + BUILDING_IS_ANIMATED | TILE_SIZE_1x1, + HZ_TEMP | HZ_SUBARTC_BELOW | HZ_SUBTROPIC | HZ_ZON5), + M( 1975, MAX_YEAR, 220, 160, STR_2013_LARGE_OFFICE_BLOCK, 160, 85, 10, 4, 6, 0, + BUILDING_IS_ANIMATED | TILE_SIZE_1x1, + HZ_SUBARTC_ABOVE | HZ_ZON5), + M( 0, MAX_YEAR, 30, 80, STR_2014_TOWN_HOUSES, 80, 12, 4, 1, 0, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_ZON4 | HZ_ZON3 | HZ_ZON2 | HZ_ZON1), + M( 1959, MAX_YEAR, 140, 180, STR_2015_HOTEL, 150, 22, 6, 1, 2, 0, + TILE_SIZE_1x2, + HZ_TEMP | HZ_ZON5 | HZ_ZON3), + M( 1959, MAX_YEAR, 0, 180, STR_2015_HOTEL, 150, 22, 6, 1, 2, 0, + TILE_NO_FLAG, + HZ_NOZNS), + M( 1945, MAX_YEAR, 0, 65, STR_2016_STATUE, 40, 0, 2, 0, 0, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_SUBARTC_BELOW | HZ_SUBTROPIC | HZ_ZON5 | HZ_ZON4), + M( 1945, MAX_YEAR, 0, 65, STR_2017_FOUNTAIN, 40, 0, 2, 0, 0, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_SUBARTC_BELOW | HZ_SUBTROPIC | HZ_ZON5), + M( 0, MAX_YEAR, 0, 60, STR_2018_PARK, 75, 0, 2, 0, 0, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_ZON3), + M( 1935, MAX_YEAR, 0, 60, STR_2018_PARK, 75, 0, 2, 0, 0, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_ZON4), + M( 1951, MAX_YEAR, 150, 130, STR_2019_OFFICE_BLOCK, 110, 65, 8, 2, 4, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_ZON5 | HZ_ZON4), + M( 1930, 1960, 95, 110, STR_201A_SHOPS_AND_OFFICES, 100, 48, 6, 2, 3, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 1930, 1960, 95, 105, STR_201A_SHOPS_AND_OFFICES, 100, 48, 6, 2, 3, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 1930, 1960, 95, 107, STR_201A_SHOPS_AND_OFFICES, 100, 48, 6, 2, 3, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 1977, MAX_YEAR, 130, 200, STR_201B_MODERN_OFFICE_BUILDING, 150, 50, 10, 3, 6, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_SUBARTC_BELOW | HZ_SUBTROPIC | HZ_ZON5), + M( 1983, MAX_YEAR, 6, 145, STR_201C_WAREHOUSE, 110, 10, 6, 3, 8, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_ZON5), + M( 1985, MAX_YEAR, 110, 155, STR_201D_OFFICE_BLOCK, 110, 55, 6, 2, 6, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_ZON5), + M( 0, MAX_YEAR, 65, 250, STR_201E_STADIUM, 300, 5, 4, 0, 0, 0, + BUILDING_IS_STADIUM | TILE_SIZE_2x2, + HZ_TEMP | HZ_ZON4 | HZ_ZON3 | HZ_ZON2 | HZ_ZON1), + M( 0, MAX_YEAR, 0, 250, STR_201E_STADIUM, 300, 5, 4, 0, 0, 0, + TILE_NO_FLAG, + HZ_NOZNS), + M( 0, MAX_YEAR, 0, 250, STR_201E_STADIUM, 300, 5, 4, 0, 0, 0, + TILE_NO_FLAG, + HZ_NOZNS), + M( 0, MAX_YEAR, 0, 250, STR_201E_STADIUM, 300, 5, 4, 0, 0, 0, + TILE_NO_FLAG, + HZ_NOZNS), + M( 0, 1951, 15, 70, STR_201F_OLD_HOUSES, 75, 6, 3, 1, 0, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_ZON2 | HZ_ZON1), + M( 0, 1952, 12, 75, STR_2036_COTTAGES, 75, 7, 3, 1, 0, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_ZON1), + M( 1931, MAX_YEAR, 13, 71, STR_2037_HOUSES, 75, 8, 3, 1, 0, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_ZON4 | HZ_ZON3 | HZ_ZON2 | HZ_ZON1), + M( 1935, MAX_YEAR, 100, 135, STR_2038_FLATS, 100, 35, 7, 2, 2, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 1963, MAX_YEAR, 170, 145, STR_2039_TALL_OFFICE_BLOCK, 170, 50, 8, 3, 3, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 0, 1955, 100, 132, STR_203A_SHOPS_AND_OFFICES, 135, 40, 6, 2, 3, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 1973, MAX_YEAR, 180, 155, STR_203B_SHOPS_AND_OFFICES, 180, 64, 8, 3, 3, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_SUBTROPIC | HZ_ZON5 | HZ_ZON3), + M( 0, MAX_YEAR, 35, 220, STR_203C_THEATER, 230, 23, 8, 2, 2, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_ZON5 | HZ_ZON4), + M( 1958, MAX_YEAR, 65, 250, STR_203D_STADIUM, 300, 5, 4, 0, 0, 0, + BUILDING_IS_STADIUM | TILE_SIZE_2x2, + HZ_TEMP | HZ_SUBARTC_BELOW | HZ_SUBTROPIC | HZ_ZON4 | HZ_ZON3 | HZ_ZON2 | HZ_ZON1), + M( 1958, MAX_YEAR, 0, 250, STR_203D_STADIUM, 300, 5, 4, 0, 0, 0, + TILE_NO_FLAG, + HZ_NOZNS), + M( 1958, MAX_YEAR, 0, 250, STR_203D_STADIUM, 300, 5, 4, 0, 0, 0, + TILE_NO_FLAG, + HZ_NOZNS), + M( 1958, MAX_YEAR, 0, 250, STR_203D_STADIUM, 300, 5, 4, 0, 0, 0, + TILE_NO_FLAG, + HZ_NOZNS), + M( 2000, MAX_YEAR, 140, 170, STR_203E_OFFICES, 250, 65, 8, 3, 2, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_SUBARTC_BELOW | HZ_SUBTROPIC | HZ_ZON5 | HZ_ZON4), + M( 0, 1960, 15, 70, STR_203F_HOUSES, 75, 6, 3, 1, 0, 1, + TILE_SIZE_1x1, + HZ_SUBARTC_BELOW | HZ_ZON2 | HZ_ZON1), + M( 0, 1960, 15, 70, STR_203F_HOUSES, 75, 6, 3, 1, 0, 1, + TILE_SIZE_1x1, + HZ_SUBARTC_ABOVE | HZ_ZON2 | HZ_ZON1), + M( 1945, MAX_YEAR, 35, 210, STR_2040_CINEMA, 230, 23, 8, 2, 2, 0, + TILE_SIZE_1x1, + HZ_TEMP | HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 1983, MAX_YEAR, 180, 250, STR_2041_SHOPPING_MALL, 300, 5, 8, 2, 3, 0, + TILE_SIZE_2x2, + HZ_TEMP | HZ_ZON5 | HZ_ZON4 | HZ_ZON3 |HZ_ZON2), + M( 1983, MAX_YEAR, 0, 250, STR_2041_SHOPPING_MALL, 300, 5, 8, 2, 3, 0, + TILE_NO_FLAG, + HZ_NOZNS), + M( 1983, MAX_YEAR, 0, 250, STR_2041_SHOPPING_MALL, 300, 5, 8, 2, 3, 0, + TILE_NO_FLAG, + HZ_NOZNS), + M( 1983, MAX_YEAR, 0, 250, STR_2041_SHOPPING_MALL, 300, 5, 8, 2, 3, 0, + TILE_NO_FLAG, + HZ_NOZNS), + M( 0, MAX_YEAR, 80, 100, STR_2038_FLATS, 90, 20, 5, 2, 0, 2, + TILE_SIZE_1x1, + HZ_SUBARTC_BELOW | HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 0, MAX_YEAR, 80, 100, STR_2038_FLATS, 90, 20, 5, 2, 0, 2, + TILE_SIZE_1x1, + HZ_SUBARTC_ABOVE | HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 0, MAX_YEAR, 16, 70, STR_203F_HOUSES, 70, 6, 3, 1, 0, 2, + TILE_SIZE_1x1, + HZ_SUBARTC_BELOW | HZ_ZON4 | HZ_ZON3 | HZ_ZON2 | HZ_ZON1), + M( 0, MAX_YEAR, 16, 70, STR_203F_HOUSES, 70, 6, 3, 1, 0, 2, + TILE_SIZE_1x1, + HZ_SUBARTC_ABOVE | HZ_ZON4 | HZ_ZON3 | HZ_ZON2 | HZ_ZON1), + M( 0, 1963, 14, 80, STR_203F_HOUSES, 70, 6, 3, 1, 0, 2, + TILE_SIZE_1x1, + HZ_SUBARTC_BELOW | HZ_ZON3 | HZ_ZON2 | HZ_ZON1), + M( 0, 1963, 14, 80, STR_203F_HOUSES, 70, 6, 3, 1, 0, 2, + TILE_SIZE_1x1, + HZ_SUBARTC_ABOVE | HZ_ZON3 | HZ_ZON2 | HZ_ZON1), + M( 1966, MAX_YEAR, 135, 150, STR_200F_TALL_OFFICE_BLOCK, 120, 60, 8, 3, 4, 0, + TILE_SIZE_1x1, + HZ_SUBARTC_BELOW | HZ_SUBTROPIC | HZ_ZON5 | HZ_ZON4), + M( 1966, MAX_YEAR, 135, 150, STR_200F_TALL_OFFICE_BLOCK, 120, 60, 8, 3, 4, 0, + TILE_SIZE_1x1, + HZ_SUBARTC_ABOVE | HZ_ZON5 | HZ_ZON4), + M( 1970, MAX_YEAR, 170, 170, STR_200F_TALL_OFFICE_BLOCK, 130, 70, 9, 3, 4, 0, + TILE_SIZE_1x1, + HZ_SUBARTC_BELOW | HZ_ZON5 | HZ_ZON4), + M( 1970, MAX_YEAR, 170, 170, STR_200F_TALL_OFFICE_BLOCK, 130, 70, 9, 3, 4, 0, + TILE_SIZE_1x1, + HZ_SUBARTC_ABOVE | HZ_ZON5 | HZ_ZON4), + M( 1974, MAX_YEAR, 210, 200, STR_200F_TALL_OFFICE_BLOCK, 140, 80, 10, 3, 5, 0, + TILE_SIZE_1x1, + HZ_SUBARTC_BELOW | HZ_SUBTROPIC | HZ_ZON5 | HZ_ZON4), + M( 1974, MAX_YEAR, 210, 200, STR_200F_TALL_OFFICE_BLOCK, 140, 80, 10, 3, 5, 0, + TILE_SIZE_1x1, + HZ_SUBARTC_ABOVE | HZ_ZON5 | HZ_ZON4), + M( 0, MAX_YEAR, 10, 60, STR_203F_HOUSES, 60, 5, 2, 1, 0, 1, + TILE_SIZE_1x1, + HZ_SUBARTC_BELOW | HZ_ZON1), + M( 0, MAX_YEAR, 10, 60, STR_203F_HOUSES, 60, 5, 2, 1, 0, 1, + TILE_SIZE_1x1, + HZ_SUBARTC_ABOVE | HZ_ZON1), + M( 0, MAX_YEAR, 25, 100, STR_201A_SHOPS_AND_OFFICES, 80, 20, 3, 1, 0, 1, + TILE_SIZE_1x1, + HZ_SUBARTC_BELOW | HZ_ZON5 | HZ_ZON4 | HZ_ZON3 | HZ_ZON2), + M( 0, MAX_YEAR, 25, 100, STR_201A_SHOPS_AND_OFFICES, 80, 20, 3, 1, 0, 1, + TILE_SIZE_1x1, + HZ_SUBARTC_ABOVE | HZ_ZON5 | HZ_ZON4 | HZ_ZON3 | HZ_ZON2), + M( 0, MAX_YEAR, 6, 85, STR_2012_CHURCH, 230, 2, 2, 0, 0, 0, + BUILDING_IS_CHURCH | TILE_SIZE_1x1, + HZ_SUBARTC_BELOW | HZ_ZON4 | HZ_ZON3 | HZ_ZON2 | HZ_ZON1), + M( 0, MAX_YEAR, 6, 85, STR_2012_CHURCH, 230, 2, 2, 0, 0, 0, + BUILDING_IS_CHURCH | TILE_SIZE_1x1, + HZ_SUBARTC_ABOVE | HZ_ZON4 | HZ_ZON3 | HZ_ZON2 | HZ_ZON1), + M( 0, MAX_YEAR, 17, 80, STR_203F_HOUSES, 80, 7, 3, 1, 0, 1, + TILE_SIZE_1x1, + HZ_SUBARTC_BELOW | HZ_ZON3 | HZ_ZON2 | HZ_ZON1), + M( 0, MAX_YEAR, 17, 80, STR_203F_HOUSES, 80, 7, 3, 1, 0, 1, + TILE_SIZE_1x1, + HZ_SUBARTC_ABOVE | HZ_ZON3 | HZ_ZON2 | HZ_ZON1), + M( 0, 1960, 90, 140, STR_201A_SHOPS_AND_OFFICES, 110, 45, 6, 2, 3, 0, + TILE_SIZE_1x1, + HZ_SUBARTC_BELOW| HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 0, 1960, 90, 140, STR_201A_SHOPS_AND_OFFICES, 110, 45, 6, 2, 3, 0, + TILE_SIZE_1x1, + HZ_SUBARTC_ABOVE| HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 1972, MAX_YEAR, 140, 160, STR_2015_HOTEL, 160, 25, 6, 1, 0, 3, + TILE_SIZE_1x2, + HZ_SUBARTC_BELOW| HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 1972, MAX_YEAR, 0, 160, STR_2015_HOTEL, 160, 25, 6, 1, 2, 0, + TILE_NO_FLAG, + HZ_NOZNS), + M( 1972, MAX_YEAR, 140, 160, STR_2015_HOTEL, 160, 25, 6, 1, 0, 3, + TILE_SIZE_1x2, + HZ_SUBARTC_ABOVE| HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 1972, MAX_YEAR, 0, 160, STR_2015_HOTEL, 160, 25, 6, 1, 2, 0, + TILE_NO_FLAG, + HZ_NOZNS), + M( 1963, MAX_YEAR, 105, 130, STR_201A_SHOPS_AND_OFFICES, 105, 50, 7, 2, 3, 0, + TILE_SIZE_1x1, + HZ_SUBARTC_BELOW | HZ_SUBTROPIC | HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 1963, MAX_YEAR, 105, 130, STR_201A_SHOPS_AND_OFFICES, 105, 50, 7, 2, 3, 0, + TILE_SIZE_1x1, + HZ_SUBARTC_ABOVE| HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 1978, MAX_YEAR, 190, 190, STR_200F_TALL_OFFICE_BLOCK, 135, 75, 9, 3, 4, 0, + TILE_SIZE_1x1, + HZ_SUBARTC_BELOW | HZ_ZON5 | HZ_ZON4), + M( 1978, MAX_YEAR, 190, 190, STR_200F_TALL_OFFICE_BLOCK, 135, 75, 9, 3, 4, 0, + TILE_SIZE_1x1, + HZ_SUBARTC_ABOVE | HZ_ZON5 | HZ_ZON4), + M( 1967, MAX_YEAR, 250, 140, STR_200F_TALL_OFFICE_BLOCK, 200, 60, 7, 2, 2, 0, + TILE_SIZE_2x1, + HZ_SUBARTC_BELOW| HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 1967, MAX_YEAR, 0, 140, STR_200F_TALL_OFFICE_BLOCK, 200, 60, 7, 2, 2, 0, + TILE_NO_FLAG, + HZ_NOZNS), + M( 1967, MAX_YEAR, 250, 140, STR_200F_TALL_OFFICE_BLOCK, 200, 60, 7, 2, 2, 0, + TILE_SIZE_2x1, + HZ_SUBARTC_ABOVE | HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 1967, MAX_YEAR, 0, 140, STR_200F_TALL_OFFICE_BLOCK, 200, 60, 7, 2, 2, 0, + TILE_NO_FLAG, + HZ_NOZNS), + M( 0, MAX_YEAR, 16, 80, STR_203F_HOUSES, 80, 6, 3, 1, 0, 2, + TILE_SIZE_1x1, + HZ_SUBTROPIC | HZ_ZON5 | HZ_ZON4 | HZ_ZON3 | HZ_ZON2), + M( 0, MAX_YEAR, 16, 80, STR_203F_HOUSES, 80, 6, 3, 1, 0, 2, + TILE_SIZE_1x1, + HZ_SUBTROPIC | HZ_ZON5 | HZ_ZON4 | HZ_ZON3 | HZ_ZON2), + M( 0, MAX_YEAR, 16, 80, STR_203F_HOUSES, 80, 5, 3, 1, 0, 2, + TILE_SIZE_1x1, + HZ_SUBTROPIC | HZ_ZON5 | HZ_ZON4 | HZ_ZON3 | HZ_ZON2), + M( 0, MAX_YEAR, 7, 30, STR_203F_HOUSES, 30, 4, 3, 1, 0, 1, + TILE_SIZE_1x1, + HZ_SUBTROPIC | HZ_ZON1), + M( 0, MAX_YEAR, 45, 130, STR_2038_FLATS, 95, 15, 6, 2, 1, 0, + TILE_SIZE_1x1, + HZ_SUBTROPIC | HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 0, MAX_YEAR, 8, 90, STR_2012_CHURCH, 200, 3, 2, 0, 0, 0, + BUILDING_IS_CHURCH | TILE_SIZE_1x1, + HZ_SUBTROPIC | HZ_ZON4 | HZ_ZON3 | HZ_ZON2), + M( 0, MAX_YEAR, 18, 80, STR_203F_HOUSES, 80, 7, 3, 1, 0, 2, + TILE_SIZE_1x1, + HZ_SUBTROPIC | HZ_ZON5 | HZ_ZON4 | HZ_ZON3 | HZ_ZON2), + M( 1973, MAX_YEAR, 90, 110, STR_2038_FLATS, 95, 24, 6, 2, 1, 0, + TILE_SIZE_1x1, + HZ_SUBTROPIC | HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 1962, MAX_YEAR, 120, 120, STR_2038_FLATS, 95, 25, 6, 2, 1, 0, + TILE_SIZE_1x1, + HZ_SUBTROPIC | HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 1984, MAX_YEAR, 250, 190, STR_200F_TALL_OFFICE_BLOCK, 140, 80, 8, 3, 4, 0, + TILE_SIZE_2x1, + HZ_SUBTROPIC | HZ_ZON5 | HZ_ZON4), + M( 1984, MAX_YEAR, 0, 190, STR_200F_TALL_OFFICE_BLOCK, 140, 80, 8, 3, 4, 0, + TILE_NO_FLAG, + HZ_SUBTROPIC), + M( 0, MAX_YEAR, 80, 110, STR_2038_FLATS, 95, 23, 6, 2, 1, 0, + TILE_SIZE_1x1, + HZ_SUBTROPIC | HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 1993, MAX_YEAR, 180, 180, STR_200F_TALL_OFFICE_BLOCK, 150, 90, 8, 3, 4, 0, + TILE_SIZE_1x1, + HZ_SUBTROPIC | HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 0, MAX_YEAR, 8, 90, STR_2012_CHURCH, 200, 3, 2, 0, 0, 0, + BUILDING_IS_CHURCH | TILE_SIZE_1x1, + HZ_TOYLND | HZ_ZON5 | HZ_ZON4 | HZ_ZON3 | HZ_ZON2 | HZ_ZON1), + M( 0, MAX_YEAR, 18, 90, STR_203F_HOUSES, 90, 5, 6, 2, 2, 0, + TILE_SIZE_1x1, + HZ_TOYLND | HZ_ZON5 | HZ_ZON4 | HZ_ZON3 | HZ_ZON2 | HZ_ZON1), + M( 0, MAX_YEAR, 7, 70, STR_203F_HOUSES, 50, 3, 3, 1, 1, 0, + TILE_SIZE_1x1, + HZ_TOYLND | HZ_ZON2 | HZ_ZON1), + M( 0, MAX_YEAR, 15, 80, STR_203F_HOUSES, 75, 6, 3, 1, 2, 0, + TILE_SIZE_1x1, + HZ_TOYLND | HZ_ZON4 | HZ_ZON3 | HZ_ZON2 | HZ_ZON1), + M( 0, MAX_YEAR, 17, 80, STR_203F_HOUSES, 75, 6, 3, 1, 2, 0, + TILE_SIZE_1x1, + HZ_TOYLND | HZ_ZON4 | HZ_ZON3 | HZ_ZON2 | HZ_ZON1), + M( 0, MAX_YEAR, 19, 80, STR_203F_HOUSES, 75, 6, 3, 1, 2, 0, + TILE_SIZE_1x1, + HZ_TOYLND | HZ_ZON4 | HZ_ZON3 | HZ_ZON2 | HZ_ZON1), + M( 0, MAX_YEAR, 21, 80, STR_203F_HOUSES, 75, 6, 3, 1, 2, 0, + TILE_SIZE_1x1, + HZ_TOYLND | HZ_ZON4 | HZ_ZON3 | HZ_ZON2 | HZ_ZON1), + M( 0, MAX_YEAR, 75, 160, STR_200F_TALL_OFFICE_BLOCK, 130, 20, 8, 4, 2, 0, + TILE_SIZE_1x1, + HZ_TOYLND | HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 0, MAX_YEAR, 35, 90, STR_203F_HOUSES, 80, 9, 4, 1, 2, 0, + TILE_SIZE_1x2, + HZ_TOYLND | HZ_ZON5 | HZ_ZON4 | HZ_ZON3 | HZ_ZON2 | HZ_ZON1), + M( 0, MAX_YEAR, 0, 90, STR_203F_HOUSES, 80, 0, 4, 1, 2, 0, + TILE_NO_FLAG, + HZ_NOZNS), + M( 0, MAX_YEAR, 85, 150, STR_200F_TALL_OFFICE_BLOCK, 130, 18, 8, 4, 2, 0, + TILE_SIZE_1x1, + HZ_TOYLND | HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 0, MAX_YEAR, 11, 60, STR_2059_IGLOO, 45, 3, 3, 1, 1, 0, + TILE_SIZE_1x1, + HZ_TOYLND | HZ_ZON1), + M( 0, MAX_YEAR, 10, 60, STR_205A_TEPEES, 45, 3, 3, 1, 1, 0, + TILE_SIZE_1x1, + HZ_TOYLND | HZ_ZON1), + M( 0, MAX_YEAR, 67, 140, STR_201A_SHOPS_AND_OFFICES, 130, 22, 8, 4, 0, 4, + TILE_SIZE_1x1, + HZ_TOYLND | HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 0, MAX_YEAR, 86, 145, STR_201A_SHOPS_AND_OFFICES, 130, 23, 8, 4, 0, 4, + TILE_SIZE_1x1, + HZ_TOYLND | HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 0, MAX_YEAR, 95, 165, STR_200F_TALL_OFFICE_BLOCK, 130, 28, 8, 4, 2, 0, + TILE_SIZE_1x1, + HZ_TOYLND | HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 0, MAX_YEAR, 30, 90, STR_2016_STATUE, 70, 10, 4, 1, 2, 0, + TILE_SIZE_1x1, + HZ_TOYLND | HZ_ZON5 | HZ_ZON4 | HZ_ZON3), + M( 0, MAX_YEAR, 25, 75, STR_205B_TEAPOT_HOUSE, 65, 8, 3, 1, 2, 0, + TILE_SIZE_1x1, + HZ_TOYLND | HZ_ZON5 | HZ_ZON4 | HZ_ZON3 | HZ_ZON2 | HZ_ZON1), + M( 0, MAX_YEAR, 18, 85, STR_205C_PIGGY_BANK, 95, 7, 3, 2, 0, 4, + TILE_SIZE_1x1, + HZ_TOYLND | HZ_ZON5 | HZ_ZON4 | HZ_ZON3 | HZ_ZON2 | HZ_ZON1), }; - -static const HousetypeYear _housetype_years[] = { - { 1963, MAX_YEAR }, - { 1957, MAX_YEAR }, - { 1968, MAX_YEAR }, - { 0, MAX_YEAR }, - { 1975, MAX_YEAR }, - { 1975, MAX_YEAR }, - { 0, MAX_YEAR }, - { 1959, MAX_YEAR }, - { 1959, MAX_YEAR }, - { 1945, MAX_YEAR }, - { 1945, MAX_YEAR }, - { 0, MAX_YEAR }, - { 1935, MAX_YEAR }, - { 1951, MAX_YEAR }, - { 1930, 1960 }, - { 1930, 1960 }, - { 1930, 1960 }, - { 1977, MAX_YEAR }, - { 1983, MAX_YEAR }, - { 1985, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, 1951 }, - { 0, 1952 }, - { 1931, MAX_YEAR }, - { 1935, MAX_YEAR }, - { 1963, MAX_YEAR }, - { 0, 1955 }, - { 1973, MAX_YEAR }, - { 0, MAX_YEAR }, - { 1958, MAX_YEAR }, - { 1958, MAX_YEAR }, - { 1958, MAX_YEAR }, - { 1958, MAX_YEAR }, - { 2000, MAX_YEAR }, - { 0, 1960 }, - { 0, 1960 }, - { 1945, MAX_YEAR }, - { 1983, MAX_YEAR }, - { 1983, MAX_YEAR }, - { 1983, MAX_YEAR }, - { 1983, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, 1963 }, - { 0, 1963 }, - { 1966, MAX_YEAR }, - { 1966, MAX_YEAR }, - { 1970, MAX_YEAR }, - { 1970, MAX_YEAR }, - { 1974, MAX_YEAR }, - { 1974, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, 1960 }, - { 0, 1960 }, - { 1972, MAX_YEAR }, - { 1972, MAX_YEAR }, - { 1972, MAX_YEAR }, - { 1972, MAX_YEAR }, - { 1963, MAX_YEAR }, - { 1963, MAX_YEAR }, - { 1978, MAX_YEAR }, - { 1978, MAX_YEAR }, - { 1967, MAX_YEAR }, - { 1967, MAX_YEAR }, - { 1967, MAX_YEAR }, - { 1967, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 1973, MAX_YEAR }, - { 1962, MAX_YEAR }, - { 1984, MAX_YEAR }, - { 1984, MAX_YEAR }, - { 0, MAX_YEAR }, - { 1993, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, - { 0, MAX_YEAR }, -}; -assert_compile(lengthof(_housetype_years) == HOUSE_MAX); - -static const byte _housetype_cargo_passengers[] = { - 8, 8, 8, 2, 10, 10, 4, 6, - 6, 2, 2, 2, 2, 8, 6, 6, - 6, 10, 6, 6, 4, 4, 4, 4, - 3, 3, 3, 7, 8, 6, 8, 8, - 4, 4, 4, 4, 8, 3, 3, 8, - 8, 8, 8, 8, 5, 5, 3, 3, - 3, 3, 8, 8, 9, 9, 10, 10, - 2, 2, 3, 3, 2, 2, 3, 3, - 6, 6, 6, 6, 6, 6, 7, 7, - 9, 9, 7, 7, 7, 7, 3, 3, - 3, 3, 6, 2, 3, 6, 6, 8, - 8, 6, 8, 2, 6, 3, 3, 3, - 3, 3, 8, 4, 4, 8, 3, 3, - 8, 8, 8, 4, 3, 3, -}; -assert_compile(lengthof(_housetype_cargo_passengers) == HOUSE_MAX); - -static const byte _housetype_cargo_mail[] = { - 3, 3, 3, 0, 4, 4, 1, 1, - 1, 0, 0, 0, 0, 2, 2, 2, - 2, 3, 3, 2, 0, 0, 0, 0, - 1, 1, 1, 2, 3, 2, 3, 2, - 0, 0, 0, 0, 3, 1, 1, 2, - 2, 2, 2, 2, 2, 2, 1, 1, - 1, 1, 3, 3, 3, 3, 3, 3, - 1, 1, 1, 1, 0, 0, 1, 1, - 2, 2, 1, 1, 1, 1, 2, 2, - 3, 3, 2, 2, 2, 2, 1, 1, - 1, 1, 2, 0, 1, 2, 2, 3, - 3, 2, 3, 0, 2, 1, 1, 1, - 1, 1, 4, 1, 1, 4, 1, 1, - 4, 4, 4, 1, 1, 2, -}; -assert_compile(lengthof(_housetype_cargo_mail) == HOUSE_MAX); - -static const byte _housetype_cargo_goods[] = { - 4, 4, 1, 0, 6, 6, 0, 2, - 2, 0, 0, 0, 0, 4, 3, 3, - 3, 6, 8, 6, 0, 0, 0, 0, - 0, 0, 0, 2, 3, 3, 3, 2, - 0, 0, 0, 0, 2, 0, 0, 2, - 3, 3, 3, 3, 0, 0, 0, 0, - 0, 0, 4, 4, 4, 4, 5, 5, - 0, 0, 0, 0, 0, 0, 0, 0, - 3, 3, 0, 2, 0, 2, 3, 3, - 4, 4, 2, 2, 2, 2, 0, 0, - 0, 0, 1, 0, 0, 1, 1, 4, - 4, 1, 4, 0, 2, 1, 2, 2, - 2, 2, 2, 2, 2, 2, 1, 1, - 0, 0, 2, 2, 2, 0 -}; -assert_compile(lengthof(_housetype_cargo_goods) == HOUSE_MAX); - -static const byte _housetype_cargo_food[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, - 0, 0, 0, 0, 2, 2, 2, 2, - 2, 2, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 0, 0, 1, 1, - 0, 0, 3, 0, 3, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 2, 2, - 2, 1, 0, 0, 2, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 4, 4, 0, 0, 0, 4 -}; -assert_compile(lengthof(_housetype_cargo_food) == HOUSE_MAX); - -static const byte _house_more_flags[] = { - 8, 8, 8, 8, 8, 8, 8, 12, - 0, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 15, 0, 0, 0, - 8, 8, 8, 8, 8, 8, 8, 8, - 15, 0, 0, 0, 8, 8, 8, 8, - 15, 0, 0, 0, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 12, 0, 12, 0, 8, 8, - 8, 8, 10, 0, 10, 0, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 10, - 0, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 12, 0, 8, 8, 8, - 8, 8, 8, 8, 8, 8, -}; -assert_compile(lengthof(_house_more_flags) == HOUSE_MAX); +#undef M +assert_compile(lengthof(_original_house_specs) == NEW_HOUSE_OFFSET); diff --git a/src/town.h b/src/town.h --- a/src/town.h +++ b/src/town.h @@ -5,9 +5,72 @@ #include "oldpool.h" #include "player.h" +#include "functions.h" +#include "helpers.hpp" enum { - INVALID_TOWN = 0xFFFF, + HOUSE_NO_CLASS = 0, + NEW_HOUSE_OFFSET = 110, + HOUSE_MAX = 512, + INVALID_TOWN = 0xFFFF, + INVALID_HOUSE_ID = 0xFFFF, + + /* There can only be as many classes as there are new houses, plus one for + * NO_CLASS, as the original houses don't have classes. */ + HOUSE_CLASS_MAX = HOUSE_MAX - NEW_HOUSE_OFFSET + 1, +}; + +enum BuildingFlags { + TILE_NO_FLAG = 0, + TILE_SIZE_1x1 = 1U << 0, + TILE_NOT_SLOPED = 1U << 1, + TILE_SIZE_2x1 = 1U << 2, + TILE_SIZE_1x2 = 1U << 3, + TILE_SIZE_2x2 = 1U << 4, + BUILDING_IS_ANIMATED = 1U << 5, + BUILDING_IS_CHURCH = 1U << 6, + BUILDING_IS_STADIUM = 1U << 7, + BUILDING_HAS_1_TILE = TILE_SIZE_1x1 | TILE_SIZE_2x1 | TILE_SIZE_1x2 | TILE_SIZE_2x2, + BUILDING_2_TILES_X = TILE_SIZE_2x1 | TILE_SIZE_2x2, + BUILDING_2_TILES_Y = TILE_SIZE_1x2 | TILE_SIZE_2x2, + BUILDING_HAS_4_TILES = TILE_SIZE_2x2, +}; + +DECLARE_ENUM_AS_BIT_SET(BuildingFlags) + +enum HouseZones { ///< Bit Value Meaning + HZ_NOZNS = 0x0000, ///< 0 This is just to get rid of zeros, meaning none + HZ_ZON1 = 0x0001, ///< 0..4 1,2,4,8,10 which town zones the building can be built in, Zone1 been the further suburb + HZ_ZON2 = 0x0002, + HZ_ZON3 = 0x0004, + HZ_ZON4 = 0x0008, + HZ_ZON5 = 0x0010, ///< center of town + HZ_ZONALL = 0x001F, ///< 1F This is just to englobe all above types at once + HZ_SUBARTC_ABOVE = 0x0800, ///< 11 800 can appear in sub-arctic climate above the snow line + HZ_TEMP = 0x1000, ///< 12 1000 can appear in temperate climate + HZ_SUBARTC_BELOW = 0x2000, ///< 13 2000 can appear in sub-arctic climate below the snow line + HZ_SUBTROPIC = 0x4000, ///< 14 4000 can appear in subtropical climate + HZ_TOYLND = 0x8000 ///< 15 8000 can appear in toyland climate +}; + +DECLARE_ENUM_AS_BIT_SET(HouseZones) + +enum HouseExtraFlags { + NO_EXTRA_FLAG = 0, + BUILDING_IS_HISTORICAL = 1U << 0, ///< this house will only appear during town generation in random games, thus the historical + BUILDING_IS_PROTECTED = 1U << 1, ///< towns and AI will not remove this house, while human players will be able tp + SYNCHRONISED_CALLBACK_1B = 1U << 2, ///< synchronized callback 1B will be performed, on multi tile houses + CALLBACK_1A_RANDOM_BITS = 1U << 3, ///< callback 1A needs random bits +}; + +DECLARE_ENUM_AS_BIT_SET(HouseExtraFlags) + +typedef uint16 HouseID; +typedef uint16 HouseClassID; + +struct BuildingCounts { + uint8 id_count[HOUSE_MAX]; + uint8 class_count[HOUSE_CLASS_MAX]; }; struct Town { @@ -78,8 +141,48 @@ struct Town { // NOSAVE: UpdateTownRadius updates this given the house count. uint16 radius[5]; + + // NOSAVE: The number of each type of building in the town. + BuildingCounts building_counts; }; +struct HouseSpec { + /* Standard properties */ + Year min_date; ///< introduction year of the house + Year max_date; ///< last year it can be built + byte population; ///< population (Zero on other tiles in multi tile house.) + byte removal_cost; ///< cost multiplier for removing it + StringID building_name; ///< building name + uint16 remove_rating_decrease; ///< rating decrease if removed + byte mail_generation; ///< mail generation multiplier (tile based, as the acceptances below) + byte passenger_acceptance; ///< passenger acceptance, given in 1/8th unit, max is 8, as the 3 next properies + byte mail_acceptance; ///< mail acceptance + byte goods_acceptance; ///< good acceptance + byte food_acceptance; ///< food (or fizzy drink) acceptance + BuildingFlags building_flags; ///< some flags that describe the house (size, stadium etc...) + HouseZones building_availability; ///< where can it be built (climates, zones) + bool enabled; ///< the house is still avaible (by default, true.newgrf can disable it, though) + + /* NewHouses properties */ + HouseID substitute_id; ///< which house this one is based on + struct SpriteGroup *spritegroup; ///< pointer to the different sprites of the house + HouseID override; ///< which house this one replaces + uint16 callback_mask; ///< House callback flags + byte random_colour[4]; ///< 4 "random" colours + byte probability; ///< Relative probability of appearing (16 is the standard value) + HouseExtraFlags extra_flags; ///< some more flags + HouseClassID class_id; ///< defines the class this house has (grf file based) @See HouseGetVariable, prop 0x44 + byte animation_frames; ///< number of animation frames + byte animation_speed; ///< amount of time between each of those frames + byte processing_time; ///< Periodic refresh multiplier + + /* grf file related properties*/ + uint8 local_id; ///< id defined by the grf file for this house + const struct GRFFile *grffile; ///< grf file that introduced this house +}; + +VARDEF HouseSpec _house_specs[HOUSE_MAX]; + uint32 GetWorldPopulation(); void UpdateTownVirtCoord(Town *t); @@ -158,6 +261,12 @@ VARDEF const Town** _town_sort; DECLARE_OLD_POOL(Town, Town, 3, 8000) +static inline HouseSpec *GetHouseSpecs(HouseID house_id) +{ + assert(house_id < HOUSE_MAX); + return &_house_specs[house_id]; +} + /** * Check if a Town really exists. */ @@ -229,4 +338,9 @@ VARDEF byte _town_sort_order; VARDEF Town *_cleared_town; VARDEF int _cleared_town_rating; +uint OriginalTileRandomiser(uint x, uint y); +void ResetHouses(); + +void ClearTownHouse(Town *t, TileIndex tile); + #endif /* TOWN_H */ diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp --- a/src/town_cmd.cpp +++ b/src/town_cmd.cpp @@ -5,6 +5,7 @@ #include "stdafx.h" #include "openttd.h" #include "functions.h" +#include "debug.h" #include "strings.h" #include "road_map.h" #include "table/strings.h" @@ -19,6 +20,7 @@ #include "gfx.h" #include "industry.h" #include "station.h" +#include "vehicle.h" #include "player.h" #include "news.h" #include "saveload.h" @@ -32,6 +34,9 @@ #include "date.h" #include "table/town_land.h" #include "genworld.h" +#include "newgrf.h" +#include "newgrf_callbacks.h" +#include "newgrf_house.h" /** * Called if a new block is added to the town-pool @@ -91,7 +96,6 @@ void DestroyTown(Town *t) static int _grow_town_result; static bool BuildTownHouse(Town *t, TileIndex tile); -static void ClearTownHouse(Town *t, TileIndex tile); static void DoBuildTownHouse(Town *t, TileIndex tile); static void TownDrawHouseLift(const TileInfo *ti) @@ -104,24 +108,31 @@ static TownDrawTileProc * const _town_dr TownDrawHouseLift }; +uint OriginalTileRandomiser(uint x, uint y) +{ + uint variant; + variant = x >> 4; + variant ^= x >> 6; + variant ^= y >> 4; + variant -= y >> 6; + variant &= 3; + return variant; +} static void DrawTile_Town(TileInfo *ti) { const DrawBuildingsTileStruct *dcts; SpriteID image; SpriteID pal; + HouseID house_id = GetHouseType(ti->tile); + + if (house_id >= NEW_HOUSE_OFFSET) { + DrawNewHouseTile(ti, house_id); + return; + } /* Retrieve pointer to the draw town tile struct */ - { - /* this "randomizes" on the (up to) 4 variants of a building */ - uint variant; - variant = ti->x >> 4; - variant ^= ti->x >> 6; - variant ^= ti->y >> 4; - variant -= ti->y >> 6; - variant &= 3; - dcts = &_town_draw_tile_data[GetHouseType(ti->tile) << 4 | variant << 2 | GetHouseBuildingStage(ti->tile)]; - } + dcts = &_town_draw_tile_data[house_id << 4 | OriginalTileRandomiser(ti->x, ti->y) << 2 | GetHouseBuildingStage(ti->tile)]; if (ti->tileh != SLOPE_FLAT) DrawFoundation(ti, ti->tileh); @@ -172,18 +183,23 @@ static void AnimateTile_Town(TileIndex t { int pos, dest; + if (GetHouseType(tile) >= NEW_HOUSE_OFFSET) { + AnimateNewHouseTile(tile); + return; + } + if (_tick_counter & 3) return; // If the house is not one with a lift anymore, then stop this animating. // Not exactly sure when this happens, but probably when a house changes. // Before this was just a return...so it'd leak animated tiles.. // That bug seems to have been here since day 1?? - if (!(_housetype_extra_flags[GetHouseType(tile)] & 0x20)) { + if (!(GetHouseSpecs(GetHouseType(tile))->building_flags & BUILDING_IS_ANIMATED)) { DeleteAnimatedTile(tile); return; } - if (!IsLiftMoving(tile)) { + if (!LiftHasDestination(tile)) { int i; /** Building has 6 floors, number 0 .. 6, where 1 is illegal. @@ -270,44 +286,53 @@ static void MakeSingleHouseBigger(TileIn IncHouseConstructionTick(tile); if (GetHouseConstructionTick(tile) != 0) return; - IncHouseBuildingStage(tile); /*increase construction stage of one more step*/ + if (HASBIT(GetHouseSpecs(GetHouseType(tile))->callback_mask, CBM_CONSTRUCTION_STATE_CHANGE)) { + uint16 callback_res = GetHouseCallback(CBID_CONSTRUCTION_STATE_CHANGE, 0, GetHouseType(tile), GetTownByTile(tile), tile); + if (callback_res != CALLBACK_FAILED) ChangeHouseAnimationFrame(tile, callback_res); + } - if (GetHouseBuildingStage(tile) == TOWN_HOUSE_COMPLETED){ - /*Now, construction is completed. Can add population of building to the town*/ - ChangePopulation(GetTownByTile(tile), _housetype_population[GetHouseType(tile)]); + if (IsHouseCompleted(tile)) { + /* Now that construction is complete, we can add the population of the + * building to the town. */ + ChangePopulation(GetTownByTile(tile), GetHouseSpecs(GetHouseType(tile))->population); } MarkTileDirtyByTile(tile); } static void MakeTownHouseBigger(TileIndex tile) { - uint flags = _house_more_flags[GetHouseType(tile)]; - if (flags & 8) MakeSingleHouseBigger(TILE_ADDXY(tile, 0, 0)); - if (flags & 4) MakeSingleHouseBigger(TILE_ADDXY(tile, 0, 1)); - if (flags & 2) MakeSingleHouseBigger(TILE_ADDXY(tile, 1, 0)); - if (flags & 1) MakeSingleHouseBigger(TILE_ADDXY(tile, 1, 1)); + uint flags = GetHouseSpecs(GetHouseType(tile))->building_flags; + if (flags & BUILDING_HAS_1_TILE) MakeSingleHouseBigger(TILE_ADDXY(tile, 0, 0)); + if (flags & BUILDING_2_TILES_Y) MakeSingleHouseBigger(TILE_ADDXY(tile, 0, 1)); + if (flags & BUILDING_2_TILES_X) MakeSingleHouseBigger(TILE_ADDXY(tile, 1, 0)); + if (flags & BUILDING_HAS_4_TILES) MakeSingleHouseBigger(TILE_ADDXY(tile, 1, 1)); } static void TileLoop_Town(TileIndex tile) { - int house; Town *t; uint32 r; + HouseID house_id = GetHouseType(tile); + HouseSpec *hs = GetHouseSpecs(house_id); - if (GetHouseBuildingStage(tile) != TOWN_HOUSE_COMPLETED) { + /* NewHouseTileLoop returns false if Callback 21 succeeded, i.e. the house + * doesn't exist any more, so don't continue here. */ + if (house_id >= NEW_HOUSE_OFFSET && !NewHouseTileLoop(tile)) return; + + if (!IsHouseCompleted(tile)) { /*Construction is not completed. See if we can go further in construction*/ MakeTownHouseBigger(tile); return; } - house = GetHouseType(tile); - if ((_housetype_extra_flags[house] & 0x20) && !LiftHasDestination(tile) && CHANCE16(1, 2) && AddAnimatedTile(tile)) BeginLiftMovement(tile); + /* If the lift has a destination, it is already an animated tile. */ + if ((hs->building_flags & BUILDING_IS_ANIMATED) && house_id < NEW_HOUSE_OFFSET && !LiftHasDestination(tile) && CHANCE16(1, 2)) AddAnimatedTile(tile); t = GetTownByTile(tile); r = Random(); - if (GB(r, 0, 8) < _housetype_population[house]) { + if (GB(r, 0, 8) < hs->population) { uint amt = GB(r, 0, 8) / 8 + 1; uint moved; @@ -317,7 +342,7 @@ static void TileLoop_Town(TileIndex tile t->new_act_pass += moved; } - if (GB(r, 8, 8) < _housetype_mailamount[house] ) { + if (GB(r, 8, 8) < hs->mail_generation) { uint amt = GB(r, 8, 8) / 8 + 1; uint moved; @@ -327,18 +352,18 @@ static void TileLoop_Town(TileIndex tile t->new_act_mail += moved; } - if (_house_more_flags[house] & 8 && HASBIT(t->flags12, TOWN_IS_FUNDED) && --t->time_until_rebuild == 0) { + _current_player = OWNER_TOWN; + + if (hs->building_flags & BUILDING_HAS_1_TILE && HASBIT(t->flags12, TOWN_IS_FUNDED) && CanDeleteHouse(tile) && --t->time_until_rebuild == 0) { t->time_until_rebuild = GB(r, 16, 6) + 130; - _current_player = OWNER_TOWN; - ClearTownHouse(t, tile); // rebuild with another house? if (GB(r, 24, 8) >= 12) DoBuildTownHouse(t, tile); + } - _current_player = OWNER_NONE; - } + _current_player = OWNER_NONE; } static void ClickTile_Town(TileIndex tile) @@ -348,16 +373,17 @@ static void ClickTile_Town(TileIndex til static int32 ClearTile_Town(TileIndex tile, byte flags) { - int house, rating; + int rating; int32 cost; Town *t; + HouseSpec *hs = GetHouseSpecs(GetHouseType(tile)); if (flags&DC_AUTO && !(flags&DC_AI_BUILDING)) return_cmd_error(STR_2004_BUILDING_MUST_BE_DEMOLISHED); + if (!CanDeleteHouse(tile)) return CMD_ERROR; - house = GetHouseType(tile); - cost = _price.remove_house * _housetype_remove_cost[house] >> 8; + cost = _price.remove_house * hs->removal_cost >> 8; - rating = _housetype_remove_ratingmod[house]; + rating = hs->remove_rating_decrease; _cleared_town_rating += rating; _cleared_town = t = GetTownByTile(tile); @@ -378,18 +404,18 @@ static int32 ClearTile_Town(TileIndex ti static void GetAcceptedCargo_Town(TileIndex tile, AcceptedCargo ac) { - byte type = GetHouseType(tile); + HouseSpec *hs = GetHouseSpecs(GetHouseType(tile)); - ac[CT_PASSENGERS] = _housetype_cargo_passengers[type]; - ac[CT_MAIL] = _housetype_cargo_mail[type]; - ac[CT_GOODS] = _housetype_cargo_goods[type]; - ac[CT_FOOD] = _housetype_cargo_food[type]; + ac[CT_PASSENGERS] = hs->passenger_acceptance; + ac[CT_MAIL] = hs->mail_acceptance; + ac[CT_GOODS] = hs->goods_acceptance; + ac[CT_FOOD] = hs->food_acceptance; } static void GetTileDesc_Town(TileIndex tile, TileDesc *td) { - td->str = _town_tile_names[GetHouseType(tile)]; - if (GetHouseBuildingStage(tile) != TOWN_HOUSE_COMPLETED) { + td->str = GetHouseSpecs(GetHouseType(tile))->building_name; + if (!IsHouseCompleted(tile)) { SetDParamX(td->dparam, 0, td->str); td->str = STR_2058_UNDER_CONSTRUCTION; } @@ -1186,10 +1212,11 @@ static void DoBuildTownHouse(Town *t, Ti { int i; uint bitmask; - int house; + HouseID house; Slope slope; uint z; uint oneof = 0; + HouseSpec *hs; // Above snow? slope = GetTileSlope(tile, &z); @@ -1208,45 +1235,63 @@ static void DoBuildTownHouse(Town *t, Ti // bits 11-15 are used // bits 5-10 are not used. { - byte houses[lengthof(_housetype_flags)]; + HouseID houses[HOUSE_MAX]; int num = 0; + uint cumulative_probs[HOUSE_MAX]; + uint probability_max = 0; // Generate a list of all possible houses that can be built. - for (i=0; i!=lengthof(_housetype_flags); i++) { - if ((~_housetype_flags[i] & bitmask) == 0) - houses[num++] = (byte)i; + for (i = 0; i < HOUSE_MAX; i++) { + hs = GetHouseSpecs(i); + if ((~hs->building_availability & bitmask) == 0 && hs->enabled) { + if (_have_newhouses) { + probability_max += hs->probability; + cumulative_probs[num] = probability_max; + } + houses[num++] = (HouseID)i; + } } for (;;) { - house = houses[RandomRange(num)]; + if (_have_newhouses) { + uint r = RandomRange(probability_max); + for (i = 0; i < num; i++) if (cumulative_probs[i] >= r) break; + + house = houses[i]; + } else { + house = houses[RandomRange(num)]; + } + + hs = GetHouseSpecs(house); - if (_cur_year < _housetype_years[house].min || _cur_year > _housetype_years[house].max) - continue; + if (_have_newhouses) { + if (hs->override != 0) hs = GetHouseSpecs(hs->override); + + if ((hs->extra_flags & BUILDING_IS_HISTORICAL) && !_generating_world) continue; + + if (HASBIT(hs->callback_mask, CBM_HOUSE_ALLOW_CONSTRUCTION)) { + uint16 callback_res = GetHouseCallback(CBID_HOUSE_ALLOW_CONSTRUCTION, 0, house, t, tile); + if (callback_res != CALLBACK_FAILED && callback_res == 0) continue; + } + } + + if (_cur_year < hs->min_date || _cur_year > hs->max_date) continue; // Special houses that there can be only one of. - switch (house) { - case HOUSE_TEMP_CHURCH: - case HOUSE_ARCT_CHURCH: - case HOUSE_SNOW_CHURCH: - case HOUSE_TROP_CHURCH: - case HOUSE_TOY_CHURCH: - SETBIT(oneof, TOWN_HAS_CHURCH); - break; - case HOUSE_STADIUM: - case HOUSE_MODERN_STADIUM: - SETBIT(oneof, TOWN_HAS_STADIUM); - break; - default: - oneof = 0; - break; + if (hs->building_flags & BUILDING_IS_CHURCH) { + SETBIT(oneof, TOWN_HAS_CHURCH); + } else if (hs->building_flags & BUILDING_IS_STADIUM) { + SETBIT(oneof, TOWN_HAS_STADIUM); + } else { + oneof = 0; } if (HASBITS(t->flags12 , oneof)) continue; // Make sure there is no slope? - if (_housetype_extra_flags[house] & 0x12 && slope != SLOPE_FLAT) continue; + if (hs->building_flags & TILE_NOT_SLOPED && slope != SLOPE_FLAT) continue; - if (_housetype_extra_flags[house] & 0x10) { + if (hs->building_flags & TILE_SIZE_2x2) { if (CheckFree2x2Area(tile) || CheckFree2x2Area(tile += TileDiffXY(-1, 0)) || CheckFree2x2Area(tile += TileDiffXY( 0, -1)) || @@ -1254,14 +1299,14 @@ static void DoBuildTownHouse(Town *t, Ti break; } tile += TileDiffXY(0, 1); - } else if (_housetype_extra_flags[house] & 4) { + } else if (hs->building_flags & TILE_SIZE_2x1) { if (CheckBuildHouseMode(tile + TileDiffXY(1, 0), slope, 0)) break; if (CheckBuildHouseMode(tile + TileDiffXY(-1, 0), slope, 1)) { tile += TileDiffXY(-1, 0); break; } - } else if (_housetype_extra_flags[house] & 8) { + } else if (hs->building_flags & TILE_SIZE_1x2) { if (CheckBuildHouseMode(tile + TileDiffXY(0, 1), slope, 2)) break; if (CheckBuildHouseMode(tile + TileDiffXY(0, -1), slope, 3)) { @@ -1275,12 +1320,13 @@ static void DoBuildTownHouse(Town *t, Ti } t->num_houses++; + IncreaseBuildingCount(t, house); // Special houses that there can be only one of. t->flags12 |= oneof; { - byte construction_counter = 0, construction_stage = 0, size_flags; + byte construction_counter = 0, construction_stage = 0; if (_generating_world) { uint32 r = Random(); @@ -1289,13 +1335,12 @@ static void DoBuildTownHouse(Town *t, Ti if (CHANCE16(1, 7)) construction_stage = GB(r, 0, 2); if (construction_stage == TOWN_HOUSE_COMPLETED) { - ChangePopulation(t, _housetype_population[house]); + ChangePopulation(t, hs->population); } else { construction_counter = GB(r, 2, 2); } } - size_flags = GB(_housetype_extra_flags[house], 2, 3); - MakeTownHouse(tile, t->index, construction_counter, construction_stage, size_flags, house); + MakeTownHouse(tile, t->index, construction_counter, construction_stage, house, VehicleRandomBits()); } } @@ -1321,60 +1366,54 @@ static void DoClearTownHouseHelper(TileI DeleteAnimatedTile(tile); } -static void ClearTownHouse(Town *t, TileIndex tile) +void ClearTownHouse(Town *t, TileIndex tile) { - uint house = GetHouseType(tile); + HouseID house = GetHouseType(tile); uint eflags; + HouseSpec *hs; assert(IsTileType(tile, MP_HOUSE)); // need to align the tile to point to the upper left corner of the house if (house >= 3) { // house id 0,1,2 MUST be single tile houses, or this code breaks. - if (_housetype_extra_flags[house-1] & 0x04) { + if (GetHouseSpecs(house-1)->building_flags & TILE_SIZE_2x1) { house--; tile += TileDiffXY(-1, 0); - } else if (_housetype_extra_flags[house-1] & 0x18) { + } else if (GetHouseSpecs(house-1)->building_flags & BUILDING_2_TILES_Y) { house--; tile += TileDiffXY(0, -1); - } else if (_housetype_extra_flags[house-2] & 0x10) { + } else if (GetHouseSpecs(house-2)->building_flags & BUILDING_HAS_4_TILES) { house-=2; tile += TileDiffXY(-1, 0); - } else if (_housetype_extra_flags[house-3] & 0x10) { + } else if (GetHouseSpecs(house-3)->building_flags & BUILDING_HAS_4_TILES) { house-=3; tile += TileDiffXY(-1, -1); } } + hs = GetHouseSpecs(house); + // Remove population from the town if the house is finished. - if (GetHouseBuildingStage(tile) == TOWN_HOUSE_COMPLETED) { - ChangePopulation(t, -_housetype_population[house]); + if (IsHouseCompleted(tile)) { + ChangePopulation(t, -hs->population); } t->num_houses--; + DecreaseBuildingCount(t, house); // Clear flags for houses that only may exist once/town. - switch (house) { - case HOUSE_TEMP_CHURCH: - case HOUSE_ARCT_CHURCH: - case HOUSE_SNOW_CHURCH: - case HOUSE_TROP_CHURCH: - case HOUSE_TOY_CHURCH: - CLRBIT(t->flags12, TOWN_HAS_CHURCH); - break; - case HOUSE_STADIUM: - case HOUSE_MODERN_STADIUM: - CLRBIT(t->flags12, TOWN_HAS_STADIUM); - break; - default: - break; + if (hs->building_flags & BUILDING_IS_CHURCH) { + CLRBIT(t->flags12, TOWN_HAS_CHURCH); + } else if (hs->building_flags & BUILDING_IS_STADIUM) { + CLRBIT(t->flags12, TOWN_HAS_STADIUM); } // Do the actual clearing of tiles - eflags = _housetype_extra_flags[house]; + eflags = hs->building_flags; DoClearTownHouseHelper(tile); - if (eflags & 0x14) DoClearTownHouseHelper(tile + TileDiffXY(1, 0)); - if (eflags & 0x18) DoClearTownHouseHelper(tile + TileDiffXY(0, 1)); - if (eflags & 0x10) DoClearTownHouseHelper(tile + TileDiffXY(1, 1)); + if (eflags & BUILDING_2_TILES_X) DoClearTownHouseHelper(tile + TileDiffXY(1, 0)); + if (eflags & BUILDING_2_TILES_Y) DoClearTownHouseHelper(tile + TileDiffXY(0, 1)); + if (eflags & BUILDING_HAS_4_TILES) DoClearTownHouseHelper(tile + TileDiffXY(1, 1)); } /** Rename a town (server-only). @@ -1922,6 +1961,37 @@ static const SaveLoad _town_desc[] = { SLE_END() }; +/* Save and load the mapping between the house id on the map, and the grf file + * it came from. */ +static const SaveLoad _house_id_mapping_desc[] = { + SLE_VAR(HouseIDMapping, grfid, SLE_UINT32), + SLE_VAR(HouseIDMapping, house_id, SLE_UINT8), + SLE_VAR(HouseIDMapping, substitute_id, SLE_UINT8), + SLE_END() +}; + +static void Save_HOUSEIDS() +{ + uint i; + + for (i = 0; i != lengthof(_house_id_mapping); i++) { + SlSetArrayIndex(i); + SlObject(&_house_id_mapping[i], _house_id_mapping_desc); + } +} + +static void Load_HOUSEIDS() +{ + int index; + + ResetHouseIDMapping(); + + while ((index = SlIterateArray()) != -1) { + if ((uint)index >= lengthof(_house_id_mapping)) break; + SlObject(&_house_id_mapping[index], _house_id_mapping_desc); + } +} + static void Save_TOWN() { Town *t; @@ -1966,7 +2036,13 @@ void AfterLoadTown() _town_sort_dirty = true; } +extern const ChunkHandler _town_chunk_handlers[] = { + { 'HIDS', Save_HOUSEIDS, Load_HOUSEIDS, CH_ARRAY }, + { 'CITY', Save_TOWN, Load_TOWN, CH_ARRAY | CH_LAST}, +}; -extern const ChunkHandler _town_chunk_handlers[] = { - { 'CITY', Save_TOWN, Load_TOWN, CH_ARRAY | CH_LAST}, -}; +void ResetHouses() +{ + memset(&_house_specs, 0, sizeof(_house_specs)); + memcpy(&_house_specs, &_original_house_specs, sizeof(_original_house_specs)); +} diff --git a/src/town_map.h b/src/town_map.h --- a/src/town_map.h +++ b/src/town_map.h @@ -6,7 +6,14 @@ #define TOWN_MAP_H #include "town.h" +#include "date.h" +/** + * Get the index of which town this house/street is attached to. + * @param t the tile + * @pre IsTileType(t, MP_HOUSE) or IsTileType(t, MP_STREET) + * @return TownID + */ static inline TownID GetTownIndex(TileIndex t) { assert(IsTileType(t, MP_HOUSE) || IsTileType(t, MP_STREET)); // XXX incomplete @@ -15,13 +22,14 @@ static inline TownID GetTownIndex(TileIn /** * Set the town index for a road or house tile. - * @param tile the tile + * @param t the tile + * @pre IsTileType(t, MP_HOUSE) or IsTileType(t, MP_STREET) * @param index the index of the town * @pre IsTileType(t, MP_STREET) || IsTileType(t, MP_HOUSE) */ static inline void SetTownIndex(TileIndex t, TownID index) { - assert(IsTileType(t, MP_STREET) || IsTileType(t, MP_HOUSE)); + assert(IsTileType(t, MP_HOUSE) || IsTileType(t, MP_STREET)); _m[t].m2 = index; } @@ -35,84 +43,189 @@ static inline Town* GetTownByTile(TileIn return GetTown(GetTownIndex(t)); } - -static inline int GetHouseType(TileIndex t) +/** + * Get the type of this house, which is an index into the house spec array + * Since m4 is only a byte and we want to support 512 houses, we use the bit 6 + * of m3 as an additional bit to house type. + * @param t the tile + * @pre IsTileType(t, MP_HOUSE) + * @return house type + */ +static inline HouseID GetHouseType(TileIndex t) { assert(IsTileType(t, MP_HOUSE)); - return _m[t].m4; + return _m[t].m4 | (GB(_m[t].m3, 6, 1) << 8); } +/** + * Set the house type. + * @param t the tile + * @param house_id the new house type + * @pre IsTileType(t, MP_HOUSE) + */ +static inline void SetHouseType(TileIndex t, HouseID house_id) +{ + assert(IsTileType(t, MP_HOUSE)); + _m[t].m4 = GB(house_id, 0, 8); + SB(_m[t].m3, 6, 1, GB(house_id, 8, 1)); +} + +/** + * Check if the lift of this animated house has a destination + * @param t the tile + * @return has destination + */ static inline bool LiftHasDestination(TileIndex t) { - return HASBIT(_m[t].m5, 7); + return HASBIT(_me[t].m7, 0); } +/** + * Set the new destination of the lift for this animated house, and activate + * the LiftHasDestination bit. + * @param t the tile + * @param dest new destination + */ static inline void SetLiftDestination(TileIndex t, byte dest) { - SB(_m[t].m5, 0, 6, dest); - SETBIT(_m[t].m1, 7); /* Start moving */ + SETBIT(_me[t].m7, 0); + SB(_me[t].m7, 1, 3, dest); } +/** + * Get the current destination for this lift + * @param t the tile + * @return destination + */ static inline byte GetLiftDestination(TileIndex t) { - return GB(_m[t].m5, 0, 6); -} - -static inline bool IsLiftMoving(TileIndex t) -{ - return HASBIT(_m[t].m1, 7); + return GB(_me[t].m7, 1, 3); } -static inline void BeginLiftMovement(TileIndex t) -{ - SETBIT(_m[t].m5, 7); -} - +/** + * Stop the lift of this animated house from moving. + * Clears the first 4 bits of m7 at once, clearing the LiftHasDestination bit + * and the destination. + * @param t the tile + */ static inline void HaltLift(TileIndex t) { - CLRBIT(_m[t].m1, 7); - CLRBIT(_m[t].m5, 7); - SB(_m[t].m5, 0, 6, 0); - + SB(_me[t].m7, 0, 4, 0); DeleteAnimatedTile(t); } +/** + * Get the position of the lift on this animated house + * @param t the tile + * @return position, from 0 to 36 + */ static inline byte GetLiftPosition(TileIndex t) { - return GB(_m[t].m1, 0, 7); + return GB(_m[t].m6, 2, 6); +} + +/** + * Set the position of the lift on this animated house + * @param t the tile + * @param pos, from 0 to 36 + */ +static inline void SetLiftPosition(TileIndex t, byte pos) +{ + SB(_m[t].m6, 2, 6, pos); +} + +/** + * Get the current animation frame for this house + * @param t the tile + * @pre IsTileType(t, MP_HOUSE) + * @return frame number + */ +static inline byte GetHouseAnimationFrame(TileIndex t) +{ + assert(IsTileType(t, MP_HOUSE)); + return GB(_m[t].m6, 3, 5); } -static inline void SetLiftPosition(TileIndex t, byte pos) +/** + * Set a new animation frame for this house + * @param t the tile + * @param frame the new frame number + * @pre IsTileType(t, MP_HOUSE) + */ +static inline void SetHouseAnimationFrame(TileIndex t, byte frame) { - SB(_m[t].m1, 0, 7, pos); + assert(IsTileType(t, MP_HOUSE)); + SB(_m[t].m6, 3, 5, frame); +} + +/** + * Get the completion of this house + * @param t the tile + * @return true if it is, false if it is not + */ +static inline bool IsHouseCompleted(TileIndex t) +{ + assert(IsTileType(t, MP_HOUSE)); + return HASBIT(_m[t].m3, 7); } -static inline void MakeHouseTile(TileIndex t, TownID tid, byte counter, byte stage, byte type) +/** + * Mark this house as been completed + * @param t the tile + * @param status + */ +static inline void SetHouseCompleted(TileIndex t, bool status) +{ + assert(IsTileType(t, MP_HOUSE)); + SB(_m[t].m3, 7, 1, !!status); +} + +/** + * Make the tile a house. + * @param t tile index + * @param tid Town index + * @param counter of construction step + * @param stage of construction (used for drawing) + * @param type of house. Index into house specs array + * @param random_bits required for newgrf houses + * @pre IsTileType(t, MP_CLEAR) + */ +static inline void MakeHouseTile(TileIndex t, TownID tid, byte counter, byte stage, HouseID type, byte random_bits) { assert(IsTileType(t, MP_CLEAR)); SetTileType(t, MP_HOUSE); - _m[t].m1 = 0; + _m[t].m1 = random_bits; _m[t].m2 = tid; - SB(_m[t].m3, 6, 2, stage); - _m[t].m4 = type; - SB(_m[t].m5, 0, 2, counter); + _m[t].m3 = 0; + SetHouseType(t, type); + SetHouseCompleted(t, stage == TOWN_HOUSE_COMPLETED); + _m[t].m5 = IsHouseCompleted(t) ? 0 : (stage << 3 | counter); + SetHouseAnimationFrame(t, 0); + _me[t].m7 = GetHouseSpecs(type)->processing_time; + if (GetHouseSpecs(type)->building_flags & BUILDING_IS_ANIMATED) AddAnimatedTile(t); MarkTileDirtyByTile(t); } -enum { - TWO_BY_TWO_BIT = 2, ///< House is two tiles in X and Y directions - ONE_BY_TWO_BIT = 1, ///< House is two tiles in Y direction - TWO_BY_ONE_BIT = 0, ///< House is two tiles in X direction -}; - -static inline void MakeTownHouse(TileIndex t, TownID tid, byte counter, byte stage, byte size, byte type) +/** + * Helper function for MakeHouseTile. + * It is called for each tile of a multi-tile house. + * Parametes are the same. + * @param t tile index + * @param tid Town index + * @param counter of construction step + * @param stage of construction (used for drawing) + * @param type of house. Index into house specs array + * @param random_bits required for newgrf houses + */ +static inline void MakeTownHouse(TileIndex t, TownID tid, byte counter, byte stage, HouseID type, byte random_bits) { - MakeHouseTile(t, tid, counter, stage, type); - if (HASBIT(size, TWO_BY_TWO_BIT) || HASBIT(size, ONE_BY_TWO_BIT)) MakeHouseTile(t + TileDiffXY(0, 1), tid, counter, stage, ++type); - if (HASBIT(size, TWO_BY_TWO_BIT) || HASBIT(size, TWO_BY_ONE_BIT)) MakeHouseTile(t + TileDiffXY(1, 0), tid, counter, stage, ++type); - if (HASBIT(size, TWO_BY_TWO_BIT)) MakeHouseTile(t + TileDiffXY(1, 1), tid, counter, stage, ++type); + BuildingFlags size = GetHouseSpecs(type)->building_flags; + MakeHouseTile(t, tid, counter, stage, type, random_bits); + if (size & BUILDING_2_TILES_Y) MakeHouseTile(t + TileDiffXY(0, 1), tid, counter, stage, ++type, random_bits); + if (size & BUILDING_2_TILES_X) MakeHouseTile(t + TileDiffXY(1, 0), tid, counter, stage, ++type, random_bits); + if (size & BUILDING_HAS_4_TILES) MakeHouseTile(t + TileDiffXY(1, 1), tid, counter, stage, ++type, random_bits); } /** @@ -120,81 +233,143 @@ static inline void MakeTownHouse(TileInd * Construction counter, for buildings under construction. Incremented on every * periodic tile processing. * On wraparound, the stage of building in is increased. - * (Get|Set|Inc)HouseBuildingStage are taking care of the real stages, + * GetHouseBuildingStage is taking care of the real stages, * (as the sprite for the next phase of house building) - * (Get|Set|Inc)HouseConstructionTick is simply a tick counter between the + * (Get|Inc)HouseConstructionTick is simply a tick counter between the * different stages */ /** * Gets the building stage of a house - * @param tile the tile of the house to get the building stage of + * Since the stage is used for determining what sprite to use, + * if the house is complete (and that stage no longuer is available), + * fool the system by returning the TOWN_HOUSE_COMPLETE (3), + * thus showing a beautiful complete house. + * @param t the tile of the house to get the building stage of * @pre IsTileType(t, MP_HOUSE) * @return the building stage of the house */ static inline byte GetHouseBuildingStage(TileIndex t) { assert(IsTileType(t, MP_HOUSE)); - return GB(_m[t].m3, 6, 2); -} - -/** - * Sets the building stage of a house - * @param tile the tile of the house to set the building stage of - * @param stage the new stage - * @pre IsTileType(t, MP_HOUSE) - */ -static inline void SetHouseBuildingStage(TileIndex t, byte stage) -{ - assert(IsTileType(t, MP_HOUSE)); - SB(_m[t].m3, 6, 2, stage); -} - -/** - * Increments the building stage of a house - * @param tile the tile of the house to increment the building stage of - * @pre IsTileType(t, MP_HOUSE) - */ -static inline void IncHouseBuildingStage( TileIndex t ) -{ - assert(IsTileType(t, MP_HOUSE)); - AB(_m[t].m3, 6, 2, 1); + return IsHouseCompleted(t) ? (byte)TOWN_HOUSE_COMPLETED : GB(_m[t].m5, 3, 2); } /** * Gets the construction stage of a house - * @param tile the tile of the house to get the construction stage of + * @param t the tile of the house to get the construction stage of * @pre IsTileType(t, MP_HOUSE) * @return the construction stage of the house */ static inline byte GetHouseConstructionTick(TileIndex t) { assert(IsTileType(t, MP_HOUSE)); - return GB(_m[t].m5, 0, 3); -} - -/** - * Sets the construction stage of a house - * @param tile the tile of the house to set the construction stage of - * @param stage the new stage - * @pre IsTileType(t, MP_HOUSE) - */ -static inline void SetHouseConstructionTick(TileIndex t, byte stage) -{ - assert(IsTileType(t, MP_HOUSE)); - SB(_m[t].m5, 0, 3, stage); + return IsHouseCompleted(t) ? 0 : GB(_m[t].m5, 0, 3); } /** * Sets the increment stage of a house - * @param tile the tile of the house to increment the construction stage of + * It is working with the whole counter + stage 5 bits, making it + * easier to work: the wraparound is automatic. + * @param t the tile of the house to increment the construction stage of * @pre IsTileType(t, MP_HOUSE) */ static inline void IncHouseConstructionTick(TileIndex t) { assert(IsTileType(t, MP_HOUSE)); - AB(_m[t].m5, 0, 3, 1); + AB(_m[t].m5, 0, 5, 1); + + if (GB(_m[t].m5, 3, 2) == TOWN_HOUSE_COMPLETED) { + /* House is now completed. + * Store the year of construction as well, for newgrf house purpose */ + SetHouseCompleted(t, true); + _m[t].m5 = clamp(_cur_year - ORIGINAL_BASE_YEAR, 0, 0xFF); + } +} + +/** + * Get the year that this house was constructed (between 1920 and 2175). + * @param t the tile of this house + * @pre IsTileType(t, MP_HOUSE) + * @return year + */ +static inline Year GetHouseConstructionYear(TileIndex t) +{ + assert(IsTileType(t, MP_HOUSE)); + return IsHouseCompleted(t) ? _m[t].m5 + ORIGINAL_BASE_YEAR : 0; +} + +/** + * Get the random bits for this house. + * This is required for newgrf house + * @param t the tile of this house + * @pre IsTileType(t, MP_HOUSE) + * @return random bits + */ +static inline byte GetHouseRandomBits(TileIndex t) +{ + assert(IsTileType(t, MP_HOUSE)); + return _m[t].m1; +} + +/** + * Set the activated triggers bits for this house. + * This is required for newgrf house + * @param t the tile of this house + * @pre IsTileType(t, MP_HOUSE) + */ +static inline void SetHouseTriggers(TileIndex t, byte triggers) +{ + assert(IsTileType(t, MP_HOUSE)); + SB(_m[t].m3, 0, 5, triggers); } +/** + * Get the already activated triggers bits for this house. + * This is required for newgrf house + * @param t the tile of this house + * @pre IsTileType(t, MP_HOUSE) + * @return triggers + */ +static inline byte GetHouseTriggers(TileIndex t) +{ + assert(IsTileType(t, MP_HOUSE)); + return GB(_m[t].m3, 0, 5); +} + +/** + * Get the amount of time remaining before the tile loop processes this tile. + * @param t the house tile + * @pre IsTileType(t, MP_HOUSE) + * @return time remaining + */ +static inline byte GetHouseProcessingTime(TileIndex t) +{ + assert(IsTileType(t, MP_HOUSE)); + return _me[t].m7; +} + +/** + * Set the amount of time remaining before the tile loop processes this tile. + * @param t the house tile + * @param time the time to be set + * @pre IsTileType(t, MP_HOUSE) + */ +static inline void SetHouseProcessingTime(TileIndex t, byte time) +{ + assert(IsTileType(t, MP_HOUSE)); + _me[t].m7 = time; +} + +/** + * Decrease the amount of time remaining before the tile loop processes this tile. + * @param t the house tile + * @pre IsTileType(t, MP_HOUSE) + */ +static inline void DecHouseProcessingTime(TileIndex t) +{ + assert(IsTileType(t, MP_HOUSE)); + _me[t].m7--; +} #endif /* TOWN_MAP_H */ diff --git a/src/void_map.h b/src/void_map.h --- a/src/void_map.h +++ b/src/void_map.h @@ -13,6 +13,7 @@ static inline void MakeVoid(TileIndex t) _m[t].m4 = 0; _m[t].m5 = 0; _m[t].m6 = 0; + _me[t].m7 = 0; } #endif /* VOID_MAP_H */