(svn r4473) - Newstations:
authorpeter1138
Wed, 19 Apr 2006 07:17:00 +0000
changeset 3587 2eb52db0b872
parent 3586 e339fc4349bb
child 3588 a1adf8a40756
(svn r4473) - Newstations:
- Alter parameters of CMD_BUILD_RAILROAD_STATION to accept a custom station class and id.
- Add a dynamically allocated list of custom stations that the SpecIndex (m4) references.
newgrf_station.c
newgrf_station.h
station.h
station_cmd.c
--- a/newgrf_station.c	Tue Apr 18 21:17:54 2006 +0000
+++ b/newgrf_station.c	Wed Apr 19 07:17:00 2006 +0000
@@ -7,6 +7,7 @@
 #include "debug.h"
 #include "sprite.h"
 #include "station.h"
+#include "station_map.h"
 #include "newgrf_station.h"
 
 static StationClass station_classes[STAT_CLASS_MAX];
@@ -70,6 +71,17 @@
 }
 
 /**
+ * Get the number of station classes in use.
+ * @return Number of station classes.
+ */
+uint GetNumStationClasses(void)
+{
+	uint i;
+	for (i = 0; i < STAT_CLASS_MAX && station_classes[i].id != 0; i++);
+	return i;
+}
+
+/**
  * Return the number of stations for the given station class.
  * @param sclass Index of the station class.
  * @return Number of stations in the class.
@@ -198,3 +210,94 @@
 	 * emergency measure. */
 	return 0;
 }
+
+
+/**
+ * Allocate a StationSpec to a Station. This is called once per build operation.
+ * @param spec StationSpec to allocate.
+ * @param st Station to allocate it to.
+ * @param exec Whether to actually allocate the spec.
+ * @return Index within the Station's spec list, or -1 if the allocation failed.
+ */
+int AllocateSpecToStation(const StationSpec *spec, Station *st, bool exec)
+{
+	uint i;
+
+	if (spec == NULL) return 0;
+
+	/* Check if this spec has already been allocated */
+	for (i = 1; i < st->num_specs && i < 256; i++) {
+		if (st->speclist[i].spec == spec) return i;
+	}
+
+	for (i = 1; i < st->num_specs && i < 256; i++) {
+		if (st->speclist[i].spec == NULL && st->speclist[i].grfid == 0) break;
+	}
+
+	if (i < 256) {
+		if (exec) {
+			if (i >= st->num_specs) {
+				st->num_specs = i + 1;
+				st->speclist = realloc(st->speclist, st->num_specs * sizeof(*st->speclist));
+
+				if (st->num_specs == 2) {
+					/* Initial allocation */
+					st->speclist[0].spec     = NULL;
+					st->speclist[0].grfid    = 0;
+					st->speclist[0].localidx = 0;
+				}
+			}
+
+			st->speclist[i].spec     = spec;
+			st->speclist[i].grfid    = spec->grfid;
+			st->speclist[i].localidx = spec->localidx;
+		}
+		return i;
+	}
+
+	return -1;
+}
+
+
+/** Deallocate a StationSpec from a Station. Called when removing a single station tile.
+ * @param st Station to work with.
+ * @param specindex Index of the custom station within the Station's spec list.
+ * @return Indicates whether the StationSpec was deallocated.
+ */
+bool DeallocateSpecFromStation(Station *st, byte specindex)
+{
+	bool freeable = true;
+
+	/* specindex of 0 (default) is never freeable */
+	if (specindex == 0) return false;
+
+	/* Check all tiles over the station to check if the specindex is still in use */
+	BEGIN_TILE_LOOP(tile, st->trainst_w, st->trainst_h, st->train_tile) {
+		if (IsTileType(tile, MP_STATION) && GetStationIndex(tile) == st->index && IsRailwayStation(tile) && GetCustomStationSpecIndex(tile) == specindex) {
+			freeable = false;
+			break;
+		}
+	} END_TILE_LOOP(tile, st->trainst_w, st->trainst_h, st->train_tile)
+
+	if (freeable) {
+		/* This specindex is no longer in use, so deallocate it */
+		st->speclist[specindex].spec     = NULL;
+		st->speclist[specindex].grfid    = 0;
+		st->speclist[specindex].localidx = 0;
+
+		/* If this was the highest spec index, reallocate */
+		if (specindex == st->num_specs - 1) {
+			for (; st->speclist[st->num_specs - 1].grfid == 0 && st->num_specs > 1; st->num_specs--);
+
+			if (st->num_specs > 1) {
+				st->speclist = realloc(st->speclist, st->num_specs * sizeof(*st->speclist));
+			} else {
+				free(st->speclist);
+				st->num_specs = 0;
+				st->speclist  = NULL;
+			}
+		}
+	}
+
+	return freeable;
+}
--- a/newgrf_station.h	Tue Apr 18 21:17:54 2006 +0000
+++ b/newgrf_station.h	Wed Apr 19 07:17:00 2006 +0000
@@ -84,6 +84,7 @@
 void ResetStationClasses(void);
 StationClassID AllocateStationClass(uint32 class);
 void SetStationClassName(StationClassID sclass, const char *name);
