changeset 9003:ac23e012c9d7 draft

(svn r12798) -Feature: Add some support for NewGRF station animation. (Thanks to mart3p for samples and fixes)
author peter1138 <peter1138@openttd.org>
date Sat, 19 Apr 2008 23:19:12 +0000
parents c9d48ed87e3a
children 6069d8b7ac1e
files src/economy.cpp src/newgrf.cpp src/newgrf_callbacks.h src/newgrf_station.cpp src/newgrf_station.h src/station_base.h src/station_cmd.cpp src/train_cmd.cpp
diffstat 8 files changed, 251 insertions(+), 17 deletions(-) [+]
line wrap: on
line diff
--- a/src/economy.cpp
+++ b/src/economy.cpp
@@ -26,6 +26,7 @@
 #include "newgrf_callbacks.h"
 #include "newgrf_industries.h"
 #include "newgrf_industrytiles.h"
+#include "newgrf_station.h"
 #include "unmovable.h"
 #include "cargotype.h"
 #include "player_face.h"
@@ -1691,6 +1692,8 @@
 			st->time_since_load = 0;
 			st->last_vehicle_type = v->type;
 
+			StationAnimationTrigger(st, st->xy, STAT_ANIM_CARGO_TAKEN, v->cargo_type);
+
 			unloading_time += cap;
 
 			result |= 2;
--- a/src/newgrf.cpp
+++ b/src/newgrf.cpp
@@ -1120,19 +1120,17 @@
 				statspec->blocked = grf_load_byte(&buf);
 				break;
 
-			case 0x16: // @todo Animation info
-				grf_load_word(&buf);
-				ret = true;
-				break;
-
-			case 0x17: // @todo Animation speed
-				grf_load_byte(&buf);
-				ret = true;
-				break;
-
-			case 0x18: // @todo Animation triggers
-				grf_load_word(&buf);
-				ret = true;
+			case 0x16: // Animation info
+				statspec->anim_frames = grf_load_byte(&buf);
+				statspec->anim_status = grf_load_byte(&buf);
+				break;
+
+			case 0x17: // Animation speed
+				statspec->anim_speed = grf_load_byte(&buf);
+				break;
+
+			case 0x18: // Animation triggers
+				statspec->anim_triggers = grf_load_word(&buf);
 				break;
 
 			default:
--- a/src/newgrf_callbacks.h
+++ b/src/newgrf_callbacks.h
@@ -173,13 +173,13 @@
 	/* There are no callbacks 0x3E - 0x13F */
 
 	/** Called for periodically starting or stopping the animation. */
-	CBID_STATION_ANIM_START_STOP         = 0x140, // not implemented
+	CBID_STATION_ANIM_START_STOP         = 0x140,
 
 	/** Called to determine station tile next animation frame. */
-	CBID_STATION_ANIM_NEXT_FRAME         = 0x141, // not implemented
+	CBID_STATION_ANIM_NEXT_FRAME         = 0x141,
 
 	/** Called to indicate how long the current animation frame should last. */
-	CBID_STATION_ANIMATION_SPEED         = 0x142, // not implemented
+	CBID_STATION_ANIMATION_SPEED         = 0x142,
 
 	/** Called to determine whether a town building can be destroyed. */
 	CBID_HOUSE_DENY_DESTRUCTION          = 0x143,
--- a/src/newgrf_station.cpp
+++ b/src/newgrf_station.cpp
@@ -740,8 +740,12 @@
 			free(st->speclist);
 			st->num_specs = 0;
 			st->speclist  = NULL;
+			st->cached_anim_triggers = 0;
+			return;
 		}
 	}
