mirror of
https://github.com/OpenTTD/OpenTTD.git
synced 2025-03-06 14:27:16 +00:00
Add: NewGRF road stops
This commit is contained in:
parent
a18182e24b
commit
4c1406a4b5
@ -998,6 +998,7 @@
|
||||
<li>m7: animation frame (railway stations/waypoints, airports)</li>
|
||||
<li>m8 bits 11..6: <a href="#TramType">Tramtype</a></li>
|
||||
<li>m8 bits 5..0: <a href="#TrackType">track type</a> for railway stations/waypoints</li>
|
||||
<li>m8 bits 5..0: custom road stop id; 0 means standard graphics</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -203,7 +203,7 @@ the array so you can quickly see what is used and what is not.
|
||||
<td class="bits"><span class="usable" title="Graphics index">OOOO O</span><span class="used" title="Graphics index: 00 (exit towards NE), 01 (exit towards SE), 02 (exit towards SW), 03 (exit towards NW), 04 (drive through X), 05 (drive through Y)">XXX</span></td>
|
||||
<td class="bits" rowspan=5><span class="free">OO</span><span class="used" title="Station type">XX X</span><span class="free">OOO</span></td>
|
||||
<td class="bits"><span class="free">OOO</span><span class="used" title="Owner of road">X XXXX</span></td>
|
||||
<td class="bits"><span class="free">OOOO</span> <span class="used" title="Tram type">XXXX XX<span class="free">OO OOOO</span></td>
|
||||
<td class="bits"><span class="free">OOOO</span> <span class="used" title="Tram type">XXXX XX</span> <span class="used" title="Custom road stops specifications ID">XXXXXX</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="caption">airport</td>
|
||||
|
@ -274,6 +274,8 @@ add_files(
|
||||
newgrf_properties.h
|
||||
newgrf_railtype.cpp
|
||||
newgrf_railtype.h
|
||||
newgrf_roadstop.cpp
|
||||
newgrf_roadstop.h
|
||||
newgrf_roadtype.cpp
|
||||
newgrf_roadtype.h
|
||||
newgrf_sound.cpp
|
||||
|
@ -24,6 +24,17 @@ struct StationSpecList {
|
||||
uint8 localidx; ///< Station ID within GRF of station
|
||||
};
|
||||
|
||||
struct RoadStopSpecList {
|
||||
const RoadStopSpec *spec;
|
||||
uint32 grfid; ///< GRF ID of this custom road stop
|
||||
uint8 localidx; ///< Station ID within GRF of road stop
|
||||
};
|
||||
|
||||
struct RoadStopTileData {
|
||||
TileIndex tile;
|
||||
uint8 random_bits;
|
||||
uint8 animation_frame;
|
||||
};
|
||||
|
||||
/** StationRect - used to track station spread out rectangle - cheaper than scanning whole map */
|
||||
struct StationRect : public Rect {
|
||||
@ -63,17 +74,22 @@ struct BaseStation : StationPool::PoolItem<&_station_pool> {
|
||||
StationFacility facilities; ///< The facilities that this station has
|
||||
|
||||
std::vector<StationSpecList> speclist; ///< List of rail station specs of this station.
|
||||
std::vector<RoadStopSpecList> roadstop_speclist; ///< List of road stop specs of this station
|
||||
|
||||
Date build_date; ///< Date of construction
|
||||
|
||||
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.
|
||||
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<RoadStopTileData> custom_roadstop_tile_data; ///< List of custom road stop tile data
|
||||
|
||||
/**
|
||||
* Initialize the base station.
|
||||
* @param tile The location of the station sign
|
||||
@ -167,6 +183,30 @@ struct BaseStation : StationPool::PoolItem<&_station_pool> {
|
||||
return (this->facilities & ~FACIL_WAYPOINT) != 0;
|
||||
}
|
||||
|
||||
inline byte GetRoadStopRandomBits(TileIndex tile) const
|
||||
{
|
||||
for (const RoadStopTileData &tile_data : this->custom_roadstop_tile_data) {
|
||||
if (tile_data.tile == tile) return tile_data.random_bits;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline byte GetRoadStopAnimationFrame(TileIndex tile) const
|
||||
{
|
||||
for (const RoadStopTileData &tile_data : this->custom_roadstop_tile_data) {
|
||||
if (tile_data.tile == tile) return tile_data.animation_frame;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
void SetRoadStopTileData(TileIndex tile, byte data, bool animation);
|
||||
|
||||
public:
|
||||
inline void SetRoadStopRandomBits(TileIndex tile, byte random_bits) { this->SetRoadStopTileData(tile, random_bits, false); }
|
||||
inline void SetRoadStopAnimationFrame(TileIndex tile, byte frame) { this->SetRoadStopTileData(tile, frame, true); }
|
||||
void RemoveRoadStopTileData(TileIndex tile);
|
||||
|
||||
static void PostDestructor(size_t index);
|
||||
|
||||
private:
|
||||
|
@ -115,6 +115,8 @@ static int32 ClickChangeDateCheat(int32 p1, int32 p2)
|
||||
EnginesMonthlyLoop();
|
||||
SetWindowDirty(WC_STATUS_BAR, 0);
|
||||
InvalidateWindowClassesData(WC_BUILD_STATION, 0);
|
||||
InvalidateWindowClassesData(WC_BUS_STATION, 0);
|
||||
InvalidateWindowClassesData(WC_TRUCK_STATION, 0);
|
||||
InvalidateWindowClassesData(WC_BUILD_OBJECT, 0);
|
||||
ResetSignalVariant();
|
||||
return _cur_year;
|
||||
|
@ -195,6 +195,8 @@ static void OnNewYear()
|
||||
VehiclesYearlyLoop();
|
||||
TownsYearlyLoop();
|
||||
InvalidateWindowClassesData(WC_BUILD_STATION);
|
||||
InvalidateWindowClassesData(WC_BUS_STATION);
|
||||
InvalidateWindowClassesData(WC_TRUCK_STATION);
|
||||
if (_network_server) NetworkServerYearlyLoop();
|
||||
|
||||
if (_cur_year == _settings_client.gui.semaphore_build_before) ResetSignalVariant();
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "newgrf_industrytiles.h"
|
||||
#include "newgrf_station.h"
|
||||
#include "newgrf_airporttiles.h"
|
||||
#include "newgrf_roadstop.h"
|
||||
#include "object.h"
|
||||
#include "strings_func.h"
|
||||
#include "date_func.h"
|
||||
@ -1813,6 +1814,8 @@ 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);
|
||||
TriggerRoadStopRandomisation(st, st->xy, RSRT_CARGO_TAKEN, v->cargo_type);
|
||||
TriggerRoadStopAnimation(st, st->xy, SAT_CARGO_TAKEN, v->cargo_type);
|
||||
}
|
||||
|
||||
new_load_unload_ticks += loaded;
|
||||
@ -1833,6 +1836,9 @@ static void LoadUnloadVehicle(Vehicle *front)
|
||||
if (front->type == VEH_TRAIN) {
|
||||
TriggerStationRandomisation(st, front->tile, SRT_TRAIN_LOADS);
|
||||
TriggerStationAnimation(st, front->tile, SAT_TRAIN_LOADS);
|
||||
} else if (front->type == VEH_ROAD) {
|
||||
TriggerRoadStopRandomisation(st, front->tile, RSRT_VEH_LOADS);
|
||||
TriggerRoadStopAnimation(st, front->tile, SAT_TRAIN_LOADS);
|
||||
}
|
||||
}
|
||||
|
||||
|
209
src/newgrf.cpp
209
src/newgrf.cpp
@ -48,6 +48,7 @@
|
||||
#include "language.h"
|
||||
#include "vehicle_base.h"
|
||||
#include "road.h"
|
||||
#include "newgrf_roadstop.h"
|
||||
|
||||
#include "table/strings.h"
|
||||
#include "table/build_industry.h"
|
||||
@ -4747,6 +4748,131 @@ static ChangeInfoResult AirportTilesChangeInfo(uint airtid, int numinfo, int pro
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ignore properties for roadstops
|
||||
* @param prop The property to ignore.
|
||||
* @param buf The property value.
|
||||
* @return ChangeInfoResult.
|
||||
*/
|
||||
static ChangeInfoResult IgnoreRoadStopProperty(uint prop, ByteReader *buf)
|
||||
{
|
||||
ChangeInfoResult ret = CIR_SUCCESS;
|
||||
|
||||
switch (prop) {
|
||||
case 0x09:
|
||||
case 0x0C:
|
||||
buf->ReadByte();
|
||||
break;
|
||||
|
||||
case 0x0A:
|
||||
case 0x0B:
|
||||
buf->ReadWord();
|
||||
break;
|
||||
|
||||
case 0x08:
|
||||
case 0x0D:
|
||||
buf->ReadDWord();
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = CIR_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ChangeInfoResult RoadStopChangeInfo(uint id, int numinfo, int prop, ByteReader *buf)
|
||||
{
|
||||
ChangeInfoResult ret = CIR_SUCCESS;
|
||||
|
||||
if (id + numinfo > 255) {
|
||||
grfmsg(1, "RoadStopChangeInfo: RoadStop %u is invalid, max %u, ignoring", id + numinfo, 255);
|
||||
return CIR_INVALID_ID;
|
||||
}
|
||||
|
||||
if (_cur.grffile->roadstops == nullptr) _cur.grffile->roadstops = CallocT<RoadStopSpec*>(255);
|
||||
|
||||
for (int i = 0; i < numinfo; i++) {
|
||||
RoadStopSpec *rs = _cur.grffile->roadstops[id + i];
|
||||
|
||||
if (rs == nullptr && prop != 0x08) {
|
||||
grfmsg(1, "RoadStopChangeInfo: Attempt to modify undefined road stop %u, ignoring", id + i);
|
||||
ChangeInfoResult cir = IgnoreRoadStopProperty(prop, buf);
|
||||
if (cir > ret) ret = cir;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (prop) {
|
||||
case 0x08: { // Road Stop Class ID
|
||||
RoadStopSpec **spec = &_cur.grffile->roadstops[id + i];
|
||||
|
||||
if (*spec == nullptr) {
|
||||
*spec = CallocT<RoadStopSpec>(1);
|
||||
new (*spec) RoadStopSpec();
|
||||
}
|
||||
|
||||
uint32 classid = buf->ReadDWord();
|
||||
(*spec)->cls_id = RoadStopClass::Allocate(BSWAP32(classid));
|
||||
(*spec)->spec_id = id + i;
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x09: // Road stop type
|
||||
rs->stop_type = (RoadStopAvailabilityType)buf->ReadByte();
|
||||
break;
|
||||
|
||||
case 0x0A: // Road Stop Name
|
||||
AddStringForMapping(buf->ReadWord(), &rs->name);
|
||||
break;
|
||||
|
||||
case 0x0B: // Road Stop Class name
|
||||
AddStringForMapping(buf->ReadWord(), &RoadStopClass::Get(rs->cls_id)->name);
|
||||
break;
|
||||
|
||||
case 0x0C: // The draw mode
|
||||
rs->draw_mode = (RoadStopDrawMode)buf->ReadByte();
|
||||
break;
|
||||
|
||||
case 0x0D: // Cargo types for random triggers
|
||||
rs->cargo_triggers = TranslateRefitMask(buf->ReadDWord());
|
||||
break;
|
||||
|
||||
case 0x0E: // Animation info
|
||||
rs->animation.frames = buf->ReadByte();
|
||||
rs->animation.status = buf->ReadByte();
|
||||
break;
|
||||
|
||||
case 0x0F: // Animation speed
|
||||
rs->animation.speed = buf->ReadByte();
|
||||
break;
|
||||
|
||||
case 0x10: // Animation triggers
|
||||
rs->animation.triggers = buf->ReadWord();
|
||||
break;
|
||||
|
||||
case 0x11: // Callback mask
|
||||
rs->callback_mask = buf->ReadByte();
|
||||
break;
|
||||
|
||||
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;
|
||||
|
||||
case 0x15: // Cost multipliers
|
||||
rs->build_cost_multiplier = buf->ReadByte();
|
||||
rs->clear_cost_multiplier = buf->ReadByte();
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = CIR_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool HandleChangeInfoResult(const char *caller, ChangeInfoResult cir, uint8 feature, uint8 property)
|
||||
{
|
||||
switch (cir) {
|
||||
@ -4811,6 +4937,7 @@ static void FeatureChangeInfo(ByteReader *buf)
|
||||
/* GSF_AIRPORTTILES */ AirportTilesChangeInfo,
|
||||
/* GSF_ROADTYPES */ RoadTypeChangeInfo,
|
||||
/* GSF_TRAMTYPES */ TramTypeChangeInfo,
|
||||
/* GSF_ROADSTOPS */ RoadStopChangeInfo,
|
||||
};
|
||||
static_assert(GSF_END == lengthof(handler));
|
||||
|
||||
@ -5289,7 +5416,8 @@ static void NewSpriteGroup(ByteReader *buf)
|
||||
case GSF_HOUSES:
|
||||
case GSF_AIRPORTTILES:
|
||||
case GSF_OBJECTS:
|
||||
case GSF_INDUSTRYTILES: {
|
||||
case GSF_INDUSTRYTILES:
|
||||
case GSF_ROADSTOPS: {
|
||||
byte num_building_sprites = std::max((uint8)1, type);
|
||||
|
||||
assert(TileLayoutSpriteGroup::CanAllocateItem());
|
||||
@ -5404,7 +5532,7 @@ static CargoID TranslateCargo(uint8 feature, uint8 ctype)
|
||||
}
|
||||
}
|
||||
/* Special cargo types for purchase list and stations */
|
||||
if (feature == GSF_STATIONS && ctype == 0xFE) return CT_DEFAULT_NA;
|
||||
if ((feature == GSF_STATIONS || feature == GSF_ROADSTOPS) && ctype == 0xFE) return CT_DEFAULT_NA;
|
||||
if (ctype == 0xFF) return CT_PURCHASE;
|
||||
|
||||
if (_cur.grffile->cargo_list.size() == 0) {
|
||||
@ -5923,6 +6051,61 @@ static void AirportTileMapSpriteGroup(ByteReader *buf, uint8 idcount)
|
||||
}
|
||||
}
|
||||
|
||||
static void RoadStopMapSpriteGroup(ByteReader *buf, uint8 idcount)
|
||||
{
|
||||
uint8 *roadstops = AllocaM(uint8, idcount);
|
||||
for (uint i = 0; i < idcount; i++) {
|
||||
roadstops[i] = buf->ReadByte();
|
||||
}
|
||||
|
||||
uint8 cidcount = buf->ReadByte();
|
||||
for (uint c = 0; c < cidcount; c++) {
|
||||
uint8 ctype = buf->ReadByte();
|
||||
uint16 groupid = buf->ReadWord();
|
||||
if (!IsValidGroupID(groupid, "RoadStopMapSpriteGroup")) continue;
|
||||
|
||||
ctype = TranslateCargo(GSF_ROADSTOPS, ctype);
|
||||
if (ctype == CT_INVALID) continue;
|
||||
|
||||
for (uint i = 0; i < idcount; i++) {
|
||||
RoadStopSpec *roadstopspec = _cur.grffile->roadstops == nullptr ? nullptr : _cur.grffile->roadstops[roadstops[i]];
|
||||
|
||||
if (roadstopspec == nullptr) {
|
||||
grfmsg(1, "RoadStopMapSpriteGroup: Road stop with ID 0x%02X does not exist, skipping", roadstops[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
roadstopspec->grf_prop.spritegroup[ctype] = _cur.spritegroups[groupid];
|
||||
}
|
||||
}
|
||||
|
||||
uint16 groupid = buf->ReadWord();
|
||||
if (!IsValidGroupID(groupid, "RoadStopMapSpriteGroup")) return;
|
||||
|
||||
if (_cur.grffile->roadstops == nullptr) {
|
||||
grfmsg(0, "RoadStopMapSpriteGroup: No roadstops defined, skipping.");
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint i = 0; i < idcount; i++) {
|
||||
RoadStopSpec *roadstopspec = _cur.grffile->roadstops == nullptr ? nullptr : _cur.grffile->roadstops[roadstops[i]];
|
||||
|
||||
if (roadstopspec == nullptr) {
|
||||
grfmsg(1, "RoadStopMapSpriteGroup: Road stop with ID 0x%02X does not exist, skipping.", roadstops[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (roadstopspec->grf_prop.grffile != nullptr) {
|
||||
grfmsg(1, "RoadStopMapSpriteGroup: Road stop with ID 0x%02X mapped multiple times, skipping", roadstops[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
roadstopspec->grf_prop.spritegroup[CT_DEFAULT] = _cur.spritegroups[groupid];
|
||||
roadstopspec->grf_prop.grffile = _cur.grffile;
|
||||
roadstopspec->grf_prop.local_id = roadstops[i];
|
||||
RoadStopClass::Assign(roadstopspec);
|
||||
}
|
||||
}
|
||||
|
||||
/* Action 0x03 */
|
||||
static void FeatureMapSpriteGroup(ByteReader *buf)
|
||||
@ -6023,6 +6206,10 @@ static void FeatureMapSpriteGroup(ByteReader *buf)
|
||||
AirportTileMapSpriteGroup(buf, idcount);
|
||||
return;
|
||||
|
||||
case GSF_ROADSTOPS:
|
||||
RoadStopMapSpriteGroup(buf, idcount);
|
||||
return;
|
||||
|
||||
default:
|
||||
grfmsg(1, "FeatureMapSpriteGroup: Unsupported feature 0x%02X, skipping", feature);
|
||||
return;
|
||||
@ -8601,6 +8788,20 @@ static void ResetCustomObjects()
|
||||
}
|
||||
}
|
||||
|
||||
static void ResetCustomRoadStops()
|
||||
{
|
||||
for (auto file : _grf_files) {
|
||||
RoadStopSpec **&roadstopspec = file->roadstops;
|
||||
if (roadstopspec == nullptr) continue;
|
||||
for (uint i = 0; i < NUM_ROADSTOPS_PER_GRF; i++) {
|
||||
free(roadstopspec[i]);
|
||||
}
|
||||
|
||||
free(roadstopspec);
|
||||
roadstopspec = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/** Reset and clear all NewGRFs */
|
||||
static void ResetNewGRF()
|
||||
{
|
||||
@ -8687,6 +8888,10 @@ void ResetNewGRFData()
|
||||
AirportSpec::ResetAirports();
|
||||
AirportTileSpec::ResetAirportTiles();
|
||||
|
||||
/* Reset road stop classes */
|
||||
RoadStopClass::Reset();
|
||||
ResetCustomRoadStops();
|
||||
|
||||
/* Reset canal sprite groups and flags */
|
||||
memset(_water_feature, 0, sizeof(_water_feature));
|
||||
|
||||
|
@ -84,6 +84,7 @@ enum GrfSpecFeature {
|
||||
GSF_AIRPORTTILES,
|
||||
GSF_ROADTYPES,
|
||||
GSF_TRAMTYPES,
|
||||
GSF_ROADSTOPS,
|
||||
GSF_END,
|
||||
|
||||
GSF_FAKE_TOWNS = GSF_END, ///< Fake town GrfSpecFeature for NewGRF debugging (parent scope)
|
||||
@ -118,6 +119,7 @@ struct GRFFile : ZeroedMemoryAllocator {
|
||||
struct ObjectSpec **objectspec;
|
||||
struct AirportSpec **airportspec;
|
||||
struct AirportTileSpec **airtspec;
|
||||
struct RoadStopSpec **roadstops;
|
||||
|
||||
uint32 param[0x80];
|
||||
uint param_end; ///< one more than the highest set parameter
|
||||
|
@ -278,7 +278,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 (_tick_counter % (1ULL << 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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
@ -311,6 +311,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.
|
||||
*/
|
||||
|
@ -776,6 +776,8 @@ GrfSpecFeature GetGrfSpecFeature(TileIndex tile)
|
||||
switch (GetStationType(tile)) {
|
||||
case STATION_RAIL: return GSF_STATIONS;
|
||||
case STATION_AIRPORT: return GSF_AIRPORTTILES;
|
||||
case STATION_BUS: return GSF_ROADSTOPS;
|
||||
case STATION_TRUCK: return GSF_ROADSTOPS;
|
||||
default: return GSF_INVALID;
|
||||
}
|
||||
}
|
||||
|
@ -480,7 +480,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;
|
||||
|
||||
|
@ -510,7 +510,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;
|
||||
|
||||
|
614
src/newgrf_roadstop.cpp
Normal file
614
src/newgrf_roadstop.cpp
Normal file
@ -0,0 +1,614 @@
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file command.cpp Handling of NewGRF road stops. */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "debug.h"
|
||||
#include "station_base.h"
|
||||
#include "roadstop_base.h"
|
||||
#include "newgrf_roadstop.h"
|
||||
#include "newgrf_class_func.h"
|
||||
#include "newgrf_cargo.h"
|
||||
#include "newgrf_roadtype.h"
|
||||
#include "gfx_type.h"
|
||||
#include "company_func.h"
|
||||
#include "road.h"
|
||||
#include "window_type.h"
|
||||
#include "date_func.h"
|
||||
#include "town.h"
|
||||
#include "viewport_func.h"
|
||||
#include "newgrf_animation_base.h"
|
||||
#include "newgrf_sound.h"
|
||||
|
||||
#include "safeguards.h"
|
||||
|
||||
template <typename Tspec, typename Tid, Tid Tmax>
|
||||
void NewGRFClass<Tspec, Tid, Tmax>::InsertDefaults()
|
||||
{
|
||||
/* Set up initial data */
|
||||
classes[0].global_id = 'DFLT';
|
||||
classes[0].name = STR_STATION_CLASS_DFLT;
|
||||
classes[0].Insert(nullptr);
|
||||
|
||||
classes[1].global_id = 'WAYP';
|
||||
classes[1].name = STR_STATION_CLASS_WAYP;
|
||||
classes[1].Insert(nullptr);
|
||||
}
|
||||
|
||||
template <typename Tspec, typename Tid, Tid Tmax>
|
||||
bool NewGRFClass<Tspec, Tid, Tmax>::IsUIAvailable(uint index) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
INSTANTIATE_NEWGRF_CLASS_METHODS(RoadStopClass, RoadStopSpec, RoadStopClassID, ROADSTOP_CLASS_MAX)
|
||||
|
||||
static const uint NUM_ROADSTOPSPECS_PER_STATION = 63; ///< Maximum number of parts per station.
|
||||
|
||||
uint32 RoadStopScopeResolver::GetRandomBits() const
|
||||
{
|
||||
if (this->st == nullptr) return 0;
|
||||
|
||||
uint32 bits = this->st->random_bits;
|
||||
if (this->tile != INVALID_TILE && Station::IsExpected(this->st)) {
|
||||
bits |= Station::From(this->st)->GetRoadStopRandomBits(this->tile) << 16;
|
||||
}
|
||||
return bits;
|
||||
}
|
||||
|
||||
uint32 RoadStopScopeResolver::GetTriggers() const
|
||||
{
|
||||
return this->st == nullptr ? 0 : this->st->waiting_triggers;
|
||||
}
|
||||
|
||||
uint32 RoadStopScopeResolver::GetVariable(byte variable, uint32 parameter, bool *available) const
|
||||
{
|
||||
auto get_road_type_variable = [&](RoadTramType rtt) -> uint32 {
|
||||
RoadType rt;
|
||||
if (this->tile == INVALID_TILE) {
|
||||
rt = (GetRoadTramType(this->roadtype) == rtt) ? this->roadtype : INVALID_ROADTYPE;
|
||||
} else {
|
||||
rt = GetRoadType(this->tile, rtt);
|
||||
}
|
||||
if (rt == INVALID_ROADTYPE) {
|
||||
return 0xFFFFFFFF;
|
||||
} else {
|
||||
return GetReverseRoadTypeTranslation(rt, this->roadstopspec->grf_prop.grffile);
|
||||
}
|
||||
};
|
||||
|
||||
switch (variable) {
|
||||
/* View/rotation */
|
||||
case 0x40: return this->view;
|
||||
|
||||
/* Stop type: 0: bus, 1: truck, 2: waypoint */
|
||||
case 0x41:
|
||||
if (this->type == STATION_BUS) return 0;
|
||||
if (this->type == STATION_TRUCK) return 1;
|
||||
return 2;
|
||||
|
||||
/* Terrain type */
|
||||
case 0x42: return this->tile == INVALID_TILE ? 0 : GetTerrainType(this->tile, TCX_NORMAL); // terrain_type
|
||||
|
||||
/* Road type */
|
||||
case 0x43: return get_road_type_variable(RTT_ROAD);
|
||||
|
||||
/* Tram type */
|
||||
case 0x44: return get_road_type_variable(RTT_TRAM);
|
||||
|
||||
/* Town zone and Manhattan distance of closest town */
|
||||
case 0x45: {
|
||||
if (this->tile == INVALID_TILE) return HZB_TOWN_EDGE << 16;
|
||||
const Town *t = (this->st == nullptr) ? ClosestTownFromTile(this->tile, UINT_MAX) : this->st->town;
|
||||
return t != nullptr ? (GetTownRadiusGroup(t, this->tile) << 16 | std::min(DistanceManhattan(this->tile, t->xy), 0xFFFFu)) : HZB_TOWN_EDGE << 16;
|
||||
}
|
||||
|
||||
/* Get square of Euclidian distance of closest town */
|
||||
case 0x46: {
|
||||
if (this->tile == INVALID_TILE) return 0;
|
||||
const Town *t = (this->st == nullptr) ? ClosestTownFromTile(this->tile, UINT_MAX) : this->st->town;
|
||||
return t != nullptr ? DistanceSquare(this->tile, t->xy) : 0;
|
||||
}
|
||||
|
||||
/* 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 (IsRoadStopTile(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);
|
||||
}
|
||||
|
||||
/* Road stop info of nearby tiles */
|
||||
case 0x68: {
|
||||
if (this->tile == INVALID_TILE) return 0xFFFFFFFF;
|
||||
TileIndex nearby_tile = GetNearbyTile(parameter, this->tile);
|
||||
|
||||
if (!IsRoadStopTile(nearby_tile)) return 0xFFFFFFFF;
|
||||
|
||||
uint32 grfid = this->st->roadstop_speclist[GetCustomRoadStopSpecIndex(this->tile)].grfid;
|
||||
bool same_orientation = GetStationGfx(this->tile) == GetStationGfx(nearby_tile);
|
||||
bool same_station = GetStationIndex(nearby_tile) == this->st->index;
|
||||
uint32 res = GetStationGfx(nearby_tile) << 12 | !same_orientation << 11 | !!same_station << 10;
|
||||
StationType type = GetStationType(nearby_tile);
|
||||
if (type == STATION_TRUCK) res |= (1 << 16);
|
||||
if (type == this->type) SetBit(res, 20);
|
||||
|
||||
if (IsCustomRoadStopSpecIndex(nearby_tile)) {
|
||||
const RoadStopSpecList ssl = BaseStation::GetByTile(nearby_tile)->roadstop_speclist[GetCustomRoadStopSpecIndex(nearby_tile)];
|
||||
res |= 1 << (ssl.grfid != grfid ? 9 : 8) | ssl.localidx;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/* GRFID of nearby road stop tiles */
|
||||
case 0x6A: {
|
||||
if (this->tile == INVALID_TILE) return 0xFFFFFFFF;
|
||||
TileIndex nearby_tile = GetNearbyTile(parameter, this->tile);
|
||||
|
||||
if (!IsRoadStopTile(nearby_tile)) return 0xFFFFFFFF;
|
||||
if (!IsCustomRoadStopSpecIndex(nearby_tile)) return 0;
|
||||
|
||||
const RoadStopSpecList ssl = BaseStation::GetByTile(nearby_tile)->roadstop_speclist[GetCustomRoadStopSpecIndex(nearby_tile)];
|
||||
return ssl.grfid;
|
||||
}
|
||||
|
||||
case 0xF0: return this->st == nullptr ? 0 : this->st->facilities; // facilities
|
||||
|
||||
case 0xFA: return Clamp((this->st == nullptr ? _date : this->st->build_date) - DAYS_TILL_ORIGINAL_BASE_YEAR, 0, 65535); // build date
|
||||
}
|
||||
|
||||
if (this->st != nullptr) return this->st->GetNewGRFVariable(this->ro, variable, parameter, available);
|
||||
|
||||
*available = false;
|
||||
return UINT_MAX;
|
||||
}
|
||||
|
||||
const SpriteGroup *RoadStopResolverObject::ResolveReal(const RealSpriteGroup *group) const
|
||||
{
|
||||
if (group == nullptr) return nullptr;
|
||||
|
||||
return group->loading[0];
|
||||
}
|
||||
|
||||
RoadStopResolverObject::RoadStopResolverObject(const RoadStopSpec *roadstopspec, BaseStation *st, TileIndex tile, RoadType roadtype, StationType type, uint8 view,
|
||||
CallbackID callback, uint32 param1, uint32 param2)
|
||||
: ResolverObject(roadstopspec->grf_prop.grffile, callback, param1, param2), roadstop_scope(*this, st, roadstopspec, tile, roadtype, type, view)
|
||||
{
|
||||
|
||||
this->town_scope = nullptr;
|
||||
|
||||
CargoID ctype = CT_DEFAULT_NA;
|
||||
|
||||
if (st == nullptr) {
|
||||
/* No station, so we are in a purchase list */
|
||||
ctype = CT_PURCHASE;
|
||||
} else if (Station::IsExpected(st)) {
|
||||
const Station *station = Station::From(st);
|
||||
/* Pick the first cargo that we have waiting */
|
||||
for (const CargoSpec *cs : CargoSpec::Iterate()) {
|
||||
if (roadstopspec->grf_prop.spritegroup[cs->Index()] != nullptr &&
|
||||
station->goods[cs->Index()].cargo.TotalCount() > 0) {
|
||||
ctype = cs->Index();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (roadstopspec->grf_prop.spritegroup[ctype] == nullptr) {
|
||||
ctype = CT_DEFAULT;
|
||||
}
|
||||
|
||||
/* Remember the cargo type we've picked */
|
||||
this->roadstop_scope.cargo_type = ctype;
|
||||
this->root_spritegroup = roadstopspec->grf_prop.spritegroup[ctype];
|
||||
}
|
||||
|
||||
RoadStopResolverObject::~RoadStopResolverObject()
|
||||
{
|
||||
delete this->town_scope;
|
||||
}
|
||||
|
||||
TownScopeResolver* RoadStopResolverObject::GetTown()
|
||||
{
|
||||
if (this->town_scope == nullptr) {
|
||||
Town *t;
|
||||
if (this->roadstop_scope.st != nullptr) {
|
||||
t = this->roadstop_scope.st->town;
|
||||
} else {
|
||||
t = ClosestTownFromTile(this->roadstop_scope.tile, UINT_MAX);
|
||||
}
|
||||
if (t == nullptr) return nullptr;
|
||||
this->town_scope = new TownScopeResolver(*this, t, this->roadstop_scope.st == nullptr);
|
||||
}
|
||||
return this->town_scope;
|
||||
}
|
||||
|
||||
uint16 GetRoadStopCallback(CallbackID callback, uint32 param1, uint32 param2, const RoadStopSpec *roadstopspec, BaseStation *st, TileIndex tile, RoadType roadtype, StationType type, uint8 view)
|
||||
{
|
||||
RoadStopResolverObject object(roadstopspec, st, tile, roadtype, type, view, callback, param1, param2);
|
||||
return object.ResolveCallback();
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw representation of a road stop tile for GUI purposes.
|
||||
* @param x position x of image.
|
||||
* @param y position y of image.
|
||||
* @param image an int offset for the sprite.
|
||||
* @param roadtype the RoadType of the underlying road.
|
||||
* @param spec the RoadStop's spec.
|
||||
* @return true of the tile was drawn (allows for fallback to default graphics)
|
||||
*/
|
||||
void DrawRoadStopTile(int x, int y, RoadType roadtype, const RoadStopSpec *spec, StationType type, int view)
|
||||
{
|
||||
assert(roadtype != INVALID_ROADTYPE);
|
||||
assert(spec != nullptr);
|
||||
|
||||
const RoadTypeInfo *rti = GetRoadTypeInfo(roadtype);
|
||||
RoadStopResolverObject object(spec, nullptr, INVALID_TILE, roadtype, type, view);
|
||||
const SpriteGroup *group = object.Resolve();
|
||||
if (group == nullptr || group->type != SGT_TILELAYOUT) return;
|
||||
const DrawTileSprites *dts = ((const TileLayoutSpriteGroup *)group)->ProcessRegisters(nullptr);
|
||||
|
||||
PaletteID palette = COMPANY_SPRITE_COLOUR(_local_company);
|
||||
|
||||
SpriteID image = dts->ground.sprite;
|
||||
PaletteID pal = dts->ground.pal;
|
||||
|
||||
if (GB(image, 0, SPRITE_WIDTH) != 0) {
|
||||
DrawSprite(image, GroundSpritePaletteTransform(image, pal, palette), x, y);
|
||||
}
|
||||
|
||||
if (view >= 4) {
|
||||
/* Drive-through stop */
|
||||
uint sprite_offset = 5 - view;
|
||||
|
||||
/* Road underlay takes precedence over tram */
|
||||
if (spec->draw_mode & ROADSTOP_DRAW_MODE_OVERLAY) {
|
||||
if (rti->UsesOverlay()) {
|
||||
SpriteID ground = GetCustomRoadSprite(rti, INVALID_TILE, ROTSG_GROUND);
|
||||
DrawSprite(ground + sprite_offset, PAL_NONE, x, y);
|
||||
|
||||
SpriteID overlay = GetCustomRoadSprite(rti, INVALID_TILE, ROTSG_OVERLAY);
|
||||
if (overlay) DrawSprite(overlay + sprite_offset, PAL_NONE, x, y);
|
||||
} else if (RoadTypeIsTram(roadtype)) {
|
||||
DrawSprite(SPR_TRAMWAY_TRAM + sprite_offset, PAL_NONE, x, y);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Drive-in stop */
|
||||
if ((spec->draw_mode & ROADSTOP_DRAW_MODE_ROAD) && rti->UsesOverlay()) {
|
||||
SpriteID ground = GetCustomRoadSprite(rti, INVALID_TILE, ROTSG_ROADSTOP);
|
||||
DrawSprite(ground + view, PAL_NONE, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
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, INVALID_ROADTYPE, 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));
|
||||
}
|
||||
|
||||
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 (const RoadStopTileData &tile_data : st->custom_roadstop_tile_data) {
|
||||
process_tile(tile_data.tile);
|
||||
}
|
||||
} else {
|
||||
process_tile(trigger_tile);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger road stop randomisation
|
||||
*
|
||||
* @param st the station being triggered
|
||||
* @param tile the exact tile of the station that should be triggered
|
||||
* @param trigger trigger type
|
||||
* @param cargo_type cargo type causing the trigger
|
||||
*/
|
||||
void TriggerRoadStopRandomisation(Station *st, TileIndex tile, RoadStopRandomTrigger trigger, CargoID cargo_type)
|
||||
{
|
||||
if (st == nullptr) st = Station::GetByTile(tile);
|
||||
|
||||
/* Check the cached cargo trigger bitmask to see if we need
|
||||
* to bother with any further processing. */
|
||||
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);
|
||||
|
||||
uint32 whole_reseed = 0;
|
||||
|
||||
CargoTypes empty_mask = 0;
|
||||
if (trigger == RSRT_CARGO_TAKEN) {
|
||||
/* Create a bitmask of completely empty cargo types to be matched */
|
||||
for (CargoID i = 0; i < NUM_CARGO; i++) {
|
||||
if (st->goods[i].cargo.TotalCount() == 0) {
|
||||
SetBit(empty_mask, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32 used_triggers = 0;
|
||||
auto process_tile = [&](TileIndex cur_tile) {
|
||||
const RoadStopSpec *ss = GetRoadStopSpec(cur_tile);
|
||||
if (ss == nullptr) return;
|
||||
|
||||
/* Cargo taken "will only be triggered if all of those
|
||||
* cargo types have no more cargo waiting." */
|
||||
if (trigger == RSRT_CARGO_TAKEN) {
|
||||
if ((ss->cargo_triggers & ~empty_mask) != 0) return;
|
||||
}
|
||||
|
||||
if (cargo_type == CT_INVALID || HasBit(ss->cargo_triggers, cargo_type)) {
|
||||
RoadStopResolverObject object(ss, st, cur_tile, INVALID_ROADTYPE, GetStationType(cur_tile), GetStationGfx(cur_tile));
|
||||
object.waiting_triggers = st->waiting_triggers;
|
||||
|
||||
const SpriteGroup *group = object.Resolve();
|
||||
if (group == nullptr) return;
|
||||
|
||||
used_triggers |= object.used_triggers;
|
||||
|
||||
uint32 reseed = object.GetReseedSum();
|
||||
if (reseed != 0) {
|
||||
whole_reseed |= reseed;
|
||||
reseed >>= 16;
|
||||
|
||||
/* Set individual tile random bits */
|
||||
uint8 random_bits = st->GetRoadStopRandomBits(cur_tile);
|
||||
random_bits &= ~reseed;
|
||||
random_bits |= Random() & reseed;
|
||||
st->SetRoadStopRandomBits(cur_tile, random_bits);
|
||||
|
||||
MarkTileDirtyByTile(cur_tile);
|
||||
}
|
||||
}
|
||||
};
|
||||
if (trigger == RSRT_NEW_CARGO || trigger == RSRT_CARGO_TAKEN) {
|
||||
for (const RoadStopTileData &tile_data : st->custom_roadstop_tile_data) {
|
||||
process_tile(tile_data.tile);
|
||||
}
|
||||
} else {
|
||||
process_tile(tile);
|
||||
}
|
||||
|
||||
/* Update whole station random bits */
|
||||
st->waiting_triggers &= ~used_triggers;
|
||||
if ((whole_reseed & 0xFFFF) != 0) {
|
||||
st->random_bits &= ~whole_reseed;
|
||||
st->random_bits |= Random() & whole_reseed;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there's any new stations by a specific RoadStopType
|
||||
* @param rs the RoadStopType to check.
|
||||
* @param roadtype the RoadType to check.
|
||||
* @return true if there was any new RoadStopSpec's found for the given RoadStopType and RoadType, else false.
|
||||
*/
|
||||
bool GetIfNewStopsByType(RoadStopType rs, RoadType roadtype)
|
||||
{
|
||||
if (!(RoadStopClass::GetClassCount() > 1 || RoadStopClass::Get(ROADSTOP_CLASS_DFLT)->GetSpecCount() > 1)) return false;
|
||||
for (uint i = 0; i < RoadStopClass::GetClassCount(); i++) {
|
||||
// We don't want to check the default or waypoint classes. These classes are always available.
|
||||
if (i == ROADSTOP_CLASS_DFLT || i == ROADSTOP_CLASS_WAYP) continue;
|
||||
RoadStopClass *roadstopclass = RoadStopClass::Get((RoadStopClassID)i);
|
||||
if (GetIfClassHasNewStopsByType(roadstopclass, rs, roadtype)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given RoadStopClass has any specs assigned to it, compatible with the given RoadStopType.
|
||||
* @param roadstopclass the RoadStopClass to check.
|
||||
* @param rs the RoadStopType to check.
|
||||
* @param roadtype the RoadType to check.
|
||||
* @return true if the RoadStopSpec has any specs compatible with the given RoadStopType and RoadType.
|
||||
*/
|
||||
bool GetIfClassHasNewStopsByType(RoadStopClass *roadstopclass, RoadStopType rs, RoadType roadtype)
|
||||
{
|
||||
for (uint j = 0; j < roadstopclass->GetSpecCount(); j++) {
|
||||
if (GetIfStopIsForType(roadstopclass->GetSpec(j), rs, roadtype)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given RoadStopSpec is compatible with the given RoadStopType.
|
||||
* @param roadstopspec the RoadStopSpec to check.
|
||||
* @param rs the RoadStopType to check.
|
||||
* @param roadtype the RoadType to check.
|
||||
* @return true if the RoadStopSpec is compatible with the given RoadStopType and RoadType.
|
||||
*/
|
||||
bool GetIfStopIsForType(const RoadStopSpec *roadstopspec, RoadStopType rs, RoadType roadtype)
|
||||
{
|
||||
// The roadstopspec is nullptr, must be the default station, always return true.
|
||||
if (roadstopspec == nullptr) return true;
|
||||
|
||||
if (HasBit(roadstopspec->flags, RSF_BUILD_MENU_ROAD_ONLY) && !RoadTypeIsRoad(roadtype)) return false;
|
||||
if (HasBit(roadstopspec->flags, RSF_BUILD_MENU_TRAM_ONLY) && !RoadTypeIsTram(roadtype)) return false;
|
||||
|
||||
if (roadstopspec->stop_type == ROADSTOPTYPE_ALL) return true;
|
||||
|
||||
switch (rs) {
|
||||
case ROADSTOP_BUS:
|
||||
if (roadstopspec->stop_type == ROADSTOPTYPE_PASSENGER) return true;
|
||||
break;
|
||||
|
||||
case ROADSTOP_TRUCK:
|
||||
if (roadstopspec->stop_type == ROADSTOPTYPE_FREIGHT) return true;
|
||||
break;
|
||||
|
||||
default:
|
||||
NOT_REACHED();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const RoadStopSpec *GetRoadStopSpec(TileIndex t)
|
||||
{
|
||||
if (!IsCustomRoadStopSpecIndex(t)) return nullptr;
|
||||
|
||||
const BaseStation *st = BaseStation::GetByTile(t);
|
||||
uint specindex = GetCustomRoadStopSpecIndex(t);
|
||||
return specindex < st->roadstop_speclist.size() ? st->roadstop_speclist[specindex].spec : nullptr;
|
||||
}
|
||||
|
||||
int AllocateSpecToRoadStop(const RoadStopSpec *statspec, BaseStation *st, bool exec)
|
||||
{
|
||||
uint i;
|
||||
|
||||
if (statspec == nullptr || st == nullptr) return 0;
|
||||
|
||||
/* Try to find the same spec and return that one */
|
||||
for (i = 1; i < st->roadstop_speclist.size() && i < NUM_ROADSTOPSPECS_PER_STATION; i++) {
|
||||
if (st->roadstop_speclist[i].spec == statspec) return i;
|
||||
}
|
||||
|
||||
/* Try to find an unused spec slot */
|
||||
for (i = 1; i < st->roadstop_speclist.size() && i < NUM_ROADSTOPSPECS_PER_STATION; i++) {
|
||||
if (st->roadstop_speclist[i].spec == nullptr && st->roadstop_speclist[i].grfid == 0) break;
|
||||
}
|
||||
|
||||
if (i == NUM_ROADSTOPSPECS_PER_STATION) {
|
||||
/* Full, give up */
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (exec) {
|
||||
if (i >= st->roadstop_speclist.size()) st->roadstop_speclist.resize(i + 1);
|
||||
st->roadstop_speclist[i].spec = statspec;
|
||||
st->roadstop_speclist[i].grfid = statspec->grf_prop.grffile->grfid;
|
||||
st->roadstop_speclist[i].localidx = statspec->grf_prop.local_id;
|
||||
|
||||
RoadStopUpdateCachedTriggers(st);
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
void DeallocateSpecFromRoadStop(BaseStation *st, byte specindex)
|
||||
{
|
||||
/* specindex of 0 (default) is never freeable */
|
||||
if (specindex == 0) return;
|
||||
|
||||
/* Check custom road stop tiles if the specindex is still in use */
|
||||
for (const RoadStopTileData &tile_data : st->custom_roadstop_tile_data) {
|
||||
if (GetCustomRoadStopSpecIndex(tile_data.tile) == specindex) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* This specindex is no longer in use, so deallocate it */
|
||||
st->roadstop_speclist[specindex].spec = nullptr;
|
||||
st->roadstop_speclist[specindex].grfid = 0;
|
||||
st->roadstop_speclist[specindex].localidx = 0;
|
||||
|
||||
/* If this was the highest spec index, reallocate */
|
||||
if (specindex == st->roadstop_speclist.size() - 1) {
|
||||
size_t num_specs;
|
||||
for (num_specs = st->roadstop_speclist.size() - 1; num_specs > 0; num_specs--) {
|
||||
if (st->roadstop_speclist[num_specs].grfid != 0) break;
|
||||
}
|
||||
|
||||
if (num_specs > 0) {
|
||||
st->roadstop_speclist.resize(num_specs + 1);
|
||||
} else {
|
||||
st->roadstop_speclist.clear();
|
||||
st->cached_roadstop_anim_triggers = 0;
|
||||
st->cached_roadstop_cargo_triggers = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
RoadStopUpdateCachedTriggers(st);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the cached animation trigger bitmask for a station.
|
||||
* @param st Station to update.
|
||||
*/
|
||||
void RoadStopUpdateCachedTriggers(BaseStation *st)
|
||||
{
|
||||
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->roadstop_speclist.size(); i++) {
|
||||
const RoadStopSpec *ss = st->roadstop_speclist[i].spec;
|
||||
if (ss != nullptr) {
|
||||
st->cached_roadstop_anim_triggers |= ss->animation.triggers;
|
||||
st->cached_roadstop_cargo_triggers |= ss->cargo_triggers;
|
||||
}
|
||||
}
|
||||
}
|
189
src/newgrf_roadstop.h
Normal file
189
src/newgrf_roadstop.h
Normal file
@ -0,0 +1,189 @@
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file newgrf_roadstop.h NewGRF definitions and structures for road stops.
|
||||
*/
|
||||
|
||||
#ifndef NEWGRF_ROADSTATION_H
|
||||
#define NEWGRF_ROADSTATION_H
|
||||
|
||||
#include "newgrf_animation_type.h"
|
||||
#include "newgrf_spritegroup.h"
|
||||
#include "newgrf_class.h"
|
||||
#include "newgrf_commons.h"
|
||||
#include "newgrf_town.h"
|
||||
#include "road.h"
|
||||
|
||||
/** The maximum amount of roadstops a single GRF is allowed to add */
|
||||
static const int NUM_ROADSTOPS_PER_GRF = 255;
|
||||
|
||||
enum RoadStopClassID : byte {
|
||||
ROADSTOP_CLASS_BEGIN = 0, ///< The lowest valid value
|
||||
ROADSTOP_CLASS_DFLT = 0, ///< Default road stop class.
|
||||
ROADSTOP_CLASS_WAYP, ///< Waypoint class (unimplemented: this is reserved for future use with road waypoints).
|
||||
ROADSTOP_CLASS_MAX = 255, ///< Maximum number of classes.
|
||||
};
|
||||
DECLARE_POSTFIX_INCREMENT(RoadStopClassID)
|
||||
|
||||
/* Some Triggers etc. */
|
||||
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.
|
||||
};
|
||||
|
||||
/**
|
||||
* Various different options for availability, restricting
|
||||
* the roadstop to be only for busses or for trucks.
|
||||
*/
|
||||
enum RoadStopAvailabilityType : byte {
|
||||
ROADSTOPTYPE_PASSENGER, ///< This RoadStop is for passenger (bus) stops.
|
||||
ROADSTOPTYPE_FREIGHT, ///< This RoadStop is for freight (truck) stops.
|
||||
ROADSTOPTYPE_ALL, ///< This RoadStop is for both types of station road stops.
|
||||
|
||||
ROADSTOPTYPE_END,
|
||||
};
|
||||
|
||||
/**
|
||||
* Different draw modes to disallow rendering of some parts of the stop
|
||||
* or road.
|
||||
*/
|
||||
enum RoadStopDrawMode : byte {
|
||||
ROADSTOP_DRAW_MODE_NONE = 0,
|
||||
ROADSTOP_DRAW_MODE_ROAD = 1 << 0, ///< Bay stops: Draw the road itself
|
||||
ROADSTOP_DRAW_MODE_OVERLAY = 1 << 1, ///< Drive-through stops: Draw the road overlay, e.g. pavement
|
||||
};
|
||||
DECLARE_ENUM_AS_BIT_SET(RoadStopDrawMode)
|
||||
|
||||
enum RoadStopSpecFlags {
|
||||
RSF_CB141_RANDOM_BITS = 0, ///< Callback 141 needs random bits.
|
||||
RSF_NO_CATENARY = 2, ///< Do not show catenary.
|
||||
RSF_DRIVE_THROUGH_ONLY = 3, ///< Stop is drive-through only.
|
||||
RSF_NO_AUTO_ROAD_CONNECTION = 4, ///< No auto road connection.
|
||||
RSF_BUILD_MENU_ROAD_ONLY = 5, ///< Only show in the road build menu (not tram).
|
||||
RSF_BUILD_MENU_TRAM_ONLY = 6, ///< Only show in the tram build menu (not road).
|
||||
};
|
||||
|
||||
/** Scope resolver for road stops. */
|
||||
struct RoadStopScopeResolver : public ScopeResolver {
|
||||
TileIndex tile; ///< %Tile of the station.
|
||||
struct BaseStation *st; ///< Instance of the station.
|
||||
const struct RoadStopSpec *roadstopspec; ///< Station (type) specification.
|
||||
CargoID cargo_type; ///< Type of cargo of the station.
|
||||
StationType type; ///< Station type.
|
||||
uint8 view; ///< Station axis.
|
||||
RoadType roadtype; ///< Road type (used when no tile)
|
||||
|
||||
RoadStopScopeResolver(ResolverObject& ro, BaseStation* st, const RoadStopSpec *roadstopspec, TileIndex tile, RoadType roadtype, StationType type, uint8 view = 0)
|
||||
: ScopeResolver(ro), tile(tile), st(st), roadstopspec(roadstopspec), type(type), view(view), roadtype(roadtype)
|
||||
{
|
||||
}
|
||||
|
||||
uint32 GetRandomBits() const override;
|
||||
uint32 GetTriggers() const override;
|
||||
|
||||
uint32 GetVariable(byte variable, uint32 parameter, bool *available) const override;
|
||||
};
|
||||
|
||||
/** Road stop resolver. */
|
||||
struct RoadStopResolverObject : public ResolverObject {
|
||||
RoadStopScopeResolver roadstop_scope; ///< The stop scope resolver.
|
||||
TownScopeResolver *town_scope; ///< The town scope resolver (created on the first call).
|
||||
|
||||
RoadStopResolverObject(const RoadStopSpec* roadstopspec, BaseStation* st, TileIndex tile, RoadType roadtype, StationType type, uint8 view, CallbackID callback = CBID_NO_CALLBACK, uint32 param1 = 0, uint32 param2 = 0);
|
||||
~RoadStopResolverObject();
|
||||
|
||||
ScopeResolver* GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, byte relative = 0) override
|
||||
{
|
||||
switch (scope) {
|
||||
case VSG_SCOPE_SELF: return &this->roadstop_scope;
|
||||
case VSG_SCOPE_PARENT: {
|
||||
TownScopeResolver *tsr = this->GetTown();
|
||||
if (tsr != nullptr) return tsr;
|
||||
FALLTHROUGH;
|
||||
}
|
||||
default: return ResolverObject::GetScope(scope, relative);
|
||||
}
|
||||
}
|
||||
|
||||
TownScopeResolver *GetTown();
|
||||
|
||||
const SpriteGroup *ResolveReal(const RealSpriteGroup *group) const override;
|
||||
};
|
||||
|
||||
/** Road stop specification. */
|
||||
struct RoadStopSpec {
|
||||
/**
|
||||
* Properties related the the grf file.
|
||||
* NUM_CARGO real cargo plus three pseudo cargo sprite groups.
|
||||
* Used for obtaining the sprite offset of custom sprites, and for
|
||||
* evaluating callbacks.
|
||||
*/
|
||||
GRFFilePropsBase<NUM_CARGO + 3> grf_prop;
|
||||
RoadStopClassID cls_id; ///< The class to which this spec belongs.
|
||||
int spec_id; ///< The ID of this spec inside the class.
|
||||
StringID name; ///< Name of this stop
|
||||
|
||||
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;
|
||||
|
||||
byte bridge_height[6]; ///< Minimum height for a bridge above, 0 for none
|
||||
byte bridge_disallowed_pillars[6]; ///< Disallowed pillar flags for a bridge above
|
||||
|
||||
uint8 build_cost_multiplier = 16; ///< Build cost multiplier per tile.
|
||||
uint8 clear_cost_multiplier = 16; ///< Clear cost multiplier per tile.
|
||||
|
||||
/**
|
||||
* Get the cost for building a road stop of this type.
|
||||
* @return The cost for building.
|
||||
*/
|
||||
Money GetBuildCost(Price category) const { return GetPrice(category, this->build_cost_multiplier, this->grf_prop.grffile, -4); }
|
||||
|
||||
/**
|
||||
* Get the cost for clearing a road stop of this type.
|
||||
* @return The cost for clearing.
|
||||
*/
|
||||
Money GetClearCost(Price category) const { return GetPrice(category, this->clear_cost_multiplier, this->grf_prop.grffile, -4); }
|
||||
|
||||
static const RoadStopSpec *Get(uint16 index);
|
||||
};
|
||||
|
||||
template <>
|
||||
struct EnumPropsT<RoadStopClassID> : MakeEnumPropsT<RoadStopClassID, byte, ROADSTOP_CLASS_BEGIN, ROADSTOP_CLASS_MAX, ROADSTOP_CLASS_MAX, 8> {};
|
||||
|
||||
typedef NewGRFClass<RoadStopSpec, RoadStopClassID, ROADSTOP_CLASS_MAX> RoadStopClass;
|
||||
|
||||
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, RoadType roadtype, 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, RoadType roadtype);
|
||||
bool GetIfClassHasNewStopsByType(RoadStopClass *roadstopclass, RoadStopType rs, RoadType roadtype);
|
||||
bool GetIfStopIsForType(const RoadStopSpec *roadstopspec, RoadStopType rs, RoadType roadtype);
|
||||
|
||||
uint GetCountOfCompatibleStopsByType(RoadStopClass *roadstopclass, RoadStopType rs);
|
||||
|
||||
const RoadStopSpec *GetRoadStopSpec(TileIndex t);
|
||||
int AllocateSpecToRoadStop(const RoadStopSpec *statspec, BaseStation *st, bool exec);
|
||||
void DeallocateSpecFromRoadStop(BaseStation *st, byte specindex);
|
||||
void RoadStopUpdateCachedTriggers(BaseStation *st);
|
||||
|
||||
#endif /* NEWGRF_ROADSTATION_H */
|
@ -894,7 +894,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;
|
||||
|
||||
|
@ -14,6 +14,8 @@
|
||||
#include "road_type.h"
|
||||
#include "command_type.h"
|
||||
|
||||
enum RoadStopClassID : byte;
|
||||
|
||||
void DrawRoadDepotSprite(int x, int y, DiagDirection dir, RoadType rt);
|
||||
void UpdateNearestTownForRoadTiles(bool invalidate);
|
||||
|
||||
@ -32,6 +34,6 @@ DEF_CMD_TRAIT(CMD_CONVERT_ROAD, CmdConvertRoad, 0,
|
||||
CommandCallback CcPlaySound_CONSTRUCTION_OTHER;
|
||||
CommandCallback CcBuildRoadTunnel;
|
||||
void CcRoadDepot(Commands cmd, const CommandCost &result, TileIndex tile, RoadType rt, DiagDirection dir);
|
||||
void CcRoadStop(Commands cmd, const CommandCost &result, TileIndex tile, uint8 width, uint8 length, RoadStopType, bool is_drive_through, DiagDirection dir, RoadType, StationID, bool);
|
||||
void CcRoadStop(Commands cmd, const CommandCost &result, TileIndex tile, uint8 width, uint8 length, RoadStopType, bool is_drive_through, DiagDirection dir, RoadType, RoadStopClassID spec_class, byte spec_index, StationID, bool);
|
||||
|
||||
#endif /* ROAD_CMD_H */
|
||||
|
646
src/road_gui.cpp
646
src/road_gui.cpp
@ -34,6 +34,11 @@
|
||||
#include "station_cmd.h"
|
||||
#include "road_cmd.h"
|
||||
#include "tunnelbridge_cmd.h"
|
||||
#include "newgrf_roadstop.h"
|
||||
#include "querystring_gui.h"
|
||||
#include "sortlist_type.h"
|
||||
#include "stringfilter_type.h"
|
||||
#include "string_func.h"
|
||||
|
||||
#include "widgets/road_widget.h"
|
||||
|
||||
@ -55,7 +60,42 @@ static bool _place_road_end_half;
|
||||
static RoadType _cur_roadtype;
|
||||
|
||||
static DiagDirection _road_depot_orientation;
|
||||
static DiagDirection _road_station_picker_orientation;
|
||||
|
||||
struct RoadStopGUISettings {
|
||||
DiagDirection orientation;
|
||||
|
||||
RoadStopClassID roadstop_class;
|
||||
byte roadstop_type;
|
||||
byte roadstop_count;
|
||||
};
|
||||
static RoadStopGUISettings _roadstop_gui_settings;
|
||||
|
||||
/**
|
||||
* 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) return true;
|
||||
|
||||
if (HasBit(roadstopspec->flags, RSF_BUILD_MENU_ROAD_ONLY) && !RoadTypeIsRoad(_cur_roadtype)) return false;
|
||||
if (HasBit(roadstopspec->flags, RSF_BUILD_MENU_TRAM_ONLY) && !RoadTypeIsTram(_cur_roadtype)) return false;
|
||||
|
||||
if (roadstopspec->stop_type != ROADSTOPTYPE_ALL) {
|
||||
switch (type) {
|
||||
case STATION_BUS: if (roadstopspec->stop_type != ROADSTOPTYPE_PASSENGER) return false; break;
|
||||
case STATION_TRUCK: if (roadstopspec->stop_type != ROADSTOPTYPE_FREIGHT) return false; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!HasBit(roadstopspec->callback_mask, CBM_ROAD_STOP_AVAIL)) return true;
|
||||
|
||||
uint16 cb_res = GetRoadStopCallback(CBID_STATION_AVAILABILITY, 0, 0, roadstopspec, nullptr, INVALID_TILE, _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(Commands cmd, const CommandCost &result, TileIndex tile)
|
||||
{
|
||||
@ -135,20 +175,32 @@ void CcRoadDepot(Commands cmd, const CommandCost &result, TileIndex tile, RoadTy
|
||||
* @param length Length of the road stop.
|
||||
* @param is_drive_through False for normal stops, true for drive-through.
|
||||
* @param dir Entrance direction (#DiagDirection) for normal stops. Converted to the axis for drive-through stops.
|
||||
* @param spec_class Road stop spec class.
|
||||
* @param spec_index Road stop spec index.
|
||||
* @see CmdBuildRoadStop
|
||||
*/
|
||||
void CcRoadStop(Commands cmd, const CommandCost &result, TileIndex tile, uint8 width, uint8 length, RoadStopType, bool is_drive_through, DiagDirection dir, RoadType, StationID, bool)
|
||||
void CcRoadStop(Commands cmd, const CommandCost &result, TileIndex tile, uint8 width, uint8 length, RoadStopType, bool is_drive_through,
|
||||
DiagDirection dir, RoadType, RoadStopClassID spec_class, byte spec_index, StationID, bool)
|
||||
{
|
||||
if (result.Failed()) return;
|
||||
|
||||
if (_settings_client.sound.confirm) SndPlayTileFx(SND_1F_CONSTRUCTION_OTHER, tile);
|
||||
if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace();
|
||||
|
||||
bool connect_to_road = true;
|
||||
if ((uint)spec_class < RoadStopClass::GetClassCount() && spec_index < RoadStopClass::Get(spec_class)->GetSpecCount()) {
|
||||
const RoadStopSpec *roadstopspec = RoadStopClass::Get(spec_class)->GetSpec(spec_index);
|
||||
if (roadstopspec != nullptr && HasBit(roadstopspec->flags, RSF_NO_AUTO_ROAD_CONNECTION)) connect_to_road = false;
|
||||
}
|
||||
|
||||
if (connect_to_road) {
|
||||
TileArea roadstop_area(tile, width, length);
|
||||
for (TileIndex cur_tile : roadstop_area) {
|
||||
ConnectRoadToStructure(cur_tile, dir);
|
||||
/* For a drive-through road stop build connecting road for other entrance. */
|
||||
if (is_drive_through) ConnectRoadToStructure(cur_tile, ReverseDiagDir(dir));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -164,15 +216,19 @@ void CcRoadStop(Commands cmd, const CommandCost &result, TileIndex tile, uint8 w
|
||||
static void PlaceRoadStop(TileIndex start_tile, TileIndex end_tile, RoadStopType stop_type, bool adjacent, RoadType rt, StringID err_msg)
|
||||
{
|
||||
TileArea ta(start_tile, end_tile);
|
||||
DiagDirection ddir = _road_station_picker_orientation;
|
||||
DiagDirection ddir = _roadstop_gui_settings.orientation;
|
||||
bool drive_through = ddir >= DIAGDIR_END;
|
||||
if (drive_through) ddir = static_cast<DiagDirection>(ddir - DIAGDIR_END); // Adjust picker result to actual direction.
|
||||
RoadStopClassID spec_class = _roadstop_gui_settings.roadstop_class;
|
||||
byte spec_index = _roadstop_gui_settings.roadstop_type;
|
||||
|
||||
auto proc = [=](bool test, StationID to_join) -> bool {
|
||||
if (test) {
|
||||
return Command<CMD_BUILD_ROAD_STOP>::Do(CommandFlagsToDCFlags(GetCommandFlags<CMD_BUILD_ROAD_STOP>()), ta.tile, ta.w, ta.h, stop_type, drive_through, ddir, rt, INVALID_STATION, adjacent).Succeeded();
|
||||
return Command<CMD_BUILD_ROAD_STOP>::Do(CommandFlagsToDCFlags(GetCommandFlags<CMD_BUILD_ROAD_STOP>()), ta.tile, ta.w, ta.h, stop_type, drive_through,
|
||||
ddir, rt, spec_class, spec_index, INVALID_STATION, adjacent).Succeeded();
|
||||
} else {
|
||||
return Command<CMD_BUILD_ROAD_STOP>::Post(err_msg, CcRoadStop, ta.tile, ta.w, ta.h, stop_type, drive_through, ddir, rt, to_join, adjacent);
|
||||
return Command<CMD_BUILD_ROAD_STOP>::Post(err_msg, CcRoadStop, ta.tile, ta.w, ta.h, stop_type, drive_through,
|
||||
ddir, rt, spec_class, spec_index, to_join, adjacent);
|
||||
}
|
||||
};
|
||||
|
||||
@ -188,8 +244,8 @@ static void PlaceRoad_BusStation(TileIndex tile)
|
||||
if (_remove_button_clicked) {
|
||||
VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_REMOVE_BUSSTOP);
|
||||
} else {
|
||||
if (_road_station_picker_orientation < DIAGDIR_END) { // Not a drive-through stop.
|
||||
VpStartPlaceSizing(tile, (DiagDirToAxis(_road_station_picker_orientation) == AXIS_X) ? VPM_X_LIMITED : VPM_Y_LIMITED, DDSP_BUILD_BUSSTOP);
|
||||
if (_roadstop_gui_settings.orientation < DIAGDIR_END) { // Not a drive-through stop.
|
||||
VpStartPlaceSizing(tile, (DiagDirToAxis(_roadstop_gui_settings.orientation) == AXIS_X) ? VPM_X_LIMITED : VPM_Y_LIMITED, DDSP_BUILD_BUSSTOP);
|
||||
} else {
|
||||
VpStartPlaceSizing(tile, VPM_X_AND_Y_LIMITED, DDSP_BUILD_BUSSTOP);
|
||||
}
|
||||
@ -206,8 +262,8 @@ static void PlaceRoad_TruckStation(TileIndex tile)
|
||||
if (_remove_button_clicked) {
|
||||
VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_REMOVE_TRUCKSTOP);
|
||||
} else {
|
||||
if (_road_station_picker_orientation < DIAGDIR_END) { // Not a drive-through stop.
|
||||
VpStartPlaceSizing(tile, (DiagDirToAxis(_road_station_picker_orientation) == AXIS_X) ? VPM_X_LIMITED : VPM_Y_LIMITED, DDSP_BUILD_TRUCKSTOP);
|
||||
if (_roadstop_gui_settings.orientation < DIAGDIR_END) { // Not a drive-through stop.
|
||||
VpStartPlaceSizing(tile, (DiagDirToAxis(_roadstop_gui_settings.orientation) == AXIS_X) ? VPM_X_LIMITED : VPM_Y_LIMITED, DDSP_BUILD_TRUCKSTOP);
|
||||
} else {
|
||||
VpStartPlaceSizing(tile, VPM_X_AND_Y_LIMITED, DDSP_BUILD_TRUCKSTOP);
|
||||
}
|
||||
@ -652,7 +708,7 @@ struct BuildRoadToolbarWindow : Window {
|
||||
|
||||
case DDSP_BUILD_BUSSTOP:
|
||||
case DDSP_REMOVE_BUSSTOP:
|
||||
if (this->IsWidgetLowered(WID_ROT_BUS_STATION)) {
|
||||
if (this->IsWidgetLowered(WID_ROT_BUS_STATION) && GetIfClassHasNewStopsByType(RoadStopClass::Get(_roadstop_gui_settings.roadstop_class), ROADSTOP_BUS, _cur_roadtype)) {
|
||||
if (_remove_button_clicked) {
|
||||
TileArea ta(start_tile, end_tile);
|
||||
Command<CMD_REMOVE_ROAD_STOP>::Post(this->rti->strings.err_remove_station[ROADSTOP_BUS], CcPlaySound_CONSTRUCTION_OTHER, ta.tile, ta.w, ta.h, ROADSTOP_BUS, _ctrl_pressed);
|
||||
@ -664,7 +720,7 @@ struct BuildRoadToolbarWindow : Window {
|
||||
|
||||
case DDSP_BUILD_TRUCKSTOP:
|
||||
case DDSP_REMOVE_TRUCKSTOP:
|
||||
if (this->IsWidgetLowered(WID_ROT_TRUCK_STATION)) {
|
||||
if (this->IsWidgetLowered(WID_ROT_TRUCK_STATION) && GetIfClassHasNewStopsByType(RoadStopClass::Get(_roadstop_gui_settings.roadstop_class), ROADSTOP_TRUCK, _cur_roadtype)) {
|
||||
if (_remove_button_clicked) {
|
||||
TileArea ta(start_tile, end_tile);
|
||||
Command<CMD_REMOVE_ROAD_STOP>::Post(this->rti->strings.err_remove_station[ROADSTOP_TRUCK], CcPlaySound_CONSTRUCTION_OTHER, ta.tile, ta.w, ta.h, ROADSTOP_TRUCK, _ctrl_pressed);
|
||||
@ -1046,14 +1102,91 @@ static void ShowRoadDepotPicker(Window *parent)
|
||||
new BuildRoadDepotWindow(&_build_road_depot_desc, parent);
|
||||
}
|
||||
|
||||
/** Enum referring to the Hotkeys in the build road stop window */
|
||||
enum BuildRoadStopHotkeys {
|
||||
BROSHK_FOCUS_FILTER_BOX, ///< Focus the edit box for editing the filter string
|
||||
};
|
||||
|
||||
struct BuildRoadStationWindow : public PickerWindowBase {
|
||||
BuildRoadStationWindow(WindowDesc *desc, Window *parent, RoadStopType rs) : PickerWindowBase(desc, parent)
|
||||
private:
|
||||
RoadStopType roadStopType; ///< The RoadStopType for this Window.
|
||||
uint line_height; ///< Height of a single line in the newstation selection matrix.
|
||||
uint coverage_height; ///< Height of the coverage texts.
|
||||
Scrollbar *vscrollList; ///< Vertical scrollbar of the new station list.
|
||||
Scrollbar *vscrollMatrix; ///< Vertical scrollbar of the station picker matrix.
|
||||
|
||||
typedef GUIList<RoadStopClassID, StringFilter &> GUIRoadStopClassList; ///< Type definition for the list to hold available road stop classes.
|
||||
|
||||
static const uint EDITBOX_MAX_SIZE = 16; ///< The maximum number of characters for the filter edit box.
|
||||
|
||||
static Listing last_sorting; ///< Default sorting of #GUIRoadStopClassList.
|
||||
static Filtering last_filtering; ///< Default filtering of #GUIRoadStopClassList.
|
||||
static GUIRoadStopClassList::SortFunction * const sorter_funcs[]; ///< Sort functions of the #GUIRoadStopClassList.
|
||||
static GUIRoadStopClassList::FilterFunction * const filter_funcs[]; ///< Filter functions of the #GUIRoadStopClassList.
|
||||
GUIRoadStopClassList roadstop_classes; ///< Available road stop classes.
|
||||
StringFilter string_filter; ///< Filter for available road stop classes.
|
||||
QueryString filter_editbox; ///< Filter editbox.
|
||||
|
||||
void EnsureSelectedClassIsVisible()
|
||||
{
|
||||
uint pos = 0;
|
||||
for (auto rs_class : this->roadstop_classes) {
|
||||
if (rs_class == _roadstop_gui_settings.roadstop_class) break;
|
||||
pos++;
|
||||
}
|
||||
this->vscrollList->SetCount((int)this->roadstop_classes.size());
|
||||
this->vscrollList->ScrollTowards(pos);
|
||||
}
|
||||
|
||||
void CheckOrientationValid()
|
||||
{
|
||||
if (_roadstop_gui_settings.orientation >= DIAGDIR_END) return;
|
||||
const RoadStopSpec *spec = RoadStopClass::Get(_roadstop_gui_settings.roadstop_class)->GetSpec(_roadstop_gui_settings.roadstop_type);
|
||||
if (spec != nullptr && HasBit(spec->flags, RSF_DRIVE_THROUGH_ONLY)) {
|
||||
this->RaiseWidget(_roadstop_gui_settings.orientation + WID_BROS_STATION_NE);
|
||||
_roadstop_gui_settings.orientation = DIAGDIR_END;
|
||||
this->LowerWidget(_roadstop_gui_settings.orientation + WID_BROS_STATION_NE);
|
||||
this->SetDirty();
|
||||
CloseWindowById(WC_SELECT_STATION, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
BuildRoadStationWindow(WindowDesc *desc, Window *parent, RoadStopType rs) : PickerWindowBase(desc, parent), filter_editbox(EDITBOX_MAX_SIZE * MAX_CHAR_LENGTH, EDITBOX_MAX_SIZE)
|
||||
{
|
||||
this->coverage_height = 2 * FONT_HEIGHT_NORMAL + 3 * WidgetDimensions::scaled.vsep_normal;
|
||||
this->vscrollList = nullptr;
|
||||
this->vscrollMatrix = nullptr;
|
||||
this->roadStopType = rs;
|
||||
bool newstops = GetIfNewStopsByType(rs, _cur_roadtype);
|
||||
|
||||
this->CreateNestedTree();
|
||||
|
||||
/* Hide the station class filter if no stations other than the default one are available. */
|
||||
this->GetWidget<NWidgetStacked>(WID_BROS_SHOW_NEWST_DEFSIZE)->SetDisplayedPlane(newstops ? 0 : SZSP_NONE);
|
||||
this->GetWidget<NWidgetStacked>(WID_BROS_FILTER_CONTAINER)->SetDisplayedPlane(newstops ? 0 : SZSP_HORIZONTAL);
|
||||
this->GetWidget<NWidgetStacked>(WID_BROS_SHOW_NEWST_ADDITIONS)->SetDisplayedPlane(newstops ? 0 : SZSP_HORIZONTAL);
|
||||
this->GetWidget<NWidgetStacked>(WID_BROS_SHOW_NEWST_ORIENTATION)->SetDisplayedPlane(newstops ? 0 : SZSP_HORIZONTAL);
|
||||
this->GetWidget<NWidgetStacked>(WID_BROS_SHOW_NEWST_TYPE_SEL)->SetDisplayedPlane(newstops ? 0 : SZSP_HORIZONTAL);
|
||||
this->GetWidget<NWidgetStacked>(WID_BROS_SHOW_NEWST_MATRIX)->SetDisplayedPlane(newstops ? 0 : SZSP_NONE);
|
||||
this->GetWidget<NWidgetStacked>(WID_BROS_SHOW_NEWST_RESIZE)->SetDisplayedPlane(newstops ? 0 : SZSP_NONE);
|
||||
if (newstops) {
|
||||
this->vscrollList = this->GetScrollbar(WID_BROS_NEWST_SCROLL);
|
||||
this->vscrollMatrix = this->GetScrollbar(WID_BROS_MATRIX_SCROLL);
|
||||
|
||||
this->querystrings[WID_BROS_FILTER_EDITBOX] = &this->filter_editbox;
|
||||
this->roadstop_classes.SetListing(this->last_sorting);
|
||||
this->roadstop_classes.SetFiltering(this->last_filtering);
|
||||
this->roadstop_classes.SetSortFuncs(this->sorter_funcs);
|
||||
this->roadstop_classes.SetFilterFuncs(this->filter_funcs);
|
||||
}
|
||||
|
||||
this->roadstop_classes.ForceRebuild();
|
||||
BuildRoadStopClassesAvailable();
|
||||
|
||||
/* Trams don't have non-drivethrough stations */
|
||||
if (RoadTypeIsTram(_cur_roadtype) && _road_station_picker_orientation < DIAGDIR_END) {
|
||||
_road_station_picker_orientation = DIAGDIR_END;
|
||||
if (RoadTypeIsTram(_cur_roadtype) && _roadstop_gui_settings.orientation < DIAGDIR_END) {
|
||||
_roadstop_gui_settings.orientation = DIAGDIR_END;
|
||||
}
|
||||
const RoadTypeInfo *rti = GetRoadTypeInfo(_cur_roadtype);
|
||||
this->GetWidget<NWidgetCore>(WID_BROS_CAPTION)->widget_data = rti->strings.picker_title[rs];
|
||||
@ -1062,12 +1195,42 @@ struct BuildRoadStationWindow : public PickerWindowBase {
|
||||
this->GetWidget<NWidgetCore>(i)->tool_tip = rti->strings.picker_tooltip[rs];
|
||||
}
|
||||
|
||||
this->LowerWidget(_road_station_picker_orientation + WID_BROS_STATION_NE);
|
||||
this->LowerWidget(_roadstop_gui_settings.orientation + WID_BROS_STATION_NE);
|
||||
this->LowerWidget(_settings_client.gui.station_show_coverage + WID_BROS_LT_OFF);
|
||||
|
||||
this->FinishInitNested(TRANSPORT_ROAD);
|
||||
|
||||
this->window_class = (rs == ROADSTOP_BUS) ? WC_BUS_STATION : WC_TRUCK_STATION;
|
||||
if (!newstops || _roadstop_gui_settings.roadstop_class >= (int)RoadStopClass::GetClassCount()) {
|
||||
/* There's no new stops available or the list has reduced in size.
|
||||
* Now, set the default road stops as selected. */
|
||||
_roadstop_gui_settings.roadstop_class = ROADSTOP_CLASS_DFLT;
|
||||
_roadstop_gui_settings.roadstop_type = 0;
|
||||
}
|
||||
if (newstops) {
|
||||
/* The currently selected class doesn't have any stops for this RoadStopType, reset the selection. */
|
||||
if (!GetIfClassHasNewStopsByType(RoadStopClass::Get(_roadstop_gui_settings.roadstop_class), rs, _cur_roadtype)) {
|
||||
_roadstop_gui_settings.roadstop_class = ROADSTOP_CLASS_DFLT;
|
||||
_roadstop_gui_settings.roadstop_type = 0;
|
||||
}
|
||||
_roadstop_gui_settings.roadstop_count = RoadStopClass::Get(_roadstop_gui_settings.roadstop_class)->GetSpecCount();
|
||||
_roadstop_gui_settings.roadstop_type = std::min((int)_roadstop_gui_settings.roadstop_type, _roadstop_gui_settings.roadstop_count - 1);
|
||||
|
||||
/* Reset back to default class if the previously selected class is not available for this road stop type. */
|
||||
if (!GetIfClassHasNewStopsByType(RoadStopClass::Get(_roadstop_gui_settings.roadstop_class), roadStopType, _cur_roadtype)) {
|
||||
_roadstop_gui_settings.roadstop_class = ROADSTOP_CLASS_DFLT;
|
||||
}
|
||||
|
||||
this->SelectFirstAvailableTypeIfUnavailable();
|
||||
|
||||
NWidgetMatrix *matrix = this->GetWidget<NWidgetMatrix>(WID_BROS_MATRIX);
|
||||
matrix->SetScrollbar(this->vscrollMatrix);
|
||||
matrix->SetCount(_roadstop_gui_settings.roadstop_count);
|
||||
matrix->SetClicked(_roadstop_gui_settings.roadstop_type);
|
||||
|
||||
this->EnsureSelectedClassIsVisible();
|
||||
this->CheckOrientationValid();
|
||||
}
|
||||
}
|
||||
|
||||
void Close() override
|
||||
@ -1076,6 +1239,94 @@ struct BuildRoadStationWindow : public PickerWindowBase {
|
||||
this->PickerWindowBase::Close();
|
||||
}
|
||||
|
||||
/** Sort classes by RoadStopClassID. */
|
||||
static bool RoadStopClassIDSorter(RoadStopClassID const &a, RoadStopClassID const &b)
|
||||
{
|
||||
return a < b;
|
||||
}
|
||||
|
||||
/** Filter classes by class name. */
|
||||
static bool CDECL TagNameFilter(RoadStopClassID const *sc, StringFilter &filter)
|
||||
{
|
||||
char buffer[DRAW_STRING_BUFFER];
|
||||
GetString(buffer, RoadStopClass::Get(*sc)->name, lastof(buffer));
|
||||
|
||||
filter.ResetState();
|
||||
filter.AddLine(buffer);
|
||||
return filter.GetState();
|
||||
}
|
||||
|
||||
inline bool ShowNewStops() const
|
||||
{
|
||||
return this->vscrollList != nullptr;
|
||||
}
|
||||
|
||||
void BuildRoadStopClassesAvailable()
|
||||
{
|
||||
if (!this->roadstop_classes.NeedRebuild()) return;
|
||||
|
||||
this->roadstop_classes.clear();
|
||||
|
||||
for (uint i = 0; i < RoadStopClass::GetClassCount(); i++) {
|
||||
RoadStopClassID rs_id = (RoadStopClassID)i;
|
||||
if (rs_id == ROADSTOP_CLASS_WAYP) {
|
||||
// Skip waypoints.
|
||||
continue;
|
||||
}
|
||||
RoadStopClass *rs_class = RoadStopClass::Get(rs_id);
|
||||
if (GetIfClassHasNewStopsByType(rs_class, this->roadStopType, _cur_roadtype)) this->roadstop_classes.push_back(rs_id);
|
||||
}
|
||||
|
||||
if (this->ShowNewStops()) {
|
||||
this->roadstop_classes.Filter(this->string_filter);
|
||||
this->roadstop_classes.shrink_to_fit();
|
||||
this->roadstop_classes.RebuildDone();
|
||||
this->roadstop_classes.Sort();
|
||||
|
||||
this->vscrollList->SetCount((uint)this->roadstop_classes.size());
|
||||
}
|
||||
}
|
||||
|
||||
void SelectFirstAvailableTypeIfUnavailable()
|
||||
{
|
||||
const RoadStopClass *rs_class = RoadStopClass::Get(_roadstop_gui_settings.roadstop_class);
|
||||
StationType st = GetRoadStationTypeByWindowClass(this->window_class);
|
||||
|
||||
if (IsRoadStopAvailable(rs_class->GetSpec(_roadstop_gui_settings.roadstop_type), st)) return;
|
||||
for (uint i = 0; i < _roadstop_gui_settings.roadstop_count; i++) {
|
||||
if (IsRoadStopAvailable(rs_class->GetSpec(i), st)) {
|
||||
_roadstop_gui_settings.roadstop_type = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnInvalidateData(int data = 0, bool gui_scope = true) override
|
||||
{
|
||||
if (!gui_scope) return;
|
||||
|
||||
this->BuildRoadStopClassesAvailable();
|
||||
}
|
||||
|
||||
EventState OnHotkey(int hotkey) override
|
||||
{
|
||||
if (hotkey == BROSHK_FOCUS_FILTER_BOX) {
|
||||
this->SetFocusedWidget(WID_BROS_FILTER_EDITBOX);
|
||||
SetFocusedWindow(this); // The user has asked to give focus to the text box, so make sure this window is focused.
|
||||
return ES_HANDLED;
|
||||
}
|
||||
|
||||
return ES_NOT_HANDLED;
|
||||
}
|
||||
|
||||
void OnEditboxChanged(int wid) override
|
||||
{
|
||||
string_filter.SetFilterTerm(this->filter_editbox.text.buf);
|
||||
this->roadstop_classes.SetFilterState(!string_filter.IsEmpty());
|
||||
this->roadstop_classes.ForceRebuild();
|
||||
this->InvalidateData();
|
||||
}
|
||||
|
||||
void OnPaint() override
|
||||
{
|
||||
this->DrawWidgets();
|
||||
@ -1087,6 +1338,8 @@ struct BuildRoadStationWindow : public PickerWindowBase {
|
||||
SetTileSelectSize(1, 1);
|
||||
}
|
||||
|
||||
if (this->IsShaded()) return;
|
||||
|
||||
/* 'Accepts' and 'Supplies' texts. */
|
||||
StationCoverageType sct = (this->window_class == WC_BUS_STATION) ? SCT_PASSENGERS_ONLY : SCT_NON_PASSENGERS_ONLY;
|
||||
Rect r = this->GetWidget<NWidgetBase>(WID_BROS_ACCEPTANCE)->GetCurrentRect();
|
||||
@ -1097,45 +1350,175 @@ struct BuildRoadStationWindow : public PickerWindowBase {
|
||||
* Never make the window smaller to avoid oscillating if the size change affects the acceptance.
|
||||
* (This is the case, if making the window bigger moves the mouse into the window.) */
|
||||
if (top > r.bottom) {
|
||||
ResizeWindow(this, 0, top - r.bottom, false);
|
||||
this->coverage_height += top - r.bottom;
|
||||
this->ReInit();
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
|
||||
{
|
||||
if (!IsInsideMM(widget, WID_BROS_STATION_NE, WID_BROS_STATION_Y + 1)) return;
|
||||
|
||||
size->width = ScaleGUITrad(64) + WidgetDimensions::scaled.fullbevel.Horizontal();
|
||||
size->height = ScaleGUITrad(48) + WidgetDimensions::scaled.fullbevel.Vertical();
|
||||
}
|
||||
|
||||
void DrawWidget(const Rect &r, int widget) const override
|
||||
{
|
||||
if (!IsInsideMM(widget, WID_BROS_STATION_NE, WID_BROS_STATION_Y + 1)) return;
|
||||
|
||||
StationType st = (this->window_class == WC_BUS_STATION) ? STATION_BUS : STATION_TRUCK;
|
||||
|
||||
DrawPixelInfo tmp_dpi;
|
||||
if (FillDrawPixelInfo(&tmp_dpi, r.left, r.top, r.Width(), r.Height())) {
|
||||
AutoRestoreBackup dpi_backup(_cur_dpi, &tmp_dpi);
|
||||
int x = (r.Width() - ScaleSpriteTrad(64)) / 2 + ScaleSpriteTrad(31);
|
||||
int y = (r.Height() + ScaleSpriteTrad(48)) / 2 - ScaleSpriteTrad(31);
|
||||
StationPickerDrawSprite(x, y, st, INVALID_RAILTYPE, _cur_roadtype, widget - WID_BROS_STATION_NE);
|
||||
}
|
||||
}
|
||||
|
||||
void OnClick(Point pt, int widget, int click_count) override
|
||||
{
|
||||
switch (widget) {
|
||||
case WID_BROS_NEWST_LIST: {
|
||||
Dimension d = { 0, 0 };
|
||||
for (auto rs_class : this->roadstop_classes) {
|
||||
d = maxdim(d, GetStringBoundingBox(RoadStopClass::Get(rs_class)->name));
|
||||
}
|
||||
size->width = std::max(size->width, d.width + padding.width);
|
||||
this->line_height = FONT_HEIGHT_NORMAL + WidgetDimensions::scaled.matrix.Vertical();
|
||||
size->height = 5 * this->line_height;
|
||||
resize->height = this->line_height;
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_BROS_SHOW_NEWST_TYPE: {
|
||||
Dimension d = {0, 0};
|
||||
StringID str = this->GetWidget<NWidgetCore>(widget)->widget_data;
|
||||
for (auto roadstop_class : this->roadstop_classes) {
|
||||
RoadStopClass *rs_class = RoadStopClass::Get(roadstop_class);
|
||||
for (uint j = 0; j < rs_class->GetSpecCount(); j++) {
|
||||
const RoadStopSpec *roadstopspec = rs_class->GetSpec(j);
|
||||
SetDParam(0, (roadstopspec != nullptr && roadstopspec->name != 0) ? roadstopspec->name : STR_STATION_CLASS_DFLT);
|
||||
d = maxdim(d, GetStringBoundingBox(str));
|
||||
}
|
||||
}
|
||||
size->width = std::max(size->width, d.width + padding.width);
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_BROS_STATION_NE:
|
||||
case WID_BROS_STATION_SE:
|
||||
case WID_BROS_STATION_SW:
|
||||
case WID_BROS_STATION_NW:
|
||||
case WID_BROS_STATION_X:
|
||||
case WID_BROS_STATION_Y:
|
||||
this->RaiseWidget(_road_station_picker_orientation + WID_BROS_STATION_NE);
|
||||
_road_station_picker_orientation = (DiagDirection)(widget - WID_BROS_STATION_NE);
|
||||
this->LowerWidget(_road_station_picker_orientation + WID_BROS_STATION_NE);
|
||||
case WID_BROS_IMAGE:
|
||||
size->width = ScaleGUITrad(64) + WidgetDimensions::scaled.fullbevel.Horizontal();
|
||||
size->height = ScaleGUITrad(48) + WidgetDimensions::scaled.fullbevel.Vertical();
|
||||
break;
|
||||
|
||||
case WID_BROS_MATRIX:
|
||||
fill->height = 1;
|
||||
resize->height = 1;
|
||||
break;
|
||||
|
||||
case WID_BROS_ACCEPTANCE:
|
||||
size->height = this->coverage_height;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simply to have a easier way to get the StationType for bus, truck and trams from the WindowClass.
|
||||
*/
|
||||
StationType GetRoadStationTypeByWindowClass(WindowClass window_class) const {
|
||||
switch (window_class) {
|
||||
case WC_BUS_STATION: return STATION_BUS;
|
||||
case WC_TRUCK_STATION: return STATION_TRUCK;
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawWidget(const Rect &r, int widget) const override
|
||||
{
|
||||
switch (GB(widget, 0, 16)) {
|
||||
case WID_BROS_STATION_NE:
|
||||
case WID_BROS_STATION_SE:
|
||||
case WID_BROS_STATION_SW:
|
||||
case WID_BROS_STATION_NW:
|
||||
case WID_BROS_STATION_X:
|
||||
case WID_BROS_STATION_Y: {
|
||||
StationType st = GetRoadStationTypeByWindowClass(this->window_class);
|
||||
const RoadStopSpec *spec = RoadStopClass::Get(_roadstop_gui_settings.roadstop_class)->GetSpec(_roadstop_gui_settings.roadstop_type);
|
||||
bool disabled = (spec != nullptr && widget < WID_BROS_STATION_X && HasBit(spec->flags, RSF_DRIVE_THROUGH_ONLY));
|
||||
DrawPixelInfo tmp_dpi;
|
||||
if (FillDrawPixelInfo(&tmp_dpi, r.left, r.top, r.Width(), r.Height())) {
|
||||
AutoRestoreBackup dpi_backup(_cur_dpi, &tmp_dpi);
|
||||
int x = (r.Width() - ScaleSpriteTrad(64)) / 2 + ScaleSpriteTrad(31);
|
||||
int y = (r.Height() + ScaleSpriteTrad(48)) / 2 - ScaleSpriteTrad(31);
|
||||
if (spec == nullptr || disabled) {
|
||||
StationPickerDrawSprite(x, y, st, INVALID_RAILTYPE, _cur_roadtype, widget - WID_BROS_STATION_NE);
|
||||
if (disabled) GfxFillRect(1, 1, r.Width() - 1, r.Height() - 1, PC_BLACK, FILLRECT_CHECKER);
|
||||
} else {
|
||||
DrawRoadStopTile(x, y, _cur_roadtype, spec, st, widget - WID_BROS_STATION_NE);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_BROS_NEWST_LIST: {
|
||||
uint statclass = 0;
|
||||
uint row = 0;
|
||||
for (auto rs_class : this->roadstop_classes) {
|
||||
if (this->vscrollList->IsVisible(statclass)) {
|
||||
DrawString(r.left + WidgetDimensions::scaled.matrix.left, r.right, row * this->line_height + r.top + WidgetDimensions::scaled.matrix.top,
|
||||
RoadStopClass::Get(rs_class)->name,
|
||||
rs_class == _roadstop_gui_settings.roadstop_class ? TC_WHITE : TC_BLACK);
|
||||
row++;
|
||||
}
|
||||
statclass++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_BROS_IMAGE: {
|
||||
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.Shrink(WidgetDimensions::scaled.bevel), PC_BLACK, FILLRECT_CHECKER);
|
||||
}
|
||||
|
||||
/* Set up a clipping area for the sprite preview. */
|
||||
DrawPixelInfo tmp_dpi;
|
||||
if (FillDrawPixelInfo(&tmp_dpi, r.left, r.top, r.Width(), r.Height())) {
|
||||
AutoRestoreBackup dpi_backup(_cur_dpi, &tmp_dpi);
|
||||
int x = (r.Width() - ScaleSpriteTrad(64)) / 2 + ScaleSpriteTrad(31);
|
||||
int y = (r.Height() + ScaleSpriteTrad(48)) / 2 - ScaleSpriteTrad(31);
|
||||
if (spec == nullptr) {
|
||||
StationPickerDrawSprite(x, y, st, INVALID_RAILTYPE, _cur_roadtype, _roadstop_gui_settings.orientation);
|
||||
} else {
|
||||
DiagDirection orientation = _roadstop_gui_settings.orientation;
|
||||
if (orientation < DIAGDIR_END && HasBit(spec->flags, RSF_DRIVE_THROUGH_ONLY)) orientation = DIAGDIR_END;
|
||||
DrawRoadStopTile(x, y, _cur_roadtype, spec, st, (uint8)orientation);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnResize() override {
|
||||
if (this->vscrollList != nullptr) {
|
||||
this->vscrollList->SetCapacityFromWidget(this, WID_BROS_NEWST_LIST);
|
||||
}
|
||||
}
|
||||
|
||||
void SetStringParameters(int widget) const override {
|
||||
if (widget == WID_BROS_SHOW_NEWST_TYPE) {
|
||||
const RoadStopSpec *roadstopspec = RoadStopClass::Get(_roadstop_gui_settings.roadstop_class)->GetSpec(_roadstop_gui_settings.roadstop_type);
|
||||
SetDParam(0, (roadstopspec != nullptr && roadstopspec->name != 0) ? roadstopspec->name : STR_STATION_CLASS_DFLT);
|
||||
}
|
||||
}
|
||||
|
||||
void OnClick(Point pt, int widget, int click_count) override
|
||||
{
|
||||
switch (GB(widget, 0, 16)) {
|
||||
case WID_BROS_STATION_NE:
|
||||
case WID_BROS_STATION_SE:
|
||||
case WID_BROS_STATION_SW:
|
||||
case WID_BROS_STATION_NW:
|
||||
case WID_BROS_STATION_X:
|
||||
case WID_BROS_STATION_Y:
|
||||
if (widget < WID_BROS_STATION_X) {
|
||||
const RoadStopSpec *spec = RoadStopClass::Get(_roadstop_gui_settings.roadstop_class)->GetSpec(_roadstop_gui_settings.roadstop_type);
|
||||
if (spec != nullptr && HasBit(spec->flags, RSF_DRIVE_THROUGH_ONLY)) return;
|
||||
}
|
||||
this->RaiseWidget(_roadstop_gui_settings.orientation + WID_BROS_STATION_NE);
|
||||
_roadstop_gui_settings.orientation = (DiagDirection)(widget - WID_BROS_STATION_NE);
|
||||
this->LowerWidget(_roadstop_gui_settings.orientation + WID_BROS_STATION_NE);
|
||||
if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
|
||||
this->SetDirty();
|
||||
CloseWindowById(WC_SELECT_STATION, 0);
|
||||
@ -1151,6 +1534,48 @@ struct BuildRoadStationWindow : public PickerWindowBase {
|
||||
SetViewportCatchmentStation(nullptr, true);
|
||||
break;
|
||||
|
||||
case WID_BROS_NEWST_LIST: {
|
||||
int y = this->vscrollList->GetScrolledRowFromWidget(pt.y, this, WID_BROS_NEWST_LIST);
|
||||
if (y >= (int)this->roadstop_classes.size()) return;
|
||||
RoadStopClassID class_id = this->roadstop_classes[y];
|
||||
if (_roadstop_gui_settings.roadstop_class != class_id && GetIfClassHasNewStopsByType(RoadStopClass::Get(class_id), roadStopType, _cur_roadtype)) {
|
||||
_roadstop_gui_settings.roadstop_class = class_id;
|
||||
RoadStopClass *rsclass = RoadStopClass::Get(_roadstop_gui_settings.roadstop_class);
|
||||
_roadstop_gui_settings.roadstop_count = rsclass->GetSpecCount();
|
||||
_roadstop_gui_settings.roadstop_type = std::min((int)_roadstop_gui_settings.roadstop_type, std::max(0, (int)_roadstop_gui_settings.roadstop_count - 1));
|
||||
this->SelectFirstAvailableTypeIfUnavailable();
|
||||
|
||||
NWidgetMatrix *matrix = this->GetWidget<NWidgetMatrix>(WID_BROS_MATRIX);
|
||||
matrix->SetCount(_roadstop_gui_settings.roadstop_count);
|
||||
matrix->SetClicked(_roadstop_gui_settings.roadstop_type);
|
||||
this->CheckOrientationValid();
|
||||
}
|
||||
if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
|
||||
this->SetDirty();
|
||||
CloseWindowById(WC_SELECT_STATION, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_BROS_IMAGE: {
|
||||
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;
|
||||
|
||||
_roadstop_gui_settings.roadstop_type = y;
|
||||
|
||||
this->GetWidget<NWidgetMatrix>(WID_BROS_MATRIX)->SetClicked(_roadstop_gui_settings.roadstop_type);
|
||||
|
||||
if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
|
||||
this->SetDirty();
|
||||
CloseWindowById(WC_SELECT_STATION, 0);
|
||||
this->CheckOrientationValid();
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -1160,6 +1585,25 @@ struct BuildRoadStationWindow : public PickerWindowBase {
|
||||
{
|
||||
CheckRedrawStationCoverage(this);
|
||||
}
|
||||
|
||||
static HotkeyList hotkeys;
|
||||
};
|
||||
|
||||
static Hotkey buildroadstop_hotkeys[] = {
|
||||
Hotkey('F', "focus_filter_box", BROSHK_FOCUS_FILTER_BOX),
|
||||
HOTKEY_LIST_END
|
||||
};
|
||||
HotkeyList BuildRoadStationWindow::hotkeys("buildroadstop", buildroadstop_hotkeys);
|
||||
|
||||
Listing BuildRoadStationWindow::last_sorting = { false, 0 };
|
||||
Filtering BuildRoadStationWindow::last_filtering = { false, 0 };
|
||||
|
||||
BuildRoadStationWindow::GUIRoadStopClassList::SortFunction * const BuildRoadStationWindow::sorter_funcs[] = {
|
||||
&RoadStopClassIDSorter,
|
||||
};
|
||||
|
||||
BuildRoadStationWindow::GUIRoadStopClassList::FilterFunction * const BuildRoadStationWindow::filter_funcs[] = {
|
||||
&TagNameFilter,
|
||||
};
|
||||
|
||||
/** Widget definition of the build road station window */
|
||||
@ -1167,8 +1611,31 @@ static const NWidgetPart _nested_road_station_picker_widgets[] = {
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
|
||||
NWidget(WWT_CAPTION, COLOUR_DARK_GREEN, WID_BROS_CAPTION),
|
||||
NWidget(WWT_SHADEBOX, COLOUR_DARK_GREEN),
|
||||
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_DEFSIZE),
|
||||
NWidget(WWT_DEFSIZEBOX, COLOUR_DARK_GREEN),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BROS_BACKGROUND),
|
||||
NWidget(NWID_HORIZONTAL), SetPadding(2, 0, 0, 2),
|
||||
NWidget(NWID_VERTICAL),
|
||||
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_FILTER_CONTAINER),
|
||||
NWidget(NWID_HORIZONTAL), SetPadding(0, 5, 2, 0),
|
||||
NWidget(WWT_TEXT, COLOUR_DARK_GREEN), SetFill(0, 1), SetDataTip(STR_LIST_FILTER_TITLE, STR_NULL),
|
||||
NWidget(WWT_EDITBOX, COLOUR_GREY, WID_BROS_FILTER_EDITBOX), SetFill(1, 0), SetResize(1, 0),
|
||||
SetDataTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_ADDITIONS),
|
||||
NWidget(NWID_HORIZONTAL), SetPadding(0, 5, 2, 0),
|
||||
NWidget(WWT_MATRIX, COLOUR_GREY, WID_BROS_NEWST_LIST), SetMinimalSize(122, 71), SetFill(1, 0),
|
||||
SetMatrixDataTip(1, 0, STR_STATION_BUILD_STATION_CLASS_TOOLTIP), SetScrollbar(WID_BROS_NEWST_SCROLL),
|
||||
NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_BROS_NEWST_SCROLL),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_ORIENTATION),
|
||||
NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetMinimalSize(144, 11), SetDataTip(STR_STATION_BUILD_ORIENTATION, STR_NULL), SetPadding(4, 2, 1, 2), SetFill(1, 0),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL), SetPadding(3),
|
||||
NWidget(NWID_SPACER), SetFill(1, 0),
|
||||
NWidget(NWID_VERTICAL), SetPIP(0, 2, 0),
|
||||
@ -1185,16 +1652,41 @@ static const NWidgetPart _nested_road_station_picker_widgets[] = {
|
||||
EndContainer(),
|
||||
NWidget(NWID_SPACER), SetFill(1, 0),
|
||||
EndContainer(),
|
||||
NWidget(WWT_LABEL, COLOUR_DARK_GREEN, WID_BROS_INFO), SetPadding(WidgetDimensions::unscaled.framerect), SetDataTip(STR_STATION_BUILD_COVERAGE_AREA_TITLE, STR_NULL), SetFill(1, 0),
|
||||
NWidget(NWID_HORIZONTAL), SetPadding(3),
|
||||
NWidget(NWID_SPACER), SetFill(1, 0),
|
||||
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_TYPE_SEL),
|
||||
NWidget(WWT_LABEL, COLOUR_DARK_GREEN, WID_BROS_SHOW_NEWST_TYPE), SetMinimalSize(144, 8), SetDataTip(STR_ORANGE_STRING, STR_NULL), SetPadding(4, 2, 4, 2), SetFill(1, 0),
|
||||
EndContainer(),
|
||||
NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetDataTip(STR_STATION_BUILD_COVERAGE_AREA_TITLE, STR_NULL), SetPadding(WidgetDimensions::unscaled.framerect), SetFill(1, 0),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(NWID_SPACER), SetMinimalSize(2, 0), SetFill(1, 0),
|
||||
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BROS_LT_OFF), SetMinimalSize(60, 12),
|
||||
SetDataTip(STR_STATION_BUILD_COVERAGE_OFF, STR_STATION_BUILD_COVERAGE_AREA_OFF_TOOLTIP),
|
||||
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BROS_LT_ON), SetMinimalSize(60, 12),
|
||||
SetDataTip(STR_STATION_BUILD_COVERAGE_ON, STR_STATION_BUILD_COVERAGE_AREA_ON_TOOLTIP),
|
||||
NWidget(NWID_SPACER), SetFill(1, 0),
|
||||
NWidget(NWID_SPACER), SetMinimalSize(2, 0), SetFill(1, 0),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_MATRIX),
|
||||
NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetScrollbar(WID_BROS_MATRIX_SCROLL),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(NWID_MATRIX, COLOUR_DARK_GREEN, WID_BROS_MATRIX), SetScrollbar(WID_BROS_MATRIX_SCROLL), SetPIP(0, 2, 0),
|
||||
NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BROS_IMAGE), SetMinimalSize(66, 60),
|
||||
SetFill(0, 0), SetResize(0, 0), SetDataTip(0x0, STR_STATION_BUILD_STATION_TYPE_TOOLTIP), SetScrollbar(WID_BROS_MATRIX_SCROLL),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_VSCROLLBAR, COLOUR_DARK_GREEN, WID_BROS_MATRIX_SCROLL),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_EMPTY, INVALID_COLOUR, WID_BROS_ACCEPTANCE), SetPadding(WidgetDimensions::unscaled.framerect), SetFill(1, 1), SetResize(1, 0),
|
||||
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_RESIZE),
|
||||
NWidget(NWID_VERTICAL),
|
||||
NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetFill(0, 1), EndContainer(),
|
||||
NWidget(WWT_RESIZEBOX, COLOUR_DARK_GREEN),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(WWT_EMPTY, COLOUR_DARK_GREEN, WID_BROS_ACCEPTANCE), SetPadding(WidgetDimensions::unscaled.framerect), SetResize(0, 1),
|
||||
EndContainer(),
|
||||
};
|
||||
|
||||
@ -1210,8 +1702,31 @@ static const NWidgetPart _nested_tram_station_picker_widgets[] = {
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
|
||||
NWidget(WWT_CAPTION, COLOUR_DARK_GREEN, WID_BROS_CAPTION),
|
||||
NWidget(WWT_SHADEBOX, COLOUR_DARK_GREEN),
|
||||
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_DEFSIZE),
|
||||
NWidget(WWT_DEFSIZEBOX, COLOUR_DARK_GREEN),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BROS_BACKGROUND),
|
||||
NWidget(NWID_HORIZONTAL), SetPadding(2, 0, 0, 2),
|
||||
NWidget(NWID_VERTICAL),
|
||||
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_FILTER_CONTAINER),
|
||||
NWidget(NWID_HORIZONTAL), SetPadding(0, 5, 2, 0),
|
||||
NWidget(WWT_TEXT, COLOUR_DARK_GREEN), SetFill(0, 1), SetDataTip(STR_LIST_FILTER_TITLE, STR_NULL),
|
||||
NWidget(WWT_EDITBOX, COLOUR_GREY, WID_BROS_FILTER_EDITBOX), SetFill(1, 0), SetResize(1, 0),
|
||||
SetDataTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_ADDITIONS),
|
||||
NWidget(NWID_HORIZONTAL), SetPadding(0, 5, 2, 0),
|
||||
NWidget(WWT_MATRIX, COLOUR_GREY, WID_BROS_NEWST_LIST), SetMinimalSize(122, 71), SetFill(1, 0),
|
||||
SetMatrixDataTip(1, 0, STR_STATION_BUILD_STATION_CLASS_TOOLTIP), SetScrollbar(WID_BROS_NEWST_SCROLL),
|
||||
NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_BROS_NEWST_SCROLL),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_ORIENTATION),
|
||||
NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetMinimalSize(144, 11), SetDataTip(STR_STATION_BUILD_ORIENTATION, STR_NULL), SetPadding(4, 2, 1, 2), SetFill(1, 0),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL), SetPadding(3),
|
||||
NWidget(NWID_SPACER), SetFill(1, 0),
|
||||
NWidget(NWID_VERTICAL), SetPIP(0, 2, 0),
|
||||
@ -1222,16 +1737,41 @@ static const NWidgetPart _nested_tram_station_picker_widgets[] = {
|
||||
EndContainer(),
|
||||
NWidget(NWID_SPACER), SetFill(1, 0),
|
||||
EndContainer(),
|
||||
NWidget(WWT_LABEL, COLOUR_DARK_GREEN, WID_BROS_INFO), SetPadding(WidgetDimensions::unscaled.framerect), SetMinimalSize(140, 14), SetDataTip(STR_STATION_BUILD_COVERAGE_AREA_TITLE, STR_NULL), SetFill(1, 0),
|
||||
NWidget(NWID_HORIZONTAL), SetPadding(3),
|
||||
NWidget(NWID_SPACER), SetFill(1, 0),
|
||||
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_TYPE_SEL),
|
||||
NWidget(WWT_LABEL, COLOUR_DARK_GREEN, WID_BROS_SHOW_NEWST_TYPE), SetMinimalSize(144, 8), SetDataTip(STR_ORANGE_STRING, STR_NULL), SetPadding(4, 2, 4, 2), SetFill(1, 0),
|
||||
EndContainer(),
|
||||
NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetDataTip(STR_STATION_BUILD_COVERAGE_AREA_TITLE, STR_NULL), SetPadding(WidgetDimensions::unscaled.framerect), SetFill(1, 0),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(NWID_SPACER), SetMinimalSize(2, 0), SetFill(1, 0),
|
||||
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BROS_LT_OFF), SetMinimalSize(60, 12),
|
||||
SetDataTip(STR_STATION_BUILD_COVERAGE_OFF, STR_STATION_BUILD_COVERAGE_AREA_OFF_TOOLTIP),
|
||||
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BROS_LT_ON), SetMinimalSize(60, 12),
|
||||
SetDataTip(STR_STATION_BUILD_COVERAGE_ON, STR_STATION_BUILD_COVERAGE_AREA_ON_TOOLTIP),
|
||||
NWidget(NWID_SPACER), SetFill(1, 0),
|
||||
NWidget(NWID_SPACER), SetMinimalSize(2, 0), SetFill(1, 0),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_MATRIX),
|
||||
NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetScrollbar(WID_BROS_MATRIX_SCROLL),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(NWID_MATRIX, COLOUR_DARK_GREEN, WID_BROS_MATRIX), SetScrollbar(WID_BROS_MATRIX_SCROLL), SetPIP(0, 2, 0), SetPadding(2, 0, 0, 0),
|
||||
NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BROS_IMAGE), SetMinimalSize(66, 60),
|
||||
SetFill(0, 0), SetResize(0, 0), SetDataTip(0x0, STR_STATION_BUILD_STATION_TYPE_TOOLTIP), SetScrollbar(WID_BROS_MATRIX_SCROLL),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_VSCROLLBAR, COLOUR_DARK_GREEN, WID_BROS_MATRIX_SCROLL),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_EMPTY, INVALID_COLOUR, WID_BROS_ACCEPTANCE), SetPadding(WidgetDimensions::unscaled.framerect), SetFill(1, 1), SetResize(1, 0),
|
||||
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_RESIZE),
|
||||
NWidget(NWID_VERTICAL),
|
||||
NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetFill(0, 1), EndContainer(),
|
||||
NWidget(WWT_RESIZEBOX, COLOUR_DARK_GREEN),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(WWT_EMPTY, COLOUR_DARK_GREEN, WID_BROS_ACCEPTANCE), SetPadding(WidgetDimensions::unscaled.framerect), SetResize(0, 1),
|
||||
EndContainer(),
|
||||
};
|
||||
|
||||
@ -1250,7 +1790,7 @@ static void ShowRVStationPicker(Window *parent, RoadStopType rs)
|
||||
void InitializeRoadGui()
|
||||
{
|
||||
_road_depot_orientation = DIAGDIR_NW;
|
||||
_road_station_picker_orientation = DIAGDIR_NW;
|
||||
_roadstop_gui_settings.orientation = DIAGDIR_NW;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -32,6 +32,12 @@ const SaveLoadCompat _station_spec_list_sl_compat[] = {
|
||||
SLC_VAR("localidx"),
|
||||
};
|
||||
|
||||
/** Nominal field order for SlRoadStopSpecList. */
|
||||
const SaveLoadCompat _station_road_stop_spec_list_sl_compat[] = {
|
||||
SLC_VAR("grfid"),
|
||||
SLC_VAR("localidx"),
|
||||
};
|
||||
|
||||
/** Original field order for SlStationCargo. */
|
||||
const SaveLoadCompat _station_cargo_sl_compat[] = {
|
||||
SLC_VAR("first"),
|
||||
|
@ -343,6 +343,7 @@ enum SaveLoadVersion : uint16 {
|
||||
SLV_U64_TICK_COUNTER, ///< 300 PR#10035 Make _tick_counter 64bit to avoid wrapping.
|
||||
SLV_LAST_LOADING_TICK, ///< 301 PR#9693 Store tick of last loading for vehicles.
|
||||
SLV_MULTITRACK_LEVEL_CROSSINGS, ///< 302 PR#9931 v13.0 Multi-track level crossings.
|
||||
SLV_NEWGRF_ROAD_STOPS, ///< 303 PR#10144 NewGRF road stops.
|
||||
|
||||
SL_MAX_VERSION, ///< Highest possible saveload version
|
||||
};
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "../roadstop_base.h"
|
||||
#include "../vehicle_base.h"
|
||||
#include "../newgrf_station.h"
|
||||
#include "../newgrf_roadstop.h"
|
||||
|
||||
#include "table/strings.h"
|
||||
|
||||
@ -114,6 +115,11 @@ void AfterLoadStations()
|
||||
|
||||
st->speclist[i].spec = StationClass::GetByGrf(st->speclist[i].grfid, st->speclist[i].localidx, nullptr);
|
||||
}
|
||||
for (uint i = 0; i < st->roadstop_speclist.size(); i++) {
|
||||
if (st->roadstop_speclist[i].grfid == 0) continue;
|
||||
|
||||
st->roadstop_speclist[i].spec = RoadStopClass::GetByGrf(st->roadstop_speclist[i].grfid, st->roadstop_speclist[i].localidx, nullptr);
|
||||
}
|
||||
|
||||
if (Station::IsExpected(st)) {
|
||||
Station *sta = Station::From(st);
|
||||
@ -122,6 +128,7 @@ void AfterLoadStations()
|
||||
}
|
||||
|
||||
StationUpdateCachedTriggers(st);
|
||||
RoadStopUpdateCachedTriggers(st);
|
||||
}
|
||||
}
|
||||
|
||||
@ -224,6 +231,33 @@ public:
|
||||
|
||||
uint8 SlStationSpecList::last_num_specs;
|
||||
|
||||
class SlRoadStopSpecList : public DefaultSaveLoadHandler<SlRoadStopSpecList, BaseStation> {
|
||||
public:
|
||||
inline static const SaveLoad description[] = {
|
||||
SLE_VAR(RoadStopSpecList, grfid, SLE_UINT32),
|
||||
SLE_VAR(RoadStopSpecList, localidx, SLE_UINT8),
|
||||
};
|
||||
inline const static SaveLoadCompatTable compat_description = _station_road_stop_spec_list_sl_compat;
|
||||
|
||||
void Save(BaseStation *bst) const override
|
||||
{
|
||||
SlSetStructListLength(bst->roadstop_speclist.size());
|
||||
for (uint i = 0; i < bst->roadstop_speclist.size(); i++) {
|
||||
SlObject(&bst->roadstop_speclist[i], this->GetDescription());
|
||||
}
|
||||
}
|
||||
|
||||
void Load(BaseStation *bst) const override
|
||||
{
|
||||
uint8 num_specs = (uint8)SlGetStructListLength(UINT8_MAX);
|
||||
|
||||
bst->roadstop_speclist.resize(num_specs);
|
||||
for (uint i = 0; i < num_specs; i++) {
|
||||
SlObject(&bst->roadstop_speclist[i], this->GetLoadDescription());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class SlStationCargo : public DefaultSaveLoadHandler<SlStationCargo, GoodsEntry> {
|
||||
public:
|
||||
inline static const SaveLoad description[] = {
|
||||
@ -516,6 +550,35 @@ struct STNSChunkHandler : ChunkHandler {
|
||||
}
|
||||
};
|
||||
|
||||
class SlRoadStopTileData : public DefaultSaveLoadHandler<SlRoadStopTileData, BaseStation> {
|
||||
public:
|
||||
inline static const SaveLoad description[] = {
|
||||
SLE_VAR(RoadStopTileData, tile, SLE_UINT32),
|
||||
SLE_VAR(RoadStopTileData, random_bits, SLE_UINT8),
|
||||
SLE_VAR(RoadStopTileData, animation_frame, SLE_UINT8),
|
||||
};
|
||||
inline const static SaveLoadCompatTable compat_description = {};
|
||||
|
||||
static uint8 last_num_specs; ///< Number of specs of the last loaded station.
|
||||
|
||||
void Save(BaseStation *bst) const override
|
||||
{
|
||||
SlSetStructListLength(bst->custom_roadstop_tile_data.size());
|
||||
for (uint i = 0; i < bst->custom_roadstop_tile_data.size(); i++) {
|
||||
SlObject(&bst->custom_roadstop_tile_data[i], this->GetDescription());
|
||||
}
|
||||
}
|
||||
|
||||
void Load(BaseStation *bst) const override
|
||||
{
|
||||
uint32 num_tiles = (uint32)SlGetStructListLength(UINT32_MAX);
|
||||
bst->custom_roadstop_tile_data.resize(num_tiles);
|
||||
for (uint i = 0; i < num_tiles; i++) {
|
||||
SlObject(&bst->custom_roadstop_tile_data[i], this->GetLoadDescription());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* SaveLoad handler for the BaseStation, which all other stations / waypoints
|
||||
* make use of.
|
||||
@ -593,6 +656,7 @@ public:
|
||||
SLE_REFLIST(Station, loading_vehicles, REF_VEHICLE),
|
||||
SLE_CONDVAR(Station, always_accepted, SLE_FILE_U32 | SLE_VAR_U64, SLV_127, SLV_EXTEND_CARGOTYPES),
|
||||
SLE_CONDVAR(Station, always_accepted, SLE_UINT64, SLV_EXTEND_CARGOTYPES, SL_MAX_VERSION),
|
||||
SLEG_CONDSTRUCTLIST("speclist", SlRoadStopTileData, SLV_NEWGRF_ROAD_STOPS, SL_MAX_VERSION),
|
||||
SLEG_STRUCTLIST("goods", SlStationGoods),
|
||||
};
|
||||
inline const static SaveLoadCompatTable compat_description = _station_normal_sl_compat;
|
||||
@ -652,6 +716,7 @@ static const SaveLoad _station_desc[] = {
|
||||
SLEG_STRUCT("normal", SlStationNormal),
|
||||
SLEG_STRUCT("waypoint", SlStationWaypoint),
|
||||
SLEG_CONDSTRUCTLIST("speclist", SlStationSpecList, SLV_27, SL_MAX_VERSION),
|
||||
SLEG_CONDSTRUCTLIST("roadstopspeclist", SlRoadStopSpecList, SLV_NEWGRF_ROAD_STOPS, SL_MAX_VERSION),
|
||||
};
|
||||
|
||||
struct STNNChunkHandler : ChunkHandler {
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "../../landscape_cmd.h"
|
||||
#include "../../road_cmd.h"
|
||||
#include "../../station_cmd.h"
|
||||
#include "../../newgrf_roadstop.h"
|
||||
#include "../../script/squirrel_helper_type.hpp"
|
||||
|
||||
#include "../../safeguards.h"
|
||||
@ -549,7 +550,7 @@ static bool NeighbourHasReachableRoad(::RoadType rt, TileIndex start_tile, DiagD
|
||||
DiagDirection entrance_dir = DiagdirBetweenTiles(tile, front);
|
||||
RoadStopType stop_type = road_veh_type == ROADVEHTYPE_TRUCK ? ROADSTOP_TRUCK : ROADSTOP_BUS;
|
||||
StationID to_join = ScriptStation::IsValidStation(station_id) ? station_id : INVALID_STATION;
|
||||
return ScriptObject::Command<CMD_BUILD_ROAD_STOP>::Do(tile, 1, 1, stop_type, drive_through, entrance_dir, ScriptObject::GetRoadType(), to_join, station_id != ScriptStation::STATION_JOIN_ADJACENT);
|
||||
return ScriptObject::Command<CMD_BUILD_ROAD_STOP>::Do(tile, 1, 1, stop_type, drive_through, entrance_dir, ScriptObject::GetRoadType(), ROADSTOP_CLASS_DFLT, 0, to_join, station_id != ScriptStation::STATION_JOIN_ADJACENT);
|
||||
}
|
||||
|
||||
/* static */ bool ScriptRoad::BuildRoadStation(TileIndex tile, TileIndex front, RoadVehicleType road_veh_type, StationID station_id)
|
||||
|
@ -170,6 +170,36 @@ void BaseStation::PostDestructor(size_t index)
|
||||
InvalidateWindowData(WC_SELECT_STATION, 0, 0);
|
||||
}
|
||||
|
||||
void BaseStation::SetRoadStopTileData(TileIndex tile, byte data, bool animation)
|
||||
{
|
||||
for (RoadStopTileData &tile_data : this->custom_roadstop_tile_data) {
|
||||
if (tile_data.tile == tile) {
|
||||
if (animation) {
|
||||
tile_data.animation_frame = data;
|
||||
} else {
|
||||
tile_data.random_bits = data;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
RoadStopTileData tile_data;
|
||||
tile_data.tile = tile;
|
||||
tile_data.animation_frame = animation ? data : 0;
|
||||
tile_data.random_bits = animation ? 0 : data;
|
||||
this->custom_roadstop_tile_data.push_back(tile_data);
|
||||
}
|
||||
|
||||
void BaseStation::RemoveRoadStopTileData(TileIndex tile)
|
||||
{
|
||||
for (RoadStopTileData &tile_data : this->custom_roadstop_tile_data) {
|
||||
if (tile_data.tile == tile) {
|
||||
tile_data = this->custom_roadstop_tile_data.back();
|
||||
this->custom_roadstop_tile_data.pop_back();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the primary road stop (the first road stop) that the given vehicle can load/unload.
|
||||
* @param v the vehicle to get the first road stop for
|
||||
|
@ -521,6 +521,11 @@ public:
|
||||
return IsRailStationTile(tile) && GetStationIndex(tile) == this->index;
|
||||
}
|
||||
|
||||
inline bool TileBelongsToRoadStop(TileIndex tile) const
|
||||
{
|
||||
return IsRoadStopTile(tile) && GetStationIndex(tile) == this->index;
|
||||
}
|
||||
|
||||
inline bool TileBelongsToAirport(TileIndex tile) const
|
||||
{
|
||||
return IsAirportTile(tile) && GetStationIndex(tile) == this->index;
|
||||
|
@ -59,6 +59,7 @@
|
||||
#include "waypoint_cmd.h"
|
||||
#include "landscape_cmd.h"
|
||||
#include "rail_cmd.h"
|
||||
#include "newgrf_roadstop.h"
|
||||
|
||||
#include "table/strings.h"
|
||||
|
||||
@ -1794,7 +1795,7 @@ static RoadStop **FindRoadStopSpot(bool truck_station, Station *st)
|
||||
}
|
||||
}
|
||||
|
||||
static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlag flags);
|
||||
static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlag flags, int replacement_spec_index = -1);
|
||||
|
||||
/**
|
||||
* Find a nearby station that joins this road stop.
|
||||
@ -1820,17 +1821,31 @@ static CommandCost FindJoiningRoadStop(StationID existing_stop, StationID statio
|
||||
* @param is_drive_through False for normal stops, true for drive-through.
|
||||
* @param ddir Entrance direction (#DiagDirection) for normal stops. Converted to the axis for drive-through stops.
|
||||
* @param rt The roadtype.
|
||||
* @param spec_class Road stop spec class.
|
||||
* @param spec_index Road stop spec index.
|
||||
* @param station_to_join Station ID to join (NEW_STATION if build new one).
|
||||
* @param adjacent Allow stations directly adjacent to other stations.
|
||||
* @return The cost of this operation or an error.
|
||||
*/
|
||||
CommandCost CmdBuildRoadStop(DoCommandFlag flags, TileIndex tile, uint8 width, uint8 length, RoadStopType stop_type, bool is_drive_through, DiagDirection ddir, RoadType rt, StationID station_to_join, bool adjacent)
|
||||
CommandCost CmdBuildRoadStop(DoCommandFlag flags, TileIndex tile, uint8 width, uint8 length, RoadStopType stop_type, bool is_drive_through,
|
||||
DiagDirection ddir, RoadType rt, RoadStopClassID spec_class, byte spec_index, StationID station_to_join, bool adjacent)
|
||||
{
|
||||
if (!ValParamRoadType(rt) || !IsValidDiagDirection(ddir) || stop_type >= ROADSTOP_END) return CMD_ERROR;
|
||||
bool reuse = (station_to_join != NEW_STATION);
|
||||
if (!reuse) station_to_join = INVALID_STATION;
|
||||
bool distant_join = (station_to_join != INVALID_STATION);
|
||||
|
||||
/* Check if the given station class is valid */
|
||||
if ((uint)spec_class >= RoadStopClass::GetClassCount() || spec_class == ROADSTOP_CLASS_WAYP) return CMD_ERROR;
|
||||
if (spec_index >= RoadStopClass::Get(spec_class)->GetSpecCount()) return CMD_ERROR;
|
||||
|
||||
const RoadStopSpec *roadstopspec = RoadStopClass::Get(spec_class)->GetSpec(spec_index);
|
||||
if (roadstopspec != nullptr) {
|
||||
if (stop_type == ROADSTOP_TRUCK && roadstopspec->stop_type != ROADSTOPTYPE_FREIGHT && roadstopspec->stop_type != ROADSTOPTYPE_ALL) return CMD_ERROR;
|
||||
if (stop_type == ROADSTOP_BUS && roadstopspec->stop_type != ROADSTOPTYPE_PASSENGER && roadstopspec->stop_type != ROADSTOPTYPE_ALL) return CMD_ERROR;
|
||||
if (!is_drive_through && HasBit(roadstopspec->flags, RSF_DRIVE_THROUGH_ONLY)) return CMD_ERROR;
|
||||
}
|
||||
|
||||
/* Check if the requested road stop is too big */
|
||||
if (width > _settings_game.station.station_spread || length > _settings_game.station.station_spread) return_cmd_error(STR_ERROR_STATION_TOO_SPREAD_OUT);
|
||||
/* Check for incorrect width / length. */
|
||||
@ -1853,7 +1868,13 @@ CommandCost CmdBuildRoadStop(DoCommandFlag flags, TileIndex tile, uint8 width, u
|
||||
bool is_truck_stop = stop_type != ROADSTOP_BUS;
|
||||
|
||||
/* Total road stop cost. */
|
||||
CommandCost cost(EXPENSES_CONSTRUCTION, roadstop_area.w * roadstop_area.h * _price[is_truck_stop ? PR_BUILD_STATION_TRUCK : PR_BUILD_STATION_BUS]);
|
||||
Money unit_cost;
|
||||
if (roadstopspec != nullptr) {
|
||||
unit_cost = roadstopspec->GetBuildCost(is_truck_stop ? PR_BUILD_STATION_TRUCK : PR_BUILD_STATION_BUS);
|
||||
} else {
|
||||
unit_cost = _price[is_truck_stop ? PR_BUILD_STATION_TRUCK : PR_BUILD_STATION_BUS];
|
||||
}
|
||||
CommandCost cost(EXPENSES_CONSTRUCTION, roadstop_area.w * roadstop_area.h * unit_cost);
|
||||
StationID est = INVALID_STATION;
|
||||
ret = CheckFlatLandRoadStop(roadstop_area, flags, is_drive_through ? 5 << axis : 1 << ddir, is_drive_through, is_truck_stop, axis, &est, rt);
|
||||
if (ret.Failed()) return ret;
|
||||
@ -1869,6 +1890,20 @@ CommandCost CmdBuildRoadStop(DoCommandFlag flags, TileIndex tile, uint8 width, u
|
||||
ret = BuildStationPart(&st, flags, reuse, roadstop_area, STATIONNAMING_ROAD);
|
||||
if (ret.Failed()) return ret;
|
||||
|
||||
/* Check if we can allocate a custom stationspec to this station */
|
||||
int specindex = AllocateSpecToRoadStop(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, rt, is_truck_stop ? 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) {
|
||||
@ -1879,7 +1914,13 @@ CommandCost CmdBuildRoadStop(DoCommandFlag flags, TileIndex tile, uint8 width, u
|
||||
Owner tram_owner = tram_rt != INVALID_ROADTYPE ? GetRoadOwner(cur_tile, RTT_TRAM) : _current_company;
|
||||
|
||||
if (IsTileType(cur_tile, MP_STATION) && IsRoadStop(cur_tile)) {
|
||||
RemoveRoadStop(cur_tile, flags);
|
||||
RemoveRoadStop(cur_tile, flags, specindex);
|
||||
}
|
||||
|
||||
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);
|
||||
@ -1921,6 +1962,15 @@ CommandCost CmdBuildRoadStop(DoCommandFlag flags, TileIndex tile, uint8 width, u
|
||||
UpdateCompanyRoadInfrastructure(tram_rt, tram_owner, ROAD_STOP_TRACKBIT_FACTOR);
|
||||
Company::Get(st->owner)->infrastructure.station++;
|
||||
|
||||
UpdateCompanyRoadInfrastructure(road_rt, road_owner, ROAD_STOP_TRACKBIT_FACTOR);
|
||||
UpdateCompanyRoadInfrastructure(tram_rt, tram_owner, ROAD_STOP_TRACKBIT_FACTOR);
|
||||
|
||||
SetCustomRoadStopSpecIndex(cur_tile, specindex);
|
||||
if (roadstopspec != nullptr) {
|
||||
st->SetRoadStopRandomBits(cur_tile, GB(Random(), 0, 8));
|
||||
TriggerRoadStopAnimation(st, cur_tile, SAT_BUILT);
|
||||
}
|
||||
|
||||
MarkTileDirtyByTile(cur_tile);
|
||||
}
|
||||
|
||||
@ -1953,9 +2003,10 @@ static Vehicle *ClearRoadStopStatusEnum(Vehicle *v, void *)
|
||||
* Remove a bus station/truck stop
|
||||
* @param tile TileIndex been queried
|
||||
* @param flags operation to perform
|
||||
* @param replacement_spec_index replacement spec index to avoid deallocating, if < 0, tile is not being replaced
|
||||
* @return cost or failure of operation
|
||||
*/
|
||||
static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlag flags)
|
||||
static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlag flags, int replacement_spec_index)
|
||||
{
|
||||
Station *st = Station::GetByTile(tile);
|
||||
|
||||
@ -1987,6 +2038,8 @@ static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlag flags)
|
||||
if (ret.Failed()) return ret;
|
||||
}
|
||||
|
||||
const RoadStopSpec *spec = GetRoadStopSpec(tile);
|
||||
|
||||
if (flags & DC_EXEC) {
|
||||
if (*primary_stop == cur_stop) {
|
||||
/* removed the first stop in the list */
|
||||
@ -2011,6 +2064,12 @@ static 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);
|
||||
|
||||
if (IsDriveThroughStopTile(tile)) {
|
||||
/* Clears the tile for us */
|
||||
cur_stop->ClearDriveThrough();
|
||||
@ -2030,7 +2089,10 @@ static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlag flags)
|
||||
|
||||
st->rect.AfterRemoveTile(st, tile);
|
||||
|
||||
st->AfterStationTileSetChange(false, is_truck ? STATION_TRUCK: STATION_BUS);
|
||||
if (replacement_spec_index < 0) st->AfterStationTileSetChange(false, is_truck ? STATION_TRUCK: STATION_BUS);
|
||||
|
||||
st->RemoveRoadStopTileData(tile);
|
||||
if ((int)specindex != replacement_spec_index) DeallocateSpecFromRoadStop(st, specindex);
|
||||
|
||||
/* Update the tile area of the truck/bus stop */
|
||||
if (is_truck) {
|
||||
@ -2042,7 +2104,8 @@ static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlag flags)
|
||||
}
|
||||
}
|
||||
|
||||
return CommandCost(EXPENSES_CONSTRUCTION, _price[is_truck ? PR_CLEAR_STATION_TRUCK : PR_CLEAR_STATION_BUS]);
|
||||
Price category = is_truck ? PR_CLEAR_STATION_TRUCK : PR_CLEAR_STATION_BUS;
|
||||
return CommandCost(EXPENSES_CONSTRUCTION, spec != nullptr ? spec->GetClearCost(category) : _price[category]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2932,6 +2995,8 @@ draw_default_foundation:
|
||||
}
|
||||
}
|
||||
|
||||
bool draw_ground = false;
|
||||
|
||||
if (IsBuoy(ti->tile)) {
|
||||
DrawWaterClassGround(ti);
|
||||
SpriteID sprite = GetCanalSprite(CF_BUOY, ti->tile);
|
||||
@ -2970,6 +3035,10 @@ draw_default_foundation:
|
||||
ground_relocation += rti->fallback_railtype;
|
||||
}
|
||||
|
||||
draw_ground = true;
|
||||
}
|
||||
|
||||
if (draw_ground && !IsRoadStop(ti->tile)) {
|
||||
SpriteID image = t->ground.sprite;
|
||||
PaletteID pal = t->ground.pal;
|
||||
RailTrackOffset overlay_offset;
|
||||
@ -3002,8 +3071,32 @@ draw_default_foundation:
|
||||
const RoadTypeInfo* road_rti = road_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(road_rt);
|
||||
const RoadTypeInfo* tram_rti = tram_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(tram_rt);
|
||||
|
||||
if (IsDriveThroughStopTile(ti->tile)) {
|
||||
Axis axis = GetRoadStopDir(ti->tile) == DIAGDIR_NE ? AXIS_X : AXIS_Y;
|
||||
DiagDirection dir = GetRoadStopDir(ti->tile);
|
||||
StationType type = GetStationType(ti->tile);
|
||||
|
||||
const RoadStopSpec *stopspec = GetRoadStopSpec(ti->tile);
|
||||
if (stopspec != nullptr) {
|
||||
int view = dir;
|
||||
if (IsDriveThroughStopTile(ti->tile)) view += 4;
|
||||
st = BaseStation::GetByTile(ti->tile);
|
||||
RoadStopResolverObject object(stopspec, st, ti->tile, INVALID_ROADTYPE, type, view);
|
||||
const SpriteGroup *group = object.Resolve();
|
||||
if (group != nullptr && group->type == SGT_TILELAYOUT) {
|
||||
t = ((const TileLayoutSpriteGroup *)group)->ProcessRegisters(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
/* Draw ground sprite */
|
||||
if (draw_ground) {
|
||||
SpriteID image = t->ground.sprite;
|
||||
PaletteID pal = t->ground.pal;
|
||||
image += HasBit(image, SPRITE_MODIFIER_CUSTOM_SPRITE) ? ground_relocation : total_offset;
|
||||
if (HasBit(pal, SPRITE_MODIFIER_CUSTOM_SPRITE)) pal += ground_relocation;
|
||||
DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, palette));
|
||||
}
|
||||
|
||||
if (IsDriveThroughStopTile(ti->tile)) {
|
||||
uint sprite_offset = axis == AXIS_X ? 1 : 0;
|
||||
|
||||
DrawRoadOverlays(ti, PAL_NONE, road_rti, tram_rti, sprite_offset, sprite_offset);
|
||||
@ -3011,16 +3104,17 @@ draw_default_foundation:
|
||||
/* Non-drivethrough road stops are only valid for roads. */
|
||||
assert(road_rt != INVALID_ROADTYPE && tram_rt == INVALID_ROADTYPE);
|
||||
|
||||
if (road_rti->UsesOverlay()) {
|
||||
DiagDirection dir = GetRoadStopDir(ti->tile);
|
||||
if ((stopspec == nullptr || (stopspec->draw_mode & ROADSTOP_DRAW_MODE_ROAD) != 0) && road_rti->UsesOverlay()) {
|
||||
SpriteID ground = GetCustomRoadSprite(road_rti, ti->tile, ROTSG_ROADSTOP);
|
||||
DrawGroundSprite(ground + dir, PAL_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
if (stopspec == nullptr || !HasBit(stopspec->flags, RSF_NO_CATENARY)) {
|
||||
/* Draw road, tram catenary */
|
||||
DrawRoadCatenary(ti);
|
||||
}
|
||||
}
|
||||
|
||||
if (IsRailWaypoint(ti->tile)) {
|
||||
/* Don't offset the waypoint graphics; they're always the same. */
|
||||
@ -3278,6 +3372,12 @@ static void AnimateTile_Station(TileIndex tile)
|
||||
|
||||
if (IsAirport(tile)) {
|
||||
AnimateAirportTile(tile);
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsRoadStopTile(tile)) {
|
||||
AnimateRoadStopTile(tile);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3805,6 +3905,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);
|
||||
}
|
||||
}
|
||||
@ -3877,6 +3978,9 @@ 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);
|
||||
TriggerRoadStopRandomisation(st, st->xy, RSRT_NEW_CARGO, type);
|
||||
TriggerRoadStopAnimation(st, st->xy, SAT_NEW_CARGO, type);
|
||||
|
||||
|
||||
SetWindowDirty(WC_STATION_VIEW, st->index);
|
||||
st->MarkTilesDirty(true);
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "station_type.h"
|
||||
|
||||
enum StationClassID : byte;
|
||||
enum RoadStopClassID : byte;
|
||||
|
||||
extern Town *AirportGetNearestTown(const struct AirportSpec *as, const TileIterator &it, uint &mindist);
|
||||
extern uint8 GetAirportNoiseLevelForDistance(const struct AirportSpec *as, uint distance);
|
||||
@ -22,7 +23,7 @@ CommandCost CmdBuildAirport(DoCommandFlag flags, TileIndex tile, byte airport_ty
|
||||
CommandCost CmdBuildDock(DoCommandFlag flags, TileIndex tile, StationID station_to_join, bool adjacent);
|
||||
CommandCost CmdBuildRailStation(DoCommandFlag flags, TileIndex tile_org, RailType rt, Axis axis, byte numtracks, byte plat_len, StationClassID spec_class, byte spec_index, StationID station_to_join, bool adjacent);
|
||||
CommandCost CmdRemoveFromRailStation(DoCommandFlag flags, TileIndex start, TileIndex end, bool keep_rail);
|
||||
CommandCost CmdBuildRoadStop(DoCommandFlag flags, TileIndex tile, uint8 width, uint8 length, RoadStopType stop_type, bool is_drive_through, DiagDirection ddir, RoadType rt, StationID station_to_join, bool adjacent);
|
||||
CommandCost CmdBuildRoadStop(DoCommandFlag flags, TileIndex tile, uint8 width, uint8 length, RoadStopType stop_type, bool is_drive_through, DiagDirection ddir, RoadType rt, RoadStopClassID spec_class, byte spec_index, StationID station_to_join, bool adjacent);
|
||||
CommandCost CmdRemoveRoadStop(DoCommandFlag flags, TileIndex tile, uint8 width, uint8 height, RoadStopType stop_type, bool remove_road);
|
||||
CommandCost CmdRenameStation(DoCommandFlag flags, StationID station_id, const std::string &text);
|
||||
CommandCost CmdOpenCloseAirport(DoCommandFlag flags, StationID station_id);
|
||||
|
@ -497,6 +497,42 @@ static inline uint GetCustomStationSpecIndex(TileIndex t)
|
||||
return _m[t].m4;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is there a custom road stop spec on this tile?
|
||||
* @param t Tile to query
|
||||
* @pre IsRoadStopTile(t)
|
||||
* @return True if this station is part of a newgrf station.
|
||||
*/
|
||||
static inline bool IsCustomRoadStopSpecIndex(TileIndex t)
|
||||
{
|
||||
assert(IsRoadStopTile(t));
|
||||
return GB(_me[t].m8, 0, 6) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the custom road stop spec for this tile.
|
||||
* @param t Tile to set the stationspec of.
|
||||
* @param specindex The new spec.
|
||||
* @pre IsRoadStopTile(t)
|
||||
*/
|
||||
static inline void SetCustomRoadStopSpecIndex(TileIndex t, byte specindex)
|
||||
{
|
||||
assert(IsRoadStopTile(t));
|
||||
SB(_me[t].m8, 0, 6, specindex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the custom road stop spec for this tile.
|
||||
* @param t Tile to query
|
||||
* @pre IsRoadStopTile(t)
|
||||
* @return The custom station spec of this tile.
|
||||
*/
|
||||
static inline uint GetCustomRoadStopSpecIndex(TileIndex t)
|
||||
{
|
||||
assert(IsRoadStopTile(t));
|
||||
return GB(_me[t].m8, 0, 6);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the random bits for a station tile.
|
||||
* @param t Tile to set random bits for.
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "../newgrf_house.h"
|
||||
#include "../newgrf_engine.h"
|
||||
#include "../newgrf_roadtype.h"
|
||||
#include "../newgrf_roadstop.h"
|
||||
|
||||
/* Helper for filling property tables */
|
||||
#define NIP(prop, base, variable, type, name) { name, [] (const void *b) -> const void * { return std::addressof(static_cast<const base *>(b)->variable); }, cpp_sizeof(base, variable), prop, type }
|
||||
@ -607,6 +608,64 @@ static const NIFeature _nif_tramtype = {
|
||||
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"),
|
||||
NIV(0x42, "terrain type"),
|
||||
NIV(0x43, "road type"),
|
||||
NIV(0x44, "tram type"),
|
||||
NIV(0x45, "town zone and Manhattan distance of town"),
|
||||
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"),
|
||||
NIV(0x6A, "GRFID of nearby road stop tiles"),
|
||||
NIV_END(),
|
||||
};
|
||||
|
||||
class NIHRoadStop : public NIHelper {
|
||||
bool IsInspectable(uint index) const override { return GetRoadStopSpec(index) != nullptr; }
|
||||
uint GetParent(uint index) const override { return GetInspectWindowNumber(GSF_FAKE_TOWNS, BaseStation::GetByTile(index)->town->index); }
|
||||
const void *GetInstance(uint index)const override { return nullptr; }
|
||||
const void *GetSpec(uint index) const override { return GetRoadStopSpec(index); }
|
||||
void SetStringParameters(uint index) const override { this->SetObjectAtStringParameters(STR_STATION_NAME, GetStationIndex(index), index); }
|
||||
uint32 GetGRFID(uint index) const override { return (this->IsInspectable(index)) ? GetRoadStopSpec(index)->grf_prop.grffile->grfid : 0; }
|
||||
|
||||
uint Resolve(uint index, uint var, uint32 param, bool *avail) const override
|
||||
{
|
||||
int view = GetRoadStopDir(index);
|
||||
if (IsDriveThroughStopTile(index)) view += 4;
|
||||
RoadStopResolverObject ro(GetRoadStopSpec(index), BaseStation::GetByTile(index), index, INVALID_ROADTYPE, GetStationType(index), view);
|
||||
return ro.GetScope(VSG_SCOPE_SELF)->GetVariable(var, param, avail);
|
||||
}
|
||||
};
|
||||
|
||||
static const NIFeature _nif_roadstop = {
|
||||
nullptr,
|
||||
_nic_roadstops,
|
||||
_nif_roadstops,
|
||||
new NIHRoadStop(),
|
||||
};
|
||||
|
||||
/** Table with all NIFeatures. */
|
||||
static const NIFeature * const _nifeatures[] = {
|
||||
&_nif_vehicle, // GSF_TRAINS
|
||||
@ -629,6 +688,7 @@ static const NIFeature * const _nifeatures[] = {
|
||||
&_nif_airporttile, // GSF_AIRPORTTILES
|
||||
&_nif_roadtype, // GSF_ROADTYPES
|
||||
&_nif_tramtype, // GSF_TRAMTYPES
|
||||
&_nif_roadstop, // GSF_ROADSTOPS
|
||||
&_nif_town, // GSF_FAKE_TOWNS
|
||||
};
|
||||
static_assert(lengthof(_nifeatures) == GSF_FAKE_END);
|
||||
|
@ -55,6 +55,7 @@
|
||||
#include "misc_cmd.h"
|
||||
#include "train_cmd.h"
|
||||
#include "vehicle_cmd.h"
|
||||
#include "newgrf_roadstop.h"
|
||||
|
||||
#include "table/strings.h"
|
||||
|
||||
@ -2303,6 +2304,14 @@ 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 (IsRoadStopTile(this->tile)) {
|
||||
TriggerRoadStopRandomisation(st, this->tile, RSRT_VEH_DEPARTS);
|
||||
TriggerRoadStopAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this->MarkDirty();
|
||||
}
|
||||
|
@ -51,8 +51,21 @@ enum BuildRoadStationWidgets {
|
||||
WID_BROS_STATION_Y, ///< Drive-through station in y-direction.
|
||||
WID_BROS_LT_OFF, ///< Turn off area highlight.
|
||||
WID_BROS_LT_ON, ///< Turn on area highlight.
|
||||
WID_BROS_INFO, ///< Station acceptance toggle.
|
||||
WID_BROS_ACCEPTANCE, ///< Station acceptance.
|
||||
WID_BROS_ACCEPTANCE, ///< Station acceptance info.
|
||||
WID_BROS_MATRIX, ///< Matrix widget displaying all available road stops.
|
||||
WID_BROS_IMAGE, ///< Panel used for each image of the matrix.
|
||||
WID_BROS_MATRIX_SCROLL, ///< Scrollbar of the #WID_BROS_SHOW_NEWST_ADDITIONS.
|
||||
WID_BROS_FILTER_CONTAINER, ///< Container for the filter text box for the road stop class list.
|
||||
WID_BROS_FILTER_EDITBOX, ///< Filter text box for the road stop class list.
|
||||
WID_BROS_SHOW_NEWST_DEFSIZE, ///< Selection for default-size button for new road stops.
|
||||
WID_BROS_SHOW_NEWST_ADDITIONS, ///< Selection for new class selection list.
|
||||
WID_BROS_SHOW_NEWST_MATRIX, ///< Selection for new stop image matrix.
|
||||
WID_BROS_SHOW_NEWST_RESIZE, ///< Selection for panel and resize at bottom right for new stops.
|
||||
WID_BROS_SHOW_NEWST_ORIENTATION, ///< Selection for the orientation string for new stops.
|
||||
WID_BROS_SHOW_NEWST_TYPE_SEL, ///< Selection for the type name.
|
||||
WID_BROS_SHOW_NEWST_TYPE, ///< Display of selected stop type.
|
||||
WID_BROS_NEWST_LIST, ///< List with new road stops.
|
||||
WID_BROS_NEWST_SCROLL, ///< Scrollbar of the #WID_BROS_NEWST_LIST.
|
||||
};
|
||||
|
||||
#endif /* WIDGETS_ROAD_WIDGET_H */
|
||||
|
Loading…
Reference in New Issue
Block a user