(svn r605) -newgrf: Framework for supporting variational spritegroups . Deterministic only at the moment, but random ones support shouldn't be that difficult now It doesn't do anything, but makes these actions actually possible (pasky).
authordarkvater
Sun, 14 Nov 2004 18:18:28 +0000
changeset 408 63a8c0505aab
parent 407 6de2d3c7f0e3
child 409 98c586218b01
(svn r605) -newgrf: Framework for supporting variational spritegroups . Deterministic only at the moment, but random ones support shouldn't be that difficult now It doesn't do anything, but makes these actions actually possible (pasky).
engine.c
grfspecial.c
rail_cmd.c
sprite.h
station.h
station_cmd.c
--- a/engine.c	Sun Nov 14 17:16:26 2004 +0000
+++ b/engine.c	Sun Nov 14 18:18:28 2004 +0000
@@ -196,6 +196,9 @@
 	                         wos->overrides_count * sizeof(struct WagonOverride));
 
 	wo = &wos->overrides[wos->overrides_count - 1];
+	/* FIXME: If we are replacing an override, release original SpriteGroup
+	 * to prevent leaks. But first we need to refcount the SpriteGroup.
+	 * --pasky */
 	wo->group = *group;
 	wo->trains = trains;
 	wo->train_id = malloc(trains);
@@ -207,8 +210,10 @@
 	struct WagonOverrides *wos = &_engine_wagon_overrides[engine];
 	int i;
 
-	// XXX: This could turn out to be a timesink on profiles. We could always just
-	// dedicate 65535 bytes for an [engine][train] trampoline.
+	// XXX: This could turn out to be a timesink on profiles. We could
+	// always just dedicate 65535 bytes for an [engine][train] trampoline
+	// for O(1). Or O(logMlogN) and searching binary tree or smt. like
+	// that. --pasky
 
 	for (i = 0; i < wos->overrides_count; i++) {
 		struct WagonOverride *wo = &wos->overrides[i];
@@ -232,7 +237,9 @@
 
 void SetCustomEngineSprites(byte engine, byte cargo, struct SpriteGroup *group)
 {
-	assert(group->sprites_per_set == 4 || group->sprites_per_set == 8);
+	/* FIXME: If we are replacing an override, release original SpriteGroup
+	 * to prevent leaks. But first we need to refcount the SpriteGroup.
+	 * --pasky */
 	_engine_custom_sprites[engine][cargo] = *group;
 }
 
@@ -240,6 +247,7 @@
                           byte loaded, byte in_motion, byte direction)
 {
 	struct SpriteGroup *group = &_engine_custom_sprites[engine][cargo];
+	struct RealSpriteGroup *rsg;
 	int totalsets, spriteset;
 	int r;
 
@@ -250,22 +258,26 @@
 		if (overset) group = overset;
 	}
 
-	if (!group->sprites_per_set && cargo != 29) {
+	/* TODO: Resolve surreal groups properly. --pasky */
+	rsg = TriviallyGetRSG(group);
+
+	if (!rsg->sprites_per_set && cargo != 29) {
 		// This group is empty but perhaps there'll be a default one.
-		group = &_engine_custom_sprites[engine][29];
+		/* TODO: Resolve surreal groups properly. --pasky */
+		rsg = TriviallyGetRSG(&_engine_custom_sprites[engine][29]);
 	}
 
-	if (!group->sprites_per_set) {
+	if (!rsg->sprites_per_set) {
 		// This group is empty. This function users should therefore
 		// look up the sprite number in _engine_original_sprites.
 		return 0;
 	}
 
 	direction %= 8;
-	if (group->sprites_per_set == 4)
+	if (rsg->sprites_per_set == 4)
 		direction %= 4;
 
-	totalsets = in_motion ? group->loaded_count : group->loading_count;
+	totalsets = in_motion ? rsg->loaded_count : rsg->loading_count;
 
 	// My aim here is to make it possible to visually determine absolutely
 	// empty and totally full vehicles. --pasky
@@ -282,7 +294,7 @@
 			spriteset--;
 	}
 
-	r = (in_motion ? group->loaded[spriteset] : group->loading[spriteset]) + direction;
+	r = (in_motion ? rsg->loaded[spriteset] : rsg->loading[spriteset]) + direction;
 	return r;
 }
 
