diff options
-rw-r--r-- | ai.c | 8 | ||||
-rw-r--r-- | ai_build.c | 5 | ||||
-rw-r--r-- | command.c | 8 | ||||
-rw-r--r-- | command.h | 3 | ||||
-rw-r--r-- | functions.h | 1 | ||||
-rw-r--r-- | lang/english.txt | 2 | ||||
-rw-r--r-- | misc.c | 29 | ||||
-rw-r--r-- | oldloader.c | 18 | ||||
-rw-r--r-- | order_cmd.c | 8 | ||||
-rw-r--r-- | road_gui.c | 4 | ||||
-rw-r--r-- | roadveh_cmd.c | 190 | ||||
-rw-r--r-- | saveload.c | 11 | ||||
-rw-r--r-- | saveload.h | 5 | ||||
-rw-r--r-- | station.h | 47 | ||||
-rw-r--r-- | station_cmd.c | 456 | ||||
-rw-r--r-- | ttd.c | 2 | ||||
-rw-r--r-- | vehicle.c | 3 | ||||
-rw-r--r-- | vehicle.h | 3 |
18 files changed, 590 insertions, 213 deletions
@@ -2567,10 +2567,10 @@ static int32 AiDoBuildDefaultRoadBlock(TileIndex tile, const AiDefaultBlockData } else if (p->mode == 1) { if (_want_road_truck_station) { // Truck station - r = DoCommandByTile(c, p->attr, 0, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_BUILD_TRUCK_STATION); + r = DoCommandByTile(c, p->attr, RS_TRUCK, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_BUILD_ROAD_STOP); } else { // Bus station - r = DoCommandByTile(c, p->attr, 0, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_BUILD_BUS_STATION); + r = DoCommandByTile(c, p->attr, RS_BUS, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_BUILD_ROAD_STOP); } clear_town_stuff:; @@ -3627,8 +3627,8 @@ static void AiStateRemoveStation(Player *p) used=in_use; FOR_ALL_STATIONS(st) { if (st->xy != 0 && st->owner == _current_player && !*used && - ((tile = st->bus_tile) != 0 || - (tile = st->lorry_tile) != 0 || + ( (st->bus_stops != NULL && (tile = st->bus_stops->xy) != 0) || + (st->truck_stops != NULL && (tile = st->truck_stops->xy)) != 0 || (tile = st->train_tile) != 0 || (tile = st->dock_tile) != 0 || (tile = st->airport_tile) != 0)) { diff --git a/ai_build.c b/ai_build.c index f53d7793f..e4dcf42cd 100644 --- a/ai_build.c +++ b/ai_build.c @@ -5,6 +5,7 @@ #include "command.h" #include "ai.h" #include "engine.h" +#include "station.h" // Build HQ // Params: @@ -28,9 +29,9 @@ int AiNew_Build_Station(Player *p, byte type, uint tile, byte length, byte numtr if (type == AI_TRAIN) return DoCommandByTile(tile, direction + (numtracks << 8) + (length << 16), 0, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_RAILROAD_STATION); else if (type == AI_BUS) - return DoCommandByTile(tile, direction, 0, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_BUS_STATION); + return DoCommandByTile(tile, direction, RS_BUS, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD_STOP); else - return DoCommandByTile(tile, direction, 0, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_TRUCK_STATION); + return DoCommandByTile(tile, direction, RS_TRUCK, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD_STOP); } // Builds a brdige. The second best out of the ones available for this player @@ -37,9 +37,7 @@ DEF_COMMAND(CmdBuildTrainWaypoint); DEF_COMMAND(CmdRenameWaypoint); DEF_COMMAND(CmdRemoveTrainWaypoint); -DEF_COMMAND(CmdBuildTruckStation); - -DEF_COMMAND(CmdBuildBusStation); +DEF_COMMAND(CmdBuildRoadStop); DEF_COMMAND(CmdBuildLongRoad); DEF_COMMAND(CmdRemoveLongRoad); @@ -190,9 +188,9 @@ static CommandProc * const _command_proc_table[] = { CmdBuildTrainWaypoint, /* 16 */ CmdRenameWaypoint, /* 17 */ CmdRemoveTrainWaypoint, /* 18 */ - CmdBuildTruckStation, /* 19 */ + NULL, /* 19 */ NULL, /* 20 */ - CmdBuildBusStation, /* 21 */ + CmdBuildRoadStop, /* 21 */ NULL, /* 22 */ CmdBuildLongRoad, /* 23 */ CmdRemoveLongRoad, /* 24 */ @@ -24,8 +24,7 @@ enum { CMD_RENAME_WAYPOINT = 17, CMD_REMOVE_TRAIN_WAYPOINT = 18, - CMD_BUILD_TRUCK_STATION = 19, - CMD_BUILD_BUS_STATION = 21, + CMD_BUILD_ROAD_STOP = 21, CMD_BUILD_LONG_ROAD = 23, CMD_REMOVE_LONG_ROAD = 24, CMD_BUILD_ROAD = 25, diff --git a/functions.h b/functions.h index e39e27d38..36482e9a1 100644 --- a/functions.h +++ b/functions.h @@ -290,4 +290,5 @@ byte GetOSVersion(void); void DeterminePaths(void); char * CDECL str_fmt(const char *str, ...); +void bubblesort(void *base, size_t nmemb, size_t size, int(*compar)(const void *, const void *)); #endif /* FUNCTIONS_H */ diff --git a/lang/english.txt b/lang/english.txt index 28d214327..4071f47c7 100644 --- a/lang/english.txt +++ b/lang/english.txt @@ -1616,6 +1616,8 @@ STR_3005_TOO_CLOSE_TO_ANOTHER_RAILROAD :{WHITE}Too close to another railway s STR_3006_ADJOINS_MORE_THAN_ONE_EXISTING :{WHITE}Adjoins more than one existing station/loading area STR_3007_TOO_MANY_STATIONS_LOADING :{WHITE}Too many stations/loading areas in this town STR_3008_TOO_MANY_STATIONS_LOADING :{WHITE}Too many stations/loading areas +STR_3008A_TOO_MANY_BUS_STOPS :{WHITE}Too many bus stops +STR_3008B_TOO_MANY_TRUCK_STOPS :{WHITE}Too many lorry stations STR_3009_TOO_CLOSE_TO_ANOTHER_STATION :{WHITE}Too close to another station/loading area STR_300A_0 :{WHITE}{STATION} {STRINL 0x30D1} STR_300B_MUST_DEMOLISH_RAILROAD :{WHITE}Must demolish railway station first @@ -743,6 +743,35 @@ int FindFirstBit(uint32 value) return i; } +//!We're writing an own sort algorithm here, as +//!qsort isn't stable +//!Since the number of elements will be low, a +//!simple bubble sort will have to do :) + +void bubblesort(void *base, size_t nmemb, size_t size, int(*compar)(const void *, const void *)) +{ + uint i,k; + void *buffer = malloc(size); + char *start = base; + + nmemb--; + + for (i = 0; i < nmemb; i++) { + for (k = 0; k < nmemb; k++) { + void *a, *b; + a = start + size * k; + b = start + size * (k + 1); + if (compar(a, b) > 0) { + memcpy(buffer, a, size); + memcpy(a, b, size); + memcpy(b, buffer, size); + } + } + } + + free(buffer); + buffer = NULL; +} static void Save_NAME(void) { diff --git a/oldloader.c b/oldloader.c index 07a4dde3f..4862c14a2 100644 --- a/oldloader.c +++ b/oldloader.c @@ -711,8 +711,16 @@ static void FixStation(OldStation *o, int num) s->xy = o->xy; s->town = REMAP_TOWN_PTR(o->town); - s->bus_tile = o->bus_tile; - s->lorry_tile = o->lorry_tile; + if (o->bus_tile != 0) { + s->bus_stops = GetFirstFreeRoadStop(); + s->bus_stops->xy = o->bus_tile; + } else + s->bus_stops = NULL; + if (o->lorry_tile != 0) { + s->truck_stops = GetFirstFreeRoadStop(); + s->truck_stops->xy = o->lorry_tile; + } else + s->truck_stops = 0; s->train_tile = o->train_tile; s->airport_tile = o->airport_tile; s->dock_tile = o->dock_tile; @@ -734,8 +742,10 @@ static void FixStation(OldStation *o, int num) s->owner = o->owner; s->facilities = o->facilities; s->airport_type = o->airport_type; - s->truck_stop_status = o->truck_stop_status; - s->bus_stop_status = o->bus_stop_status; + if (s->truck_stops != NULL) + s->truck_stops->status = o->truck_stop_status; + if (s->bus_stops != NULL) + s->bus_stops->status = o->bus_stop_status; s->blocked_months_obsolete = o->blocked_months_obsolete; s->airport_flags = o->airport_flags; s->last_vehicle = o->last_vehicle; diff --git a/order_cmd.c b/order_cmd.c index b0e638cb5..480e058ae 100644 --- a/order_cmd.c +++ b/order_cmd.c @@ -332,6 +332,12 @@ int32 CmdSkipOrder(int x, int y, uint32 flags, uint32 vehicle_id, uint32 not_use if (v->type == VEH_Train) v->u.rail.days_since_order_progr = 0; + + if (v->type == VEH_Road && v->u.road.slot != NULL) { + //Clear the slot + v->u.road.slot->slot[v->u.road.slotindex] = 0; + v->u.road.slot = NULL; + } } /* NON-stop flag is misused to see if a train is in a station that is @@ -482,7 +488,7 @@ int32 CmdCloneOrder(int x, int y, uint32 flags, uint32 veh1_veh2, uint32 mode) FOR_VEHICLE_ORDERS(src, order) { if (order->type == OT_GOTO_STATION) { const Station *st = GetStation(order->station); - required_dst = (dst->cargo_type == CT_PASSENGERS) ? st->bus_tile : st->lorry_tile; + required_dst = (dst->cargo_type == CT_PASSENGERS) ? st->bus_stops->xy : st->truck_stops->xy; /* This station has not the correct road-bay, so we can't copy! */ if (!required_dst) return CMD_ERROR; diff --git a/road_gui.c b/road_gui.c index 04be3ac27..2c3158c1e 100644 --- a/road_gui.c +++ b/road_gui.c @@ -89,12 +89,12 @@ static void PlaceRoad_Depot(uint tile) static void PlaceRoad_BusStation(uint tile) { - DoCommandP(tile, _road_station_picker_orientation, 0, CcRoadDepot, CMD_BUILD_BUS_STATION | CMD_AUTO | CMD_NO_WATER | CMD_MSG(STR_1808_CAN_T_BUILD_BUS_STATION)); + DoCommandP(tile, _road_station_picker_orientation, RS_BUS, CcRoadDepot, CMD_BUILD_ROAD_STOP | CMD_AUTO | CMD_NO_WATER | CMD_MSG(STR_1808_CAN_T_BUILD_BUS_STATION)); } static void PlaceRoad_TruckStation(uint tile) { - DoCommandP(tile, _road_station_picker_orientation, 0, CcRoadDepot, CMD_BUILD_TRUCK_STATION | CMD_AUTO | CMD_NO_WATER | CMD_MSG(STR_1809_CAN_T_BUILD_TRUCK_STATION)); + DoCommandP(tile, _road_station_picker_orientation, RS_TRUCK, CcRoadDepot, CMD_BUILD_ROAD_STOP | CMD_AUTO | CMD_NO_WATER | CMD_MSG(STR_1809_CAN_T_BUILD_TRUCK_STATION)); } static void PlaceRoad_DemolishArea(uint tile) diff --git a/roadveh_cmd.c b/roadveh_cmd.c index 9e34c797f..445530008 100644 --- a/roadveh_cmd.c +++ b/roadveh_cmd.c @@ -164,6 +164,10 @@ int32 CmdBuildRoadVeh(int x, int y, uint32 flags, uint32 p1, uint32 p2) // v->u.road.unk2 = 0; // v->u.road.overtaking = 0; + v->u.road.slot = NULL; + v->u.road.slotindex = 0; + v->u.road.slot_age = 0; + v->last_station_visited = 0xFFFF; v->max_speed = rvi->max_speed; v->engine_type = (byte)p1; @@ -409,10 +413,10 @@ static void UpdateRoadVehDeltaXY(Vehicle *v) static void ClearCrashedStation(Vehicle *v) { uint tile = v->tile; - Station *st = GetStation(_map2[tile]); byte *b, bb; - b = (_map5[tile] >= 0x47) ? &st->bus_stop_status : &st->truck_stop_status; + RoadStop *rs = GetRoadStopByTile(tile, GetRoadStopType(tile)); + b = &rs->status; bb = *b; @@ -607,9 +611,34 @@ static void ProcessRoadVehOrder(Vehicle *v) if (order->type == OT_GOTO_STATION) { if (order->station == v->last_station_visited) v->last_station_visited = 0xFFFF; - st = GetStation(order->station); - v->dest_tile = v->cargo_type == CT_PASSENGERS ? st->bus_tile : st->lorry_tile; + + { + int32 *dist; + int32 mindist = 0xFFFFFFFF; + int num; + RoadStopType type; + RoadStop *rs; + + type = (v->cargo_type == CT_PASSENGERS) ? RS_BUS : RS_TRUCK; + num = GetNumRoadStops(st, type); + rs = GetPrimaryRoadStop(st, type); + + assert (rs != NULL); + + dist = malloc(num * sizeof(int32)); + + do { + *dist = GetTileDistAdv(v->tile, rs->xy); + if (*dist < mindist) { + v->dest_tile = rs->xy; + } + rs = rs->next; + } while ( rs != NULL ); + + free(dist); + dist = NULL; + } } else if (order->type == OT_GOTO_DEPOT) { v->dest_tile = _depots[order->station].xy; } @@ -990,10 +1019,10 @@ static int RoadFindPathToDest(Vehicle *v, uint tile, int direction) Station *st = GetStation(_map2[tile]); byte val = _map5[tile]; if (v->cargo_type != CT_PASSENGERS) { - if (IS_BYTE_INSIDE(val, 0x43, 0x47) && (_patches.roadveh_queue || st->truck_stop_status&3)) + if (IS_BYTE_INSIDE(val, 0x43, 0x47) && (_patches.roadveh_queue || st->truck_stops->status&3)) bitmask |= _road_veh_fp_ax_or[(val-0x43)&3]; } else { - if (IS_BYTE_INSIDE(val, 0x47, 0x4B) && (_patches.roadveh_queue || st->bus_stop_status&3)) + if (IS_BYTE_INSIDE(val, 0x47, 0x4B) && (_patches.roadveh_queue || st->bus_stops->status&3)) bitmask |= _road_veh_fp_ax_or[(val-0x47)&3]; } } @@ -1073,6 +1102,29 @@ found_best_track:; return best_track; } +static int RoadFindPathToStation(const Vehicle *v, TileIndex tile) +{ + FindRoadToChooseData frd; + int i, best_track = -1; + uint best_dist = (uint) -1, best_maxlen = (uint) -1; + + frd.dest = tile; + frd.maxtracklen = (uint) -1; + frd.mindist = (uint) -1; + + for (i = 0; i < 4; i++) { + FollowTrack(v->tile, 0x2000 | TRANSPORT_ROAD, i, (TPFEnumProc*)EnumRoadTrackFindDist, NULL, &frd); + + if (frd.mindist < best_dist || (frd.mindist == best_dist && frd.maxtracklen < best_maxlen )) { + best_dist = frd.mindist; + best_maxlen = frd.maxtracklen; + best_track = i; + } + } + return best_maxlen; +} + + typedef struct RoadDriveEntry { byte x,y; } RoadDriveEntry; @@ -1088,6 +1140,13 @@ static const byte _road_veh_data_1[] = { static const byte _roadveh_data_2[4] = { 0,1,8,9 }; +static inline void ClearSlot(Vehicle *v, RoadStop *rs) +{ + v->u.road.slot = NULL; + v->u.road.slot_age = 0; + rs->slot[v->u.road.slotindex] = INVALID_SLOT; +} + static void RoadVehEventHandler(Vehicle *v) { GetNewVehiclePosResult gp; @@ -1247,15 +1306,12 @@ again: if (IS_BYTE_INSIDE(v->u.road.state, 0x20, 0x30) && IsTileType(v->tile, MP_STATION)) { if ((tmp&7) >= 6) { v->cur_speed = 0; return; } if (IS_BYTE_INSIDE(_map5[v->tile], 0x43, 0x4B)) { - Station *st = GetStation(_map2[v->tile]); - byte *b; + RoadStop *rs = GetRoadStopByTile(v->tile, GetRoadStopType(v->tile)); + byte *b = &rs->status; - if (_map5[v->tile] >= 0x47) { - b = &st->bus_stop_status; - } else { - b = &st->truck_stop_status; - } - *b = (*b | ((v->u.road.state&2)?2:1)) & 0x7F; + //we have reached a loading bay, mark it as used + //and clear the usage bit (0x80) of the stop + *b = (*b | ((v->u.road.state&2)?2:1)) & ~0x80; } } @@ -1341,10 +1397,10 @@ again: if (v->u.road.state >= 0x20 && _road_veh_data_1[v->u.road.state - 0x20 + (_opt.road_side<<4)] == v->u.road.frame) { - byte *b; + RoadStop *rs = GetRoadStopByTile(v->tile, GetRoadStopType(v->tile)); + byte *b = &rs->status; st = GetStation(_map2[v->tile]); - b = IS_BYTE_INSIDE(_map5[v->tile], 0x43, 0x47) ? &st->truck_stop_status : &st->bus_stop_status; if (v->current_order.type != OT_LEAVESTATION && v->current_order.type != OT_GOTO_DEPOT) { @@ -1385,6 +1441,17 @@ again: } *b |= 0x80; + if (rs == v->u.road.slot) { + //we have arrived at the correct station + ClearSlot(v, rs); + } else if (v->u.road.slot != NULL) { + //we have arrived at the wrong station + //XXX The question is .. what to do? Actually we shouldn't be here + //but I guess we need to clear the slot + DEBUG(misc, 2) ("Multistop: Wrong station, force a slot clearing"); + ClearSlot(v, rs); + } + StartRoadVehSound(v); InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR); } @@ -1482,6 +1549,10 @@ static void CheckIfRoadVehNeedsService(Vehicle *v) (v->current_order.flags & (OF_FULL_LOAD | OF_UNLOAD)) != 0) return; + //If we already got a slot at a stop, use that FIRST, and go to a depot later + if (v->u.road.slot != NULL) + return; + i = FindClosestRoadDepot(v); if (i < 0 || GetTileDist(v->tile, (&_depots[i])->xy) > 12) { @@ -1508,11 +1579,15 @@ static void CheckIfRoadVehNeedsService(Vehicle *v) InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR); } +int dist_compare(const void *a, const void *b) +{ + return ( *(const uint32 *)a) - ( *(const uint32 *) b); +} + void OnNewDay_RoadVeh(Vehicle *v) { int32 cost; Station *st; - uint tile; if ((++v->day_counter & 7) == 0) DecreaseVehicleValue(v); @@ -1527,9 +1602,86 @@ void OnNewDay_RoadVeh(Vehicle *v) /* update destination */ if (v->current_order.type == OT_GOTO_STATION) { + RoadStop *rs; + uint32 mindist = 0xFFFFFFFF; + int num; + RoadStopType type = (v->cargo_type == CT_PASSENGERS) ? RS_BUS : RS_TRUCK; + + typedef struct { + uint32 dist; + RoadStop *rs; + } StopStruct; + + StopStruct *stop, *firststop; + st = GetStation(v->current_order.station); - if ((tile=(v->cargo_type==CT_PASSENGERS ? st->bus_tile : st->lorry_tile)) != 0) - v->dest_tile = tile; + rs = GetPrimaryRoadStop(st, type); + num = GetNumRoadStops(st, type); + + firststop = stop = malloc(num * sizeof(StopStruct)); + + //Current slot has expired + if ( (v->u.road.slot_age++ <= 0) && (v->u.road.slot != NULL)) { + ClearSlot(v, v->u.road.slot); + } + + //We do not have a slot, so make one + if (v->u.road.slot == NULL) { + //first we need to find out how far our stations are away. + assert( rs != NULL); + + do { + stop->dist = 0xFFFFFFFF; + + //FIXME This doesn't fully work yet, as it only goes + //to one tile BEFORE the stop in question and doesn't + //regard the direction of the exit + stop->dist = RoadFindPathToStation(v, rs->xy); + stop->rs = rs; + + if (stop->dist < mindist) { + mindist = stop->dist; + } + + stop++; + rs = rs->next; + } while (rs != NULL); + + if (mindist < 120) { //if we're reasonably close, get us a slot + int k; + bubblesort(firststop, num, sizeof(StopStruct), dist_compare); + + stop = firststop; + for (k = 0; k < num; k++) { + int i; + for (i = 0; i < NUM_SLOTS; i++) { + if ((stop->rs->slot[i] == INVALID_SLOT) && (stop->dist < 120)) { + + //Hooray we found a free slot. Assign it + stop->rs->slot[i] = v->index; + v->u.road.slot = stop->rs; + + v->dest_tile = stop->rs->xy; + v->u.road.slot_age = -30; + v->u.road.slotindex = i; + + goto have_slot; //jump out of BOTH loops + + } + } + stop++; + } + } + +have_slot: + //now we couldn't assign a slot for one reason or another. + //so we just go to the nearest station + if (v->u.road.slot == NULL) + v->dest_tile = firststop->rs->xy; + } + + free(firststop); + firststop = stop = NULL; } if (v->vehstatus & VS_STOPPED) diff --git a/saveload.c b/saveload.c index deeedbb8c..580665c49 100644 --- a/saveload.c +++ b/saveload.c @@ -7,8 +7,8 @@ #include "saveload.h" enum { - SAVEGAME_MAJOR_VERSION = 5, - SAVEGAME_MINOR_VERSION = 2, + SAVEGAME_MAJOR_VERSION = 6, + SAVEGAME_MINOR_VERSION = 0, SAVEGAME_LOADABLE_VERSION = (SAVEGAME_MAJOR_VERSION << 8) + SAVEGAME_MINOR_VERSION }; @@ -918,6 +918,10 @@ static uint ReferenceToInt(void *v, uint t) case REF_TOWN: return ((Town *)v)->index + 1; case REF_ORDER: return ((Order *)v)->index + 1; + case REF_ROADSTOPS: + //return ((byte*)v - (byte*)_roadstops) / sizeof(_roadstops[0]) + 1; + return (RoadStop *)v - _roadstops + 1; + default: NOT_REACHED(); } @@ -942,6 +946,9 @@ static void *IntToReference(uint r, uint t) case REF_STATION: return GetStation(r - 1); case REF_TOWN: return GetTown(r - 1); + case REF_ROADSTOPS: + //return (byte*)_roadstops + (r - 1) * sizeof(_roadstops[0]); + return &_roadstops[r - 1]; case REF_VEHICLE_OLD: { /* Old vehicles were saved differently: invalid vehicle was 0xFFFF, and the index was not - 1.. correct for this */ diff --git a/saveload.h b/saveload.h index edbbdc549..ebe3ecc25 100644 --- a/saveload.h +++ b/saveload.h @@ -74,7 +74,8 @@ enum { REF_VEHICLE = 1, REF_STATION = 2, REF_TOWN = 3, - REF_VEHICLE_OLD = 4 + REF_VEHICLE_OLD = 4, + REF_ROADSTOPS = 5 }; @@ -151,7 +152,7 @@ enum { #define SLE_VARX(t,c) 0x00 | ((t) & 0xF), (t) >> 4, c #define SLE_REFX(t,c) 0x10 | ((t) & 0xF), (t) >> 4, c #define SLE_CONDVARX(t,c,from,to) 0x40 | ((t) & 0xF), (t) >> 4, c, from, to -#define SLE_CONDREFX(t,c,co) 0x50 | ((t) & 0xF), (t) >> 4, c, co +#define SLE_CONDREFX(t,c,from,to) 0x50 | ((t) & 0xF), (t) >> 4, c, from, to #define SLE_WRITEBYTEX(t,b) 0x80 | ((t) & 0xF), (t) >> 4, b #define SLE_INCLUDEX(t,c) 0x90 | ((t) & 0xF), (t) >> 4, c @@ -14,10 +14,32 @@ typedef struct GoodsEntry { byte last_age; } GoodsEntry; +typedef enum RoadStopType { + RS_BUS, + RS_TRUCK +} RoadStopType; + +enum { NUM_ROAD_STOPS = 250 }; +enum { ROAD_STOP_LIMIT = 8 }; +enum { NUM_SLOTS = 2 }; +enum { INVALID_SLOT = 0xFFFF }; + +typedef struct RoadStop { + TileIndex xy; + bool used; + byte status; + uint32 index; + uint16 slot[NUM_SLOTS]; + uint16 station; //XXX should be StationIndex + uint8 type; + struct RoadStop *next; + struct RoadStop *prev; +} RoadStop; + struct Station { TileIndex xy; - TileIndex bus_tile; - TileIndex lorry_tile; + RoadStop *bus_stops; + RoadStop *truck_stops; TileIndex train_tile; TileIndex airport_tile; TileIndex dock_tile; @@ -36,9 +58,6 @@ struct Station { byte owner; byte facilities; byte airport_type; - byte truck_stop_status; - byte bus_stop_status; - byte blocked_months_obsolete; // trainstation width/height byte trainst_w, trainst_h; @@ -53,6 +72,14 @@ struct Station { VehicleID last_vehicle; GoodsEntry goods[NUM_CARGO]; + + /* Stuff that is no longer used, but needed for conversion */ + TileIndex bus_tile_obsolete; + TileIndex lorry_tile_obsolete; + + byte truck_stop_status_obsolete; + byte bus_stop_status_obsolete; + byte blocked_months_obsolete; }; enum { @@ -68,7 +95,7 @@ enum { HVOT_TRAIN = 1<<1, HVOT_BUS = 1 << 2, HVOT_TRUCK = 1 << 3, - HVOT_AIRCRAFT = 1<<4, + HVOT_AIRCRAFT = 1 << 4, HVOT_SHIP = 1 << 5, HVOT_BUOY = 1 << 6 }; @@ -93,7 +120,9 @@ TileIndex GetStationTileForVehicle(const Vehicle *v, const Station *st); void ShowStationViewWindow(int station); void UpdateAllStationVirtCoord(void); +VARDEF RoadStop _roadstops[NUM_ROAD_STOPS * 2]; VARDEF Station _stations[250]; +VARDEF uint _roadstops_size; VARDEF uint _stations_size; VARDEF SortStruct *_station_sort; @@ -189,4 +218,10 @@ struct StationSpec *GetCustomStation(enum StationClass sclass, byte stid); uint32 GetCustomStationRelocation(struct StationSpec *spec, struct Station *stat, byte ctype); int GetCustomStationsCount(enum StationClass sclass); +RoadStop * GetRoadStopByTile(TileIndex tile, RoadStopType type); +inline int GetRoadStopType(TileIndex tile); +uint GetNumRoadStops(const Station *st, RoadStopType type); +RoadStop * GetPrimaryRoadStop(const Station *st, RoadStopType type); +RoadStop * GetFirstFreeRoadStop( void ); + #endif /* STATION_H */ diff --git a/station_cmd.c b/station_cmd.c index ebf77d903..d8cc5800b 100644 --- a/station_cmd.c +++ b/station_cmd.c @@ -43,14 +43,79 @@ static void MarkStationDirty(Station *st) } } +static void InitializeRoadStop(RoadStop *road_stop, RoadStop *previous, TileIndex tile, uint index) +{ + road_stop->xy = tile; + road_stop->used = true; + road_stop->status = 3; //stop is free + road_stop->slot[0] = road_stop->slot[1] = INVALID_SLOT; + road_stop->next = NULL; + road_stop->prev = previous; + road_stop->station = index; +} + +inline int GetRoadStopType(TileIndex tile) +{ + return (_map5[tile] < 0x47) ? RS_TRUCK : RS_BUS; +} + +RoadStop * GetPrimaryRoadStop(const Station *st, RoadStopType type) +{ + switch (type) { + case RS_BUS: return st->bus_stops; + case RS_TRUCK: return st->truck_stops; + default: + NOT_REACHED(); + } + + return NULL; +} + +RoadStop * GetRoadStopByTile(TileIndex tile, RoadStopType type) +{ + const Station *st = GetStation(_map2[tile]); + RoadStop *rs; + + for ( rs = GetPrimaryRoadStop(st, type); rs->xy != tile; rs = rs->next) + assert(rs->next != NULL); + + return rs; +} + +uint GetNumRoadStops(const Station *st, RoadStopType type) +{ + int num = 0; + const RoadStop *rs; + + assert(st != NULL); + for ( rs = GetPrimaryRoadStop(st, type); rs != NULL; num++, rs = rs->next); + + return num; +} + +RoadStop * GetFirstFreeRoadStop( void ) +{ + RoadStop *rs = _roadstops; + int i = 0; + + for ( i = 0; i < NUM_ROAD_STOPS; i++, rs++) { + if (!rs->used) { + rs->index = i; + return rs; + } + } + + return NULL; +} + /* Calculate the radius of the station. Basicly it is the biggest radius that is available within the station */ static byte FindCatchmentRadius(Station *st) { byte ret = 0; - if (st->bus_tile) ret = max(ret, CA_BUS); - if (st->lorry_tile) ret = max(ret, CA_TRUCK); + if (st->bus_stops != NULL) ret = max(ret, CA_BUS); + if (st->truck_stops != NULL) ret = max(ret, CA_TRUCK); if (st->train_tile) ret = max(ret, CA_TRAIN); if (st->dock_tile) ret = max(ret, CA_DOCK); @@ -101,7 +166,7 @@ TileIndex GetStationTileForVehicle(const Vehicle *v, const Station *st) case VEH_Train: return st->train_tile; case VEH_Aircraft: return st->airport_tile; case VEH_Ship: return st->dock_tile; - case VEH_Road: return (v->cargo_type == CT_PASSENGERS) ? st->bus_tile : st->lorry_tile; + case VEH_Road: return (v->cargo_type == CT_PASSENGERS) ? st->bus_stops->xy : st->truck_stops->xy; default: assert(false); return 0; @@ -326,7 +391,8 @@ static void StationInitialize(Station *st, TileIndex tile) GoodsEntry *ge; st->xy = tile; - st->bus_tile = st->lorry_tile = st->airport_tile = st->dock_tile = st->train_tile = 0; + st->airport_tile = st->dock_tile = st->train_tile = 0; + st->bus_stops = st->truck_stops = NULL; st->had_vehicle_of_type = 0; st->time_since_load = 255; st->time_since_unload = 255; @@ -352,8 +418,9 @@ static void StationInitialize(Station *st, TileIndex tile) static void UpdateStationVirtCoord(Station *st) { Point pt = RemapCoords2(TileX(st->xy) * 16, TileY(st->xy) * 16); + pt.y -= 32; - if (st->facilities&FACIL_AIRPORT && st->airport_type==AT_OILRIG) pt.y -= 16; + if (st->facilities & FACIL_AIRPORT && st->airport_type == AT_OILRIG) pt.y -= 16; SetDParam(0, st->index); SetDParam(1, st->facilities); @@ -498,10 +565,13 @@ void GetAcceptanceAroundTiles(uint *accepts, uint tile, int w, int h, int rad) static void UpdateStationAcceptance(Station *st, bool show_msg) { uint old_acc, new_acc; - TileIndex span[1+1+2+2+1]; + TileIndex *span; + RoadStop *cur_rs; int i; int min_x, min_y, max_x, max_y; int rad = 4; //Put this to surpress a compiler warning + int num = 0; + int num_bus, num_truck; uint accepts[NUM_CARGO]; // Don't update acceptance for a buoy @@ -511,33 +581,62 @@ static void UpdateStationAcceptance(Station *st, bool show_msg) /* old accepted goods types */ old_acc = GetAcceptanceMask(st); + if (st->train_tile != 0) num += 2; + if (st->airport_tile != 0) num += 2; + if (st->dock_tile != 0) num++; + + num_bus = GetNumRoadStops(st, RS_BUS); + num_truck = GetNumRoadStops(st, RS_TRUCK); + + num += (num_bus + num_truck); + + span = malloc(num * sizeof(*span)); + if (span == NULL) + error("UpdateStationAcceptance: Could not allocate memory"); + // Put all the tiles that span an area in the table. - span[3] = span[5] = 0; - span[0] = st->bus_tile; - span[1] = st->lorry_tile; - span[2] = st->train_tile; if (st->train_tile != 0) { - span[3] = st->train_tile + TILE_XY(st->trainst_w-1, st->trainst_h-1); + *span++ = st->train_tile; + *span++ = st->train_tile + TILE_XY(st->trainst_w-1, st->trainst_h-1); } - span[4] = st->airport_tile; + if (st->airport_tile != 0) { - span[5] = st->airport_tile + TILE_XY(_airport_size_x[st->airport_type]-1, _airport_size_y[st->airport_type]-1); + *span++ = st->airport_tile; + *span++ = st->airport_tile + TILE_XY(_airport_size_x[st->airport_type]-1, _airport_size_y[st->airport_type]-1); + } + + if (st->dock_tile != 0) + *span++ = st->dock_tile; + + cur_rs = st->bus_stops; + for (i = 0; i < num_bus; i++) { + *span++ = cur_rs->xy; + cur_rs = cur_rs->next; + } + + cur_rs = st->truck_stops; + for (i = 0; i < num_truck; i++) { + *span++ = cur_rs->xy; + cur_rs = cur_rs->next; } - span[6] = st->dock_tile; // Construct a rectangle from those points min_x = min_y = 0x7FFFFFFF; max_x = max_y = 0; - for(i=0; i!=7; i++) { - uint tile = span[i]; - if (tile) { + for(; num != 0; num--) { + TileIndex tile = *(--span); + if (tile != 0) { //assume there is no station at (0, 0) min_x = min(min_x, TileX(tile)); max_x = max(max_x, TileX(tile)); min_y = min(min_y, TileY(tile)); max_y = max(max_y, TileY(tile)); } } + + free(span); + span = NULL; + if (_patches.modified_catchment) { rad = FindCatchmentRadius(st); } else { @@ -546,7 +645,7 @@ static void UpdateStationAcceptance(Station *st, bool show_msg) // And retrieve the acceptance. if (max_x != 0) { - GetAcceptanceAroundTiles(accepts, TILE_XY(min_x, min_y), max_x - min_x + 1, max_y-min_y+1,rad); + GetAcceptanceAroundTiles(accepts, TILE_XY(min_x, min_y), max_x - min_x + 1, max_y-min_y+1, rad); } else { memset(accepts, 0, sizeof(accepts)); } @@ -1127,10 +1226,10 @@ ResolveStationSpriteGroup(struct SpriteGroup *spritegroup, struct Station *stat) value = stat->airport_type; break; case 0x82: - value = stat->truck_stop_status; + value = stat->truck_stops->status; break; case 0x83: - value = stat->bus_stop_status; + value = stat->bus_stops->status; break; case 0x86: value = stat->airport_flags & 0xFFFF; @@ -1260,16 +1359,47 @@ int32 DoConvertStationRail(uint tile, uint totype, bool exec) return _price.build_rail >> 1; } +void FindRoadStationSpot(bool truck_station, Station *st, RoadStop ***currstop, RoadStop **prev) +{ + RoadStop **primary_stop; + + primary_stop = (truck_station) ? &st->truck_stops : &st->bus_stops; + + if (*primary_stop == NULL) { + //we have no station of the type yet, so write a "primary station" + //(the one at st->foo_stops) + *currstop = primary_stop; + } else { + //there are stops already, so append to the end of the list + *prev = *primary_stop; + *currstop = &(*primary_stop)->next; + while (**currstop != NULL) { + *prev = (*prev)->next; + *currstop = &(**currstop)->next; + } + } +} + /* Build a bus station - * p1 - direction - * p2 - unused + * direction - direction of the stop exit + * type - 0 for Bus stops, 1 for truck stops */ -int32 CmdBuildBusStation(int x, int y, uint32 flags, uint32 p1, uint32 p2) +int32 CmdBuildRoadStop(int x, int y, uint32 flags, uint32 direction, uint32 type) { + RoadStop *road_stop; + RoadStop **currstop; + RoadStop *prev = NULL; uint tile; int32 cost; Station *st; + //Bus stops have a _map5 value of 0x47 + direction + //Truck stops have 0x43 + direction + byte gfxbase = (type) ? 0x43 : 0x47; + + //saveguard the parameters + if (direction > 3 || type > 1) + return CMD_ERROR; SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION); @@ -1278,7 +1408,8 @@ int32 CmdBuildBusStation(int x, int y, uint32 flags, uint32 p1, uint32 p2) if (!(flags & DC_NO_TOWN_RATING) && !CheckIfAuthorityAllows(tile)) return CMD_ERROR; - if ((cost=CheckFlatLandBelow(tile, 1, 1, flags, 1 << p1, NULL)) == CMD_ERROR) + cost = CheckFlatLandBelow(tile, 1, 1, flags, 1 << direction, NULL); + if (cost == CMD_ERROR) return CMD_ERROR; st = GetStationAround(tile, 1, 1, -1); @@ -1291,6 +1422,14 @@ int32 CmdBuildBusStation(int x, int y, uint32 flags, uint32 p1, uint32 p2) if (st!=NULL && st->facilities) st = NULL; } + //give us a road stop in the list, and check if something went wrong + road_stop = GetFirstFreeRoadStop(); + if (road_stop == NULL) + return_cmd_error( (type) ? STR_3008B_TOO_MANY_TRUCK_STOPS : STR_3008A_TOO_MANY_BUS_STOPS); + + if ( st != NULL && (GetNumRoadStops(st, RS_BUS) + GetNumRoadStops(st, RS_TRUCK) >= ROAD_STOP_LIMIT)) + return_cmd_error( (type) ? STR_3008B_TOO_MANY_TRUCK_STOPS : STR_3008A_TOO_MANY_BUS_STOPS); + if (st != NULL) { if (st->owner != OWNER_NONE && st->owner != _current_player) return_cmd_error(STR_3009_TOO_CLOSE_TO_ANOTHER_STATION); @@ -1298,8 +1437,7 @@ int32 CmdBuildBusStation(int x, int y, uint32 flags, uint32 p1, uint32 p2) if (!CheckStationSpreadOut(st, tile, 1, 1)) return CMD_ERROR; - if (st->bus_tile != 0) - return_cmd_error(STR_3044_TOO_CLOSE_TO_ANOTHER_BUS); + FindRoadStationSpot(type, st, &currstop, &prev); } else { Town *t; @@ -1309,6 +1447,8 @@ int32 CmdBuildBusStation(int x, int y, uint32 flags, uint32 p1, uint32 p2) st->town = t = ClosestTownFromTile(tile, (uint)-1); + FindRoadStationSpot(type, st, &currstop, &prev); + if (_current_player < MAX_PLAYERS && flags&DC_EXEC) SETBIT(t->have_ratings, _current_player); @@ -1321,13 +1461,17 @@ int32 CmdBuildBusStation(int x, int y, uint32 flags, uint32 p1, uint32 p2) StationInitialize(st, tile); } - cost += _price.build_bus_station; + cost += (type) ? _price.build_truck_station : _price.build_bus_station; if (flags & DC_EXEC) { - st->bus_tile = tile; + //point to the correct item in the _busstops or _truckstops array + *currstop = road_stop; + + //initialize an empty station + InitializeRoadStop(road_stop, prev, tile, st->index); + (*currstop)->type = type; if (!st->facilities) st->xy = tile; - st->facilities |= FACIL_BUS_STOP; - st->bus_stop_status = 3; + st->facilities |= (type) ? FACIL_TRUCK_STOP : FACIL_BUS_STOP; st->owner = _current_player; st->build_date = _date; @@ -1336,7 +1480,7 @@ int32 CmdBuildBusStation(int x, int y, uint32 flags, uint32 p1, uint32 p2) MP_SETTYPE(MP_STATION) | MP_MAPOWNER_CURRENT | MP_MAP2 | MP_MAP5 | MP_MAP3LO_CLEAR | MP_MAP3HI_CLEAR, st->index, /* map2 parameter */ - p1 + 0x47 /* map5 parameter */ + gfxbase + direction /* map5 parameter */ ); UpdateStationVirtCoordDirty(st); @@ -1347,143 +1491,60 @@ int32 CmdBuildBusStation(int x, int y, uint32 flags, uint32 p1, uint32 p2) } // Remove a bus station -static int32 RemoveBusStation(Station *st, uint32 flags) +static int32 RemoveRoadStop(Station *st, uint32 flags, TileIndex tile) { - uint tile; + RoadStop **primary_stop; + RoadStop *cur_stop; + bool is_truck = _map5[tile] < 0x47; if (_current_player != OWNER_WATER && !CheckOwnership(st->owner)) return CMD_ERROR; - tile = st->bus_tile; - - if (!EnsureNoVehicle(tile)) - return CMD_ERROR; - - if (flags & DC_EXEC) { - DoClearSquare(tile); - - st->bus_tile = 0; - st->facilities &= ~FACIL_BUS_STOP; - - UpdateStationVirtCoordDirty(st); - DeleteStationIfEmpty(st); + if (is_truck) { //truck stop + primary_stop = &st->truck_stops; + cur_stop = GetRoadStopByTile(tile, RS_TRUCK); + } else { + primary_stop = &st->bus_stops; + cur_stop = GetRoadStopByTile(tile, RS_BUS); } - return _price.remove_bus_station; -} - - -/* Build a truck station - * p1 - direction - * p2 - unused - */ -int32 CmdBuildTruckStation(int x, int y, uint32 flags, uint32 p1, uint32 p2) -{ - uint tile; - int32 cost = 0; - Station *st; - - SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION); + assert(cur_stop != NULL); - tile = TILE_FROM_XY(x,y); - - if (!(flags & DC_NO_TOWN_RATING) && !CheckIfAuthorityAllows(tile)) - return CMD_ERROR; - - if ((cost=CheckFlatLandBelow(tile, 1, 1, flags, 1 << p1, NULL)) == CMD_ERROR) - return CMD_ERROR; - - st = GetStationAround(tile, 1, 1, -1); - if (st == CHECK_STATIONS_ERR) + if (!EnsureNoVehicle(tile)) return CMD_ERROR; - /* Find a station close to us */ - if (st == NULL) { - st = GetClosestStationFromTile(tile, 8, _current_player); - if (st!=NULL && st->facilities) st = NULL; - } - - if (st != NULL) { - if (st->owner != OWNER_NONE && st->owner != _current_player) - return_cmd_error(STR_3009_TOO_CLOSE_TO_ANOTHER_STATION); - - if (!CheckStationSpreadOut(st, tile, 1, 1)) - return CMD_ERROR; - - if (st->lorry_tile != 0) - return_cmd_error(STR_3045_TOO_CLOSE_TO_ANOTHER_TRUCK); - } else { - Town *t; - - st = AllocateStation(); - if (st == NULL) - return CMD_ERROR; - - st->town = t = ClosestTownFromTile(tile, (uint)-1); - - if (_current_player < MAX_PLAYERS && flags&DC_EXEC) - SETBIT(t->have_ratings, _current_player); - - st->sign.width_1 = 0; - - if (!GenerateStationName(st, tile, 0)) - return CMD_ERROR; - - if (flags & DC_EXEC) - StationInitialize(st, tile); - } - - cost += _price.build_truck_station; - if (flags & DC_EXEC) { - st->lorry_tile = tile; - if (!st->facilities) st->xy = tile; - st->facilities |= FACIL_TRUCK_STOP; - st->truck_stop_status = 3; - st->owner = _current_player; - - st->build_date = _date; - - ModifyTile(tile, - MP_SETTYPE(MP_STATION) | MP_MAPOWNER_CURRENT | - MP_MAP2 | MP_MAP3LO_CLEAR | MP_MAP3HI_CLEAR | MP_MAP5, - st->index, /* map2 parameter */ - p1 + 0x43 /* map5 parameter */ - ); - - UpdateStationVirtCoordDirty(st); - UpdateStationAcceptance(st, false); - InvalidateWindow(WC_STATION_LIST, st->owner); - } - return cost; -} + DoClearSquare(tile); -// Remove a truck station -static int32 RemoveTruckStation(Station *st, uint32 flags) -{ - uint tile; + cur_stop->used = false; + if (cur_stop->prev != NULL) //alter previous stop + cur_stop->prev->next = cur_stop->next; - if (_current_player != OWNER_WATER && !CheckOwnership(st->owner)) - return CMD_ERROR; + if (cur_stop->next != NULL) //alter next stop + cur_stop->next->prev = cur_stop->prev; - tile = st->lorry_tile; + //we only had one stop left + if (cur_stop->next == NULL && cur_stop->prev == NULL) { - if (!EnsureNoVehicle(tile)) - return CMD_ERROR; + //so we remove ALL stops + *primary_stop = NULL; + st->facilities &= (is_truck) ? ~FACIL_TRUCK_STOP : ~FACIL_BUS_STOP; - if (flags & DC_EXEC) { - DoClearSquare(tile); - - st->lorry_tile = 0; - st->facilities &= ~FACIL_TRUCK_STOP; + } else if (cur_stop == *primary_stop) { + //removed the first stop in the list + //need to set the primary element to the next stop + *primary_stop = (*primary_stop)->next; + } UpdateStationVirtCoordDirty(st); DeleteStationIfEmpty(st); } - return _price.remove_truck_station; + return (is_truck) ? _price.remove_truck_station : _price.remove_bus_station; } + + // FIXME -- need to move to its corresponding Airport variable // Country Airfield (small) static const byte _airport_map5_tiles_country[] = { @@ -2215,7 +2276,7 @@ static const byte _enter_station_speedtable[12] = { static uint32 VehicleEnter_Station(Vehicle *v, uint tile, int x, int y) { - uint16 station_id; + uint16 station_id; //XXX should be stationindex byte dir; uint16 spd; @@ -2252,12 +2313,12 @@ static uint32 VehicleEnter_Station(Vehicle *v, uint tile, int x, int y) } } else if (v->type == VEH_Road) { if (v->u.road.state < 16 && (v->u.road.state&4)==0 && v->u.road.frame==0) { - Station *st = GetStation(_map2[tile]); byte m5 = _map5[tile]; byte *b, bb,state; if (IS_BYTE_INSIDE(m5, 0x43, 0x4B)) { - b = (m5 >= 0x47) ? &st->bus_stop_status : &st->truck_stop_status; + RoadStop *rs = GetRoadStopByTile(tile, GetRoadStopType(tile)); + b = &rs->status; bb = *b; @@ -2275,6 +2336,8 @@ static uint32 VehicleEnter_Station(Vehicle *v, uint tile, int x, int y) bb &= ~2; state += 2; } + + bb |= 0x80; *b = bb; v->u.road.state = state; } @@ -2625,7 +2688,8 @@ uint MoveGoodsToStation(uint tile, int w, int h, int type, uint amount) /* several stations around, find the two with the highest rating */ st2 = st1 = NULL; best_rating = best_rating2 = 0; - for(i=0; i!=8 && around[i] != 0xFF; i++) { + + for( i = 0; i != 8 && around[i] != 0xFF; i++) { if (around_ptr[i]->goods[type].rating >= best_rating) { best_rating2 = best_rating; st2 = st1; @@ -2686,8 +2750,8 @@ void BuildOilRig(uint tile) st->airport_flags = 0; st->airport_type = AT_OILRIG; st->xy = tile; - st->bus_tile = 0; - st->lorry_tile = 0; + st->bus_stops = NULL; + st->truck_stops = NULL; st->airport_tile = tile; st->dock_tile = tile; st->train_tile = 0; @@ -2768,11 +2832,8 @@ static int32 ClearTile_Station(uint tile, byte flags) { if (m5 < 0x43 || ( m5 >= 83 && m5 <= 114) ) return RemoveAirport(st, flags); - if (m5 < 0x47) - return RemoveTruckStation(st, flags); - if (m5 < 0x4B) - return RemoveBusStation(st, flags); + return RemoveRoadStop(st, flags, tile); if (m5 == 0x52) return RemoveBuoy(st, flags); @@ -2789,6 +2850,7 @@ void InitializeStations(void) int i; Station *s; + memset(_roadstops, 0, sizeof(_roadstops)); memset(_stations, 0, sizeof(_stations[0]) * _stations_size); i = 0; @@ -2820,14 +2882,27 @@ const TileTypeProcs _tile_type_station_procs = { GetSlopeTileh_Station, /* get_slope_tileh_proc */ }; +static const byte _roadstop_desc[] = { + SLE_VAR(RoadStop,xy, SLE_UINT32), + SLE_VAR(RoadStop,used, SLE_UINT8), + SLE_VAR(RoadStop,status, SLE_UINT8), + SLE_VAR(RoadStop,index, SLE_UINT32), + SLE_VAR(RoadStop,station, SLE_UINT16), + SLE_VAR(RoadStop,type, SLE_UINT8), + + SLE_REF(RoadStop,next, REF_ROADSTOPS), + SLE_REF(RoadStop,prev, REF_ROADSTOPS), + + SLE_ARR(RoadStop,slot, SLE_UINT16, NUM_SLOTS), + + SLE_END() +}; static const byte _station_desc[] = { SLE_CONDVAR(Station, xy, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), SLE_CONDVAR(Station, xy, SLE_UINT32, 6, 255), - SLE_CONDVAR(Station, bus_tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), - SLE_CONDVAR(Station, bus_tile, SLE_UINT32, 6, 255), - SLE_CONDVAR(Station, lorry_tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), - SLE_CONDVAR(Station, lorry_tile, SLE_UINT32, 6, 255), + SLE_CONDVAR(Station, bus_tile_obsolete, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), + SLE_CONDVAR(Station, lorry_tile_obsolete, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), SLE_CONDVAR(Station, train_tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), SLE_CONDVAR(Station, train_tile, SLE_UINT32, 6, 255), SLE_CONDVAR(Station, airport_tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), @@ -2850,8 +2925,10 @@ static const byte _station_desc[] = { SLE_VAR(Station,owner, SLE_UINT8), SLE_VAR(Station,facilities, SLE_UINT8), SLE_VAR(Station,airport_type, SLE_UINT8), - SLE_VAR(Station,truck_stop_status, SLE_UINT8), - SLE_VAR(Station,bus_stop_status, SLE_UINT8), + + // truck/bus_stop_status was stored here in savegame format 0 - 6 + SLE_CONDVAR(Station,truck_stop_status_obsolete, SLE_UINT8, 0, 5), + SLE_CONDVAR(Station,bus_stop_status_obsolete, SLE_UINT8, 0, 5), // blocked_months was stored here in savegame format 0 - 4.0 SLE_CONDVAR(Station,blocked_months_obsolete, SLE_UINT8, 0, 4), @@ -2865,7 +2942,10 @@ static const byte _station_desc[] = { SLE_CONDVAR(Station,stat_id, SLE_UINT8, 3, 255), SLE_CONDVAR(Station,build_date, SLE_UINT16, 3, 255), - // reserve extra space in savegame here. (currently 32 bytes) + SLE_CONDREF(Station,bus_stops, REF_ROADSTOPS, 6, 255), + SLE_CONDREF(Station,truck_stops, REF_ROADSTOPS, 6, 255), + + // reserve extra space in savegame here. (currently 28 bytes) SLE_CONDARR(NullStruct,null,SLE_FILE_U8 | SLE_VAR_NULL, 32, 2, 255), SLE_END() @@ -2887,8 +2967,9 @@ static const byte _goods_desc[] = { static void SaveLoad_STNS(Station *st) { int i; + SlObject(st, _station_desc); - for(i=0; i!=NUM_CARGO; i++) + for (i = 0; i != NUM_CARGO; i++) SlObject(&st->goods[i], _goods_desc); } @@ -2920,6 +3001,32 @@ static void Load_STNS(void) st->trainst_w = w; st->trainst_h = h; } + + if (_sl.full_version < 0x600) { + /* Convert old bus and truck tile to new-ones */ + RoadStop **currstop; + RoadStop *prev = NULL; + RoadStop *road_stop; + + if (st->bus_tile_obsolete != 0) { + road_stop = GetFirstFreeRoadStop(); + if (road_stop == NULL) + error("Station: too many busstations in savegame"); + + FindRoadStationSpot(RS_BUS, st, &currstop, &prev); + *currstop = road_stop; + InitializeRoadStop(road_stop, prev, st->bus_tile_obsolete, st->index); + } + if (st->lorry_tile_obsolete != 0) { + road_stop = GetFirstFreeRoadStop(); + if (road_stop == NULL) + error("Station: too many truckstations in savegame"); + + FindRoadStationSpot(RS_TRUCK, st, &currstop, &prev); + *currstop = road_stop; + InitializeRoadStop(road_stop, prev, st->lorry_tile_obsolete, st->index); + } + } } /* This is to ensure all pointers are within the limits of @@ -2928,7 +3035,28 @@ static void Load_STNS(void) _station_tick_ctr = 0; } +static void Save_ROADSTOP( void ) +{ + uint i; + + for (i = 0; i < lengthof(_roadstops); i++) { + if (_roadstops[i].used) { + SlSetArrayIndex(i); + SlObject(&_roadstops[i], _roadstop_desc); + } + } +} + +static void Load_ROADSTOP( void ) +{ + int index; + + while ((index = SlIterateArray()) != -1) + SlObject(&_roadstops[index], _roadstop_desc); +} + const ChunkHandler _station_chunk_handlers[] = { - { 'STNS', Save_STNS, Load_STNS, CH_ARRAY | CH_LAST}, + { 'STNS', Save_STNS, Load_STNS, CH_ARRAY }, + { 'ROAD', Save_ROADSTOP, Load_ROADSTOP, CH_ARRAY | CH_LAST}, }; @@ -496,6 +496,8 @@ static void InitializeDynamicVariables(void) _stations_size = lengthof(_stations); _station_sort = NULL; + _roadstops_size = lengthof(_roadstops); + _vehicles_size = lengthof(_vehicles); _vehicle_sort = NULL; @@ -1886,6 +1886,9 @@ static const byte _roadveh_desc[] = { SLE_VARX(offsetof(Vehicle,u)+offsetof(VehicleRoad,crashed_ctr), SLE_UINT16), SLE_VARX(offsetof(Vehicle,u)+offsetof(VehicleRoad,reverse_ctr), SLE_UINT8), + SLE_CONDREFX(offsetof(Vehicle,u)+offsetof(VehicleRoad,slot), REF_ROADSTOPS, 6, 255), + SLE_CONDVARX(offsetof(Vehicle,u)+offsetof(VehicleRoad,slotindex), SLE_UINT8, 6, 255), + SLE_CONDVARX(offsetof(Vehicle,u)+offsetof(VehicleRoad,slot_age), SLE_UINT8, 6, 255), // reserve extra space in savegame here. (currently 16 bytes) SLE_CONDARR(NullStruct,null,SLE_FILE_U64 | SLE_VAR_NULL, 2, 2, 255), @@ -47,6 +47,9 @@ typedef struct VehicleRoad { byte overtaking_ctr; uint16 crashed_ctr; byte reverse_ctr; + struct RoadStop *slot; + byte slotindex; + byte slot_age; } VehicleRoad; typedef struct VehicleSpecial { |