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