--- a/grfspecial.c	Sun Nov 14 17:16:26 2004 +0000
+++ b/grfspecial.c	Sun Nov 14 18:18:28 2004 +0000
@@ -1055,6 +1055,7 @@
 	uint8 numloaded;
 	uint8 numloading;
 	struct SpriteGroup *group;
+	struct RealSpriteGroup *rg;
 	byte *loaded_ptr;
 	byte *loading_ptr;
 	int i;
@@ -1065,27 +1066,59 @@
 	numloaded = buf[3];
 	numloading = buf[4];
 
-	if (numloaded == 0x81) {
-		/* XXX: This just goes for the default superset for now,
-		 * straight and safe. --pasky */
-		uint8 var = buf[4];
-		//uint8 shiftnum = buf[5];
-		//uint8 andmask = buf[6];
-		uint8 nvar = buf[7];
-		//uint32 val;
-		uint16 def;
+	if (numloaded == 0x81 || numloaded == 0x82) {
+		struct DeterministicSpriteGroup *dg;
+		int i;
 
-		grfmsg(GMS_WARN, "NewSpriteGroup(0x81): Unsupported variable %x. Using default cid.", var);
+		// Ok, this is gonna get a little wild, so hold your breath...
 
-		//val = (0xff << shiftnum) & andmask;
+		/* This stuff is getting actually evaluated in
+		 * EvalDeterministicSpriteGroup(). */
+
+		buf += 4; len -= 4;
+		check_length(len, 6, "NewSpriteGroup 0x81/0x82");
 
 		if (setid >= _cur_grffile->spritegroups_count) {
 			_cur_grffile->spritegroups_count = setid + 1;
 			_cur_grffile->spritegroups = realloc(_cur_grffile->spritegroups, _cur_grffile->spritegroups_count * sizeof(struct SpriteGroup));
 		}
-		buf += 8 + nvar * 4;
-		def = grf_load_word(&buf);
-		_cur_grffile->spritegroups[setid] = _cur_grffile->spritegroups[def];
+
+		group = &_cur_grffile->spritegroups[setid];
+		memset(group, 0, sizeof(struct SpriteGroup));
+		group->type = SGT_DETERMINISTIC;
+		dg = &group->g.determ;
+
+		/* XXX: We don't free() anything, assuming that if there was
+		 * some action here before, it got associated by action 3.
+		 * We should perhaps keep some refcount? --pasky */
+
+		dg->var_scope = numloaded == 0x82 ? VSG_SCOPE_PARENT : VSG_SCOPE_SELF;
+		dg->variable = grf_load_byte(&buf);
+
+		dg->shift_num = grf_load_byte(&buf);
+		dg->and_mask = grf_load_byte(&buf);
+		dg->operation = dg->shift_num >> 6; /* w00t */
+		dg->shift_num &= 0x3F;
+		if (dg->operation != DSG_OP_NONE) {
+			dg->add_val = grf_load_byte(&buf);
+			dg->divmod_val = grf_load_byte(&buf);
+		}
+
+		dg->num_ranges = grf_load_byte(&buf);
+		dg->ranges = calloc(dg->num_ranges, sizeof(*dg->ranges));
+		for (i = 0; i < dg->num_ranges; i++) {
+			uint16 setid = grf_load_word(&buf);
+
+			/* XXX: If multiple surreal sets attach a surreal
+			 * set this way, we are in trouble. */
+			dg->ranges[i].group = _cur_grffile->spritegroups[setid];
+			dg->ranges[i].range_low = grf_load_byte(&buf);
+			dg->ranges[i].range_high = grf_load_byte(&buf);
+		}
+
+		dg->default_group = malloc(sizeof(*dg->default_group));
+		memcpy(dg->default_group, &_cur_grffile->spritegroups[grf_load_word(&buf)], sizeof(*dg->default_group));
+
 		return;
 
 	} else if (numloaded & 0x80) {
@@ -1124,25 +1157,28 @@
 	}
 	group = &_cur_grffile->spritegroups[setid];
 	memset(group, 0, sizeof(struct SpriteGroup));
-	group->sprites_per_set = _cur_grffile->spriteset_numents;
-	group->loaded_count  = numloaded;
-	group->loading_count = numloading;
+	group->type = SGT_REAL;
+	rg = &group->g.real;
+
+	rg->sprites_per_set = _cur_grffile->spriteset_numents;
+	rg->loaded_count  = numloaded;
+	rg->loading_count = numloading;
 
 	DEBUG(grf, 6) ("NewSpriteGroup: New SpriteGroup 0x%02hhx, %u views, %u loaded, %u loading, sprites %u - %u",
-			setid, group->sprites_per_set, group->loaded_count, group->loading_count,
+			setid, rg->sprites_per_set, rg->loaded_count, rg->loading_count,
 			_cur_grffile->spriteset_start - _cur_grffile->sprite_offset,
 			_cur_grffile->spriteset_start + (_cur_grffile->spriteset_numents * (numloaded + numloading)) - _cur_grffile->sprite_offset);
 
 	for (i = 0; i < numloaded; i++) {
 		uint16 spriteset_id = grf_load_word(&loaded_ptr);
-		group->loaded[i] = _cur_grffile->spriteset_start + spriteset_id * _cur_grffile->spriteset_numents;
-		DEBUG(grf, 8) ("NewSpriteGroup: + group->loaded[%i]  = %u (subset %u)", i, group->loaded[i], spriteset_id);
+		rg->loaded[i] = _cur_grffile->spriteset_start + spriteset_id * _cur_grffile->spriteset_numents;
+		DEBUG(grf, 8) ("NewSpriteGroup: + rg->loaded[%i]  = %u (subset %u)", i, rg->loaded[i], spriteset_id);
 	}
 
 	for (i = 0; i < numloading; i++) {
 		uint16 spriteset_id = grf_load_word(&loading_ptr);
-		group->loading[i] = _cur_grffile->spriteset_start + spriteset_id * _cur_grffile->spriteset_numents;
-		DEBUG(grf, 8) ("NewSpriteGroup: + group->loading[%i] = %u (subset %u)", i, group->loading[i], spriteset_id);
+		rg->loading[i] = _cur_grffile->spriteset_start + spriteset_id * _cur_grffile->spriteset_numents;
+		DEBUG(grf, 8) ("NewSpriteGroup: + rg->loading[%i] = %u (subset %u)", i, rg->loading[i], spriteset_id);
 	}
 }
 