+
+	StationUpdateAnimTriggers(st);
 }
 
 /** Draw representation of a station tile for GUI purposes.
@@ -853,3 +857,178 @@
 		HasBit(statspec->pylons, GetStationGfx(tile)) ||
 		!HasBit(statspec->wires, GetStationGfx(tile));
 }
+
+void AnimateStationTile(TileIndex tile)
+{
+	const StationSpec *ss = GetStationSpec(tile);
+	if (ss == NULL) return;
+
+	const Station *st = GetStationByTile(tile);
+
+	uint8 animation_speed = ss->anim_speed;
+
+	if (HasBit(ss->callbackmask, CBM_STATION_ANIMATION_SPEED)) {
+		uint16 callback = GetStationCallback(CBID_STATION_ANIMATION_SPEED, 0, 0, ss, st, tile);
+		if (callback != CALLBACK_FAILED) animation_speed = Clamp(callback & 0xFF, 0, 16);
+	}
+
+	if (_tick_counter % (1 << animation_speed) != 0) return;
+
+	uint8 frame      = GetStationAnimationFrame(tile);
+	uint8 num_frames = ss->anim_frames;
+
+	bool frame_set_by_callback = false;
+
+	if (HasBit(ss->callbackmask, CBM_STATION_ANIMATION_NEXT_FRAME)) {
+		uint32 param = HasBit(ss->flags, 2) ? Random() : 0;
+		uint16 callback = GetStationCallback(CBID_STATION_ANIM_NEXT_FRAME, param, 0, ss, st, tile);
+
+		if (callback != CALLBACK_FAILED) {
+			frame_set_by_callback = true;
+
+			switch (callback & 0xFF) {
+				case 0xFF:
+					DeleteAnimatedTile(tile);
+					break;
+
+				case 0xFE:
+					frame_set_by_callback = false;
+					break;
+
+				default:
+					frame = callback & 0xFF;
+					break;
+			}
+		}
+	}
+
+	if (!frame_set_by_callback) {
+		if (frame < num_frames) {
+			frame++;
+		} else if (frame == num_frames && HasBit(ss->anim_status, 0)) {
+			/* This animation loops, so start again from the beginning */
+			frame = 0;
+		} else {
+			/* This animation doesn't loop, so stay here */
+			DeleteAnimatedTile(tile);
+		}
+	}
+
+	SetStationAnimationFrame(tile, frame);
+	MarkTileDirtyByTile(tile);
+}
+
+
+static void ChangeStationAnimationFrame(const StationSpec *ss, const Station *st, TileIndex tile, uint16 random_bits, StatAnimTrigger trigger, CargoID cargo_type)
+{
+	uint16 callback = GetStationCallback(CBID_STATION_ANIM_START_STOP, (random_bits << 16) | Random(), (uint8)trigger | (cargo_type << 8), ss, st, tile);
+	if (callback == CALLBACK_FAILED) return;
+
+	switch (callback & 0xFF) {
+		case 0xFD: /* Do nothing. */         break;
+		case 0xFE: AddAnimatedTile(tile);    break;
+		case 0xFF: DeleteAnimatedTile(tile); break;
+		default:
+			SetStationAnimationFrame(tile, callback);
+			AddAnimatedTile(tile);
+			break;
+	}
+}
+
+enum TriggerArea {
+	TA_TILE,
+	TA_PLATFORM,
+	TA_WHOLE,
+};
+
+struct TileArea {
+	TileIndex tile;
+	uint8 w;
+	uint8 h;
+
+	TileArea(const Station *st, TileIndex tile, TriggerArea ta)
+	{
+		switch (ta) {
+			default: NOT_REACHED();
+
+			case TA_TILE:
+				this->tile = tile;
+				this->w    = 1;
+				this->h    = 1;
+				break;
+
+			case TA_PLATFORM: {
+				TileIndex start, end;
+				Axis axis = GetRailStationAxis(tile);
+				TileIndexDiff delta = TileOffsByDiagDir(AxisToDiagDir(axis));
+
+				for (end = tile; IsRailwayStationTile(end + delta) && IsCompatibleTrainStationTile(tile, end + delta); end += delta);
+				for (start = tile; IsRailwayStationTile(start - delta) && IsCompatibleTrainStationTile(tile, start - delta); start -= delta);
+
+				this->tile = start;
+				this->w = TileX(end) - TileX(start) + 1;
+				this->h = TileY(end) - TileY(start) + 1;
+				break;
+			}
+
+			case TA_WHOLE:
+				this->tile = st->train_tile;
+				this->w    = st->trainst_w + 1;
+				this->h    = st->trainst_h + 1;
+				break;
+		}
+	}
+};
+
+void StationAnimationTrigger(const Station *st, TileIndex tile, StatAnimTrigger trigger, CargoID cargo_type)
+{
+	/* List of coverage areas for each animation trigger */
+	static const TriggerArea tas[] = {
+		TA_TILE, TA_WHOLE, TA_WHOLE, TA_PLATFORM, TA_PLATFORM, TA_PLATFORM, TA_WHOLE
+	};
+
+	/* Get Station if it wasn't supplied */
+	if (st == NULL) st = GetStationByTile(tile);
+
+	/* Check the cached animation trigger bitmask to see if we need
+	 * to bother with any further processing. */
+	if (!HasBit(st->cached_anim_triggers, trigger)) return;
+
+	uint16 random_bits = Random();
+	TileArea area = TileArea(st, tile, tas[trigger]);
+
+	for (uint y = 0; y < area.h; y++) {
+		for (uint x = 0; x < area.w; x++) {
+			if (st->TileBelongsToRailStation(area.tile)) {
+				const StationSpec *ss = GetStationSpec(area.tile);
+				if (ss != NULL && HasBit(ss->anim_triggers, trigger)) {
+					CargoID cargo;
+					if (cargo_type == CT_INVALID) {
+						cargo = CT_INVALID;
+					} else {
+						cargo = GetReverseCargoTranslation(cargo_type, ss->grffile);
+					}
+					ChangeStationAnimationFrame(ss, st, area.tile, random_bits, trigger, cargo);
+				}
+			}
+			area.tile += TileDiffXY(1, 0);
+		}
+		area.tile += TileDiffXY(-area.w, 1);
+	}
+}
+
+/**
+ * Update the cached animation trigger bitmask for a station.
+ * @param st Station to update.
+ */
+void StationUpdateAnimTriggers(Station *st)
+{
+	st->cached_anim_triggers = 0;
+
+	/* Combine animation trigger bitmask for all station specs
+	 * of this station. */
+	for (uint i = 0; i < st->num_specs; i++) {
+		const StationSpec *ss = st->speclist[i].spec;
+		if (ss != NULL) st->cached_anim_triggers |= ss->anim_triggers;
+	}
+}
--- a/src/newgrf_station.h
+++ b/src/newgrf_station.h
@@ -83,6 +83,11 @@
 	StationLayout **layouts;
 	bool copied_layouts;
 
