diff options
-rw-r--r-- | src/economy.cpp | 3 | ||||
-rw-r--r-- | src/newgrf.cpp | 16 | ||||
-rw-r--r-- | src/newgrf_callbacks.h | 6 | ||||
-rw-r--r-- | src/newgrf_station.cpp | 179 | ||||
-rw-r--r-- | src/newgrf_station.h | 19 | ||||
-rw-r--r-- | src/station_base.h | 1 | ||||
-rw-r--r-- | src/station_cmd.cpp | 31 | ||||
-rw-r--r-- | src/train_cmd.cpp | 5 |
8 files changed, 247 insertions, 13 deletions
diff --git a/src/economy.cpp b/src/economy.cpp index 9c4b5e079..b386416f8 100644 --- 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 @@ static void LoadUnloadVehicle(Vehicle *v, int *cargo_left) 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; diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 3719aaf13..e222a63c0 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -1120,19 +1120,17 @@ static bool StationChangeInfo(uint stid, int numinfo, int prop, byte **bufp, int statspec->blocked = grf_load_byte(&buf); break; - case 0x16: // @todo Animation info - 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: // @todo Animation speed - grf_load_byte(&buf); - ret = true; + case 0x17: // Animation speed + statspec->anim_speed = grf_load_byte(&buf); break; - case 0x18: // @todo Animation triggers - grf_load_word(&buf); - ret = true; + case 0x18: // Animation triggers + statspec->anim_triggers = grf_load_word(&buf); break; default: diff --git a/src/newgrf_callbacks.h b/src/newgrf_callbacks.h index 46545e9e9..9b49bb2f6 100644 --- a/src/newgrf_callbacks.h +++ b/src/newgrf_callbacks.h @@ -173,13 +173,13 @@ enum CallbackID { /* 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, diff --git a/src/newgrf_station.cpp b/src/newgrf_station.cpp index 98c93c62c..019bfce2e 100644 --- a/src/newgrf_station.cpp +++ b/src/newgrf_station.cpp @@ -740,8 +740,12 @@ void DeallocateSpecFromStation(Station* st, byte specindex) 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 @@ bool IsStationTileElectrifiable(TileIndex tile) 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; + } +} diff --git a/src/newgrf_station.h b/src/newgrf_station.h index 81443577c..124e71b56 100644 --- a/src/newgrf_station.h +++ b/src/newgrf_station.h @@ -83,6 +83,11 @@ struct StationSpec { 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 @@ void DeallocateSpecFromStation(Station* st, byte specindex); /* 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 */ diff --git a/src/station_base.h b/src/station_base.h index eed0a7253..ca311d0f1 100644 --- a/src/station_base.h +++ b/src/station_base.h @@ -159,6 +159,7 @@ public: 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 diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 19b83d99b..d99a5fc3f 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -1038,6 +1038,12 @@ CommandCost CmdBuildRailroadStation(TileIndex tile_org, uint32 flags, uint32 p1, 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 @@ CommandCost CmdBuildRailroadStation(TileIndex tile_org, uint32 flags, uint32 p1, 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 @@ CommandCost CmdBuildRailroadStation(TileIndex tile_org, uint32 flags, uint32 p1, /* 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 @@ static CommandCost RemoveRailroadStation(Station *st, TileIndex tile, uint32 fla 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 @@ static void AnimateTile_Station(TileIndex tile) { 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 @@ void OnTick_Station() 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 @@ static void UpdateStationWaiting(Station *st, CargoID type, uint amount) 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 @@ void AfterLoadStations() } for (CargoID c = 0; c < NUM_CARGO; c++) st->goods[c].cargo.InvalidateCache(); + + StationUpdateAnimTriggers(st); } } diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index cf6478b4c..15c47450f 100644 --- 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 @@ void Train::PlayLeaveStationSound() const 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 @@ static void TrainEnterStation(Vehicle *v, StationID station) } v->BeginLoading(); + + StationAnimationTrigger(st, v->tile, STAT_ANIM_TRAIN_ARRIVES); } static byte AfterSetTrainPos(Vehicle *v, bool new_tile) |