@@ -1214,7 +1250,7 @@
 					continue;
 				}
 
-				stat->relocation[1] = _cur_grffile->spritegroups[groupid];
+				stat->spritegroup[1] = _cur_grffile->spritegroups[groupid];
 			}
 		}
 
@@ -1232,7 +1268,7 @@
 				uint8 stid = buf[3 + i];
 				struct StationSpec *stat = &_cur_grffile->stations[stid];
 
-				stat->relocation[0] = _cur_grffile->spritegroups[groupid];
+				stat->spritegroup[0] = _cur_grffile->spritegroups[groupid];
 				stat->grfid = _cur_grffile->grfid;
 				SetCustomStation(stid, stat);
 				stat->classid = 0;
--- a/rail_cmd.c	Sun Nov 14 17:16:26 2004 +0000
+++ b/rail_cmd.c	Sun Nov 14 18:18:28 2004 +0000
@@ -1551,7 +1551,8 @@
 				DrawTileSeqStruct const *seq;
 				// emulate station tile - open with building
 				DrawTileSprites *cust = &stat->renderdata[2 + (m5 & 0x1)];
-				uint32 relocation = GetCustomStationRelocation(stat, 0);
+				/* FIXME: NULL Station! --pasky */
+				uint32 relocation = GetCustomStationRelocation(stat, NULL, 0);
 
 				image = cust->ground_sprite;
 				if (image & 0x8000) image = (image & 0x7FFF) + tracktype_offs;
