diff --git a/src/base_station_base.h b/src/base_station_base.h index c9385e24c1..539fdafff1 100644 --- a/src/base_station_base.h +++ b/src/base_station_base.h @@ -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 custom_road_stop_tiles; ///< List of custom road stop tiles - std::vector custom_road_stop_random_bits; ///< Custom road stop random bits in same order as custom_road_stop_tiles + std::vector 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); diff --git a/src/economy.cpp b/src/economy.cpp index 3b18aec3e0..00556593c5 100644 --- a/src/economy.cpp +++ b/src/economy.cpp @@ -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); } } diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 366482cf9d..be53e438b9 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -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; diff --git a/src/newgrf_airporttiles.cpp b/src/newgrf_airporttiles.cpp index a18564ce00..8f4fc836c9 100644 --- a/src/newgrf_airporttiles.cpp +++ b/src/newgrf_airporttiles.cpp @@ -279,7 +279,7 @@ bool DrawNewAirportTile(TileInfo *ti, Station *st, StationGfx gfx, const Airport } /** Helper class for animation control. */ -struct AirportTileAnimationBase : public AnimationBase { +struct AirportTileAnimationBase : public AnimationBase > { static const CallbackID cb_animation_speed = CBID_AIRPTILE_ANIMATION_SPEED; static const CallbackID cb_animation_next_frame = CBID_AIRPTILE_ANIM_NEXT_FRAME; diff --git a/src/newgrf_animation_base.h b/src/newgrf_animation_base.h index f36cac27c0..95787c9c8c 100644 --- a/src/newgrf_animation_base.h +++ b/src/newgrf_animation_base.h @@ -17,6 +17,12 @@ #include "newgrf_callbacks.h" #include "tile_map.h" +template +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 +template 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; } diff --git a/src/newgrf_callbacks.h b/src/newgrf_callbacks.h index ed32a3abf1..a00f360b64 100644 --- a/src/newgrf_callbacks.h +++ b/src/newgrf_callbacks.h @@ -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. */ diff --git a/src/newgrf_extension.cpp b/src/newgrf_extension.cpp index 4d078853bd..5695cf3af5 100644 --- a/src/newgrf_extension.cpp +++ b/src/newgrf_extension.cpp @@ -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(), }; diff --git a/src/newgrf_extension.h b/src/newgrf_extension.h index 4c243556ad..c68c916270 100644 --- a/src/newgrf_extension.h +++ b/src/newgrf_extension.h @@ -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, }; diff --git a/src/newgrf_house.cpp b/src/newgrf_house.cpp index ca2324ba9c..116713bf18 100644 --- a/src/newgrf_house.cpp +++ b/src/newgrf_house.cpp @@ -637,7 +637,7 @@ uint16 GetSimpleHouseCallback(CallbackID callback, uint32 param1, uint32 param2, } /** Helper class for animation control. */ -struct HouseAnimationBase : public AnimationBase { +struct HouseAnimationBase : public AnimationBase > { static const CallbackID cb_animation_speed = CBID_HOUSE_ANIMATION_SPEED; static const CallbackID cb_animation_next_frame = CBID_HOUSE_ANIMATION_NEXT_FRAME; diff --git a/src/newgrf_industrytiles.cpp b/src/newgrf_industrytiles.cpp index 6648d6966e..68771d4cec 100644 --- a/src/newgrf_industrytiles.cpp +++ b/src/newgrf_industrytiles.cpp @@ -257,7 +257,7 @@ uint16 GetSimpleIndustryCallback(CallbackID callback, uint32 param1, uint32 para } /** Helper class for animation control. */ -struct IndustryAnimationBase : public AnimationBase { +struct IndustryAnimationBase : public AnimationBase > { static const CallbackID cb_animation_speed = CBID_INDTILE_ANIMATION_SPEED; static const CallbackID cb_animation_next_frame = CBID_INDTILE_ANIM_NEXT_FRAME; diff --git a/src/newgrf_object.cpp b/src/newgrf_object.cpp index 01e3f36970..cefd897d83 100644 --- a/src/newgrf_object.cpp +++ b/src/newgrf_object.cpp @@ -558,7 +558,7 @@ uint16 StubGetObjectCallback(CallbackID callback, uint32 param1, uint32 param2, } /** Helper class for animation control. */ -struct ObjectAnimationBase : public AnimationBase { +struct ObjectAnimationBase : public AnimationBase > { static const CallbackID cb_animation_speed = CBID_OBJECT_ANIMATION_SPEED; static const CallbackID cb_animation_next_frame = CBID_OBJECT_ANIMATION_NEXT_FRAME; diff --git a/src/newgrf_roadstop.cpp b/src/newgrf_roadstop.cpp index 04ed8c2f9e..b797107f99 100644 --- a/src/newgrf_roadstop.cpp +++ b/src/newgrf_roadstop.cpp @@ -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 { + 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; } } } diff --git a/src/newgrf_roadstop.h b/src/newgrf_roadstop.h index bbcd9190ac..43121b845b 100644 --- a/src/newgrf_roadstop.h +++ b/src/newgrf_roadstop.h @@ -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 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); diff --git a/src/newgrf_station.cpp b/src/newgrf_station.cpp index 8cf5c22ef5..fd7278655c 100644 --- a/src/newgrf_station.cpp +++ b/src/newgrf_station.cpp @@ -906,7 +906,7 @@ uint16 GetAnimStationCallback(CallbackID callback, uint32 param1, uint32 param2, } /** Helper class for animation control. */ -struct StationAnimationBase : public AnimationBase { +struct StationAnimationBase : public AnimationBase > { static const CallbackID cb_animation_speed = CBID_STATION_ANIMATION_SPEED; static const CallbackID cb_animation_next_frame = CBID_STATION_ANIM_NEXT_FRAME; diff --git a/src/road_gui.cpp b/src/road_gui.cpp index 8694ad4fab..7a6c6c1abe 100644 --- a/src/road_gui.cpp +++ b/src/road_gui.cpp @@ -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(WID_BROW_WAYPOINT_MATRIX)->SetClicked(_cur_waypoint_type); _cur_waypoint_type = type; diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp index e1630f6330..b2256444e2 100644 --- a/src/roadveh_cmd.cpp +++ b/src/roadveh_cmd.cpp @@ -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 { diff --git a/src/saveload/station_sl.cpp b/src/saveload/station_sl.cpp index 62439eb110..12d74da133 100644 --- a/src/saveload/station_sl.cpp +++ b/src/saveload/station_sl.cpp @@ -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; diff --git a/src/station.cpp b/src/station.cpp index ee6d9c739e..7b15893e33 100644 --- a/src/station.cpp +++ b/src/station.cpp @@ -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; } } diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 5a56ba4882..798d198b08 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -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); diff --git a/src/table/newgrf_debug_data.h b/src/table/newgrf_debug_data.h index e4b80a7e65..21cf4b8616 100644 --- a/src/table/newgrf_debug_data.h +++ b/src/table/newgrf_debug_data.h @@ -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(), }; diff --git a/src/vehicle.cpp b/src/vehicle.cpp index ba2c6a88b5..ad21da31dc 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -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); diff --git a/src/waypoint_cmd.cpp b/src/waypoint_cmd.cpp index d5a0f64e02..237c26a420 100644 --- a/src/waypoint_cmd.cpp +++ b/src/waypoint_cmd.cpp @@ -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;