+	uint8  anim_frames;
+	uint8  anim_status;
+	uint8  anim_speed;
+	uint16 anim_triggers;
+
 	/**
 	 * NUM_CARGO real cargo plus three pseudo cargo sprite groups.
 	 * Used for obtaining the sprite offset of custom sprites, and for
@@ -132,4 +137,18 @@
 /* Draw representation of a station tile for GUI purposes. */
 bool DrawStationTile(int x, int y, RailType railtype, Axis axis, StationClassID sclass, uint station);
 
+enum StatAnimTrigger {
+	STAT_ANIM_BUILT,
+	STAT_ANIM_NEW_CARGO,
+	STAT_ANIM_CARGO_TAKEN,
+	STAT_ANIM_TRAIN_ARRIVES,
+	STAT_ANIM_TRAIN_DEPARTS,
+	STAT_ANIM_TRAIN_LOADS,
+	STAT_ANIM_250_TICKS,
+};
+
+void AnimateStationTile(TileIndex tile);
+void StationAnimationTrigger(const Station *st, TileIndex tile, StatAnimTrigger trigger, CargoID cargo_type = CT_INVALID);
+void StationUpdateAnimTriggers(Station *st);
+
 #endif /* NEWGRF_STATION_H */
--- a/src/station_base.h
+++ b/src/station_base.h
@@ -159,6 +159,7 @@
 
 	uint16 random_bits;
 	byte waiting_triggers;
+	uint8 cached_anim_triggers; ///< Combined animation trigger bitmask, used to determine if trigger processing should happen.
 
 	StationRect rect; ///< Station spread out rectangle (not saved) maintained by StationRect_xxx() functions
 
--- a/src/station_cmd.cpp
+++ b/src/station_cmd.cpp
@@ -1038,6 +1038,12 @@
 
 		st->rect.BeforeAddRect(tile_org, w_org, h_org, StationRect::ADD_TRY);
 
+		if (statspec != NULL) {
+			/* Include this station spec's animation trigger bitmask
+			 * in the station's cached copy. */
+			st->cached_anim_triggers |= statspec->anim_triggers;
+		}
+
 		tile_delta = (axis == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1));
 		track = AxisToTrack(axis);
 