@@ -1633,7 +1634,7 @@
 
 	assert(stat);
 
-	relocation = GetCustomStationRelocation(stat, 1);
+	relocation = GetCustomStationRelocation(stat, NULL, 1);
 	// emulate station tile - open with building
 	// add 1 to get the other direction
 	cust = &stat->renderdata[2];
--- a/sprite.h	Sun Nov 14 17:16:26 2004 +0000
+++ b/sprite.h	Sun Nov 14 18:18:28 2004 +0000
@@ -7,7 +7,7 @@
  * depots or stations): */
 
 typedef struct DrawTileSeqStruct {
-	int8 delta_x;
+	int8 delta_x; // 0x80 is sequence terminator
 	int8 delta_y;
 	int8 delta_z;
 	byte width,height;
@@ -20,12 +20,16 @@
 	DrawTileSeqStruct const *seq;
 } DrawTileSprites;
 
+// Iterate through all DrawTileSeqStructs in DrawTileSprites.
 #define foreach_draw_tile_seq(idx, list) for (idx = list; ((byte) idx->delta_x) != 0x80; idx++)
 
 
 /* This is for custom sprites: */
 
-struct SpriteGroup {
+
+struct SpriteGroup;
+
+struct RealSpriteGroup {
 	// XXX: Would anyone ever need more than 16 spritesets? Maybe we should
 	// use even less, now we take whole 8kb for custom sprites table, oh my!
 	byte sprites_per_set; // means number of directions - 4 or 8
@@ -43,4 +47,74 @@
 	uint16 loading[16]; // sprite ids
 };
 
+/* Shared by deterministic and random groups. */
+enum VarSpriteGroupScope {
+	VSG_SCOPE_SELF,
+	// Engine of consists for vehicles, city for stations.
+	VSG_SCOPE_PARENT,
+};
+
+struct DeterministicSpriteGroupRanges;
+
+struct DeterministicSpriteGroup {
+	// Take this variable:
+	enum VarSpriteGroupScope var_scope;
+	byte variable;
+
+	// Do this with it:
+	byte shift_num;
+	byte and_mask;
+
+	// Then do this with it:
+	enum DeterministicSpriteGroupOperation {
+		DSG_OP_NONE,
+		DSG_OP_DIV,
+		DSG_OP_MOD,
+	} operation;
+	byte add_val;
+	byte divmod_val;
+	
+	// And apply it to this:
+	byte num_ranges;
+	struct DeterministicSpriteGroupRanges *ranges; // Dynamically allocated
+
+	// Dynamically allocated, this is the sole owner
+	struct SpriteGroup *default_group;
+};
+
+struct SpriteGroup {
+	enum SpriteGroupType {
+		SGT_REAL,
+		SGT_DETERMINISTIC,
+		SGT_RANDOM, /* TODO */
+	} type;
+
+	union {
+		struct RealSpriteGroup real;
+		struct DeterministicSpriteGroup determ;
+	} g;
+};
+
+struct DeterministicSpriteGroupRanges {
+	struct SpriteGroup group;
+	byte range_low;
+	byte range_high;
+};
+
+/* This is a temporary helper for SpriteGroup users not supporting variational
+ * sprite groups yet - it just traverses those cowardly, always taking the
+ * default choice until it hits a real sprite group, returning it. */
+static struct RealSpriteGroup *TriviallyGetRSG(struct SpriteGroup *sg);
+
+
+
+/**** Inline functions ****/
+
+static INLINE struct RealSpriteGroup *TriviallyGetRSG(struct SpriteGroup *sg)
+{
+	if (sg->type == SGT_REAL)
+		return &sg->g.real;
+	return TriviallyGetRSG(sg->g.determ.default_group);
+}
+
 #endif
--- a/station.h	Sun Nov 14 17:16:26 2004 +0000
+++ b/station.h	Sun Nov 14 18:18:28 2004 +0000
@@ -102,9 +102,9 @@
 	byte tiles;
 	DrawTileSprites renderdata[8];
 
-	/* Sprite offsets for renderdata->seq->image. relocation[0] is default
-	 * whilst relocation[1] is "CID_PURCHASE". */
-	struct SpriteGroup relocation[2];
+	/* Sprite offsets for renderdata->seq->image. spritegroup[0] is default
+	 * whilst spritegroup[1] is "CID_PURCHASE". */
+	struct SpriteGroup spritegroup[2];
 };
 
 /* Here, @stid is local per-GRFFile station index. If spec->localidx is not yet
@@ -115,7 +115,10 @@
 /* Here, @stid is global station index (in continous range 0..GetCustomStationsCount())
  * (lookup is therefore very fast as we do this very frequently). */
 struct StationSpec *GetCustomStation(uint32 classid, byte stid);