+uint GetNumStationClasses(void);
 uint GetNumCustomStations(StationClassID sclass);
 
 void SetCustomStation(StationSpec *spec);
@@ -94,4 +95,10 @@
  * structure is used for variational sprite groups. */
 uint32 GetCustomStationRelocation(const StationSpec *spec, const Station *st, byte ctype);
 
+/* Allocate a StationSpec to a Station. This is called once per build operation. */
+int AllocateSpecToStation(const StationSpec *spec, Station *st, bool exec);
+
+/* Deallocate a StationSpec from a Station. Called when removing a single station tile. */
+bool DeallocateSpecFromStation(Station *st, byte specindex);
+
 #endif /* NEWGRF_STATION_H */
--- a/station.h	Tue Apr 18 21:17:54 2006 +0000
+++ b/station.h	Wed Apr 19 07:17:00 2006 +0000
@@ -41,6 +41,12 @@
 	struct RoadStop *prev;
 } RoadStop;
 
+typedef struct StationSpecList {
+	const StationSpec *spec;
+	uint32 grfid;      /// GRF ID of this custom station
+	uint8  localidx;   /// Station ID within GRF of station
+} StationSpecList;
+
 struct Station {
 	TileIndex xy;
 	RoadStop *bus_stops;
@@ -65,6 +71,10 @@
 	// trainstation width/height
 	byte trainst_w, trainst_h;
 
+	/** List of custom stations (StationSpecs) allocated to the station */
+	uint num_specs;
+	StationSpecList *speclist;
+
 	uint16 build_date;
 
 	//uint16 airport_flags;
--- a/station_cmd.c	Tue Apr 18 21:17:54 2006 +0000
+++ b/station_cmd.c	Wed Apr 19 07:17:00 2006 +0000
@@ -50,6 +50,17 @@
 	FOR_ALL_STATIONS_FROM(st, start_item) st->index = start_item++;
 }
 
+static void StationPoolCleanBlock(uint start_item, uint end_item)
+{
+	uint i;
+
+	for (i = start_item; i <= end_item; i++) {
+		Station *st = GetStation(i);
+		free(st->speclist);
+		st->speclist = NULL;
+	}
+}
+
 /**
  * Called if a new block is added to the roadstop-pool
  */
@@ -61,7 +72,7 @@
 }
 
 /* Initialize the station-pool and roadstop-pool */
-MemoryPool _station_pool = { "Stations", STATION_POOL_MAX_BLOCKS, STATION_POOL_BLOCK_SIZE_BITS, sizeof(Station), &StationPoolNewBlock, NULL, 0, 0, NULL };
+MemoryPool _station_pool = { "Stations", STATION_POOL_MAX_BLOCKS, STATION_POOL_BLOCK_SIZE_BITS, sizeof(Station), &StationPoolNewBlock, &StationPoolCleanBlock, 0, 0, NULL };
 MemoryPool _roadstop_pool = { "RoadStop", ROADSTOP_POOL_MAX_BLOCKS, ROADSTOP_POOL_BLOCK_SIZE_BITS, sizeof(RoadStop), &RoadStopPoolNewBlock, NULL, 0, 0, NULL };
 
 
