Add support for road stop animation, availability callback

Add animation, callback mask, general flags properties
Add animation frame variables
pull/374/head
Jonathan G Rennison 2 years ago
parent 0c3988c39b
commit 23c472d2a0

@ -80,15 +80,16 @@ struct BaseStation : StationPool::PoolItem<&_station_pool> {
uint16 random_bits; ///< Random bits assigned to this station
byte waiting_triggers; ///< Waiting triggers (NewGRF) for this station
uint8 cached_anim_triggers; ///< NOSAVE: Combined animation trigger bitmask, used to determine if trigger processing should happen.
CargoTypes cached_cargo_triggers; ///< NOSAVE: Combined cargo trigger bitmask
CargoTypes roadstop_cached_cargo_triggers; ///< NOSAVE: Combined cargo trigger bitmask for road stops
uint8 cached_anim_triggers; ///< NOSAVE: Combined animation trigger bitmask, used to determine if trigger processing should happen.
uint8 cached_roadstop_anim_triggers; ///< NOSAVE: Combined animation trigger bitmask for road stops, used to determine if trigger processing should happen.
CargoTypes cached_cargo_triggers; ///< NOSAVE: Combined cargo trigger bitmask
CargoTypes cached_roadstop_cargo_triggers; ///< NOSAVE: Combined cargo trigger bitmask for road stops
TileArea train_station; ///< Tile area the train 'station' part covers
StationRect rect; ///< NOSAVE: Station spread out rectangle maintained by StationRect::xxx() functions
std::vector<TileIndex> custom_road_stop_tiles; ///< List of custom road stop tiles
std::vector<byte> custom_road_stop_random_bits; ///< Custom road stop random bits in same order as custom_road_stop_tiles
std::vector<uint16> custom_road_stop_data; ///< Custom road stop random bits (low) and animation byte (high) in same order as custom_road_stop_tiles
/**
* Initialize the base station.
@ -193,16 +194,31 @@ struct BaseStation : StationPool::PoolItem<&_station_pool> {
return (this->facilities & facilities) != 0;
}
inline byte GetRoadStopRandomBits(TileIndex tile) const
inline uint GetRoadStopData(TileIndex tile) const
{
for (size_t i = 0; i < this->custom_road_stop_tiles.size(); i++) {
if (this->custom_road_stop_tiles[i] == tile) return this->custom_road_stop_random_bits[i];
if (this->custom_road_stop_tiles[i] == tile) return this->custom_road_stop_data[i];
}
return 0;
}
void SetRoadStopRandomBits(TileIndex tile, byte random_bits);
void RemoveRoadStopRandomBits(TileIndex tile);
inline byte GetRoadStopRandomBits(TileIndex tile) const
{
return GB(this->GetRoadStopData(tile), 0, 8);
}
inline byte GetRoadStopAnimationFrame(TileIndex tile) const
{
return GB(this->GetRoadStopData(tile), 8, 8);
}
private:
void SetRoadStopTileData(TileIndex tile, byte data, byte offset);
public:
inline void SetRoadStopRandomBits(TileIndex tile, byte random_bits) { this->SetRoadStopTileData(tile, random_bits, 0); }
inline void SetRoadStopAnimationFrame(TileIndex tile, byte frame) { this->SetRoadStopTileData(tile, random_bits, 8); }
void RemoveRoadStopTileData(TileIndex tile);
static void PostDestructor(size_t index);

@ -2147,6 +2147,7 @@ static void LoadUnloadVehicle(Vehicle *front)
TriggerStationRandomisation(st, st->xy, SRT_CARGO_TAKEN, v->cargo_type);
TriggerStationAnimation(st, st->xy, SAT_CARGO_TAKEN, v->cargo_type);
AirportAnimationTrigger(st, AAT_STATION_CARGO_TAKEN, v->cargo_type);
TriggerRoadStopAnimation(st, st->xy, SAT_NEW_CARGO, v->cargo_type);
TriggerRoadStopRandomisation(st, st->xy, RSRT_CARGO_TAKEN, v->cargo_type);
}
@ -2170,6 +2171,7 @@ static void LoadUnloadVehicle(Vehicle *front)
TriggerStationAnimation(st, station_tile, SAT_TRAIN_LOADS);
} else if (front->type == VEH_ROAD) {
TriggerRoadStopRandomisation(st, station_tile, RSRT_VEH_LOADS);
TriggerRoadStopAnimation(st, station_tile, SAT_TRAIN_LOADS);
}
}

@ -4995,7 +4995,7 @@ static ChangeInfoResult RoadStopChangeInfo(uint id, int numinfo, int prop, const
}
case A0RPI_ROADSTOP_STOP_TYPE:
if (MappedPropertyLengthMismatch(buf, 4, mapping_entry)) break;
if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
FALLTHROUGH;
case 0x09: // Road stop type
rs->stop_type = (RoadStopAvailabilityType)buf->ReadByte();
@ -5029,6 +5029,42 @@ static ChangeInfoResult RoadStopChangeInfo(uint id, int numinfo, int prop, const
rs->cargo_triggers = TranslateRefitMask(buf->ReadDWord());
break;
case A0RPI_ROADSTOP_ANIMATION_INFO:
if (MappedPropertyLengthMismatch(buf, 2, mapping_entry)) break;
FALLTHROUGH;
case 0x0E: // Animation info
rs->animation.frames = buf->ReadByte();
rs->animation.status = buf->ReadByte();
break;
case A0RPI_ROADSTOP_ANIMATION_SPEED:
if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
FALLTHROUGH;
case 0x0F: // Animation speed
rs->animation.speed = buf->ReadByte();
break;
case A0RPI_ROADSTOP_ANIMATION_TRIGGERS:
if (MappedPropertyLengthMismatch(buf, 2, mapping_entry)) break;
FALLTHROUGH;
case 0x10: // Animation triggers
rs->animation.triggers = buf->ReadWord();
break;
case A0RPI_ROADSTOP_CALLBACK_MASK:
if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
FALLTHROUGH;
case 0x11: // Callback mask
rs->callback_mask = buf->ReadByte();
break;
case A0RPI_ROADSTOP_GENERAL_FLAGS:
if (MappedPropertyLengthMismatch(buf, 4, mapping_entry)) break;
FALLTHROUGH;
case 0x12: // General flags
rs->flags = (uint8)buf->ReadDWord(); // Future-proofing, size this as 4 bytes, but we only need one byte's worth of flags at present
break;
default:
ret = CIR_UNKNOWN;
break;

@ -279,7 +279,7 @@ bool DrawNewAirportTile(TileInfo *ti, Station *st, StationGfx gfx, const Airport
}
/** Helper class for animation control. */
struct AirportTileAnimationBase : public AnimationBase<AirportTileAnimationBase, AirportTileSpec, Station, int, GetAirportTileCallback> {
struct AirportTileAnimationBase : public AnimationBase<AirportTileAnimationBase, AirportTileSpec, Station, int, GetAirportTileCallback, TileAnimationFrameAnimationHelper<Station> > {
static const CallbackID cb_animation_speed = CBID_AIRPTILE_ANIMATION_SPEED;
static const CallbackID cb_animation_next_frame = CBID_AIRPTILE_ANIM_NEXT_FRAME;

@ -17,6 +17,12 @@
#include "newgrf_callbacks.h"
#include "tile_map.h"
template <typename Tobj>
struct TileAnimationFrameAnimationHelper {
static byte Get(Tobj *obj, TileIndex tile) { return GetAnimationFrame(tile); }
static void Set(Tobj *obj, TileIndex tile, byte frame) { SetAnimationFrame(tile, frame); }
};
/**
* Helper class for a unified approach to NewGRF animation.
* @tparam Tbase Instantiation of this class.
@ -24,8 +30,9 @@
* @tparam Tobj Object related to the animated tile.
* @tparam Textra Custom extra callback data.
* @tparam GetCallback The callback function pointer.
* @tparam Tframehelper The animation frame get/set helper.
*/
template <typename Tbase, typename Tspec, typename Tobj, typename Textra, uint16 (*GetCallback)(CallbackID callback, uint32 param1, uint32 param2, const Tspec *statspec, Tobj *st, TileIndex tile, Textra extra_data)>
template <typename Tbase, typename Tspec, typename Tobj, typename Textra, uint16 (*GetCallback)(CallbackID callback, uint32 param1, uint32 param2, const Tspec *statspec, Tobj *st, TileIndex tile, Textra extra_data), typename Tframehelper>
struct AnimationBase {
/**
* Animate a single tile.
@ -55,7 +62,7 @@ struct AnimationBase {
* maximum, corresponding to around 33 minutes. */
if (_scaled_tick_counter % (1 << animation_speed) != 0) return;
uint8 frame = GetAnimationFrame(tile);
uint8 frame = Tframehelper::Get(obj, tile);
uint8 num_frames = spec->animation.frames;
bool frame_set_by_callback = false;
@ -98,7 +105,7 @@ struct AnimationBase {
}
}
SetAnimationFrame(tile, frame);
Tframehelper::Set(obj, tile, frame);
MarkTileDirtyByTile(tile, VMDF_NOT_MAP_MODE);
}
@ -124,7 +131,7 @@ struct AnimationBase {
case 0xFE: AddAnimatedTile(tile); break;
case 0xFF: DeleteAnimatedTile(tile); break;
default:
SetAnimationFrame(tile, callback);
Tframehelper::Set(obj, tile, callback);
AddAnimatedTile(tile);
break;
}

@ -307,6 +307,15 @@ enum StationCallbackMask {
CBM_STATION_SLOPE_CHECK = 4, ///< Check slope of new station tiles
};
/**
* Callback masks for road stops.
*/
enum RoadStopCallbackMask {
CBM_ROAD_STOP_AVAIL = 0, ///< Availability of road stop in construction window
CBM_ROAD_STOP_ANIMATION_NEXT_FRAME = 1, ///< Use a custom next frame callback
CBM_ROAD_STOP_ANIMATION_SPEED = 2, ///< Customize the animation speed of the road stop
};
/**
* Callback masks for houses.
*/

@ -95,7 +95,12 @@ extern const GRFPropertyMapDefinition _grf_action0_remappable_properties[] = {
GRFPropertyMapDefinition(GSF_ROADSTOPS, A0RPI_ROADSTOP_STOP_NAME, "roadstop_stop_name"),
GRFPropertyMapDefinition(GSF_ROADSTOPS, A0RPI_ROADSTOP_CLASS_NAME, "roadstop_class_name"),
GRFPropertyMapDefinition(GSF_ROADSTOPS, A0RPI_ROADSTOP_DRAW_MODE, "roadstop_draw_mode"),
GRFPropertyMapDefinition(GSF_ROADSTOPS, A0RPI_ROADSTOP_TRIGGER_CARGOES, "roadstop_trigger_cargoes"),
GRFPropertyMapDefinition(GSF_ROADSTOPS, A0RPI_ROADSTOP_TRIGGER_CARGOES, "roadstop_random_trigger_cargoes"),
GRFPropertyMapDefinition(GSF_ROADSTOPS, A0RPI_ROADSTOP_ANIMATION_INFO, "roadstop_animation_info"),
GRFPropertyMapDefinition(GSF_ROADSTOPS, A0RPI_ROADSTOP_ANIMATION_SPEED, "roadstop_animation_speed"),
GRFPropertyMapDefinition(GSF_ROADSTOPS, A0RPI_ROADSTOP_ANIMATION_TRIGGERS, "roadstop_animation_triggers"),
GRFPropertyMapDefinition(GSF_ROADSTOPS, A0RPI_ROADSTOP_CALLBACK_MASK, "roadstop_callback_mask"),
GRFPropertyMapDefinition(GSF_ROADSTOPS, A0RPI_ROADSTOP_GENERAL_FLAGS, "roadstop_general_flags"),
GRFPropertyMapDefinition(),
};

@ -45,6 +45,11 @@ enum Action0RemapPropertyIds {
A0RPI_ROADSTOP_CLASS_NAME,
A0RPI_ROADSTOP_DRAW_MODE,
A0RPI_ROADSTOP_TRIGGER_CARGOES,
A0RPI_ROADSTOP_ANIMATION_INFO,
A0RPI_ROADSTOP_ANIMATION_SPEED,
A0RPI_ROADSTOP_ANIMATION_TRIGGERS,
A0RPI_ROADSTOP_CALLBACK_MASK,
A0RPI_ROADSTOP_GENERAL_FLAGS,
};

@ -637,7 +637,7 @@ uint16 GetSimpleHouseCallback(CallbackID callback, uint32 param1, uint32 param2,
}
/** Helper class for animation control. */
struct HouseAnimationBase : public AnimationBase<HouseAnimationBase, HouseSpec, Town, CargoTypes, GetSimpleHouseCallback> {
struct HouseAnimationBase : public AnimationBase<HouseAnimationBase, HouseSpec, Town, CargoTypes, GetSimpleHouseCallback, TileAnimationFrameAnimationHelper<Town> > {
static const CallbackID cb_animation_speed = CBID_HOUSE_ANIMATION_SPEED;
static const CallbackID cb_animation_next_frame = CBID_HOUSE_ANIMATION_NEXT_FRAME;

@ -257,7 +257,7 @@ uint16 GetSimpleIndustryCallback(CallbackID callback, uint32 param1, uint32 para
}
/** Helper class for animation control. */
struct IndustryAnimationBase : public AnimationBase<IndustryAnimationBase, IndustryTileSpec, Industry, int, GetSimpleIndustryCallback> {
struct IndustryAnimationBase : public AnimationBase<IndustryAnimationBase, IndustryTileSpec, Industry, int, GetSimpleIndustryCallback, TileAnimationFrameAnimationHelper<Industry> > {
static const CallbackID cb_animation_speed = CBID_INDTILE_ANIMATION_SPEED;
static const CallbackID cb_animation_next_frame = CBID_INDTILE_ANIM_NEXT_FRAME;

@ -558,7 +558,7 @@ uint16 StubGetObjectCallback(CallbackID callback, uint32 param1, uint32 param2,
}
/** Helper class for animation control. */
struct ObjectAnimationBase : public AnimationBase<ObjectAnimationBase, ObjectSpec, Object, int, StubGetObjectCallback> {
struct ObjectAnimationBase : public AnimationBase<ObjectAnimationBase, ObjectSpec, Object, int, StubGetObjectCallback, TileAnimationFrameAnimationHelper<Object> > {
static const CallbackID cb_animation_speed = CBID_OBJECT_ANIMATION_SPEED;
static const CallbackID cb_animation_next_frame = CBID_OBJECT_ANIMATION_NEXT_FRAME;

@ -22,6 +22,8 @@
#include "date_func.h"
#include "town.h"
#include "viewport_func.h"
#include "newgrf_animation_base.h"
#include "newgrf_sound.h"
#include "safeguards.h"
@ -102,11 +104,23 @@ uint32 RoadStopScopeResolver::GetVariable(uint16 variable, uint32 parameter, Get
/* Company information */
case 0x47: return GetCompanyInfo(this->st == nullptr ? _current_company : this->st->owner);
/* Animation frame */
case 0x49: return this->tile == INVALID_TILE ? 0 : this->st->GetRoadStopAnimationFrame(this->tile);
/* Variables which use the parameter */
/* Variables 0x60 to 0x65 and 0x69 are handled separately below */
/* Animation frame of nearby tile */
case 0x66: {
if (this->tile == INVALID_TILE) return UINT_MAX;
TileIndex tile = this->tile;
if (parameter != 0) tile = GetNearbyTile(parameter, tile);
return (IsAnyRoadStopTile(tile) && GetStationIndex(tile) == this->st->index) ? this->st->GetRoadStopAnimationFrame(tile) : UINT_MAX;
}
/* Land info of nearby tile */
case 0x67: {
if (this->tile == INVALID_TILE) return 0;
TileIndex tile = this->tile;
if (parameter != 0) tile = GetNearbyTile(parameter, tile); // only perform if it is required
return GetNearbyTileInformation(tile, this->ro.grffile->grf_version >= 8);
@ -114,6 +128,7 @@ uint32 RoadStopScopeResolver::GetVariable(uint16 variable, uint32 parameter, Get
/* Road stop info of nearby tiles */
case 0x68: {
if (this->tile == INVALID_TILE) return 0xFFFFFFFF;
TileIndex nearby_tile = GetNearbyTile(parameter, this->tile);
if (!IsAnyRoadStopTile(nearby_tile)) return 0xFFFFFFFF;
@ -132,6 +147,7 @@ uint32 RoadStopScopeResolver::GetVariable(uint16 variable, uint32 parameter, Get
/* GRFID of nearby road stop tiles */
case 0x6A: {
if (this->tile == INVALID_TILE) return 0xFFFFFFFF;
TileIndex nearby_tile = GetNearbyTile(parameter, this->tile);
if (!IsAnyRoadStopTile(nearby_tile)) return 0xFFFFFFFF;
@ -212,6 +228,12 @@ TownScopeResolver* RoadStopResolverObject::GetTown()
return this->town_scope;
}
uint16 GetRoadStopCallback(CallbackID callback, uint32 param1, uint32 param2, const RoadStopSpec *roadstopspec, BaseStation *st, TileIndex tile, const RoadTypeInfo *rti, StationType type, uint8 view)
{
RoadStopResolverObject object(roadstopspec, st, tile, rti, type, view, callback, param1, param2);
return object.ResolveCallback();
}
/**
* Draw representation of a road stop tile for GUI purposes.
* @param x position x of image.
@ -272,6 +294,74 @@ void DrawRoadStopTile(int x, int y, RoadType roadtype, const RoadStopSpec *spec,
DrawCommonTileSeqInGUI(x, y, dts, 0, 0, palette, true);
}
/** Wrapper for animation control, see GetRoadStopCallback. */
uint16 GetAnimRoadStopCallback(CallbackID callback, uint32 param1, uint32 param2, const RoadStopSpec *roadstopspec, BaseStation *st, TileIndex tile, int extra_data)
{
return GetRoadStopCallback(callback, param1, param2, roadstopspec, st, tile, nullptr, GetStationType(tile), GetStationGfx(tile));
}
struct RoadStopAnimationFrameAnimationHelper {
static byte Get(BaseStation *st, TileIndex tile) { return st->GetRoadStopAnimationFrame(tile); }
static void Set(BaseStation *st, TileIndex tile, byte frame) { st->SetRoadStopAnimationFrame(tile, frame); }
};
/** Helper class for animation control. */
struct RoadStopAnimationBase : public AnimationBase<RoadStopAnimationBase, RoadStopSpec, BaseStation, int, GetAnimRoadStopCallback, RoadStopAnimationFrameAnimationHelper> {
static const CallbackID cb_animation_speed = CBID_STATION_ANIMATION_SPEED;
static const CallbackID cb_animation_next_frame = CBID_STATION_ANIM_NEXT_FRAME;
static const RoadStopCallbackMask cbm_animation_speed = CBM_ROAD_STOP_ANIMATION_SPEED;
static const RoadStopCallbackMask cbm_animation_next_frame = CBM_ROAD_STOP_ANIMATION_NEXT_FRAME;
};
void AnimateRoadStopTile(TileIndex tile)
{
const RoadStopSpec *ss = GetRoadStopSpec(tile);
if (ss == nullptr) return;
RoadStopAnimationBase::AnimateTile(ss, BaseStation::GetByTile(tile), tile, HasBit(ss->flags, RSF_CB141_RANDOM_BITS));
}
uint8 GetRoadStopTileAnimationSpeed(TileIndex tile)
{
const RoadStopSpec *ss = GetRoadStopSpec(tile);
if (ss == nullptr) return 0;
return RoadStopAnimationBase::GetAnimationSpeed(ss);
}
void TriggerRoadStopAnimation(BaseStation *st, TileIndex trigger_tile, StationAnimationTrigger trigger, CargoID cargo_type)
{
/* Get Station if it wasn't supplied */
if (st == nullptr) st = BaseStation::GetByTile(trigger_tile);
/* Check the cached animation trigger bitmask to see if we need
* to bother with any further processing. */
if (!HasBit(st->cached_roadstop_anim_triggers, trigger)) return;
uint16 random_bits = Random();
auto process_tile = [&](TileIndex cur_tile) {
const RoadStopSpec *ss = GetRoadStopSpec(cur_tile);
if (ss != nullptr && HasBit(ss->animation.triggers, trigger)) {
CargoID cargo;
if (cargo_type == CT_INVALID) {
cargo = CT_INVALID;
} else {
cargo = ss->grf_prop.grffile->cargo_map[cargo_type];
}
RoadStopAnimationBase::ChangeAnimationFrame(CBID_STATION_ANIM_START_STOP, ss, st, cur_tile, (random_bits << 16) | Random(), (uint8)trigger | (cargo << 8));
}
};
if (trigger == SAT_NEW_CARGO || trigger == SAT_CARGO_TAKEN || trigger == SAT_250_TICKS) {
for (TileIndex cur_tile : st->custom_road_stop_tiles) {
process_tile(cur_tile);
}
} else {
process_tile(trigger_tile);
}
}
/**
* Trigger road stop randomisation
*
@ -286,8 +376,8 @@ void TriggerRoadStopRandomisation(Station *st, TileIndex tile, RoadStopRandomTri
/* Check the cached cargo trigger bitmask to see if we need
* to bother with any further processing. */
if (st->roadstop_cached_cargo_triggers == 0) return;
if (cargo_type != CT_INVALID && !HasBit(st->roadstop_cached_cargo_triggers, cargo_type)) return;
if (st->cached_roadstop_cargo_triggers == 0) return;
if (cargo_type != CT_INVALID && !HasBit(st->cached_roadstop_cargo_triggers, cargo_type)) return;
SetBit(st->waiting_triggers, trigger);
@ -305,7 +395,7 @@ void TriggerRoadStopRandomisation(Station *st, TileIndex tile, RoadStopRandomTri
uint32 used_triggers = 0;
auto process_tile = [&](TileIndex cur_tile) {
const RoadStopSpec *ss = GetRoadStopSpec(tile);
const RoadStopSpec *ss = GetRoadStopSpec(cur_tile);
if (ss == nullptr) return;
/* Cargo taken "will only be triggered if all of those
@ -315,10 +405,7 @@ void TriggerRoadStopRandomisation(Station *st, TileIndex tile, RoadStopRandomTri
}
if (cargo_type == CT_INVALID || HasBit(ss->cargo_triggers, cargo_type)) {
int dir = GetRoadStopDir(cur_tile);
if (IsDriveThroughStopTile(cur_tile)) dir += 4;
RoadStopResolverObject object(ss, st, cur_tile, nullptr, GetStationType(cur_tile), dir);
RoadStopResolverObject object(ss, st, cur_tile, nullptr, GetStationType(cur_tile), GetStationGfx(cur_tile));
object.waiting_triggers = st->waiting_triggers;
const SpriteGroup *group = object.Resolve();
@ -487,7 +574,8 @@ void DeallocateRoadStopSpecFromStation(BaseStation *st, byte specindex)
free(st->roadstop_speclist);
st->num_roadstop_specs = 0;
st->roadstop_speclist = nullptr;
st->roadstop_cached_cargo_triggers = 0;
st->cached_roadstop_anim_triggers = 0;
st->cached_roadstop_cargo_triggers = 0;
return;
}
}
@ -501,14 +589,16 @@ void DeallocateRoadStopSpecFromStation(BaseStation *st, byte specindex)
*/
void StationUpdateRoadStopCachedTriggers(BaseStation *st)
{
st->roadstop_cached_cargo_triggers = 0;
st->cached_roadstop_anim_triggers = 0;
st->cached_roadstop_cargo_triggers = 0;
/* Combine animation trigger bitmask for all road stop specs
* of this station. */
for (uint i = 0; i < st->num_roadstop_specs; i++) {
const RoadStopSpec *ss = st->roadstop_speclist[i].spec;
if (ss != nullptr) {
st->roadstop_cached_cargo_triggers |= ss->cargo_triggers;
st->cached_roadstop_anim_triggers |= ss->animation.triggers;
st->cached_roadstop_cargo_triggers |= ss->cargo_triggers;
}
}
}

@ -35,6 +35,8 @@ DECLARE_POSTFIX_INCREMENT(RoadStopClassID)
enum RoadStopRandomTrigger {
RSRT_NEW_CARGO, ///< Trigger roadstop on arrival of new cargo.
RSRT_CARGO_TAKEN, ///< Trigger roadstop when cargo is completely taken.
RSRT_VEH_ARRIVES, ///< Trigger roadstop when road vehicle arrives.
RSRT_VEH_DEPARTS, ///< Trigger roadstop when road vehicle leaves.
RSRT_VEH_LOADS, ///< Trigger roadstop when road vehicle loads.
};
@ -61,6 +63,10 @@ enum RoadStopDrawMode : byte {
};
DECLARE_ENUM_AS_BIT_SET(RoadStopDrawMode)
enum RoadStopSpecFlags {
RSF_CB141_RANDOM_BITS, ///< Callback 141 needs random bits.
};
/** Scope resolver for road stops. */
struct RoadStopScopeResolver : public ScopeResolver {
TileIndex tile; ///< %Tile of the station.
@ -123,9 +129,13 @@ struct RoadStopSpec {
RoadStopAvailabilityType stop_type = ROADSTOPTYPE_ALL;
RoadStopDrawMode draw_mode = ROADSTOP_DRAW_MODE_ROAD | ROADSTOP_DRAW_MODE_OVERLAY;
uint8 callback_mask = 0;
uint8 flags = 0;
CargoTypes cargo_triggers = 0; ///< Bitmask of cargo types which cause trigger re-randomizing
AnimationInfo animation;
static const RoadStopSpec *Get(uint16 index);
};
@ -136,6 +146,11 @@ typedef NewGRFClass<RoadStopSpec, RoadStopClassID, ROADSTOP_CLASS_MAX> RoadStopC
void DrawRoadStopTile(int x, int y, RoadType roadtype, const RoadStopSpec *spec, StationType type, int view);
uint16 GetRoadStopCallback(CallbackID callback, uint32 param1, uint32 param2, const RoadStopSpec *roadstopspec, BaseStation *st, TileIndex tile, const RoadTypeInfo *rti, StationType type, uint8 view);
void AnimateRoadStopTile(TileIndex tile);
uint8 GetRoadStopTileAnimationSpeed(TileIndex tile);
void TriggerRoadStopAnimation(BaseStation *st, TileIndex tile, StationAnimationTrigger trigger, CargoID cargo_type = CT_INVALID);
void TriggerRoadStopRandomisation(Station *st, TileIndex tile, RoadStopRandomTrigger trigger, CargoID cargo_type = CT_INVALID);
bool GetIfNewStopsByType(RoadStopType rs);

@ -906,7 +906,7 @@ uint16 GetAnimStationCallback(CallbackID callback, uint32 param1, uint32 param2,
}
/** Helper class for animation control. */
struct StationAnimationBase : public AnimationBase<StationAnimationBase, StationSpec, BaseStation, int, GetAnimStationCallback> {
struct StationAnimationBase : public AnimationBase<StationAnimationBase, StationSpec, BaseStation, int, GetAnimStationCallback, TileAnimationFrameAnimationHelper<BaseStation> > {
static const CallbackID cb_animation_speed = CBID_STATION_ANIMATION_SPEED;
static const CallbackID cb_animation_next_frame = CBID_STATION_ANIM_NEXT_FRAME;

@ -89,6 +89,20 @@ static RoadFlags _place_road_flag;
static RoadType _cur_roadtype;
/**
* Check whether a road stop type can be built.
* @return true if building is allowed.
*/
static bool IsRoadStopAvailable(const RoadStopSpec *roadstopspec, StationType type)
{
if (roadstopspec == nullptr || !HasBit(roadstopspec->callback_mask, CBM_ROAD_STOP_AVAIL)) return true;
uint16 cb_res = GetRoadStopCallback(CBID_STATION_AVAILABILITY, 0, 0, roadstopspec, nullptr, INVALID_TILE, GetRoadTypeInfo(_cur_roadtype), type, 0);
if (cb_res == CALLBACK_FAILED) return true;
return Convert8bitBooleanCallback(roadstopspec->grf_prop.grffile, CBID_STATION_AVAILABILITY, cb_res);
}
void CcPlaySound_CONSTRUCTION_OTHER(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2, uint64 p3, uint32 cmd)
{
if (result.Succeeded() && _settings_client.sound.confirm) SndPlayTileFx(SND_1F_CONSTRUCTION_OTHER, tile);
@ -1488,6 +1502,13 @@ public:
byte type = GB(widget, 16, 16);
assert(type < _roadstop_gui_settings.roadstop_count);
const RoadStopSpec *spec = RoadStopClass::Get(_roadstop_gui_settings.roadstop_class)->GetSpec(type);
StationType st = GetRoadStationTypeByWindowClass(this->window_class);
if (!IsRoadStopAvailable(spec, st)) {
GfxFillRect(r.left + 1, r.top + 1, r.right - 1, r.bottom - 1, PC_BLACK, FILLRECT_CHECKER);
}
// Set up a clipping area for the sprite preview.
if (FillDrawPixelInfo(&tmp_dpi, r.left, r.top, r.right - r.left + 1, r.bottom - r.top + 1)) {
DrawPixelInfo *old_dpi = _cur_dpi;
@ -1495,8 +1516,6 @@ public:
int x = ScaleGUITrad(31) + 1;
int y = r.bottom - r.top - ScaleGUITrad(31);
// Instead of "5" (5th view), pass the orientation clicked in the selection.
const RoadStopSpec *spec = RoadStopClass::Get(_roadstop_gui_settings.roadstop_class)->GetSpec(type);
StationType st = GetRoadStationTypeByWindowClass(this->window_class);
if (spec == nullptr) {
StationPickerDrawSprite(r.left + 1 + ScaleGUITrad(31), r.bottom - ScaleGUITrad(31), st, INVALID_RAILTYPE, _cur_roadtype, _roadstop_gui_settings.orientation);
} else {
@ -1573,6 +1592,11 @@ public:
int y = GB(widget, 16, 16);
if (y >= _roadstop_gui_settings.roadstop_count) return;
const RoadStopSpec *spec = RoadStopClass::Get(_roadstop_gui_settings.roadstop_class)->GetSpec(y);
StationType st = GetRoadStationTypeByWindowClass(this->window_class);
if (!IsRoadStopAvailable(spec, st)) return;
/* Check station availability callback */
_roadstop_gui_settings.roadstop_type = y;
@ -1830,6 +1854,9 @@ struct BuildRoadWaypointWindow : PickerWindowBase {
} else {
DrawRoadStopTile(r.left + 1 + ScaleGUITrad(31), r.bottom - ScaleGUITrad(31), _cur_roadtype, spec, STATION_ROADWAYPOINT, 4);
}
if (!IsRoadStopAvailable(spec, STATION_ROADWAYPOINT)) {
GfxFillRect(r.left + 1, r.top + 1, r.right - 1, r.bottom - 1, PC_BLACK, FILLRECT_CHECKER);
}
}
}
}
@ -1839,6 +1866,10 @@ struct BuildRoadWaypointWindow : PickerWindowBase {
switch (GB(widget, 0, 16)) {
case WID_BROW_WAYPOINT: {
uint type = GB(widget, 16, 16);
const RoadStopSpec *spec = RoadStopClass::Get(ROADSTOP_CLASS_WAYP)->GetSpec(type);
if (!IsRoadStopAvailable(spec, STATION_ROADWAYPOINT)) return;
this->GetWidget<NWidgetMatrix>(WID_BROW_WAYPOINT_MATRIX)->SetClicked(_cur_waypoint_type);
_cur_waypoint_type = type;

@ -38,6 +38,7 @@
#include "scope_info.h"
#include "string_func.h"
#include "core/checksum_func.hpp"
#include "newgrf_roadstop.h"
#include "table/strings.h"
@ -1950,6 +1951,8 @@ again:
v->last_station_visited = st->index;
RoadVehArrivesAt(v, st);
v->BeginLoading();
TriggerRoadStopRandomisation(st, v->tile, RSRT_VEH_ARRIVES);
TriggerRoadStopAnimation(st, v->tile, SAT_TRAIN_ARRIVES);
}
return false;
}
@ -2012,6 +2015,8 @@ again:
if (IsDriveThroughStopTile(v->tile) || (v->current_order.IsType(OT_GOTO_STATION) && v->current_order.GetDestination() == st->index)) {
RoadVehArrivesAt(v, st);
v->BeginLoading();
TriggerRoadStopRandomisation(st, v->tile, RSRT_VEH_ARRIVES);
TriggerRoadStopAnimation(st, v->tile, SAT_TRAIN_ARRIVES);
return false;
}
} else {

@ -411,7 +411,7 @@ static const SaveLoad _base_station_desc[] = {
SLE_VAR(BaseStation, num_specs, SLE_UINT8),
SLE_CONDVAR_X(BaseStation, num_roadstop_specs, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_GRF_ROADSTOPS)),
SLE_CONDVARVEC_X(BaseStation, custom_road_stop_tiles, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_GRF_ROADSTOPS)),
SLE_CONDVARVEC_X(BaseStation, custom_road_stop_random_bits, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_GRF_ROADSTOPS)),
SLE_CONDVARVEC_X(BaseStation, custom_road_stop_data, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_GRF_ROADSTOPS)),
};
static OldPersistentStorage _old_st_persistent_storage;

@ -185,26 +185,26 @@ void BaseStation::PostDestructor(size_t index)
InvalidateWindowData(WC_SELECT_STATION, 0, 0);
}
void BaseStation::SetRoadStopRandomBits(TileIndex tile, byte random_bits)
void BaseStation::SetRoadStopTileData(TileIndex tile, byte data, byte offset)
{
for (size_t i = 0; i < this->custom_road_stop_tiles.size(); i++) {
if (this->custom_road_stop_tiles[i] == tile) {
this->custom_road_stop_random_bits[i] = random_bits;
SB(this->custom_road_stop_data[i], offset, 8, data);
return;
}
}
this->custom_road_stop_tiles.push_back(tile);
this->custom_road_stop_random_bits.push_back(random_bits);
this->custom_road_stop_data.push_back(((uint)data) << offset);
}
void BaseStation::RemoveRoadStopRandomBits(TileIndex tile)
void BaseStation::RemoveRoadStopTileData(TileIndex tile)
{
for (size_t i = 0; i < this->custom_road_stop_tiles.size(); i++) {
if (this->custom_road_stop_tiles[i] == tile) {
this->custom_road_stop_tiles[i] = this->custom_road_stop_tiles.back();
this->custom_road_stop_random_bits[i] = this->custom_road_stop_random_bits.back();
this->custom_road_stop_data[i] = this->custom_road_stop_data.back();
this->custom_road_stop_tiles.pop_back();
this->custom_road_stop_random_bits.pop_back();
this->custom_road_stop_data.pop_back();
return;
}
}

@ -2132,6 +2132,16 @@ CommandCost CmdBuildRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, uin
int specindex = AllocateRoadStopSpecToStation(roadstopspec, st, (flags & DC_EXEC) != 0);
if (specindex == -1) return_cmd_error(STR_ERROR_TOO_MANY_STATION_SPECS);
if (roadstopspec != nullptr) {
/* Perform NewGRF checks */
/* Check if the road stop is buildable */
if (HasBit(roadstopspec->callback_mask, CBM_ROAD_STOP_AVAIL)) {
uint16 cb_res = GetRoadStopCallback(CBID_STATION_AVAILABILITY, 0, 0, roadstopspec, nullptr, INVALID_TILE, GetRoadTypeInfo(rt), type ? STATION_TRUCK : STATION_BUS, 0);
if (cb_res != CALLBACK_FAILED && !Convert8bitBooleanCallback(roadstopspec->grf_prop.grffile, CBID_STATION_AVAILABILITY, cb_res)) return CMD_ERROR;
}
}
if (flags & DC_EXEC) {
/* Check every tile in the area. */
for (TileIndex cur_tile : roadstop_area) {
@ -2146,6 +2156,12 @@ CommandCost CmdBuildRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, uin
RemoveRoadStop(cur_tile, flags);
}
if (roadstopspec != nullptr) {
/* Include this road stop spec's animation trigger bitmask
* in the station's cached copy. */
st->cached_roadstop_anim_triggers |= roadstopspec->animation.triggers;
}
RoadStop *road_stop = new RoadStop(cur_tile);
/* Insert into linked list of RoadStops. */
RoadStop **currstop = FindRoadStopSpot(type, st);
@ -2190,7 +2206,10 @@ CommandCost CmdBuildRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, uin
Company::Get(st->owner)->infrastructure.station++;
SetCustomRoadStopSpecIndex(cur_tile, specindex);
if (roadstopspec != nullptr) st->SetRoadStopRandomBits(cur_tile, GB(Random(), 0, 4));
if (roadstopspec != nullptr) {
st->SetRoadStopRandomBits(cur_tile, GB(Random(), 0, 4));
TriggerRoadStopAnimation(st, cur_tile, SAT_BUILT);
}
MarkTileDirtyByTile(cur_tile);
UpdateRoadCachedOneWayStatesAroundTile(cur_tile);
@ -2245,6 +2264,8 @@ CommandCost RemoveRoadWaypointStop(TileIndex tile, DoCommandFlag flags)
Company::Get(wp->owner)->infrastructure.station--;
DirtyCompanyInfrastructureWindows(wp->owner);
DeleteAnimatedTile(tile);
uint specindex = GetCustomRoadStopSpecIndex(tile);
DeleteNewGRFInspectWindow(GSF_ROADSTOPS, tile);
@ -2253,7 +2274,7 @@ CommandCost RemoveRoadWaypointStop(TileIndex tile, DoCommandFlag flags)
wp->rect.AfterRemoveTile(wp, tile);
wp->RemoveRoadStopRandomBits(tile);
wp->RemoveRoadStopTileData(tile);
DeallocateRoadStopSpecFromStation(wp, specindex);
MakeRoadWaypointStationAreaSmaller(wp, wp->road_waypoint_area);
@ -2341,6 +2362,8 @@ CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlag flags)
Company::Get(st->owner)->infrastructure.station--;
DirtyCompanyInfrastructureWindows(st->owner);
DeleteAnimatedTile(tile);
uint specindex = GetCustomRoadStopSpecIndex(tile);
DeleteNewGRFInspectWindow(GSF_ROADSTOPS, tile);
@ -2366,7 +2389,7 @@ CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlag flags)
st->AfterStationTileSetChange(false, is_truck ? STATION_TRUCK: STATION_BUS);
st->RemoveRoadStopRandomBits(tile);
st->RemoveRoadStopTileData(tile);
DeallocateRoadStopSpecFromStation(st, specindex);
/* Update the tile area of the truck/bus stop */
@ -3788,6 +3811,12 @@ void AnimateTile_Station(TileIndex tile)
if (IsAirport(tile)) {
AnimateAirportTile(tile);
return;
}
if (IsAnyRoadStopTile(tile)) {
AnimateRoadStopTile(tile);
return;
}
}
@ -3800,6 +3829,10 @@ uint8 GetAnimatedTileSpeed_Station(TileIndex tile)
if (IsAirport(tile)) {
return GetAirportTileAnimationSpeed(tile);
}
if (IsAnyRoadStopTile(tile)) {
return GetRoadStopTileAnimationSpeed(tile);
}
return 0;
}
@ -4485,6 +4518,7 @@ void OnTick_Station()
/* Stop processing this station if it was deleted */
if (!StationHandleBigTick(st)) continue;
TriggerStationAnimation(st, st->xy, SAT_250_TICKS);
TriggerRoadStopAnimation(st, st->xy, SAT_250_TICKS);
if (Station::IsExpected(st)) AirportAnimationTrigger(Station::From(st), AAT_STATION_250_TICKS);
}
}
@ -4569,6 +4603,7 @@ static uint UpdateStationWaiting(Station *st, CargoID type, uint amount, SourceT
TriggerStationRandomisation(st, st->xy, SRT_NEW_CARGO, type);
TriggerStationAnimation(st, st->xy, SAT_NEW_CARGO, type);
AirportAnimationTrigger(st, AAT_STATION_NEW_CARGO, type);
TriggerRoadStopAnimation(st, st->xy, SAT_NEW_CARGO, type);
TriggerRoadStopRandomisation(st, st->xy, RSRT_NEW_CARGO, type);
SetWindowDirty(WC_STATION_VIEW, st->index);

@ -1371,6 +1371,15 @@ static const NIFeature _nif_roadtype = {
new NIHRoadType(),
};
#define NICRS(cb_id, bit) NIC(cb_id, RoadStopSpec, callback_mask, bit)
static const NICallback _nic_roadstops[] = {
NICRS(CBID_STATION_AVAILABILITY, CBM_ROAD_STOP_AVAIL),
NICRS(CBID_STATION_ANIM_START_STOP, CBM_NO_BIT),
NICRS(CBID_STATION_ANIM_NEXT_FRAME, CBM_ROAD_STOP_ANIMATION_NEXT_FRAME),
NICRS(CBID_STATION_ANIMATION_SPEED, CBM_ROAD_STOP_ANIMATION_SPEED),
NIC_END()
};
static const NIVariable _nif_roadstops[] = {
NIV(0x40, "view/rotation"),
NIV(0x41, "stop type"),
@ -1381,12 +1390,14 @@ static const NIVariable _nif_roadstops[] = {
NIV(0x46, "square of Euclidean distance of town"),
NIV(0x47, "player info"),
NIV(0x48, "bitmask of accepted cargoes"),
NIV(0x49, "current animation frame"),
NIV(0x60, "amount of cargo waiting"),
NIV(0x61, "time since last cargo pickup"),
NIV(0x62, "rating of cargo"),
NIV(0x63, "time spent on route"),
NIV(0x64, "information about last vehicle picking cargo up"),
NIV(0x65, "amount of cargo acceptance"),
NIV(0x66, "animation frame of nearby tile"),
NIV(0x67, "land info of nearby tiles"),
NIV(0x68, "road stop info of nearby tiles"),
NIV(0x69, "information about cargo accepted in the past"),
@ -1420,11 +1431,15 @@ class NIHRoadStop : public NIHelper {
uint class_id = RoadStopClass::Get(spec->cls_id)->global_id;
seprintf(buffer, lastof(buffer), " class ID: %c%c%c%c, spec ID: %u", class_id >> 24, class_id >> 16, class_id >> 8, class_id, spec->spec_id);
output.print(buffer);
seprintf(buffer, lastof(buffer), " spec: stop type: %u, draw mode: %u, cargo triggers: 0x" OTTD_PRINTFHEX64, spec->stop_type, spec->draw_mode, spec->cargo_triggers);
seprintf(buffer, lastof(buffer), " spec: stop type: %X, draw mode: %X, cargo triggers: " OTTD_PRINTFHEX64, spec->stop_type, spec->draw_mode, spec->cargo_triggers);
output.print(buffer);
seprintf(buffer, lastof(buffer), " spec: callback mask: %X, flags: %X", spec->callback_mask, spec->flags);
output.print(buffer);
seprintf(buffer, lastof(buffer), " animation: frames: %u, status: %u, speed: %u, triggers: 0x%X", spec->animation.frames, spec->animation.status, spec->animation.speed, spec->animation.triggers);
output.print(buffer);
const BaseStation *st = BaseStation::GetByTile(index);
seprintf(buffer, lastof(buffer), " stop random bits: %02X", st->GetRoadStopRandomBits(index));
seprintf(buffer, lastof(buffer), " road stop: random bits: %02X, animation frame: %02X", st->GetRoadStopRandomBits(index), st->GetRoadStopAnimationFrame(index));
output.print(buffer);
}
}
@ -1438,7 +1453,7 @@ class NIHRoadStop : public NIHelper {
static const NIFeature _nif_roadstop = {
nullptr,
nullptr,
_nic_roadstops,
_nif_roadstops,
new NIHRoadStop(),
};

@ -22,6 +22,7 @@
#include "newgrf_debug.h"
#include "newgrf_sound.h"
#include "newgrf_station.h"
#include "newgrf_roadstop.h"
#include "group_gui.h"
#include "strings_func.h"
#include "zoom_func.h"
@ -3418,6 +3419,13 @@ void Vehicle::LeaveStation()
SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
}
if (this->type == VEH_ROAD && !(this->vehstatus & VS_CRASHED)) {
/* Trigger road stop animation */
if (IsAnyRoadStopTile(this->tile)) {
TriggerRoadStopRandomisation(st, this->tile, RSRT_VEH_DEPARTS);
TriggerRoadStopAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
}
}
if (this->cur_real_order_index < this->GetNumOrders()) {
Order *real_current_order = this->GetOrder(this->cur_real_order_index);

@ -420,6 +420,12 @@ CommandCost CmdBuildRoadWaypoint(TileIndex start_tile, DoCommandFlag flags, uint
wp->rect.BeforeAddRect(start_tile, width, height, StationRect::ADD_TRY);
if (spec != nullptr) {
/* Include this road stop spec's animation trigger bitmask
* in the station's cached copy. */
wp->cached_roadstop_anim_triggers |= spec->animation.triggers;
}
wp->delete_ctr = 0;
wp->facilities |= FACIL_BUS_STOP | FACIL_TRUCK_STOP;
wp->build_date = _date;

Loading…
Cancel
Save