-uint32 GetCustomStationRelocation(struct StationSpec *spec, byte ctype);
+/* Get sprite offset for a given custom station and station structure (may be
+ * NULL if ctype is set - that means we are in a build dialog). The station
+ * structure is used for variational sprite groups. */
+uint32 GetCustomStationRelocation(struct StationSpec *spec, struct Station *stat, byte ctype);
 int GetCustomStationsCount(uint32 classid);
 
 #endif /* STATION_H */
--- a/station_cmd.c	Sun Nov 14 17:16:26 2004 +0000
+++ b/station_cmd.c	Sun Nov 14 18:18:28 2004 +0000
@@ -977,6 +977,9 @@
 			if (_waypoint_data[i].grfid == spec->grfid
 			    && _waypoint_data[i].localidx == local_stid + 1) {
 				stid = i;
+				/* FIXME: Release original SpriteGroup to
+				 * prevent leaks. But first we need to
+				 * refcount the SpriteGroup. --pasky */
 				break;
 			}
 		}
@@ -1003,25 +1006,30 @@
 	return &_waypoint_data[stid];
 }
 
-uint32 GetCustomStationRelocation(struct StationSpec *spec, byte ctype)
+uint32 GetCustomStationRelocation(struct StationSpec *spec, struct Station *stat, byte ctype)
 {
+	struct RealSpriteGroup *rsg;
+
 	assert(spec->classid == 'WAYP');
 
 	/* In the future, variational spritegroups will kick in through this
-	 * accessor.  */
-
-	if (spec->relocation[ctype].loading_count != 0) {
-		return spec->relocation[ctype].loading[0];
-	} else if (spec->relocation[ctype].loading_count != 0) {
-		return spec->relocation[ctype].loaded[0];
-	} else {
-		error("Custom station 0x%08x::0x%02x has no sprites associated.",
-		       spec->grfid, spec->localidx);
-		/* This is what gets subscribed of dtss->image in grfspecial.c,
-		 * so it's probably kinda "default offset". Try to use it as
-		 * emergency measure. */
-		return 0x42D;
+	 * accessor, using @stat.  */
+	rsg = TriviallyGetRSG(&spec->spritegroup[ctype]);
+
+	if (rsg->sprites_per_set != 0) {
+		if (rsg->loading_count != 0) {
+			return rsg->loading[0];
+		} else if (rsg->loading_count != 0) {
+			return rsg->loaded[0];
+		}
 	}
+
+	error("Custom station 0x%08x::0x%02x has no sprites associated.",
+	       spec->grfid, spec->localidx);
+	/* This is what gets subscribed of dtss->image in grfspecial.c,
+	 * so it's probably kinda "default offset". Try to use it as
+	 * emergency measure. */
+	return 0x42D;
 }
 
 int GetCustomStationsCount(uint32 classid)