@@ -939,8 +950,8 @@
  * - p1 = (bit 16-23) - platform length
  * @param p2 various bitstuffed elements
  * - p2 = (bit  0- 3) - railtype (p2 & 0xF)
- * - p2 = (bit  4)    - set for custom station (p2 & 0x10)
- * - p2 = (bit  8-..) - custom station id (p2 >> 8)
+ * - p2 = (bit  8-15) - custom station class
+ * - p2 = (bit 16-23) - custom station id
  */
 int32 CmdBuildRailroadStation(TileIndex tile_org, uint32 flags, uint32 p1, uint32 p2)
 {
@@ -951,6 +962,8 @@
 	int plat_len, numtracks;
 	Axis axis;
 	uint finalvalues[3];
+	const StationSpec *statspec;
+	int specindex;
 
 	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 
@@ -1025,10 +1038,17 @@
 		if (flags & DC_EXEC) StationInitialize(st, tile_org);
 	}
 
+	/* Check if the given station class is valid */
+	if (GB(p2, 8, 8) >= STAT_CLASS_MAX) return CMD_ERROR;
+
+	/* Check if we can allocate a custom stationspec to this station */
+	statspec = GetCustomStation(GB(p2, 8, 8), GB(p2, 16, 8));
+	specindex = AllocateSpecToStation(statspec, st, flags & DC_EXEC);
+	if (specindex == -1) return CMD_ERROR;
+
 	if (flags & DC_EXEC) {
 		TileIndexDiff tile_delta;
 		byte *layout_ptr;
-		const StationSpec *statspec;
 		Track track;
 
 		// Now really clear the land below the station
@@ -1050,7 +1070,6 @@
 		tile_delta = (axis == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1));
 		track = (axis == AXIS_X ? TRACK_X : TRACK_Y);
 
-		statspec = (p2 & 0x10) != 0 ? GetCustomStation(STAT_CLASS_DFLT, p2 >> 8) : NULL;
 		layout_ptr = alloca(numtracks * plat_len);
 		GetStationLayout(layout_ptr, numtracks, plat_len, statspec);
 
@@ -1060,8 +1079,7 @@
 			do {
 
 				MakeRailStation(tile, st->owner, st->index, axis, *layout_ptr++, GB(p2, 0, 4));
-
-				if (HASBIT(p2, 4)) SetCustomStationSpecIndex(tile, GB(p2, 8, 8));
+				SetCustomStationSpecIndex(tile, specindex);
 
 				tile += tile_delta;
 			} while (--w);
@@ -1158,9 +1176,13 @@
 
 	// if we reached here, it means we can actually delete it. do that.
 	if (flags & DC_EXEC) {
+		uint specindex = GetCustomStationSpecIndex(tile);
 		Track track = GetRailStationTrack(tile);
 		DoClearSquare(tile);
 		SetSignalsOnBothDir(tile, track);
+
+		DeallocateSpecFromStation(st, specindex);
+
 		// now we need to make the "spanned" area of the railway station smaller if we deleted something at the edges.
 		// we also need to adjust train_tile.
 		MakeRailwayStationAreaSmaller(st);
@@ -1252,6 +1274,10 @@
 		st->train_tile = 0;
 		st->facilities &= ~FACIL_TRAIN;
 
+		free(st->speclist);
+		st->num_specs = 0;
+		st->speclist  = NULL;
+
 		UpdateStationVirtCoordDirty(st);
 		DeleteStationIfEmpty(st);
 	}
@@ -1936,12 +1962,12 @@
 
 	if (IsCustomStationSpecIndex(ti->tile)) {
 		// look for customization
-		const StationSpec *statspec = GetCustomStation(STAT_CLASS_DFLT, GetCustomStationSpecIndex(ti->tile));
+		const Station *st = GetStationByTile(ti->tile);
+		const StationSpec *statspec = st->speclist[GetCustomStationSpecIndex(ti->tile)].spec;
 
 		//debug("Cust-o-mized %p", statspec);
 
 		if (statspec != NULL) {
-			const Station* st = GetStationByTile(ti->tile);
 			uint tile = GetStationGfx(ti->tile);
 
 			relocation = GetCustomStationRelocation(statspec, st, 0);