@@ -1054,6 +1060,7 @@
 				MakeRailStation(tile, st->owner, st->index, axis, layout & ~1, (RailType)GB(p2, 0, 4));
 				SetCustomStationSpecIndex(tile, specindex);
 				SetStationTileRandomBits(tile, GB(Random(), 0, 4));
+				SetStationAnimationFrame(tile, 0);
 
 				if (statspec != NULL) {
 					/* Use a fixed axis for GetPlatformInfo as our platforms / numtracks are always the right way around */
@@ -1062,6 +1069,9 @@
 					/* As the station is not yet completely finished, the station does not yet exist. */
 					uint16 callback = GetStationCallback(CBID_STATION_TILE_LAYOUT, platinfo, 0, statspec, NULL, tile);
 					if (callback != CALLBACK_FAILED && callback < 8) SetStationGfx(tile, (callback & ~1) + axis);
+
+					/* Trigger station animation -- after building? */
+					StationAnimationTrigger(st, tile, STAT_ANIM_BUILT);
 				}
 
 				tile += tile_delta;
@@ -1283,6 +1293,7 @@
 		free(st->speclist);
 		st->num_specs = 0;
 		st->speclist  = NULL;
+		st->cached_anim_triggers = 0;
 
 		InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_TRAINS);
 		UpdateStationVirtCoordDirty(st);
@@ -2368,6 +2379,11 @@
 		{ GFX_WINDSACK_INTERCON_FIRST,   GFX_WINDSACK_INTERCON_LAST,   1 }
 	};
 
+	if (IsRailwayStation(tile)) {
+		AnimateStationTile(tile);
+		return;
+	}
+
 	StationGfx gfx = GetStationGfx(tile);
 
 	for (const AnimData *i = data; i != endof(data); i++) {
@@ -2614,7 +2630,16 @@
 	if (IsValidStationID(i)) StationHandleBigTick(GetStation(i));
 
 	Station *st;
-	FOR_ALL_STATIONS(st) StationHandleSmallTick(st);
+	FOR_ALL_STATIONS(st) {
+		StationHandleSmallTick(st);
+
+		/* Run 250 tick interval trigger for station animation.
+		 * Station index is included so that triggers are not all done
+		 * at the same time. */
+		if ((_tick_counter + st->index) % 250 == 0) {
+			StationAnimationTrigger(st, st->xy, STAT_ANIM_250_TICKS);
+		}
+	}
 }
 
 void StationMonthlyLoop()
@@ -2646,6 +2671,8 @@
 	st->goods[type].cargo.Append(new CargoPacket(st->index, amount));
 	SetBit(st->goods[type].acceptance_pickup, GoodsEntry::PICKUP);
 
+	StationAnimationTrigger(st, st->xy, STAT_ANIM_NEW_CARGO, type);
+
 	InvalidateWindow(WC_STATION_VIEW, st->index);
 	st->MarkTilesDirty(true);
 }
@@ -3008,6 +3035,8 @@
 		}
 
 		for (CargoID c = 0; c < NUM_CARGO; c++) st->goods[c].cargo.InvalidateCache();
+
+		StationUpdateAnimTriggers(st);
 	}
 }
 
--- a/src/train_cmd.cpp
+++ b/src/train_cmd.cpp
@@ -48,6 +48,7 @@
 #include "gfx_func.h"
 #include "settings_type.h"
 #include "order_func.h"
+#include "newgrf_station.h"
 
 #include "table/strings.h"
 #include "table/train_cmd.h"
@@ -2218,6 +2219,8 @@
 		SND_41_MAGLEV
 	};
 
+	if (IsTileType(this->tile, MP_STATION)) StationAnimationTrigger(NULL, this->tile, STAT_ANIM_TRAIN_DEPARTS);
+
 	if (PlayVehicleSound(this, VSE_START)) return;
 
 	EngineID engtype = this->engine_type;
@@ -2638,6 +2641,8 @@
 	}
 
 	v->BeginLoading();
+
+	StationAnimationTrigger(st, v->tile, STAT_ANIM_TRAIN_ARRIVES);
 }
 
 static byte AfterSetTrainPos(Vehicle *v, bool new_tile)