src/newgrf_station.c
changeset 5475 2e6990a8c7c4
parent 5409 725ef180e977
equal deleted inserted replaced
5474:ac55aefc54f3 5475:2e6990a8c7c4
       
     1 /* $Id$ */
       
     2 
       
     3 /** @file newgrf_station.c Functions for dealing with station classes and custom stations. */
       
     4 
       
     5 #include "stdafx.h"
       
     6 #include "openttd.h"
       
     7 #include "variables.h"
       
     8 #include "functions.h"
       
     9 #include "debug.h"
       
    10 #include "sprite.h"
       
    11 #include "table/sprites.h"
       
    12 #include "table/strings.h"
       
    13 #include "station.h"
       
    14 #include "station_map.h"
       
    15 #include "newgrf.h"
       
    16 #include "newgrf_callbacks.h"
       
    17 #include "newgrf_station.h"
       
    18 #include "newgrf_spritegroup.h"
       
    19 #include "date.h"
       
    20 
       
    21 static StationClass station_classes[STAT_CLASS_MAX];
       
    22 
       
    23 enum {
       
    24 	MAX_SPECLIST = 255,
       
    25 };
       
    26 
       
    27 /**
       
    28  * Reset station classes to their default state.
       
    29  * This includes initialising the Default and Waypoint classes with an empty
       
    30  * entry, for standard stations and waypoints.
       
    31  */
       
    32 void ResetStationClasses(void)
       
    33 {
       
    34 	StationClassID i;
       
    35 	for (i = 0; i < STAT_CLASS_MAX; i++) {
       
    36 		station_classes[i].id = 0;
       
    37 		station_classes[i].name = STR_EMPTY;
       
    38 		station_classes[i].stations = 0;
       
    39 
       
    40 		free(station_classes[i].spec);
       
    41 		station_classes[i].spec = NULL;
       
    42 	}
       
    43 
       
    44 	// Set up initial data
       
    45 	station_classes[0].id = 'DFLT';
       
    46 	station_classes[0].name = STR_STAT_CLASS_DFLT;
       
    47 	station_classes[0].stations = 1;
       
    48 	station_classes[0].spec = malloc(sizeof(*station_classes[0].spec));
       
    49 	station_classes[0].spec[0] = NULL;
       
    50 
       
    51 	station_classes[1].id = 'WAYP';
       
    52 	station_classes[1].name = STR_STAT_CLASS_WAYP;
       
    53 	station_classes[1].stations = 1;
       
    54 	station_classes[1].spec = malloc(sizeof(*station_classes[1].spec));
       
    55 	station_classes[1].spec[0] = NULL;
       
    56 }
       
    57 
       
    58 /**
       
    59  * Allocate a station class for the given class id.
       
    60  * @param classid A 32 bit value identifying the class.
       
    61  * @return Index into station_classes of allocated class.
       
    62  */
       
    63 StationClassID AllocateStationClass(uint32 class)
       
    64 {
       
    65 	StationClassID i;
       
    66 
       
    67 	for (i = 0; i < STAT_CLASS_MAX; i++) {
       
    68 		if (station_classes[i].id == class) {
       
    69 			// ClassID is already allocated, so reuse it.
       
    70 			return i;
       
    71 		} else if (station_classes[i].id == 0) {
       
    72 			// This class is empty, so allocate it to the ClassID.
       
    73 			station_classes[i].id = class;
       
    74 			return i;
       
    75 		}
       
    76 	}
       
    77 
       
    78 	grfmsg(2, "StationClassAllocate: already allocated %d classes, using default", STAT_CLASS_MAX);
       
    79 	return STAT_CLASS_DFLT;
       
    80 }
       
    81 
       
    82 /** Set the name of a custom station class */
       
    83 void SetStationClassName(StationClassID sclass, StringID name)
       
    84 {
       
    85 	assert(sclass < STAT_CLASS_MAX);
       
    86 	station_classes[sclass].name = name;
       
    87 }
       
    88 
       
    89 /** Retrieve the name of a custom station class */
       
    90 StringID GetStationClassName(StationClassID sclass)
       
    91 {
       
    92 	assert(sclass < STAT_CLASS_MAX);
       
    93 	return station_classes[sclass].name;
       
    94 }
       
    95 
       
    96 /** Build a list of station class name StringIDs to use in a dropdown list
       
    97  * @return Pointer to a (static) array of StringIDs
       
    98  */
       
    99 StringID *BuildStationClassDropdown(void)
       
   100 {
       
   101 	/* Allow room for all station classes, plus a terminator entry */
       
   102 	static StringID names[STAT_CLASS_MAX + 1];
       
   103 	uint i;
       
   104 
       
   105 	/* Add each name */
       
   106 	for (i = 0; i < STAT_CLASS_MAX && station_classes[i].id != 0; i++) {
       
   107 		names[i] = station_classes[i].name;
       
   108 	}
       
   109 	/* Terminate the list */
       
   110 	names[i] = INVALID_STRING_ID;
       
   111 
       
   112 	return names;
       
   113 }
       
   114 
       
   115 /**
       
   116  * Get the number of station classes in use.
       
   117  * @return Number of station classes.
       
   118  */
       
   119 uint GetNumStationClasses(void)
       
   120 {
       
   121 	uint i;
       
   122 	for (i = 0; i < STAT_CLASS_MAX && station_classes[i].id != 0; i++);
       
   123 	return i;
       
   124 }
       
   125 
       
   126 /**
       
   127  * Return the number of stations for the given station class.
       
   128  * @param sclass Index of the station class.
       
   129  * @return Number of stations in the class.
       
   130  */
       
   131 uint GetNumCustomStations(StationClassID sclass)
       
   132 {
       
   133 	assert(sclass < STAT_CLASS_MAX);
       
   134 	return station_classes[sclass].stations;
       
   135 }
       
   136 
       
   137 /**
       
   138  * Tie a station spec to its station class.
       
   139  * @param spec The station spec.
       
   140  */
       
   141 void SetCustomStationSpec(StationSpec *statspec)
       
   142 {
       
   143 	StationClass *station_class;
       
   144 	int i;
       
   145 
       
   146 	/* If the station has already been allocated, don't reallocate it. */
       
   147 	if (statspec->allocated) return;
       
   148 
       
   149 	assert(statspec->sclass < STAT_CLASS_MAX);
       
   150 	station_class = &station_classes[statspec->sclass];
       
   151 
       
   152 	i = station_class->stations++;
       
   153 	station_class->spec = realloc(station_class->spec, station_class->stations * sizeof(*station_class->spec));
       
   154 
       
   155 	station_class->spec[i] = statspec;
       
   156 	statspec->allocated = true;
       
   157 }
       
   158 
       
   159 /**
       
   160  * Retrieve a station spec from a class.
       
   161  * @param sclass Index of the station class.
       
   162  * @param station The station index with the class.
       
   163  * @return The station spec.
       
   164  */
       
   165 const StationSpec *GetCustomStationSpec(StationClassID sclass, uint station)
       
   166 {
       
   167 	assert(sclass < STAT_CLASS_MAX);
       
   168 	if (station < station_classes[sclass].stations)
       
   169 		return station_classes[sclass].spec[station];
       
   170 
       
   171 	// If the custom station isn't defined any more, then the GRF file
       
   172 	// probably was not loaded.
       
   173 	return NULL;
       
   174 }
       
   175 
       
   176 
       
   177 const StationSpec *GetCustomStationSpecByGrf(uint32 grfid, byte localidx)
       
   178 {
       
   179 	StationClassID i;
       
   180 	uint j;
       
   181 
       
   182 	for (i = STAT_CLASS_DFLT; i < STAT_CLASS_MAX; i++) {
       
   183 		for (j = 0; j < station_classes[i].stations; j++) {
       
   184 			const StationSpec *statspec = station_classes[i].spec[j];
       
   185 			if (statspec == NULL) continue;
       
   186 			if (statspec->grfid == grfid && statspec->localidx == localidx) return statspec;
       
   187 		}
       
   188 	}
       
   189 
       
   190 	return NULL;
       
   191 }
       
   192 
       
   193 
       
   194 /* Evaluate a tile's position within a station, and return the result a bitstuffed format.
       
   195  * if not centred: .TNLcCpP, if centred: .TNL..CP
       
   196  * T = Tile layout number (GetStationGfx), N = Number of platforms, L = Length of platforms
       
   197  * C = Current platform number from start, c = from end
       
   198  * P = Position along platform from start, p = from end
       
   199  * if centred, C/P start from the centre and c/p are not available.
       
   200  */
       
   201 uint32 GetPlatformInfo(Axis axis, byte tile, int platforms, int length, int x, int y, bool centred)
       
   202 {
       
   203 	uint32 retval = 0;
       
   204 
       
   205 	if (axis == AXIS_X) {
       
   206 		intswap(platforms, length);
       
   207 		intswap(x, y);
       
   208 	}
       
   209 
       
   210 	/* Limit our sizes to 4 bits */
       
   211 	platforms = min(15, platforms);
       
   212 	length    = min(15, length);
       
   213 	x = min(15, x);
       
   214 	y = min(15, y);
       
   215 	if (centred) {
       
   216 		x -= platforms / 2;
       
   217 		y -= length / 2;
       
   218 		SB(retval,  0, 4, y & 0xF);
       
   219 		SB(retval,  4, 4, x & 0xF);
       
   220 	} else {
       
   221 		SB(retval,  0, 4, y);
       
   222 		SB(retval,  4, 4, length - y - 1);
       
   223 		SB(retval,  8, 4, x);
       
   224 		SB(retval, 12, 4, platforms - x - 1);
       
   225 	}
       
   226 	SB(retval, 16, 4, length);
       
   227 	SB(retval, 20, 4, platforms);
       
   228 	SB(retval, 24, 4, tile);
       
   229 
       
   230 	return retval;
       
   231 }
       
   232 
       
   233 
       
   234 /* Find the end of a railway station, from the tile, in the direction of delta.
       
   235  * If check_type is set, we stop if the custom station type changes.
       
   236  * If check_axis is set, we stop if the station direction changes.
       
   237  */
       
   238 static TileIndex FindRailStationEnd(TileIndex tile, TileIndexDiff delta, bool check_type, bool check_axis)
       
   239 {
       
   240 	bool waypoint;
       
   241 	byte orig_type = 0;
       
   242 	Axis orig_axis = AXIS_X;
       
   243 
       
   244 	waypoint = IsTileType(tile, MP_RAILWAY);
       
   245 
       
   246 	if (waypoint) {
       
   247 		if (check_axis) orig_axis = GetWaypointAxis(tile);
       
   248 	} else {
       
   249 		if (check_type) orig_type = GetCustomStationSpecIndex(tile);
       
   250 		if (check_axis) orig_axis = GetRailStationAxis(tile);
       
   251 	}
       
   252 
       
   253 	while (true) {
       
   254 		TileIndex new_tile = TILE_ADD(tile, delta);
       
   255 
       
   256 		if (waypoint) {
       
   257 			if (!IsTileType(new_tile, MP_RAILWAY)) break;
       
   258 			if (!IsRailWaypoint(new_tile)) break;
       
   259 			if (check_axis && GetWaypointAxis(new_tile) != orig_axis) break;
       
   260 		} else {
       
   261 			if (!IsRailwayStationTile(new_tile)) break;
       
   262 			if (check_type && GetCustomStationSpecIndex(new_tile) != orig_type) break;
       
   263 			if (check_axis && GetRailStationAxis(new_tile) != orig_axis) break;
       
   264 		}
       
   265 
       
   266 		tile = new_tile;
       
   267 	}
       
   268 	return tile;
       
   269 }
       
   270 
       
   271 
       
   272 static uint32 GetPlatformInfoHelper(TileIndex tile, bool check_type, bool check_axis, bool centred)
       
   273 {
       
   274 	int tx = TileX(tile);
       
   275 	int ty = TileY(tile);
       
   276 	int sx = TileX(FindRailStationEnd(tile, TileDiffXY(-1,  0), check_type, check_axis));
       
   277 	int sy = TileY(FindRailStationEnd(tile, TileDiffXY( 0, -1), check_type, check_axis));
       
   278 	int ex = TileX(FindRailStationEnd(tile, TileDiffXY( 1,  0), check_type, check_axis)) + 1;
       
   279 	int ey = TileY(FindRailStationEnd(tile, TileDiffXY( 0,  1), check_type, check_axis)) + 1;
       
   280 	Axis axis = IsTileType(tile, MP_RAILWAY) ? GetWaypointAxis(tile) : GetRailStationAxis(tile);
       
   281 
       
   282 	tx -= sx; ex -= sx;
       
   283 	ty -= sy; ey -= sy;
       
   284 
       
   285 	return GetPlatformInfo(axis, IsTileType(tile, MP_RAILWAY) ? 2 : GetStationGfx(tile), ex, ey, tx, ty, centred);
       
   286 }
       
   287 
       
   288 
       
   289 static uint32 GetRailContinuationInfo(TileIndex tile)
       
   290 {
       
   291 	/* Tile offsets and exit dirs for X axis */
       
   292 	static Direction x_dir[8] = { DIR_SW, DIR_NE, DIR_SE, DIR_NW, DIR_S, DIR_E, DIR_W, DIR_N };
       
   293 	static DiagDirection x_exits[8] = { DIAGDIR_SW, DIAGDIR_NE, DIAGDIR_SE, DIAGDIR_NW, DIAGDIR_SW, DIAGDIR_NE, DIAGDIR_SW, DIAGDIR_NE };
       
   294 
       
   295 	/* Tile offsets and exit dirs for Y axis */
       
   296 	static Direction y_dir[8] = { DIR_SE, DIR_NW, DIR_SW, DIR_NE, DIR_S, DIR_W, DIR_E, DIR_N };
       
   297 	static DiagDirection y_exits[8] = { DIAGDIR_SE, DIAGDIR_NW, DIAGDIR_SW, DIAGDIR_NE, DIAGDIR_SE, DIAGDIR_NW, DIAGDIR_SE, DIAGDIR_NW };
       
   298 
       
   299 	Axis axis = IsTileType(tile, MP_RAILWAY) ? GetWaypointAxis(tile) : GetRailStationAxis(tile);
       
   300 
       
   301 	/* Choose appropriate lookup table to use */
       
   302 	Direction *dir = axis == AXIS_X ? x_dir : y_dir;
       
   303 	DiagDirection *diagdir = axis == AXIS_X ? x_exits : y_exits;
       
   304 
       
   305 	uint32 res = 0;
       
   306 	uint i;
       
   307 
       
   308 	for (i = 0; i < lengthof(x_dir); i++, dir++, diagdir++) {
       
   309 		uint32 ts = GetTileTrackStatus(tile + TileOffsByDir(*dir), TRANSPORT_RAIL);
       
   310 		if (ts != 0) {
       
   311 			/* If there is any track on the tile, set the bit in the second byte */
       
   312 			SETBIT(res, i + 8);
       
   313 
       
   314 			/* If any track reaches our exit direction, set the bit in the lower byte */
       
   315 			if (ts & DiagdirReachesTracks(*diagdir)) SETBIT(res, i);
       
   316 		}
       
   317 	}
       
   318 
       
   319 	return res;
       
   320 }
       
   321 
       
   322 
       
   323 /* Station Resolver Functions */
       
   324 static uint32 StationGetRandomBits(const ResolverObject *object)
       
   325 {
       
   326 	const Station *st = object->u.station.st;
       
   327 	const TileIndex tile = object->u.station.tile;
       
   328 	return (st == NULL ? 0 : st->random_bits) | (tile == INVALID_TILE ? 0 : GetStationTileRandomBits(tile) << 16);
       
   329 }
       
   330 
       
   331 
       
   332 static uint32 StationGetTriggers(const ResolverObject *object)
       
   333 {
       
   334 	const Station *st = object->u.station.st;
       
   335 	return st == NULL ? 0 : st->waiting_triggers;
       
   336 }
       
   337 
       
   338 
       
   339 static void StationSetTriggers(const ResolverObject *object, int triggers)
       
   340 {
       
   341 	Station *st = (Station*)object->u.station.st;
       
   342 	assert(st != NULL);
       
   343 	st->waiting_triggers = triggers;
       
   344 }
       
   345 
       
   346 
       
   347 static uint32 StationGetVariable(const ResolverObject *object, byte variable, byte parameter, bool *available)
       
   348 {
       
   349 	const Station *st = object->u.station.st;
       
   350 	TileIndex tile = object->u.station.tile;
       
   351 
       
   352 	if (st == NULL) {
       
   353 		/* Station does not exist, so we're in a purchase list */
       
   354 		switch (variable) {
       
   355 			case 0x40:
       
   356 			case 0x41:
       
   357 			case 0x46:
       
   358 			case 0x47:
       
   359 			case 0x49: return 0x2110000;       /* Platforms, tracks & position */
       
   360 			case 0x42: return 0;               /* Rail type (XXX Get current type from GUI?) */
       
   361 			case 0x43: return _current_player; /* Station owner */
       
   362 			case 0x44: return 2;               /* PBS status */
       
   363 			case 0xFA: return max(_date - DAYS_TILL_ORIGINAL_BASE_YEAR, 0); /* Build date */
       
   364 		}
       
   365 
       
   366 		*available = false;
       
   367 		return -1;
       
   368 	}
       
   369 
       
   370 	switch (variable) {
       
   371 		/* Calculated station variables */
       
   372 		case 0x40: return GetPlatformInfoHelper(tile, false, false, false);
       
   373 		case 0x41: return GetPlatformInfoHelper(tile, true,  false, false);
       
   374 		case 0x42: /* Terrain and rail type */
       
   375 			return ((_opt.landscape == LT_HILLY && GetTileZ(tile) > _opt.snow_line) ? 4 : 0) |
       
   376 			       (GetRailType(tile) << 8);
       
   377 		case 0x43: return st->owner; /* Station owner */
       
   378 		case 0x44: return 2;         /* PBS status */
       
   379 		case 0x45: return GetRailContinuationInfo(tile);
       
   380 		case 0x46: return GetPlatformInfoHelper(tile, false, false, true);
       
   381 		case 0x47: return GetPlatformInfoHelper(tile, true,  false, true);
       
   382 		case 0x48: { /* Accepted cargo types */
       
   383 			CargoID cargo_type;
       
   384 			uint32 value = 0;
       
   385 
       
   386 			for (cargo_type = 0; cargo_type < NUM_CARGO; cargo_type++) {
       
   387 				if (HASBIT(st->goods[cargo_type].waiting_acceptance, 15)) SETBIT(value, cargo_type);
       
   388 			}
       
   389 			return value;
       
   390 		}
       
   391 		case 0x49: return GetPlatformInfoHelper(tile, false, true, false);
       
   392 
       
   393 		/* Variables which use the parameter */
       
   394 		case 0x60: return GB(st->goods[parameter].waiting_acceptance, 0, 12);
       
   395 		case 0x61: return st->goods[parameter].days_since_pickup;
       
   396 		case 0x62: return st->goods[parameter].rating;
       
   397 		case 0x63: return st->goods[parameter].enroute_time;
       
   398 		case 0x64: return st->goods[parameter].last_speed | (st->goods[parameter].last_age << 8);
       
   399 		case 0x65: return GB(st->goods[parameter].waiting_acceptance, 12, 4);
       
   400 
       
   401 		/* General station properties */
       
   402 		case 0x82: return 50;
       
   403 		case 0x84: return st->string_id;
       
   404 		case 0x86: return 0;
       
   405 		case 0x8A: return st->had_vehicle_of_type;
       
   406 		case 0xF0: return st->facilities;
       
   407 		case 0xF1: return st->airport_type;
       
   408 		case 0xF2: return st->truck_stops->status;
       
   409 		case 0xF3: return st->bus_stops->status;
       
   410 		case 0xF6: return st->airport_flags;
       
   411 		case 0xF7: return GB(st->airport_flags, 8, 8);
       
   412 		case 0xFA: return max(st->build_date - DAYS_TILL_ORIGINAL_BASE_YEAR, 0);
       
   413 	}
       
   414 
       
   415 	/* Handle cargo variables (deprecated) */
       
   416 	if (variable >= 0x8C && variable <= 0xEC) {
       
   417 		const GoodsEntry *g = &st->goods[GB(variable - 0x8C, 3, 4)];
       
   418 		switch (GB(variable - 0x8C, 0, 3)) {
       
   419 			case 0: return g->waiting_acceptance;
       
   420 			case 1: return GB(g->waiting_acceptance, 8, 8);
       
   421 			case 2: return g->days_since_pickup;
       
   422 			case 3: return g->rating;
       
   423 			case 4: return g->enroute_from;
       
   424 			case 5: return g->enroute_time;
       
   425 			case 6: return g->last_speed;
       
   426 			case 7: return g->last_age;
       
   427 		}
       
   428 	}
       
   429 
       
   430 	DEBUG(grf, 1, "Unhandled station property 0x%X", variable);
       
   431 
       
   432 	*available = false;
       
   433 	return -1;
       
   434 }
       
   435 
       
   436 
       
   437 static const SpriteGroup *StationResolveReal(const ResolverObject *object, const SpriteGroup *group)
       
   438 {
       
   439 	const Station *st = object->u.station.st;
       
   440 	const StationSpec *statspec = object->u.station.statspec;
       
   441 	uint set;
       
   442 
       
   443 	uint cargo = 0;
       
   444 	CargoID cargo_type = object->u.station.cargo_type;
       
   445 
       
   446 	if (st == NULL || statspec->sclass == STAT_CLASS_WAYP) {
       
   447 		return group->g.real.loading[0];
       
   448 	}
       
   449 
       
   450 	switch (cargo_type) {
       
   451 		case GC_INVALID:
       
   452 		case GC_DEFAULT_NA:
       
   453 		case GC_PURCHASE:
       
   454 			cargo = 0;
       
   455 			break;
       
   456 
       
   457 		case GC_DEFAULT:
       
   458 			for (cargo_type = 0; cargo_type < NUM_CARGO; cargo_type++) {
       
   459 				cargo += GB(st->goods[cargo_type].waiting_acceptance, 0, 12);
       
   460 			}
       
   461 			break;
       
   462 
       
   463 		default:
       
   464 			cargo = GB(st->goods[_local_cargo_id_ctype[cargo_type]].waiting_acceptance, 0, 12);
       
   465 			break;
       
   466 	}
       
   467 
       
   468 	if (HASBIT(statspec->flags, 1)) cargo /= (st->trainst_w + st->trainst_h);
       
   469 	cargo = min(0xfff, cargo);
       
   470 
       
   471 	if (cargo > statspec->cargo_threshold) {
       
   472 		if (group->g.real.num_loading > 0) {
       
   473 			set = ((cargo - statspec->cargo_threshold) * group->g.real.num_loading) / (4096 - statspec->cargo_threshold);
       
   474 			return group->g.real.loading[set];
       
   475 		}
       
   476 	} else {
       
   477 		if (group->g.real.num_loaded > 0) {
       
   478 			set = (cargo * group->g.real.num_loaded) / (statspec->cargo_threshold + 1);
       
   479 			return group->g.real.loaded[set];
       
   480 		}
       
   481 	}
       
   482 
       
   483 	return group->g.real.loading[0];
       
   484 }
       
   485 
       
   486 
       
   487 static void NewStationResolver(ResolverObject *res, const StationSpec *statspec, const Station *st, TileIndex tile)
       
   488 {
       
   489 	res->GetRandomBits = StationGetRandomBits;
       
   490 	res->GetTriggers   = StationGetTriggers;
       
   491 	res->SetTriggers   = StationSetTriggers;
       
   492 	res->GetVariable   = StationGetVariable;
       
   493 	res->ResolveReal   = StationResolveReal;
       
   494 
       
   495 	res->u.station.st       = st;
       
   496 	res->u.station.statspec = statspec;
       
   497 	res->u.station.tile     = tile;
       
   498 
       
   499 	res->callback        = 0;
       
   500 	res->callback_param1 = 0;
       
   501 	res->callback_param2 = 0;
       
   502 	res->last_value      = 0;
       
   503 	res->trigger         = 0;
       
   504 	res->reseed          = 0;
       
   505 }
       
   506 
       
   507 static const SpriteGroup *ResolveStation(const StationSpec *statspec, const Station *st, ResolverObject *object)
       
   508 {
       
   509 	const SpriteGroup *group;
       
   510 	CargoID ctype = GC_DEFAULT_NA;
       
   511 
       
   512 	if (st == NULL) {
       
   513 		/* No station, so we are in a purchase list */
       
   514 		ctype = GC_PURCHASE;
       
   515 	} else {
       
   516 		CargoID cargo;
       
   517 
       
   518 		/* Pick the first cargo that we have waiting */
       
   519 		for (cargo = 0; cargo < NUM_GLOBAL_CID; cargo++) {
       
   520 			CargoID lcid = _local_cargo_id_ctype[cargo];
       
   521 			if (lcid != CT_INVALID && statspec->spritegroup[cargo] != NULL && GB(st->goods[lcid].waiting_acceptance, 0, 12) != 0) {
       
   522 				ctype = cargo;
       
   523 				break;
       
   524 			}
       
   525 		}
       
   526 	}
       
   527 
       
   528 	group = statspec->spritegroup[ctype];
       
   529 	if (group == NULL) {
       
   530 		ctype = GC_DEFAULT;
       
   531 		group = statspec->spritegroup[ctype];
       
   532 	}
       
   533 
       
   534 	if (group == NULL) return NULL;
       
   535 
       
   536 	/* Remember the cargo type we've picked */
       
   537 	object->u.station.cargo_type = ctype;
       
   538 
       
   539 	return Resolve(group, object);
       
   540 }
       
   541 
       
   542 SpriteID GetCustomStationRelocation(const StationSpec *statspec, const Station *st, TileIndex tile)
       
   543 {
       
   544 	const SpriteGroup *group;
       
   545 	ResolverObject object;
       
   546 
       
   547 	NewStationResolver(&object, statspec, st, tile);
       
   548 
       
   549 	group = ResolveStation(statspec, st, &object);
       
   550 	if (group == NULL || group->type != SGT_RESULT) return 0;
       
   551 	return group->g.result.sprite - 0x42D;
       
   552 }
       
   553 
       
   554 
       
   555 SpriteID GetCustomStationGroundRelocation(const StationSpec *statspec, const Station *st, TileIndex tile)
       
   556 {
       
   557 	const SpriteGroup *group;
       
   558 	ResolverObject object;
       
   559 
       
   560 	NewStationResolver(&object, statspec, st, tile);
       
   561 	object.callback_param1 = 1; /* Indicate we are resolving the ground sprite */
       
   562 
       
   563 	group = ResolveStation(statspec, st, &object);
       
   564 	if (group == NULL || group->type != SGT_RESULT) return 0;
       
   565 	return group->g.result.sprite - 0x42D;
       
   566 }
       
   567 
       
   568 
       
   569 uint16 GetStationCallback(uint16 callback, uint32 param1, uint32 param2, const StationSpec *statspec, const Station *st, TileIndex tile)
       
   570 {
       
   571 	const SpriteGroup *group;
       
   572 	ResolverObject object;
       
   573 
       
   574 	NewStationResolver(&object, statspec, st, tile);
       
   575 
       
   576 	object.callback        = callback;
       
   577 	object.callback_param1 = param1;
       
   578 	object.callback_param2 = param2;
       
   579 
       
   580 	group = ResolveStation(statspec, st, &object);
       
   581 	if (group == NULL || group->type != SGT_CALLBACK) return CALLBACK_FAILED;
       
   582 	return group->g.callback.result;
       
   583 }
       
   584 
       
   585 
       
   586 /**
       
   587  * Allocate a StationSpec to a Station. This is called once per build operation.
       
   588  * @param spec StationSpec to allocate.
       
   589  * @param st Station to allocate it to.
       
   590  * @param exec Whether to actually allocate the spec.
       
   591  * @return Index within the Station's spec list, or -1 if the allocation failed.
       
   592  */
       
   593 int AllocateSpecToStation(const StationSpec *statspec, Station *st, bool exec)
       
   594 {
       
   595 	uint i;
       
   596 
       
   597 	if (statspec == NULL) return 0;
       
   598 
       
   599 	/* Check if this spec has already been allocated */
       
   600 	for (i = 1; i < st->num_specs && i < MAX_SPECLIST; i++) {
       
   601 		if (st->speclist[i].spec == statspec) return i;
       
   602 	}
       
   603 
       
   604 	for (i = 1; i < st->num_specs && i < MAX_SPECLIST; i++) {
       
   605 		if (st->speclist[i].spec == NULL && st->speclist[i].grfid == 0) break;
       
   606 	}
       
   607 
       
   608 	if (i == MAX_SPECLIST) return -1;
       
   609 
       
   610 	if (exec) {
       
   611 		if (i >= st->num_specs) {
       
   612 			st->num_specs = i + 1;
       
   613 			st->speclist = realloc(st->speclist, st->num_specs * sizeof(*st->speclist));
       
   614 
       
   615 			if (st->num_specs == 2) {
       
   616 				/* Initial allocation */
       
   617 				st->speclist[0].spec     = NULL;
       
   618 				st->speclist[0].grfid    = 0;
       
   619 				st->speclist[0].localidx = 0;
       
   620 			}
       
   621 		}
       
   622 
       
   623 		st->speclist[i].spec     = statspec;
       
   624 		st->speclist[i].grfid    = statspec->grfid;
       
   625 		st->speclist[i].localidx = statspec->localidx;
       
   626 	}
       
   627 
       
   628 	return i;
       
   629 }
       
   630 
       
   631 
       
   632 /** Deallocate a StationSpec from a Station. Called when removing a single station tile.
       
   633  * @param st Station to work with.
       
   634  * @param specindex Index of the custom station within the Station's spec list.
       
   635  * @return Indicates whether the StationSpec was deallocated.
       
   636  */
       
   637 void DeallocateSpecFromStation(Station* st, byte specindex)
       
   638 {
       
   639 	/* specindex of 0 (default) is never freeable */
       
   640 	if (specindex == 0) return;
       
   641 
       
   642 	/* Check all tiles over the station to check if the specindex is still in use */
       
   643 	BEGIN_TILE_LOOP(tile, st->trainst_w, st->trainst_h, st->train_tile) {
       
   644 		if (IsTileType(tile, MP_STATION) && GetStationIndex(tile) == st->index && IsRailwayStation(tile) && GetCustomStationSpecIndex(tile) == specindex) {
       
   645 			return;
       
   646 		}
       
   647 	} END_TILE_LOOP(tile, st->trainst_w, st->trainst_h, st->train_tile)
       
   648 
       
   649 	/* This specindex is no longer in use, so deallocate it */
       
   650 	st->speclist[specindex].spec     = NULL;
       
   651 	st->speclist[specindex].grfid    = 0;
       
   652 	st->speclist[specindex].localidx = 0;
       
   653 
       
   654 	/* If this was the highest spec index, reallocate */
       
   655 	if (specindex == st->num_specs - 1) {
       
   656 		for (; st->speclist[st->num_specs - 1].grfid == 0 && st->num_specs > 1; st->num_specs--);
       
   657 
       
   658 		if (st->num_specs > 1) {
       
   659 			st->speclist = realloc(st->speclist, st->num_specs * sizeof(*st->speclist));
       
   660 		} else {
       
   661 			free(st->speclist);
       
   662 			st->num_specs = 0;
       
   663 			st->speclist  = NULL;
       
   664 		}
       
   665 	}
       
   666 }
       
   667 
       
   668 /** Draw representation of a station tile for GUI purposes.
       
   669  * @param x, y Position of image.
       
   670  * @param dir Direction.
       
   671  * @param railtype Rail type.
       
   672  * @param sclass, station Type of station.
       
   673  * @return True if the tile was drawn (allows for fallback to default graphic)
       
   674  */
       
   675 bool DrawStationTile(int x, int y, RailType railtype, Axis axis, StationClassID sclass, uint station)
       
   676 {
       
   677 	const StationSpec *statspec;
       
   678 	const DrawTileSprites *sprites;
       
   679 	const DrawTileSeqStruct *seq;
       
   680 	const RailtypeInfo *rti = GetRailTypeInfo(railtype);
       
   681 	SpriteID relocation;
       
   682 	PalSpriteID image;
       
   683 	PalSpriteID colourmod = SPRITE_PALETTE(PLAYER_SPRITE_COLOR(_local_player));
       
   684 	uint tile = 2;
       
   685 
       
   686 	statspec = GetCustomStationSpec(sclass, station);
       
   687 	if (statspec == NULL) return false;
       
   688 
       
   689 	relocation = GetCustomStationRelocation(statspec, NULL, INVALID_TILE);
       
   690 
       
   691 	if (HASBIT(statspec->callbackmask, CBM_CUSTOM_LAYOUT)) {
       
   692 		uint16 callback = GetStationCallback(CBID_STATION_SPRITE_LAYOUT, 0x2110000, 0, statspec, NULL, INVALID_TILE);
       
   693 		if (callback != CALLBACK_FAILED) tile = callback;
       
   694 	}
       
   695 
       
   696 	if (statspec->renderdata == NULL) {
       
   697 		sprites = GetStationTileLayout(tile + axis);
       
   698 	} else {
       
   699 		sprites = &statspec->renderdata[(tile < statspec->tiles) ? tile + axis : axis];
       
   700 	}
       
   701 
       
   702 	image = sprites->ground_sprite;
       
   703 	if (HASBIT(image, 31)) {
       
   704 		CLRBIT(image, 31);
       
   705 		image += GetCustomStationGroundRelocation(statspec, NULL, INVALID_TILE);
       
   706 		image += rti->custom_ground_offset;
       
   707 	} else {
       
   708 		image += rti->total_offset;
       
   709 	}
       
   710 
       
   711 	if (image & PALETTE_MODIFIER_COLOR) image &= SPRITE_MASK;
       
   712 	DrawSprite(image, x, y);
       
   713 
       
   714 	foreach_draw_tile_seq(seq, sprites->seq) {
       
   715 		Point pt;
       
   716 		image = seq->image;
       
   717 		if (HASBIT(image, 30)) {
       
   718 			CLRBIT(image, 30);
       
   719 			image += rti->total_offset;
       
   720 		} else {
       
   721 			image += relocation;
       
   722 		}
       
   723 
       
   724 		if ((byte)seq->delta_z != 0x80) {
       
   725 			pt = RemapCoords(seq->delta_x, seq->delta_y, seq->delta_z);
       
   726 			DrawSprite((image & SPRITE_MASK) | colourmod, x + pt.x, y + pt.y);
       
   727 		}
       
   728 	}
       
   729 
       
   730 	return true;
       
   731 }
       
   732 
       
   733 
       
   734 static const StationSpec* GetStationSpec(TileIndex t)
       
   735 {
       
   736 	const Station* st;
       
   737 	uint specindex;
       
   738 
       
   739 	if (!IsCustomStationSpecIndex(t)) return NULL;
       
   740 
       
   741 	st = GetStationByTile(t);
       
   742 	specindex = GetCustomStationSpecIndex(t);
       
   743 	return specindex < st->num_specs ? st->speclist[specindex].spec : NULL;
       
   744 }
       
   745 
       
   746 
       
   747 /* Check if a rail station tile is traversable.
       
   748  * XXX This could be cached (during build) in the map array to save on all the dereferencing */
       
   749 bool IsStationTileBlocked(TileIndex tile)
       
   750 {
       
   751 	const StationSpec* statspec = GetStationSpec(tile);
       
   752 
       
   753 	return statspec != NULL && HASBIT(statspec->blocked, GetStationGfx(tile));
       
   754 }
       
   755 
       
   756 /* Check if a rail station tile is electrifiable.
       
   757  * XXX This could be cached (during build) in the map array to save on all the dereferencing */
       
   758 bool IsStationTileElectrifiable(TileIndex tile)
       
   759 {
       
   760 	const StationSpec* statspec = GetStationSpec(tile);
       
   761 
       
   762 	return
       
   763 		statspec == NULL ||
       
   764 		HASBIT(statspec->pylons, GetStationGfx(tile)) ||
       
   765 		!HASBIT(statspec->wires, GetStationGfx(tile));
       
   766 }