1
0
Fork 0
pull/8480/merge
Joan Josep 2024-07-16 13:13:27 +01:00 committed by GitHub
commit dca0a14abc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
120 changed files with 5969 additions and 1388 deletions

View File

@ -520,7 +520,7 @@
<li>m2 bit 11: opposite track is reserved, too</li>
</ul>
</li>
<li>m5 bit 7 set, bit 6 set: railway depot
<li>m5 bit 7 set, bit 6 clear: railway depot
<ul>
<li>m2: Depot index</li>
<li>m5 bits 1..0: exit towards
@ -547,6 +547,11 @@
</table>
</li>
<li>m5 bit 4: pbs reservation state</li>
<li>m5 bit 5 clear: standard depot</li>
<li>m5 bit 5 set: extended depot
<ul>
<li>m4 bits 6..7: depot reservation state</li>
</ul>
</ul>
</li>
</ul>
@ -666,7 +671,7 @@
<ul>
<li>m1 bits 4..0: <a href="#OwnershipInfo">owner</a> of the depot</li>
<li>m2: Depot index</li>
<li>m5 bits 1..0: exit towards:
<li>m6 bits 6..7: exit towards:
<table>
<tr>
<td><tt>0</tt>&nbsp; </td>
@ -689,6 +694,15 @@
</tr>
</table>
</li>
<li>m3 bits 0..3: road layout road type 1 (tram)</li>
<li>m5 bits 0..3: road layout road type 0 (normal road)</li>
<li>m5 bit 5 clear: standard depot</li>
<li>m5 bit 5 set: extended depot
<ul>
<li>m4 bits 6..7: depot reservation state towards north direction</li>
<li>m6 bits 4..5: depot reservation state towards south direction</li>
</ul>
</li>
<li>m7 bits 4..0: <a href="#OwnershipInfo">owner</a> of the road type 0 (normal road)</li>
</ul>
</li>
@ -1047,55 +1061,55 @@
</tr>
<tr>
<td nowrap valign=top><tt>10</tt>..<tt>1B</tt>&nbsp; </td>
<td nowrap valign=top><tt>40</tt>..<tt>4B</tt>&nbsp; </td>
<td align=left>canal locks
<table>
<tr>
<td nowrap valign=top><tt>10</tt>&nbsp; </td>
<td nowrap valign=top><tt>40</tt>&nbsp; </td>
<td align=left>middle part, (SW-NE direction)</td>
</tr>
<tr>
<td nowrap valign=top><tt>11</tt>&nbsp; </td>
<td nowrap valign=top><tt>41</tt>&nbsp; </td>
<td align=left>middle part, (NW-SE direction)</td>
</tr>
<tr>
<td nowrap valign=top><tt>12</tt>&nbsp; </td>
<td nowrap valign=top><tt>42</tt>&nbsp; </td>
<td align=left>middle part, (NE-SW direction)</td>
</tr>
<tr>
<td nowrap valign=top><tt>13</tt>&nbsp; </td>
<td nowrap valign=top><tt>43</tt>&nbsp; </td>
<td align=left>middle part, (SE-NW direction)</td>
</tr>
<tr>
<td nowrap valign=top><tt>14</tt>&nbsp; </td>
<td nowrap valign=top><tt>44</tt>&nbsp; </td>
<td align=left>lower part, (SW-NE direction)</td>
</tr>
<tr>
<td nowrap valign=top><tt>15</tt>&nbsp; </td>
<td nowrap valign=top><tt>45</tt>&nbsp; </td>
<td align=left>lower part, (NW-SE direction)</td>
</tr>
<tr>
<td nowrap valign=top><tt>16</tt>&nbsp; </td>
<td nowrap valign=top><tt>46</tt>&nbsp; </td>
<td align=left>lower part, (NE-SW direction)</td>
</tr>
<tr>
<td nowrap valign=top><tt>17</tt>&nbsp; </td>
<td nowrap valign=top><tt>47</tt>&nbsp; </td>
<td align=left>lower part, (SE-NW direction)</td>
</tr>
<tr>
<td nowrap valign=top><tt>18</tt>&nbsp; </td>
<td nowrap valign=top><tt>48</tt>&nbsp; </td>
<td align=left>upper part, (SW-NE direction)</td>
</tr>
<tr>
<td nowrap valign=top><tt>19</tt>&nbsp; </td>
<td nowrap valign=top><tt>49</tt>&nbsp; </td>
<td align=left>upper part, (NW-SE direction)</td>
</tr>
<tr>
<td nowrap valign=top><tt>1A</tt>&nbsp; </td>
<td nowrap valign=top><tt>4A</tt>&nbsp; </td>
<td align=left>upper part, (NE-SW direction)</td>
</tr>
<tr>
<td nowrap valign=top><tt>1B</tt>&nbsp; </td>
<td nowrap valign=top><tt>4B</tt>&nbsp; </td>
<td align=left>upper part, (SE-NW direction)</td>
</tr>
</table>
@ -1127,6 +1141,11 @@
</tr>
</table>
</li>
<li>m5 bit 5 clear: standard depot (for depots only)</li>
<li>m5 bit 5 set: extended depot
<ul>
<li>m4 bits 6..7: depot reservation state (for depots only)</li>
</ul>
</ul>
</td>
</tr>

View File

@ -79,8 +79,8 @@ the array so you can quickly see what is used and what is not.
<tr>
<td rowspan="2">0</td>
<td class="caption">ground</td>
<td class="bits" rowspan=28><span class="used" title="Tile type">XXXX</span> <span class="used" title="Presence and direction of bridge above">XX</span> <span class="used" title="Tropic Zone: only meaningful in tropic climate. It contains the definition of the available zones">XX</span></td>
<td class="bits" rowspan=28><span class="used" title="Tile height">XXXX XXXX</span></td>
<td class="bits" rowspan=31><span class="used" title="Tile type">XXXX</span> <span class="used" title="Presence and direction of bridge above">XX</span> <span class="used" title="Tropic Zone: only meaningful in tropic climate. It contains the definition of the available zones">XX</span></td>
<td class="bits" rowspan=31><span class="used" title="Tile height">XXXX XXXX</span></td>
<td class="bits" rowspan=2><span class="free">OOO</span><span class="usable" title="Owner (always OWNER_NONE)">1 OOOO</span></td>
<td class="bits"><span class="free">OOOO OOOO OOOO OOOO</span></td>
<td class="bits"><span class="used" title="Type of hedge on NE border">XXX</span> <span class="used" title="Snow presence">X</span><span class="free">OOOO</span></td>
@ -95,16 +95,16 @@ the array so you can quickly see what is used and what is not.
<td class="bits"><span class="used" title="Type of hedge on NE border">XXX</span> <span class="used" title="Field production stage">XXXXX</span></td>
</tr>
<tr>
<td rowspan=3>1</td>
<td rowspan=4>1</td>
<td class="caption">rail</td>
<td class="bits" rowspan=3><span class="used" title="Ship docking tile status (for half-tile with water)">X</span><span class="free">OO</span><span class="used" title="Owner">X XXXX</span></td>
<td class="bits" rowspan=4><span class="used" title="Ship docking tile status (for half-tile with water)">X</span><span class="free">OO</span><span class="used" title="Owner">X XXXX</span></td>
<td class="bits"><span class="free">OOOO</span> <span class="used" title="Reserved tracks">XXXX</span> <span class="free">OOOO OOOO</span></td>
<td class="bits"><span class="free">OOOO OOOO</span> </td>
<td class="bits"><span class="free">OOOO</span> <span class="used" title="Ground type: fences, snow, desert">XXXX</span></td>
<td class="bits"><span class="used" title="Rail tile type: rail, rail with signals, depot">OO</span> <span class="used" title="Track pieces">XXXXXX</span></td>
<td class="bits" rowspan=3><span class="free">OOOO OOOO</span></td>
<td class="bits" rowspan=3><span class="free">OOOO OOOO</span></td>
<td class="bits" rowspan=3><span class="free">OOOO OOOO OO</span><span class="used" title="Railway type">XX XXXX</span></td>
<td class="bits" rowspan=4><span class="free">OOOO OOOO</span></td>
<td class="bits" rowspan=4><span class="free">OOOO OOOO</span></td>
<td class="bits" rowspan=4><span class="free">OOOO OOOO OO</span><span class="used" title="Railway type">XX XXXX</span></td>
</tr>
<tr>
<td class="caption">rail with signals</td>
@ -114,22 +114,27 @@ the array so you can quickly see what is used and what is not.
<td class="bits"><span class="used" title="Rail tile type: rail, rail with signals, depot">O1</span> <span class="used" title="Track pieces">XXXXXX</span></td>
</tr>
<tr>
<td class="caption">depot</td>
<td class="bits"><span class="pool" title="Depot index on pool">XXXX XXXX XXXX XXXX</span></td>
<td class="bits"><span class="free">OOOO</span> <span class="free">OOOO</span></td>
<td class="caption">standard rail depot</td>
<td class="bits" rowspan=2><span class="pool" title="Depot index on pool">XXXX XXXX XXXX XXXX</span></td>
<td class="bits" rowspan=2><span class="free">OOOO</span> <span class="free">OOOO</span></td>
<td class="bits"><span class="free">OOOO</span> <span class="used" title="Ground type: fences, snow, desert (fences on depot are not valid)">XXXX</span></td>
<td class="bits"><span class="used" title="Rail tile type: rail, rail with signals, depot">11</span><span class="free">O</span><span class="used" title="PBS reservation">X</span> <span class="free">OO</span><span class="used" title="Depot exit direction">XX</span></td>
<td class="bits"><span class="used" title="Rail tile type: rail, rail with signals, depot">1O</span><span class="patch" title="Big/Small depot">O</span><span class="used" title="PBS reservation">X</span> <span class="free">OO</span><span class="used" title="Depot exit direction">XX</span></td>
</tr>
<tr>
<td rowspan=3>2</td>
<td class="caption"><span class="patch">extended rail depot</span></td>
<td class="bits"><span class="patch" title="Type of reservation in this depot">XX</span><span class="free">OO</span> <span class="used" title="Ground type: fences, snow, desert (fences on depot are not valid)">XXXX</span></td>
<td class="bits"><span class="used" title="Rail tile type: rail, rail with signals, depot">1O</span><span class="patch" title="Big/Small depot">1</span><span class="used" title="PBS reservation">X</span> <span class="free">OO</span><span class="used" title="Depot exit direction">XX</span></td>
</tr>
<tr>
<td rowspan=4>2</td>
<td class="caption">road</td>
<td class="bits"><span class="free">OOO</span><span class="used" title="Owner of road">X XXXX</span></td>
<td class="bits" rowspan=2><span class="pool" title="Town index on pool">XXXX XXXX XXXX XXXX</span></td>
<td class="bits"><span class="used" title="Owner of tram">XXXX</span> <span class="used" title="Tram pieces">XXXX</span></td>
<td class="bits" rowspan=3><span class="free">OO</span><span class="used" title="Road type">XX XXXX</span></td>
<td class="bits" rowspan=3><span class="free">OO</span> <span class="used" title="Road type">XXXXXX</span></td>
<td class="bits"><span class="used" title="Tile type: simple road (00), level crossing (01), road depot (10)">OO</span> <span class="used" title="Disallow vehicles to go a specific direction">XX</span> <span class="used" title="Road pieces">XXXX</span></td>
<td class="bits"><span class="free">OO</span> <span class="used" title="Pavement type">XXX</span><span class="free">OOO</span></td>
<td class="bits"><span class="free">OO</span><span class="used" title="Snow/desert present">X</span> <span class="free">O</span><span class="used" title="Roadworks counter">XXXX</span></td>
<td class="bits"><span class="free">OO</span><span class="used" title="Pavement type">XX X</span><span class="free">OOO</span></td>
<td class="bits"><span class="free">OO</span><span class="used" title="Snow/desert present">X</span><span class="free">O</span> <span class="used" title="Roadworks counter">XXXX</span></td>
<td class="bits" rowspan=1><span class="free">OOOO</span> <span class="used" title="Tram type">XXXX XX<span class="free">OO OOOO</span></td>
</tr>
<tr>
@ -137,19 +142,26 @@ the array so you can quickly see what is used and what is not.
<td class="bits"><span class="free">OOO</span><span class="used" title="Owner of rail track">X XXXX</span></td>
<td class="bits"><span class="used" title="Owner of tram">XXXX</span> <span class="free">OOOO</span></td>
<td class="bits"><span class="used" title="Tile type: simple road (00), level crossing (01), road depot (10)">O1</span> <span class="used" title="Lights are on">X</span> <span class="used" title="PBS reservation">X</span><span class="free">OOO</span><span class="used" title="Direction of the rail and roads">X</span></td>
<td class="bits"><span class="free">OO</span> <span class="used" title="Pavement type">XXX</span><span class="free">OOO</span></td>
<td class="bits"><span class="free">OO</span><span class="used" title="Pavement type">XX X</span><span class="free">OOO</span></td>
<td class="bits"><span class="free">OO</span><span class="used" title="Snow/desert present">X</span> <span class="used" title="Owner of road">XXXXX</span></td>
<td class="bits" rowspan=1><span class="free">OOOO</span> <span class="used" title="Tram type">XXXX XX</span> <span class="used" title="Railway type">XXXXXX</span></td>
</tr>
<tr>
<td class="caption">road depot</td>
<td class="bits"><span class="free">OOO</span><span class="used" title="Owner of the depot">X XXXX</span></td>
<td class="bits"><span class="pool" title="Depot index on pool">XXXX XXXX XXXX XXXX</span></td>
<td class="bits"><span class="usable" title="Owner of tram depot">XXXX</span> <span class="free">OOOO</span></td>
<td class="bits"><span class="used" title="Tile type: simple road (00), level crossing (01), road depot (10)">1O</span><span class="free">OO OO</span><span class="used" title="Depot exit direction">XX</span></td>
<td class="bits"><span class="free">OOOO OOOO</span></td>
<td class="bits"><span class="free">OO</span><span class="used" title="Snow/desert present">X</span> <span class="usable" title="Owner of road depot">XXXXX</span></td>
<td class="bits" rowspan=1><span class="free">OOOO</span> <span class="used" title="Tram type">XXXX XX</span><span class="free">OO OOOO</span></td>
<td class="caption">standard road depot</td>
<td class="bits" rowspan=2><span class="free">OOO</span><span class="used" title="Owner of the depot">X XXXX</span></td>
<td class="bits" rowspan=2><span class="pool" title="Depot index on pool">XXXX XXXX XXXX XXXX</span></td>
<td class="bits" rowspan=2><span class="usable" title="Owner of tram depot. The owner in .m1 is enough">XXXX</span> <span class="patch" title="Tram pieces">XXXX</span></td>
<td class="bits"><span class="used" title="Tile type: simple road (00), level crossing (01), road depot (10)">1O</span><span class="patch" title="Big/Small depot">O</span><span class="free">O</span> <span class="patch" title="Road pieces">XXXX</span></td>
<td class="bits"><span class="used" title="Depot exit direction">XX</span><span class="free">OO OOOO</span></td>
<td class="bits" rowspan=2><span class="free">OO</span><span class="used" title="Snow/desert present">X</span> <span class="usable" title="Owner of road depot. The owner in .m1 is enough">XXXXX</span></td>
<td class="bits" rowspan=2><span class="free">OOOO</span> <span class="used" title="Tram type">XXXX XX</span><span class="free">OO OOOO</span></td>
</tr>
<tr>
<td class="caption"><span class="patch">extended road depot</span></td>
<td class="bits" rowspan=1><span class="patch" title="Type of reservation in this depot towards north direction">XX</span> <span class="used" title="Road type">XXXXXX</span></td>
<td class="bits"><span class="used" title="Tile type: simple road (00), level crossing (01), road depot (10)">1O</span><span class="patch" title="Big/Small depot">1</span><span class="free">O</span> <span class="patch" title="Road pieces">XXXX</span></td>
<td class="bits"><span class="used" title="Depot exit direction">XX</span> <span class="patch" title="Type of reservation in this depot towards south direction">XX</span><span class="free">OOOO</span></td>
</tr>
<tr>
<td rowspan=2>3</td>
@ -240,32 +252,37 @@ the array so you can quickly see what is used and what is not.
<td class="bits"><span class="free">OOOO OOOO</span></td>
</tr>
<tr>
<td rowspan=4>6</td>
<td rowspan=5>6</td>
<td class="caption">sea, shore</td>
<td class="bits" rowspan=4><span class="used" title="Ship docking tile status">X</span> <span class="used" title="Water class">XX</span> <span class="used" title="Owner">XXXXX</span></td>
<td class="bits" rowspan=5><span class="used" title="Ship docking tile status">X</span> <span class="used" title="Water class">XX</span> <span class="used" title="Owner">XXXXX</span></td>
<td class="bits" rowspan=3><span class="free">OOOO OOOO OOOO OOOO</span></td>
<td class="bits" rowspan=4><span class="free">OOOO OOOO</span></td>
<td class="bits" rowspan=5><span class="free">OOOO OOOO</span></td>
<td class="bits"><span class="free">OOOO OOOO</span></td>
<td class="bits"><span class="used" title="Water tile type: coast, clear, lock, depot">O<span class="usable">OO</span>O</span> <span class="free">OOO</span><span class="used" title="Sea shore flag">X</span></td>
<td class="bits" rowspan=4><span class="free">OOOO OOOO</span></td>
<td class="bits" rowspan=4><span class="free">OOOO OOOO</td>
<td class="bits" rowspan=4><span class="free">OOOO OOOO OOOO OOOO</span></td>
<td class="bits"><span class="used" title="Water tile type: coast or water (00), lock (01), depot (10)">OO</span><span class="free">OO OOO</span><span class="used" title="Sea shore flag">X</span></td>
<td class="bits" rowspan=5><span class="free">OOOO OOOO</span></td>
<td class="bits" rowspan=5><span class="free">OOOO OOOO</td>
<td class="bits" rowspan=5><span class="free">OOOO OOOO OOOO OOOO</span></td>
</tr>
<tr>
<td class="caption">canal, river</td>
<td class="bits"><span class="used" title="Canal/river random bits">XXXX XXXX</span></td>
<td class="bits"><span class="used" title="Water tile type: coast, clear, lock, depot">O<span class="usable">OO</span>O</span> <span class="free">OOOO</span></td>
<td class="bits"><span class="used" title="Water tile type: coast or water (00), lock (01), depot (10)">OO</span><span class="free">OO OOOO</span></td>
</tr>
<tr>
<td class="caption">lock</td>
<td class="bits"><span class="free">OOOO OOOO</span></td>
<td class="bits"><span class="used" title="Water tile type: coast, clear, lock, depot">O<span class="usable">OO</span>1</span> <span class="used" title="Lock part">XX</span> <span class="used" title="Lock orientation m5[1..0]">XX</span></td>
<td class="bits"><span class="used" title="Water tile type: coast or water (00), lock (01), depot (10)">O1</span><span class="free">OO</span><span class="used" title="Lock part">XX</span> <span class="used" title="Lock orientation m5[1..0]">XX</span></td>
</tr>
<tr>
<td class="caption">shipdepot</td>
<td class="bits"><span class="pool" title="Depot index on pool">XXXX XXXX XXXX XXXX</span></td>
<td class="caption">standard ship depot</td>
<td class="bits" rowspan=2><span class="pool" title="Depot index on pool">XXXX XXXX XXXX XXXX</span></td>
<td class="bits"><span class="free">OOOO OOOO</span></td>
<td class="bits"><span class="used" title="Water tile type: coast, clear, lock, depot">1<span class="usable">OOO</span></span> <span class="free">OO</span><span class="used" title="Depot axis">X</span> <span class="used" title="Depot part">X</span></td>
<td class="bits"><span class="used" title="Water tile type: coast or water (00), lock (01), depot (10)">1O</span><span class="free">OOOO</span><span class="used" title="Depot axis">X</span> <span class="used" title="Depot part">X</span></td>
</tr>
<tr>
<td class="caption"><span class="patch">extended ship depot</span></td>
<td class="bits"><span class="patch" title="Type of reservation in this depot">XX</span><span class="free">OO</span> <span class="free">OOOO</span></td>
<td class="bits"><span class="used" title="Water tile type: coast or water (00), lock (01), depot (10)">1O</span><span class="patch" title="Big/Small depot">1</span><span class="free">OOO</span><span class="used" title="Depot axis">X</span> <span class="used" title="Depot part">X</span></td>
</tr>
<tr>
<td rowspan=2>8</td>

View File

@ -7364,7 +7364,7 @@ ERROR: IsEnd() is invalid as Begin() is never called
IsBuoyTile(): false
IsLockTile(): false
IsCanalTile(): false
GetBankBalance(): 1999979304
GetBankBalance(): 1999979244
BuildWaterDepot(): true
BuildDock(): true
BuildBuoy(): true
@ -7377,7 +7377,7 @@ ERROR: IsEnd() is invalid as Begin() is never called
IsBuoyTile(): true
IsLockTile(): true
IsCanalTile(): true
GetBankBalance(): 1999964680
GetBankBalance(): 1999964620
--AIWaypointList(BUOY)--
Count(): 1
@ -7396,7 +7396,7 @@ ERROR: IsEnd() is invalid as Begin() is never called
IsBuoyTile(): false
IsLockTile(): false
IsCanalTile(): false
GetBankBalance(): 1999959285
GetBankBalance(): 1999959225
BuildWaterDepot(): true
BuildDock(): true
BuildBuoy(): true

View File

@ -342,6 +342,9 @@ add_files(
picker_func.h
picker_gui.cpp
picker_gui.h
platform_func.h
platform_type.h
platform.cpp
progress.cpp
progress.h
querystring_gui.h
@ -501,6 +504,8 @@ add_files(
train.h
train_cmd.cpp
train_cmd.h
train_placement.cpp
train_placement.h
train_gui.cpp
transparency.h
transparency_gui.cpp

View File

@ -41,6 +41,7 @@
#include "framerate_type.h"
#include "aircraft_cmd.h"
#include "vehicle_cmd.h"
#include "depot_base.h"
#include "table/strings.h"
@ -136,7 +137,7 @@ static StationID FindNearestHangar(const Aircraft *v)
if (v->current_order.IsType(OT_GOTO_STATION) ||
(v->current_order.IsType(OT_GOTO_DEPOT) && (v->current_order.GetDepotActionType() & ODATFB_NEAREST_DEPOT) == 0)) {
last_dest = Station::GetIfValid(v->last_station_visited);
next_dest = Station::GetIfValid(v->current_order.GetDestination());
next_dest = Station::GetIfValid(GetTargetDestination(v->current_order, true));
} else {
last_dest = GetTargetAirportIfValid(v);
next_dest = Station::GetIfValid(v->GetNextStoppingStation().value);
@ -407,9 +408,10 @@ ClosestDepot Aircraft::FindClosestDepot()
if (station == INVALID_STATION) return ClosestDepot();
st = Station::Get(station);
assert(st->airport.hangar != nullptr);
}
return ClosestDepot(st->xy, st->index);
return ClosestDepot(st->xy, st->airport.hangar->index);
}
static void CheckIfAircraftNeedsService(Aircraft *v)
@ -424,13 +426,13 @@ static void CheckIfAircraftNeedsService(Aircraft *v)
* we don't want to consider going to a depot too. */
if (!v->current_order.IsType(OT_GOTO_DEPOT) && !v->current_order.IsType(OT_GOTO_STATION)) return;
const Station *st = Station::Get(v->current_order.GetDestination());
const Station *st = Station::Get(GetTargetDestination(v->current_order, true));
assert(st != nullptr);
/* only goto depot if the target airport has a depot */
if (st->airport.HasHangar() && CanVehicleUseStation(v, st)) {
v->current_order.MakeGoToDepot(st->index, ODTFB_SERVICE);
v->current_order.MakeGoToDepot(st->airport.hangar->index, ODTFB_SERVICE);
SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
} else if (v->current_order.IsType(OT_GOTO_DEPOT)) {
v->current_order.MakeDummy();
@ -892,7 +894,7 @@ static bool AircraftController(Aircraft *v)
/* Jump into our "holding pattern" state machine if possible */
if (v->pos >= afc->nofelements) {
v->pos = v->previous_pos = AircraftGetEntryPoint(v, afc, DIR_N);
} else if (v->targetairport != v->current_order.GetDestination()) {
} else if (v->targetairport != GetTargetDestination(v->current_order, true)) {
/* If not possible, just get out of here fast */
v->state = FLYING;
UpdateAircraftCache(v);
@ -1449,7 +1451,7 @@ static void AircraftLandAirplane(Aircraft *v)
void AircraftNextAirportPos_and_Order(Aircraft *v)
{
if (v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_DEPOT)) {
v->targetairport = v->current_order.GetDestination();
v->targetairport = GetTargetDestination(v->current_order, true);
}
const Station *st = GetTargetAirportIfValid(v);
@ -1488,7 +1490,7 @@ void AircraftLeaveHangar(Aircraft *v, Direction exit_dir)
VehicleServiceInDepot(v);
v->LeaveUnbunchingDepot();
SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
if (IsHangarTile(v->tile)) InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile));
SetWindowClassesDirty(WC_AIRCRAFT_LIST);
}
@ -1539,7 +1541,7 @@ static void AircraftEventHandler_InHangar(Aircraft *v, const AirportFTAClass *ap
return;
/* We are leaving a hangar, but have to go to the exact same one; re-enter */
if (v->current_order.IsType(OT_GOTO_DEPOT) && v->current_order.GetDestination() == v->targetairport) {
if (v->current_order.IsType(OT_GOTO_DEPOT) && GetTargetDestination(v->current_order, true) == v->targetairport) {
VehicleEnterDepot(v);
return;
}
@ -1548,7 +1550,7 @@ static void AircraftEventHandler_InHangar(Aircraft *v, const AirportFTAClass *ap
if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
/* We are already at the target airport, we need to find a terminal */
if (v->current_order.GetDestination() == v->targetairport) {
if (GetTargetDestination(v->current_order, true) == v->targetairport) {
/* FindFreeTerminal:
* 1. Find a free terminal, 2. Occupy it, 3. Set the vehicle's state to that terminal */
if (v->subtype == AIR_HELICOPTER) {
@ -1599,7 +1601,7 @@ static void AircraftEventHandler_AtTerminal(Aircraft *v, const AirportFTAClass *
case OT_GOTO_STATION: // ready to fly to another airport
break;
case OT_GOTO_DEPOT: // visit hangar for servicing, sale, etc.
go_to_hangar = v->current_order.GetDestination() == v->targetairport;
go_to_hangar = GetTargetDestination(v->current_order, true) == v->targetairport;
break;
case OT_CONDITIONAL:
/* In case of a conditional order we just have to wait a tick
@ -2103,7 +2105,7 @@ static bool AircraftEventHandler(Aircraft *v, int loop)
/* Check the distance to the next destination. This code works because the target
* airport is only updated after take off and not on the ground. */
Station *cur_st = Station::GetIfValid(v->targetairport);
Station *next_st = v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_DEPOT) ? Station::GetIfValid(v->current_order.GetDestination()) : nullptr;
Station *next_st = Station::GetIfValid(GetTargetDestination(v->current_order, true));
if (cur_st != nullptr && cur_st->airport.tile != INVALID_TILE && next_st != nullptr && next_st->airport.tile != INVALID_TILE) {
uint dist = DistanceSquare(cur_st->airport.tile, next_st->airport.tile);
@ -2169,18 +2171,7 @@ void UpdateAirplanesOnNewStation(const Station *st)
if (!v->IsNormalAircraft() || v->targetairport != st->index) continue;
assert(v->state == FLYING);
Order *o = &v->current_order;
/* The aircraft is heading to a hangar, but the new station doesn't have one,
* or the aircraft can't land on the new station. Cancel current order. */
if (o->IsType(OT_GOTO_DEPOT) && !(o->GetDepotOrderType() & ODTFB_PART_OF_ORDERS) && o->GetDestination() == st->index &&
(!st->airport.HasHangar() || !CanVehicleUseStation(v, st))) {
o->MakeDummy();
SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
}
v->pos = v->previous_pos = AircraftGetEntryPoint(v, ap, rotation);
UpdateAircraftCache(v);
}
/* Heliports don't have a hangar. Invalidate all go to hangar orders from all aircraft. */
if (!st->airport.HasHangar()) RemoveOrderFromAllVehicles(OT_GOTO_DEPOT, st->index, true);
}

View File

@ -20,6 +20,7 @@
#include "core/random_func.hpp"
#include "vehiclelist.h"
#include "road.h"
#include "ship.h"
#include "ai/ai.hpp"
#include "news_func.h"
#include "strings_func.h"
@ -28,6 +29,9 @@
#include "order_cmd.h"
#include "train_cmd.h"
#include "vehicle_cmd.h"
#include "depot_map.h"
#include "train_placement.h"
#include "news_func.h"
#include "table/strings.h"
@ -71,7 +75,10 @@ bool CheckAutoreplaceValidity(EngineID from, EngineID to, CompanyID company)
switch (type) {
case VEH_TRAIN: {
/* make sure the railtypes are compatible */
if ((GetRailTypeInfo(e_from->u.rail.railtype)->compatible_railtypes & GetRailTypeInfo(e_to->u.rail.railtype)->compatible_railtypes) == 0) return false;
if (!_settings_game.depot.allow_no_comp_railtype_replacements &&
(GetRailTypeInfo(e_from->u.rail.railtype)->compatible_railtypes & GetRailTypeInfo(e_to->u.rail.railtype)->compatible_railtypes) == 0) {
return false;
}
/* make sure we do not replace wagons with engines or vice versa */
if ((e_from->u.rail.railveh_type == RAILVEH_WAGON) != (e_to->u.rail.railveh_type == RAILVEH_WAGON)) return false;
@ -79,11 +86,15 @@ bool CheckAutoreplaceValidity(EngineID from, EngineID to, CompanyID company)
}
case VEH_ROAD:
if (!_settings_game.depot.allow_no_comp_roadtype_replacements) {
/* make sure the roadtypes are compatible */
if ((GetRoadTypeInfo(e_from->u.road.roadtype)->powered_roadtypes & GetRoadTypeInfo(e_to->u.road.roadtype)->powered_roadtypes) == ROADTYPES_NONE) return false;
if ((GetRoadTypeInfo(e_from->u.road.roadtype)->powered_roadtypes & GetRoadTypeInfo(e_to->u.road.roadtype)->powered_roadtypes) == ROADTYPES_NONE) {
return false;
}
/* make sure that we do not replace a tram with a normal road vehicles or vice versa */
if (HasBit(e_from->info.misc_flags, EF_ROAD_TRAM) != HasBit(e_to->info.misc_flags, EF_ROAD_TRAM)) return false;
}
break;
case VEH_AIRCRAFT:
@ -364,13 +375,13 @@ static CommandCost BuildReplacementVehicle(Vehicle *old_veh, Vehicle **new_vehic
if (refit_cargo != CARGO_NO_REFIT) {
uint8_t subtype = GetBestFittingSubType(old_veh, new_veh, refit_cargo);
cost.AddCost(std::get<0>(Command<CMD_REFIT_VEHICLE>::Do(DC_EXEC, new_veh->index, refit_cargo, subtype, false, false, 0)));
cost.AddCost(std::get<0>(Command<CMD_REFIT_VEHICLE>::Do(DC_EXEC | DC_AUTOREPLACE, new_veh->index, refit_cargo, subtype, false, false, 0)));
assert(cost.Succeeded()); // This should be ensured by GetNewCargoTypeForReplace()
}
/* Try to reverse the vehicle, but do not care if it fails as the new type might not be reversible */
if (new_veh->type == VEH_TRAIN && HasBit(Train::From(old_veh)->flags, VRF_REVERSE_DIRECTION)) {
Command<CMD_REVERSE_TRAIN_DIRECTION>::Do(DC_EXEC, new_veh->index, true);
Command<CMD_REVERSE_TRAIN_DIRECTION>::Do(DC_EXEC | DC_AUTOREPLACE, new_veh->index, true);
}
return cost;
@ -465,7 +476,7 @@ static CommandCost ReplaceFreeUnit(Vehicle **single_unit, DoCommandFlag flags, b
if ((flags & DC_EXEC) != 0) {
/* Move the new vehicle behind the old */
CmdMoveVehicle(new_v, old_v, DC_EXEC, false);
CmdMoveVehicle(new_v, old_v, DC_EXEC | DC_AUTOREPLACE, false);
/* Take over cargo
* Note: We do only transfer cargo from the old to the new vehicle.
@ -485,7 +496,7 @@ static CommandCost ReplaceFreeUnit(Vehicle **single_unit, DoCommandFlag flags, b
/* If we are not in DC_EXEC undo everything */
if ((flags & DC_EXEC) == 0) {
Command<CMD_SELL_VEHICLE>::Do(DC_EXEC, new_v->index, false, false, INVALID_CLIENT_ID);
Command<CMD_SELL_VEHICLE>::Do(DC_EXEC | DC_AUTOREPLACE, new_v->index, false, false, INVALID_CLIENT_ID);
}
}
@ -507,6 +518,25 @@ struct ReplaceChainItem {
Vehicle *GetVehicle() const { return new_veh == nullptr ? old_veh : new_veh; }
};
/**
* When replacing a ship in an extended depot, copy the direction as well.
* @param old_ship The ship being replaced.
* @param new_ship The new ship that will replace the old one.
*/
void CopyShipStatusInExtendedDepot(const Ship *old_ship, Ship *new_ship)
{
assert(IsExtendedDepotTile(old_ship->tile));
assert(old_ship->tile == new_ship->tile);
new_ship->x_pos = old_ship->x_pos;
new_ship->y_pos = old_ship->y_pos;
new_ship->z_pos = old_ship->z_pos;
new_ship->state = old_ship->state;
new_ship->direction = old_ship->direction;
new_ship->rotation = old_ship->rotation;
new_ship->GetImage(new_ship->direction, EIT_ON_MAP, &new_ship->sprite_cache.sprite_seq);
}
/**
* Replace a whole vehicle chain
* @param chain vehicle chain to let autoreplace/renew operator on
@ -517,8 +547,11 @@ struct ReplaceChainItem {
*/
static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon_removal, bool *nothing_to_do)
{
assert(flags & DC_AUTOREPLACE);
Vehicle *old_head = *chain;
assert(old_head->IsPrimaryVehicle());
TileIndex tile = old_head->tile;
CommandCost cost = CommandCost(EXPENSES_NEW_VEHICLES, (Money)0);
@ -569,7 +602,7 @@ static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon
}
if (last_engine == nullptr) last_engine = append;
cost.AddCost(CmdMoveVehicle(append, new_head, DC_EXEC, false));
cost.AddCost(CmdMoveVehicle(append, new_head, DC_EXEC | DC_AUTOREPLACE, false));
if (cost.Failed()) break;
}
if (last_engine == nullptr) last_engine = new_head;
@ -588,7 +621,7 @@ static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon
if (RailVehInfo(append->engine_type)->railveh_type == RAILVEH_WAGON) {
/* Insert wagon after 'last_engine' */
CommandCost res = CmdMoveVehicle(append, last_engine, DC_EXEC, false);
CommandCost res = CmdMoveVehicle(append, last_engine, DC_EXEC | DC_AUTOREPLACE, false);
/* When we allow removal of wagons, either the move failing due
* to the train becoming too long, or the train becoming longer
@ -619,7 +652,7 @@ static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon
assert(RailVehInfo(wagon->engine_type)->railveh_type == RAILVEH_WAGON);
/* Sell wagon */
[[maybe_unused]] CommandCost ret = Command<CMD_SELL_VEHICLE>::Do(DC_EXEC, wagon->index, false, false, INVALID_CLIENT_ID);
[[maybe_unused]] CommandCost ret = Command<CMD_SELL_VEHICLE>::Do(DC_EXEC | DC_AUTOREPLACE, wagon->index, false, false, INVALID_CLIENT_ID);
assert(ret.Succeeded());
it->new_veh = nullptr;
@ -661,6 +694,9 @@ static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon
if ((flags & DC_EXEC) != 0) CheckCargoCapacity(new_head);
}
assert(IsValidTile(tile));
if (!HasCompatibleDepotTile(tile, Train::From(new_head))) cost.MakeError(STR_ERROR_UNABLE_TO_FIND_APPROPRIATE_DEPOT_TILE);
/* If we are not in DC_EXEC undo everything, i.e. rearrange old vehicles.
* We do this from back to front, so that the head of the temporary vehicle chain does not change all the time.
* Note: The vehicle attach callback is disabled here :) */
@ -682,7 +718,7 @@ static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon
if ((flags & DC_EXEC) == 0) {
for (auto it = std::rbegin(replacements); it != std::rend(replacements); ++it) {
if (it->new_veh != nullptr) {
Command<CMD_SELL_VEHICLE>::Do(DC_EXEC, it->new_veh->index, false, false, INVALID_CLIENT_ID);
Command<CMD_SELL_VEHICLE>::Do(DC_EXEC | DC_AUTOREPLACE, it->new_veh->index, false, false, INVALID_CLIENT_ID);
it->new_veh = nullptr;
}
}
@ -700,6 +736,11 @@ static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon
cost.AddCost(CopyHeadSpecificThings(old_head, new_head, flags));
if (cost.Succeeded()) {
/* Copy position and direction for ships in extended depots. */
if (old_head->type == VEH_SHIP && IsExtendedDepotTile(old_head->tile)) {
CopyShipStatusInExtendedDepot(Ship::From(old_head), Ship::From(new_head));
}
/* The new vehicle is constructed, now take over cargo */
if ((flags & DC_EXEC) != 0) {
TransferCargo(old_head, new_head, true);
@ -714,7 +755,7 @@ static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon
/* If we are not in DC_EXEC undo everything */
if ((flags & DC_EXEC) == 0) {
Command<CMD_SELL_VEHICLE>::Do(DC_EXEC, new_head->index, false, false, INVALID_CLIENT_ID);
Command<CMD_SELL_VEHICLE>::Do(DC_EXEC | DC_AUTOREPLACE, new_head->index, false, false, INVALID_CLIENT_ID);
}
}
}
@ -766,11 +807,10 @@ CommandCost CmdAutoreplaceVehicle(DoCommandFlag flags, VehicleID veh_id)
any_replacements |= (e != INVALID_ENGINE);
w = (!free_wagon && w->type == VEH_TRAIN ? Train::From(w)->GetNextUnit() : nullptr);
}
if (!any_replacements) return_cmd_error(STR_ERROR_AUTOREPLACE_NOTHING_TO_DO);
CommandCost cost = CommandCost(EXPENSES_NEW_VEHICLES, (Money)0);
bool nothing_to_do = true;
if (any_replacements) {
bool was_stopped = free_wagon || ((v->vehstatus & VS_STOPPED) != 0);
/* Stop the vehicle */
@ -778,6 +818,17 @@ CommandCost CmdAutoreplaceVehicle(DoCommandFlag flags, VehicleID veh_id)
if (cost.Failed()) return cost;
assert(free_wagon || v->IsStoppedInDepot());
if (flags & DC_EXEC) v->StopServicing();
TrainPlacement train_placement;
if (v->type == VEH_TRAIN) {
train_placement.LiftTrain(Train::From(v), flags);
} else if (IsExtendedDepotTile(v->tile)) {
UpdateExtendedDepotReservation(v, false);
}
/* Start autoreplacing the vehicle. */
flags |= DC_AUTOREPLACE;
/* We have to construct the new vehicle chain to test whether it is valid.
* Vehicle construction needs random bits, so we have to save the random seeds
@ -797,14 +848,37 @@ CommandCost CmdAutoreplaceVehicle(DoCommandFlag flags, VehicleID veh_id)
} else {
ret = ReplaceChain(&v, flags, wagon_removal, &nothing_to_do);
}
assert(ret.Succeeded() && ret.GetCost() == cost.GetCost());
assert(ret.Succeeded());
assert(ret.GetCost() == cost.GetCost());
}
/* Check whether the train can be placed on tracks. */
bool platform_error = false;
/* Autoreplacing is done. */
flags &= ~DC_AUTOREPLACE;
if (v->type == VEH_TRAIN) {
if (cost.Succeeded() && (flags & DC_EXEC) != 0) {
train_placement.LookForPlaceInDepot(Train::From(v), false);
if (train_placement.info < PI_WONT_LEAVE) {
platform_error = true;
if (v->owner == _local_company && v->IsFrontEngine()) {
SetDParam(0, v->index);
AddVehicleAdviceNewsItem(STR_ADVICE_PLATFORM_TYPE + train_placement.info - PI_ERROR_BEGIN, v->index);
}
}
}
train_placement.PlaceTrain(Train::From(v), flags);
} else if (IsExtendedDepotTile(v->tile)) {
UpdateExtendedDepotReservation(v, true);
}
/* Restart the vehicle */
if (!was_stopped) cost.AddCost(DoCmdStartStopVehicle(v, false));
}
if (!platform_error && !was_stopped) cost.AddCost(DoCmdStartStopVehicle(v, false));
if (cost.Succeeded() && nothing_to_do) cost = CommandCost(STR_ERROR_AUTOREPLACE_NOTHING_TO_DO);
assert(cost.Failed() || !nothing_to_do);
return cost;
}

View File

@ -140,24 +140,6 @@ struct BaseStation : StationPool::PoolItem<&_station_pool> {
*/
virtual void GetTileArea(TileArea *ta, StationType type) const = 0;
/**
* Obtain the length of a platform
* @pre tile must be a rail station tile
* @param tile A tile that contains the platform in question
* @return The length of the platform
*/
virtual uint GetPlatformLength(TileIndex tile) const = 0;
/**
* Determines the REMAINING length of a platform, starting at (and including)
* the given tile.
* @param tile the tile from which to start searching. Must be a rail station tile
* @param dir The direction in which to search.
* @return The platform length
*/
virtual uint GetPlatformLength(TileIndex tile, DiagDirection dir) const = 0;
/**
* Get the base station belonging to a specific tile.
* @param tile The tile to get the base station from.

View File

@ -38,6 +38,7 @@
#include "querystring_gui.h"
#include "stringfilter_type.h"
#include "hotkeys.h"
#include "depot_base.h"
#include "widgets/build_vehicle_widget.h"
@ -1167,8 +1168,8 @@ enum BuildVehicleHotkeys {
struct BuildVehicleWindow : Window {
VehicleType vehicle_type; ///< Type of vehicles shown in the window.
union {
RailType railtype; ///< Rail type to show, or #INVALID_RAILTYPE.
RoadType roadtype; ///< Road type to show, or #INVALID_ROADTYPE.
RailTypes railtypes; ///< Rail types to show, or #INVALID_RAILTYPES.
RoadTypes roadtypes; ///< Road types to show, or #INVALID_ROADTYPES.
} filter; ///< Filter to apply.
bool descending_sort_order; ///< Sort direction, @see _engine_sort_direction
uint8_t sort_criteria; ///< Current sort criterium.
@ -1201,11 +1202,12 @@ struct BuildVehicleWindow : Window {
}
}
BuildVehicleWindow(WindowDesc &desc, TileIndex tile, VehicleType type) : Window(desc), vehicle_editbox(MAX_LENGTH_VEHICLE_NAME_CHARS * MAX_CHAR_LENGTH, MAX_LENGTH_VEHICLE_NAME_CHARS)
BuildVehicleWindow(WindowDesc &desc, DepotID depot_id, VehicleType type) : Window(desc), vehicle_editbox(MAX_LENGTH_VEHICLE_NAME_CHARS * MAX_CHAR_LENGTH, MAX_LENGTH_VEHICLE_NAME_CHARS)
{
this->vehicle_type = type;
this->listview_mode = tile == INVALID_TILE;
this->window_number = this->listview_mode ? (int)type : tile.base();
this->listview_mode = (depot_id == INVALID_DEPOT);
if (this->listview_mode) depot_id -= type;
this->window_number = depot_id;
this->sel_engine = INVALID_ENGINE;
@ -1240,16 +1242,13 @@ struct BuildVehicleWindow : Window {
this->details_height = ((this->vehicle_type == VEH_TRAIN) ? 10 : 9);
if (tile == INVALID_TILE) {
this->FinishInitNested(type);
} else {
this->FinishInitNested(tile);
}
this->FinishInitNested(depot_id);
this->querystrings[WID_BV_FILTER] = &this->vehicle_editbox;
this->vehicle_editbox.cancel_button = QueryString::ACTION_CLEAR;
this->owner = (tile != INVALID_TILE) ? GetTileOwner(tile) : _local_company;
Depot *depot = Depot::GetIfValid(depot_id);
this->owner = depot != nullptr ? depot->owner : _local_company;
this->eng_list.ForceRebuild();
this->GenerateBuildList(); // generate the list, since we need it in the next line
@ -1264,28 +1263,22 @@ struct BuildVehicleWindow : Window {
/** Set the filter type according to the depot type */
void UpdateFilterByTile()
{
Depot *depot = this->listview_mode ? nullptr : Depot::Get(this->window_number);
switch (this->vehicle_type) {
default: NOT_REACHED();
case VEH_TRAIN:
if (this->listview_mode) {
this->filter.railtype = INVALID_RAILTYPE;
} else {
this->filter.railtype = GetRailType(this->window_number);
}
this->filter.railtypes = this->listview_mode ? INVALID_RAILTYPES : depot->r_types.rail_types;
break;
case VEH_ROAD:
if (this->listview_mode) {
this->filter.roadtype = INVALID_ROADTYPE;
} else {
this->filter.roadtype = GetRoadTypeRoad(this->window_number);
if (this->filter.roadtype == INVALID_ROADTYPE) {
this->filter.roadtype = GetRoadTypeTram(this->window_number);
}
}
this->filter.roadtypes = this->listview_mode ? INVALID_ROADTYPES : depot->r_types.road_types;
break;
case VEH_SHIP:
this->filter.railtypes = this->listview_mode ? INVALID_RAILTYPES : depot->r_types.rail_types;
break;
case VEH_AIRCRAFT:
break;
}
@ -1326,7 +1319,7 @@ struct BuildVehicleWindow : Window {
if (!this->listview_mode) {
/* Query for cost and refitted capacity */
auto [ret, veh_id, refit_capacity, refit_mail, cargo_capacities] = Command<CMD_BUILD_VEHICLE>::Do(DC_QUERY_COST, this->window_number, this->sel_engine, true, cargo, INVALID_CLIENT_ID);
auto [ret, veh_id, refit_capacity, refit_mail, cargo_capacities] = Command<CMD_BUILD_VEHICLE>::Do(DC_QUERY_COST, Depot::Get(this->window_number)->xy, this->sel_engine, true, cargo, INVALID_CLIENT_ID);
if (ret.Succeeded()) {
this->te.cost = ret.GetCost() - e->GetCost();
this->te.capacity = refit_capacity;
@ -1401,7 +1394,7 @@ struct BuildVehicleWindow : Window {
EngineID eid = e->index;
const RailVehicleInfo *rvi = &e->u.rail;
if (this->filter.railtype != INVALID_RAILTYPE && !HasPowerOnRail(rvi->railtype, this->filter.railtype)) continue;
if (!this->listview_mode && !HasPowerOnRails(rvi->railtype, this->filter.railtypes)) continue;
if (!IsEngineBuildable(eid, VEH_TRAIN, _local_company)) continue;
/* Filter now! So num_engines and num_wagons is valid */
@ -1461,7 +1454,7 @@ struct BuildVehicleWindow : Window {
if (!this->show_hidden_engines && e->IsVariantHidden(_local_company)) continue;
EngineID eid = e->index;
if (!IsEngineBuildable(eid, VEH_ROAD, _local_company)) continue;
if (this->filter.roadtype != INVALID_ROADTYPE && !HasPowerOnRoad(e->u.road.roadtype, this->filter.roadtype)) continue;
if (!this->listview_mode && !HasPowerOnRoads(e->u.road.roadtype, this->filter.roadtypes)) continue;
/* Filter by name or NewGRF extra text */
if (!FilterByText(e)) continue;
@ -1479,18 +1472,17 @@ struct BuildVehicleWindow : Window {
EngineID sel_id = INVALID_ENGINE;
this->eng_list.clear();
if (this->listview_mode || this->filter.railtypes != RAILTYPES_NONE) {
for (const Engine *e : Engine::IterateType(VEH_SHIP)) {
if (!this->show_hidden_engines && e->IsVariantHidden(_local_company)) continue;
EngineID eid = e->index;
if (!IsEngineBuildable(eid, VEH_SHIP, _local_company)) continue;
/* Filter by name or NewGRF extra text */
if (!FilterByText(e)) continue;
this->eng_list.emplace_back(eid, e->info.variant_id, e->display_flags, 0);
if (eid == this->sel_engine) sel_id = eid;
}
}
this->SelectEngine(sel_id);
}
@ -1501,7 +1493,7 @@ struct BuildVehicleWindow : Window {
this->eng_list.clear();
const Station *st = this->listview_mode ? nullptr : Station::GetByTile(this->window_number);
const Station *st = this->listview_mode ? nullptr : Depot::Get(this->window_number)->station;
/* Make list of all available planes.
* Also check to see if the previously selected plane is still available,
@ -1606,6 +1598,40 @@ struct BuildVehicleWindow : Window {
return list;
}
void BuildVehicle()
{
EngineID sel_eng = this->sel_engine;
if (sel_eng == INVALID_ENGINE) return;
CargoID cargo = this->cargo_filter_criteria;
if (cargo == CargoFilterCriteria::CF_ANY || cargo == CargoFilterCriteria::CF_ENGINES || cargo == CargoFilterCriteria::CF_NONE) cargo = INVALID_CARGO;
assert(Depot::IsValidID(this->window_number));
Depot *depot = Depot::Get(this->window_number);
assert(depot->xy != INVALID_TILE);
if (this->vehicle_type == VEH_TRAIN && RailVehInfo(sel_eng)->railveh_type == RAILVEH_WAGON) {
Command<CMD_BUILD_VEHICLE>::Post(GetCmdBuildVehMsg(this->vehicle_type), CcBuildWagon, depot->xy, sel_eng, true, cargo, INVALID_CLIENT_ID);
} else {
Command<CMD_BUILD_VEHICLE>::Post(GetCmdBuildVehMsg(this->vehicle_type), CcBuildPrimaryVehicle, depot->xy, sel_eng, true, cargo, INVALID_CLIENT_ID);
}
/* Update last used variant in hierarchy and refresh if necessary. */
bool refresh = false;
EngineID parent = sel_eng;
while (parent != INVALID_ENGINE) {
Engine *e = Engine::Get(parent);
refresh |= (e->display_last_variant != sel_eng);
e->display_last_variant = sel_eng;
parent = e->info.variant_id;
}
if (refresh) {
InvalidateWindowData(WC_REPLACE_VEHICLE, this->vehicle_type, 0); // Update the autoreplace window
InvalidateWindowClassesData(WC_BUILD_VEHICLE); // The build windows needs updating as well
}
}
void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
{
switch (widget) {
@ -1668,34 +1694,9 @@ struct BuildVehicleWindow : Window {
break;
}
case WID_BV_BUILD: {
EngineID sel_eng = this->sel_engine;
if (sel_eng != INVALID_ENGINE) {
CargoID cargo = this->cargo_filter_criteria;
if (cargo == CargoFilterCriteria::CF_ANY || cargo == CargoFilterCriteria::CF_ENGINES || cargo == CargoFilterCriteria::CF_NONE) cargo = INVALID_CARGO;
if (this->vehicle_type == VEH_TRAIN && RailVehInfo(sel_eng)->railveh_type == RAILVEH_WAGON) {
Command<CMD_BUILD_VEHICLE>::Post(GetCmdBuildVehMsg(this->vehicle_type), CcBuildWagon, this->window_number, sel_eng, true, cargo, INVALID_CLIENT_ID);
} else {
Command<CMD_BUILD_VEHICLE>::Post(GetCmdBuildVehMsg(this->vehicle_type), CcBuildPrimaryVehicle, this->window_number, sel_eng, true, cargo, INVALID_CLIENT_ID);
}
/* Update last used variant in hierarchy and refresh if necessary. */
bool refresh = false;
EngineID parent = sel_eng;
while (parent != INVALID_ENGINE) {
Engine *e = Engine::Get(parent);
refresh |= (e->display_last_variant != sel_eng);
e->display_last_variant = sel_eng;
parent = e->info.variant_id;
}
if (refresh) {
InvalidateWindowData(WC_REPLACE_VEHICLE, this->vehicle_type, 0); // Update the autoreplace window
InvalidateWindowClassesData(WC_BUILD_VEHICLE); // The build windows needs updating as well
return;
}
}
case WID_BV_BUILD:
this->BuildVehicle();
break;
}
case WID_BV_RENAME: {
EngineID sel_eng = this->sel_engine;
@ -1732,11 +1733,21 @@ struct BuildVehicleWindow : Window {
switch (widget) {
case WID_BV_CAPTION:
if (this->vehicle_type == VEH_TRAIN && !this->listview_mode) {
const RailTypeInfo *rti = GetRailTypeInfo(this->filter.railtype);
uint num_railtypes = CountBits(this->filter.railtypes);
if (num_railtypes != 1) {
SetDParam(0, STR_BUY_VEHICLE_TRAIN_ALL_CAPTION);
} else {
const RailTypeInfo *rti = GetRailTypeInfo((RailType)FindFirstBit(this->filter.railtypes));
SetDParam(0, rti->strings.build_caption);
}
} else if (this->vehicle_type == VEH_ROAD && !this->listview_mode) {
const RoadTypeInfo *rti = GetRoadTypeInfo(this->filter.roadtype);
uint num_roadtypes = CountBits(this->filter.roadtypes);
if (num_roadtypes != 1) {
SetDParam(0, STR_BUY_VEHICLE_ROAD_VEHICLE_CAPTION);
} else {
const RoadTypeInfo *rti = GetRoadTypeInfo((RoadType)FindFirstBit(this->filter.roadtypes));
SetDParam(0, rti->strings.build_caption);
}
} else {
SetDParam(0, (this->listview_mode ? STR_VEHICLE_LIST_AVAILABLE_TRAINS : STR_BUY_VEHICLE_TRAIN_ALL_CAPTION) + this->vehicle_type);
}
@ -1926,17 +1937,12 @@ static WindowDesc _build_vehicle_desc(
&BuildVehicleWindow::hotkeys
);
void ShowBuildVehicleWindow(TileIndex tile, VehicleType type)
void ShowBuildVehicleWindow(DepotID depot_id, VehicleType type)
{
/* We want to be able to open both Available Train as Available Ships,
* so if tile == INVALID_TILE (Available XXX Window), use 'type' as unique number.
* As it always is a low value, it won't collide with any real tile
* number. */
uint num = (tile == INVALID_TILE) ? (int)type : tile.base();
assert(IsCompanyBuildableVehicleType(type));
assert(depot_id == INVALID_DEPOT || Depot::IsValidID(depot_id));
CloseWindowById(WC_BUILD_VEHICLE, num);
CloseWindowById(WC_BUILD_VEHICLE, depot_id != INVALID_DEPOT ? depot_id : (INVALID_DEPOT - type));
new BuildVehicleWindow(_build_vehicle_desc, tile, type);
new BuildVehicleWindow(_build_vehicle_desc, depot_id, type);
}

View File

@ -193,6 +193,7 @@ enum Commands : uint16_t {
CMD_BUILD_BRIDGE, ///< build a bridge
CMD_BUILD_RAIL_STATION, ///< build a rail station
CMD_BUILD_TRAIN_DEPOT, ///< build a train depot
CMD_REMOVE_TRAIN_DEPOT, ///< remove a train depot
CMD_BUILD_SINGLE_SIGNAL, ///< build a signal
CMD_REMOVE_SINGLE_SIGNAL, ///< remove a signal
CMD_TERRAFORM_LAND, ///< terraform a tile

View File

@ -15,9 +15,15 @@
#include "core/pool_func.hpp"
#include "vehicle_gui.h"
#include "vehiclelist.h"
#include "command_func.h"
#include "vehicle_base.h"
#include "viewport_kdtree.h"
#include "platform_func.h"
#include "safeguards.h"
#include "table/strings.h"
/** All our depots tucked away in a pool. */
DepotPool _depot_pool("Depot");
INSTANTIATE_POOL_METHODS(Depot)
@ -29,21 +35,304 @@ Depot::~Depot()
{
if (CleaningPool()) return;
if (!IsDepotTile(this->xy) || GetDepotIndex(this->xy) != this->index) {
/* It can happen there is no depot here anymore (TTO/TTD savegames) */
if (this->owner == INVALID_OWNER) {
/* Deleting depot remnants of TTD savegames while saveload conversion. */
assert(this->veh_type == VEH_INVALID);
return;
}
/* Clear the order backup. */
OrderBackup::Reset(this->xy, false);
OrderBackup::Reset(this->index, false);
/* Make sure no vehicle is going to the old depot. */
for (Vehicle *v : Vehicle::Iterate()) {
if (v->First() != v) continue;
if (!v->current_order.IsType(OT_GOTO_DEPOT)) continue;
if (v->current_order.GetDestination() != this->index) continue;
v->current_order.MakeDummy();
}
/* Clear the depot from all order-lists */
RemoveOrderFromAllVehicles(OT_GOTO_DEPOT, this->index);
/* Delete the depot-window */
CloseWindowById(WC_VEHICLE_DEPOT, this->xy);
CloseWindowById(WC_VEHICLE_DEPOT, this->index);
/* Delete the depot list */
VehicleType vt = GetDepotVehicleType(this->xy);
CloseWindowById(GetWindowClassForVehicleType(vt), VehicleListIdentifier(VL_DEPOT_LIST, vt, GetTileOwner(this->xy), this->index).Pack());
CloseWindowById(GetWindowClassForVehicleType(this->veh_type),
VehicleListIdentifier(VL_DEPOT_LIST,
this->veh_type, this->owner, this->index).Pack());
InvalidateWindowData(WC_SELECT_DEPOT, this->veh_type);
/* The sign will now disappear. */
_viewport_sign_kdtree.Remove(ViewportSignKdtreeItem::MakeDepot(this->index));
this->sign.MarkDirty();
}
/**
* Cancel deletion of this depot (reuse it).
* @param xy New location of the depot.
* @see Depot::IsInUse
* @see Depot::Disuse
*/
void Depot::Reuse(TileIndex xy)
{
this->delete_ctr = 0;
this->xy = xy;
this->ta.tile = xy;
this->ta.h = this->ta.w = 1;
/* Ensure the sign is not drawn */
_viewport_sign_kdtree.Remove(ViewportSignKdtreeItem::MakeDepot(this->index));
this->sign.MarkDirty();
}
/**
* Schedule deletion of this depot.
*
* This method is ought to be called after demolishing last depot part.
* The depot will be kept in the pool for a while so it can be
* placed again later without messing vehicle orders.
*
* @see Depot::IsInUse
* @see Depot::Reuse
*/
void Depot::Disuse()
{
/* Mark that the depot is demolished and start the countdown. */
this->delete_ctr = 8;
/* Update the sign, it will be visible from now. */
this->UpdateVirtCoord();
_viewport_sign_kdtree.Insert(ViewportSignKdtreeItem::MakeDepot(this->index));
}
/**
* Of all the depot parts a depot has, return the best destination for a vehicle.
* @param v The vehicle.
* @param dep The depot vehicle \a v is heading for.
* @return The free and closest (if none is free, just closest) part of depot to vehicle v.
*/
TileIndex Depot::GetBestDepotTile(Vehicle *v) const
{
assert(this->veh_type == v->type);
TileIndex best_depot = INVALID_TILE;
DepotReservation best_found_type = DEPOT_RESERVATION_END;
uint best_distance = UINT_MAX;
for (const auto &tile : this->depot_tiles) {
bool check_south = v->type == VEH_ROAD;
uint new_distance = DistanceManhattan(v->tile, tile);
again:
DepotReservation depot_reservation = GetDepotReservation(tile, check_south);
if (((best_found_type == depot_reservation) && new_distance < best_distance) || (depot_reservation < best_found_type)) {
best_depot = tile;
best_distance = new_distance;
best_found_type = depot_reservation;
}
if (check_south) {
/* For road vehicles, check north direction as well. */
check_south = false;
goto again;
}
}
return best_depot;
}
/**
* Check we can add some tiles to this depot.
* @param ta The affected tile area.
* @return Whether it is possible to add the tiles or an error message.
*/
CommandCost Depot::BeforeAddTiles(TileArea ta)
{
assert(ta.tile != INVALID_TILE);
if (this->ta.tile != INVALID_TILE && this->IsInUse()) {
/* Important when the old rect is completely inside the new rect, resp. the old one was empty. */
ta.Add(this->ta.tile);
ta.Add(TileAddXY(this->ta.tile, this->ta.w - 1, this->ta.h - 1));
}
/* A max depot spread of 1 for VEH_SHIP is a special case,
* as ship depots consist of two tiles. */
if (this->veh_type == VEH_SHIP && _settings_game.depot.depot_spread == 1) {
/* (ta.w, ta.h) must be equal to (1, 2) or (2, 1).
* This means that ta.w * ta.h must be equal to 2. */
if (ta.w * ta.h != 2) return_cmd_error(STR_ERROR_DEPOT_TOO_SPREAD_OUT);
} else if (std::max(ta.w, ta.h) > _settings_game.depot.depot_spread) {
return_cmd_error(STR_ERROR_DEPOT_TOO_SPREAD_OUT);
}
return CommandCost();
}
/**
* Add some tiles to this depot and rescan area for depot_tiles.
* @param ta Affected tile area
* @param adding Whether adding or removing depot tiles.
*/
void Depot::AfterAddRemove(TileArea ta, bool adding)
{
assert(ta.tile != INVALID_TILE);
if (adding) {
if (this->ta.tile != INVALID_TILE) {
ta.Add(this->ta.tile);
ta.Add(TileAddXY(this->ta.tile, this->ta.w - 1, this->ta.h - 1));
}
} else {
ta = this->ta;
}
this->ta.Clear();
for (TileIndex tile : ta) {
if (!IsDepotTile(tile)) continue;
if (GetDepotIndex(tile) != this->index) continue;
this->ta.Add(tile);
}
VehicleType veh_type = this->veh_type;
if (this->ta.tile != INVALID_TILE) {
this->RescanDepotTiles();
assert(!this->depot_tiles.empty());
this->xy = this->depot_tiles[0];
InvalidateWindowData(WC_VEHICLE_DEPOT, this->index);
} else {
assert(this->IsInUse());
this->Disuse();
TileIndex old_tile = this->xy;
this->RescanDepotTiles();
assert(this->depot_tiles.empty());
this->xy = old_tile;
}
InvalidateWindowData(WC_VEHICLE_DEPOT, this->index);
InvalidateWindowData(WC_SELECT_DEPOT, veh_type);
}
/**
* Check whether a tile is a destination tile, such as the starting tiles of
* rail platforms (and not the middle tiles of the platforms).
* @param dep The depot being checked
* @param tile The tile being checked
* @return Whether the tile is of the given depot.
*/
bool IsDepotDestTile(Depot *dep, TileIndex tile)
{
assert(IsDepotTile(tile));
assert(GetDepotIndex(tile) == dep->index);
switch (dep->veh_type) {
case VEH_TRAIN:
assert(IsRailDepotTile(tile));
return !IsExtendedRailDepot(tile) || IsAnyStartPlatformTile(tile);
case VEH_ROAD:
case VEH_SHIP:
case VEH_AIRCRAFT:
return true;
default: NOT_REACHED();
}
}
/**
* Rescan depot_tiles. Done after AfterAddRemove and SaveLoad.
* Updates the tiles of the depot and its railtypes/roadtypes...
*/
void Depot::RescanDepotTiles()
{
this->depot_tiles.clear();
RailTypes old_rail_types = this->r_types.rail_types;
this->r_types.rail_types = RAILTYPES_NONE;
for (TileIndex tile : this->ta) {
if (!IsDepotTile(tile)) continue;
if (GetDepotIndex(tile) != this->index) continue;
if (IsDepotDestTile(this, tile)) this->depot_tiles.push_back(tile);
switch (veh_type) {
case VEH_ROAD:
this->r_types.road_types |= GetPresentRoadTypes(tile);
break;
case VEH_TRAIN:
this->r_types.rail_types |= (RailTypes)(1 << GetRailType(tile));
break;
case VEH_SHIP:
/* Mark this ship depot has at least one part, so ships can be built. */
this->r_types.rail_types |= INVALID_RAILTYPES;
break;
default: break;
}
}
if (old_rail_types != this->r_types.rail_types) {
InvalidateWindowData(WC_BUILD_VEHICLE, this->index, 0, true);
}
}
/**
* Fix tile reservations and vehicle on extended depots.
* @param v Vehicle to be checked.
* @param reserve Whether to reserve or free the position v is occupying.
*/
void UpdateExtendedDepotReservation(Vehicle *v, bool reserve)
{
assert(v != nullptr);
assert(IsExtendedDepotTile(v->tile));
DepotReservation res_type = DEPOT_RESERVATION_EMPTY;
if (reserve) {
res_type = (v->vehstatus & VS_STOPPED) ?
DEPOT_RESERVATION_FULL_STOPPED_VEH : DEPOT_RESERVATION_IN_USE;
}
switch (v->type) {
case VEH_ROAD: {
assert(v == v->First());
assert(IsDiagonalDirection(v->direction));
bool is_facing_south = IsDiagDirFacingSouth(DirToDiagDir(v->direction));
TileArea ta;
for (Vehicle *u = v; u != nullptr; u = u->Next()) ta.Add(u->tile);
for (TileIndex t : ta) {
res_type = DEPOT_RESERVATION_EMPTY;
if (reserve) {
res_type = (v->vehstatus & VS_STOPPED) ?
DEPOT_RESERVATION_FULL_STOPPED_VEH : DEPOT_RESERVATION_IN_USE;
}
for (Vehicle *rv : Vehicle::Iterate()) {
if (res_type == DEPOT_RESERVATION_FULL_STOPPED_VEH) break;
if (rv->IsInDepot() && ta.Contains(rv->tile) && rv->First() != v) {
assert(rv->type == v->type);
[[maybe_unused]] DiagDirection diag_dir = DirToDiagDir(rv->direction);
assert(DiagDirToAxis(DirToDiagDir(v->direction)) == DiagDirToAxis(diag_dir));
if (is_facing_south == IsDiagDirFacingSouth(DirToDiagDir(rv->direction))) {
res_type = (rv->vehstatus & VS_STOPPED) ?
DEPOT_RESERVATION_FULL_STOPPED_VEH : DEPOT_RESERVATION_IN_USE;
}
}
}
SetDepotReservation(t, res_type, is_facing_south);
}
break;
}
case VEH_SHIP:
SetDepotReservation(v->tile, res_type);
break;
case VEH_TRAIN: {
DiagDirection dir = GetRailDepotDirection(v->tile);
SetDepotReservation(GetPlatformExtremeTile(v->tile, dir), res_type);
SetDepotReservation(GetPlatformExtremeTile(v->tile, ReverseDiagDir(dir)), res_type);
break;
}
case VEH_AIRCRAFT:
break;
default: NOT_REACHED();
}
}

View File

@ -11,12 +11,18 @@
#define DEPOT_BASE_H
#include "depot_map.h"
#include "viewport_type.h"
#include "core/pool_type.hpp"
#include "timer/timer_game_calendar.h"
#include "rail_type.h"
#include "road_type.h"
typedef Pool<Depot, DepotID, 64, 64000> DepotPool;
extern DepotPool _depot_pool;
class CommandCost;
struct Vehicle;
struct Depot : DepotPool::PoolItem<&_depot_pool> {
/* DepotID index member of DepotPool is 2 bytes. */
uint16_t town_cn; ///< The N-1th depot for this town (consecutive number)
@ -25,14 +31,37 @@ struct Depot : DepotPool::PoolItem<&_depot_pool> {
std::string name;
TimerGameCalendar::Date build_date; ///< Date of construction
Depot(TileIndex xy = INVALID_TILE) : xy(xy) {}
VehicleType veh_type; ///< Vehicle type of the depot.
Owner owner; ///< Owner of the depot.
uint8_t delete_ctr; ///< Delete counter. If greater than 0 then it is decremented until it reaches 0; the depot is then deleted.
ViewportSign sign; ///< NOSAVE: Dimensions of sign
Station *station; ///< For aircraft, station associated with this hangar.
union {
RoadTypes road_types;
RailTypes rail_types;
} r_types;
TileArea ta;
std::vector<TileIndex> depot_tiles;
Depot(TileIndex xy = INVALID_TILE, VehicleType type = VEH_INVALID, Owner owner = INVALID_OWNER, Station *station = nullptr) :
xy(xy),
veh_type(type),
owner(owner),
station(station),
ta(xy, 1, 1) {}
~Depot();
static inline Depot *GetByTile(TileIndex tile)
{
assert(Depot::IsValidID(GetDepotIndex(tile)));
return Depot::Get(GetDepotIndex(tile));
}
TileIndex GetBestDepotTile(Vehicle *v) const;
/**
* Is the "type" of depot the same as the given depot,
* i.e. are both a rail, road or ship depots?
@ -41,8 +70,35 @@ struct Depot : DepotPool::PoolItem<&_depot_pool> {
*/
inline bool IsOfType(const Depot *d) const
{
return GetTileType(d->xy) == GetTileType(this->xy);
return d->veh_type == this->veh_type;
}
/**
* Check whether the depot currently is in use; in use means
* that it is not scheduled for deletion and that it still has
* a building on the map. Otherwise the building is demolished
* and the depot awaits to be deleted.
* @return true iff still in use
* @see Depot::Disuse
* @see Depot::Reuse
*/
inline bool IsInUse() const
{
return this->delete_ctr == 0;
}
void Reuse(TileIndex xy);
void Disuse();
void UpdateVirtCoord();
/* Check we can add some tiles to this depot. */
CommandCost BeforeAddTiles(TileArea ta);
/* Add some tiles to this depot and rescan area for depot_tiles. */
void AfterAddRemove(TileArea ta, bool adding);
/* Rescan depot_tiles. Done after AfterAddRemove and SaveLoad. */
void RescanDepotTiles();
};
#endif /* DEPOT_BASE_H */

View File

@ -17,6 +17,10 @@
#include "vehiclelist.h"
#include "window_func.h"
#include "depot_cmd.h"
#include "strings_func.h"
#include "landscape.h"
#include "viewport_kdtree.h"
#include "timer/timer_game_tick.h"
#include "table/strings.h"
@ -48,7 +52,7 @@ CommandCost CmdRenameDepot(DoCommandFlag flags, DepotID depot_id, const std::str
Depot *d = Depot::GetIfValid(depot_id);
if (d == nullptr) return CMD_ERROR;
CommandCost ret = CheckTileOwnership(d->xy);
CommandCost ret = CheckOwnership(d->owner);
if (ret.Failed()) return ret;
bool reset = text.empty();
@ -59,6 +63,8 @@ CommandCost CmdRenameDepot(DoCommandFlag flags, DepotID depot_id, const std::str
}
if (flags & DC_EXEC) {
/* _viewport_sign_kdtree does not need to be updated, only in-use depots can be renamed */
if (reset) {
d->name.clear();
MakeDefaultName(d);
@ -68,11 +74,135 @@ CommandCost CmdRenameDepot(DoCommandFlag flags, DepotID depot_id, const std::str
/* Update the orders and depot */
SetWindowClassesDirty(WC_VEHICLE_ORDERS);
SetWindowDirty(WC_VEHICLE_DEPOT, d->xy);
SetWindowDirty(WC_VEHICLE_DEPOT, d->index);
/* Update the depot list */
VehicleType vt = GetDepotVehicleType(d->xy);
SetWindowDirty(GetWindowClassForVehicleType(vt), VehicleListIdentifier(VL_DEPOT_LIST, vt, GetTileOwner(d->xy), d->index).Pack());
SetWindowDirty(GetWindowClassForVehicleType(d->veh_type), VehicleListIdentifier(VL_DEPOT_LIST, d->veh_type, d->owner, d->index).Pack());
}
return CommandCost();
}
/** Update the virtual coords needed to draw the depot sign. */
void Depot::UpdateVirtCoord()
{
Point pt = RemapCoords2(TileX(this->xy) * TILE_SIZE, TileY(this->xy) * TILE_SIZE);
pt.y -= 32 * ZOOM_BASE;
SetDParam(0, this->veh_type);
SetDParam(1, this->index);
this->sign.UpdatePosition(pt.x, pt.y, STR_VIEWPORT_DEPOT, STR_VIEWPORT_DEPOT_TINY);
SetWindowDirty(WC_VEHICLE_DEPOT, this->index);
}
/** Update the virtual coords needed to draw the depot sign for all depots. */
void UpdateAllDepotVirtCoords()
{
/* Only demolished depots have signs. */
for (Depot *d : Depot::Iterate()) if (!d->IsInUse()) d->UpdateVirtCoord();
}
/**
* Find a demolished depot close to a tile.
* @param ta Tile area to search for.
* @param type Depot type.
* @param cid Previous owner of the depot.
* @return The index of a demolished nearby depot, or INVALID_DEPOT if none.
*/
DepotID FindDeletedDepotCloseTo(TileArea ta, VehicleType type, CompanyID cid)
{
for (Depot *depot : Depot::Iterate()) {
if (depot->IsInUse() || depot->veh_type != type || depot->owner != cid) continue;
if (ta.Contains(depot->xy)) return depot->index;
}
return INVALID_DEPOT;
}
void OnTick_Depot()
{
if (_game_mode == GM_EDITOR) return;
/* Clean up demolished depots. */
for (Depot *d : Depot::Iterate()) {
if (d->IsInUse()) continue;
if ((TimerGameTick::counter + d->index) % Ticks::DEPOT_REMOVAL_TICKS != 0) continue;
if (--d->delete_ctr != 0) continue;
delete d;
}
}
/**
* Look for or check depot to join to, building a new one if necessary.
* @param ta The area of the new depot.
* @param veh_type The vehicle type of the new depot.
* @param join_to DepotID of the depot to join to.
* If INVALID_DEPOT, look whether it is possible to join to an existing depot.
* If NEW_DEPOT, directly create a new depot.
* @param depot The pointer to the depot.
* @param adjacent Whether adjacent depots are allowed
* @return command cost with the error or 'okay'
*/
CommandCost FindJoiningDepot(TileArea ta, VehicleType veh_type, DepotID &join_to, Depot *&depot, bool adjacent, DoCommandFlag flags)
{
/* Look for a joining depot if needed. */
if (join_to == INVALID_DEPOT) {
assert(depot == nullptr);
DepotID closest_depot = INVALID_DEPOT;
TileArea check_area(ta);
check_area.Expand(1);
/* Check around to see if there's any depot there. */
for (TileIndex tile_cur : check_area) {
if (IsValidTile(tile_cur) && IsDepotTile(tile_cur)) {
Depot *d = Depot::GetByTile(tile_cur);
assert(d != nullptr);
if (d->veh_type != veh_type) continue;
if (d->owner != _current_company) continue;
if (closest_depot == INVALID_DEPOT) {
closest_depot = d->index;
} else if (closest_depot != d->index) {
if (!adjacent) return_cmd_error(STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING_DEPOT);
}
}
}
if (closest_depot == INVALID_DEPOT) {
/* Check for close unused depots. */
check_area.Expand(7); // total distance of 8
closest_depot = FindDeletedDepotCloseTo(check_area, veh_type, _current_company);
}
if (closest_depot != INVALID_DEPOT) {
assert(Depot::IsValidID(closest_depot));
depot = Depot::Get(closest_depot);
}
join_to = depot == nullptr ? NEW_DEPOT : depot->index;
}
/* At this point, join_to is NEW_DEPOT or a valid DepotID. */
if (join_to == NEW_DEPOT) {
/* New depot needed. */
if (!Depot::CanAllocateItem()) return CMD_ERROR;
if (flags & DC_EXEC) {
depot = new Depot(ta.tile, veh_type, _current_company);
depot->build_date = TimerGameCalendar::date;
}
} else {
/* Joining depots. */
assert(Depot::IsValidID(join_to));
depot = Depot::Get(join_to);
assert(depot->owner == _current_company);
assert(depot->veh_type == veh_type);
if (!depot->IsInUse() && (flags & DC_EXEC)) depot->Reuse(ta.tile);
return depot->BeforeAddTiles(ta);
}
return CommandCost();
}

View File

@ -10,13 +10,16 @@
#ifndef DEPOT_FUNC_H
#define DEPOT_FUNC_H
#include "depot_type.h"
#include "vehicle_type.h"
#include "slope_func.h"
#include "command_type.h"
void ShowDepotWindow(TileIndex tile, VehicleType type);
void ShowDepotWindow(DepotID depot_id);
void InitDepotWindowBlockSizes();
void DeleteDepotHighlightOfVehicle(const Vehicle *v);
void UpdateAllDepotVirtCoords();
/**
* Find out if the slope of the tile is suitable to build a depot of given direction
@ -33,4 +36,13 @@ inline bool CanBuildDepotByTileh(DiagDirection direction, Slope tileh)
return IsSteepSlope(tileh) ? (tileh & entrance_corners) == entrance_corners : (tileh & entrance_corners) != 0;
}
struct Depot;
CommandCost FindJoiningDepot(TileArea ta, VehicleType veh_type, DepotID &join_to, Depot *&depot, bool adjacent, DoCommandFlag flags);
using DepotPickerCmdProc = std::function<bool(DepotID join_to)>;
void ShowSelectDepotIfNeeded(TileArea ta, DepotPickerCmdProc proc, VehicleType veh_type);
struct Window;
void CheckRedrawDepotHighlight(const Window *w, VehicleType veh_type);
#endif /* DEPOT_FUNC_H */

View File

@ -29,9 +29,12 @@
#include "zoom_func.h"
#include "error.h"
#include "depot_cmd.h"
#include "station_base.h"
#include "train_cmd.h"
#include "vehicle_cmd.h"
#include "core/geometry_func.hpp"
#include "depot_func.h"
#include "train_placement.h"
#include "widgets/depot_widget.h"
@ -78,6 +81,7 @@ static constexpr NWidgetPart _nested_train_depot_widgets[] = {
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_D_BUILD), SetDataTip(0x0, STR_NULL), SetFill(1, 1), SetResize(1, 0),
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_D_CLONE), SetDataTip(0x0, STR_NULL), SetFill(1, 1), SetResize(1, 0),
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_D_HIGHLIGHT), SetDataTip(STR_BUTTON_HIGHLIGHT_DEPOT, STR_TOOLTIP_HIGHLIGHT_DEPOT), SetFill(1, 1), SetResize(1, 0),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_D_VEHICLE_LIST), SetDataTip(0x0, STR_NULL), SetAspect(WidgetDimensions::ASPECT_VEHICLE_ICON), SetFill(0, 1),
NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_D_STOP_ALL), SetDataTip(SPR_FLAG_VEH_STOPPED, STR_NULL), SetAspect(WidgetDimensions::ASPECT_VEHICLE_FLAG), SetFill(0, 1),
NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_D_START_ALL), SetDataTip(SPR_FLAG_VEH_RUNNING, STR_NULL), SetAspect(WidgetDimensions::ASPECT_VEHICLE_FLAG), SetFill(0, 1),
@ -259,6 +263,7 @@ struct DepotWindow : Window {
VehicleType type;
bool generate_list;
WidgetID hovered_widget; ///< Index of the widget being hovered during drag/drop. -1 if no drag is in progress.
std::vector<bool> problematic_vehicles; ///< Vector associated to vehicle_list, with a value of true for vehicles that cannot leave the depot.
VehicleList vehicle_list;
VehicleList wagon_list;
uint unitnumber_digits;
@ -266,15 +271,17 @@ struct DepotWindow : Window {
Scrollbar *hscroll; ///< Only for trains.
Scrollbar *vscroll;
DepotWindow(WindowDesc &desc, TileIndex tile, VehicleType type) : Window(desc)
DepotWindow(WindowDesc &desc, DepotID depot_id) : Window(desc)
{
assert(IsCompanyBuildableVehicleType(type)); // ensure that we make the call with a valid type
assert(Depot::IsValidID(depot_id));
Depot *depot = Depot::Get(depot_id);
assert(IsCompanyBuildableVehicleType(depot->veh_type));
this->sel = INVALID_VEHICLE;
this->vehicle_over = INVALID_VEHICLE;
this->generate_list = true;
this->hovered_widget = -1;
this->type = type;
this->type = depot->veh_type;
this->num_columns = 1; // for non-trains this gets set in FinishInitNested()
this->unitnumber_digits = 2;
@ -282,23 +289,24 @@ struct DepotWindow : Window {
this->hscroll = (this->type == VEH_TRAIN ? this->GetScrollbar(WID_D_H_SCROLL) : nullptr);
this->vscroll = this->GetScrollbar(WID_D_V_SCROLL);
/* Don't show 'rename button' of aircraft hangar */
this->GetWidget<NWidgetStacked>(WID_D_SHOW_RENAME)->SetDisplayedPlane(type == VEH_AIRCRAFT ? SZSP_NONE : 0);
this->GetWidget<NWidgetStacked>(WID_D_SHOW_RENAME)->SetDisplayedPlane(this->type == VEH_AIRCRAFT ? SZSP_NONE : 0);
/* Only train depots have a horizontal scrollbar and a 'sell chain' button */
if (type == VEH_TRAIN) this->GetWidget<NWidgetCore>(WID_D_MATRIX)->widget_data = 1 << MAT_COL_START;
this->GetWidget<NWidgetStacked>(WID_D_SHOW_H_SCROLL)->SetDisplayedPlane(type == VEH_TRAIN ? 0 : SZSP_HORIZONTAL);
this->GetWidget<NWidgetStacked>(WID_D_SHOW_SELL_CHAIN)->SetDisplayedPlane(type == VEH_TRAIN ? 0 : SZSP_NONE);
this->SetupWidgetData(type);
this->FinishInitNested(tile);
if (this->type == VEH_TRAIN) this->GetWidget<NWidgetCore>(WID_D_MATRIX)->widget_data = 1 << MAT_COL_START;
this->GetWidget<NWidgetStacked>(WID_D_SHOW_H_SCROLL)->SetDisplayedPlane(this->type == VEH_TRAIN ? 0 : SZSP_HORIZONTAL);
this->GetWidget<NWidgetStacked>(WID_D_SHOW_SELL_CHAIN)->SetDisplayedPlane(this->type == VEH_TRAIN ? 0 : SZSP_NONE);
this->SetupWidgetData(this->type);
this->FinishInitNested(depot_id);
this->owner = GetTileOwner(tile);
this->owner = depot->owner;
OrderBackup::Reset();
}
void Close([[maybe_unused]] int data = 0) override
{
CloseWindowById(WC_BUILD_VEHICLE, this->window_number);
CloseWindowById(GetWindowClassForVehicleType(this->type), VehicleListIdentifier(VL_DEPOT_LIST, this->type, this->owner, this->GetDepotIndex()).Pack(), false);
CloseWindowById(GetWindowClassForVehicleType(this->type), VehicleListIdentifier(VL_DEPOT_LIST, this->type, this->owner, this->window_number).Pack(), false);
OrderBackup::Reset(this->window_number);
SetViewportHighlightDepot(this->window_number, false);
this->Window::Close();
}
@ -405,6 +413,11 @@ struct DepotWindow : Window {
for (; num < maxval; ir = ir.Translate(0, this->resize.step_height)) { // Draw the rows
Rect cell = ir; /* Keep track of horizontal cells */
for (uint i = 0; i < this->num_columns && num < maxval; i++, num++) {
/* Draw a dark red background if train cannot be placed. */
if (this->type == VEH_TRAIN && this->problematic_vehicles[num] == 1) {
GfxFillRect(cell.left, cell.top, cell.right, cell.bottom, PC_DARK_GREY);
}
/* Draw all vehicles in the current row */
const Vehicle *v = this->vehicle_list[num];
this->DrawVehicleInDepot(v, cell);
@ -426,7 +439,7 @@ struct DepotWindow : Window {
if (widget != WID_D_CAPTION) return;
SetDParam(0, this->type);
SetDParam(1, this->GetDepotIndex());
SetDParam(1, (this->type == VEH_AIRCRAFT) ? Depot::Get(this->window_number)->station->index : this->window_number);
}
struct GetDepotVehiclePtData {
@ -708,12 +721,23 @@ struct DepotWindow : Window {
void OnPaint() override
{
extern DepotID _viewport_highlight_depot;
this->SetWidgetLoweredState(WID_D_HIGHLIGHT, _viewport_highlight_depot == this->window_number);
if (this->generate_list) {
/* Generate the vehicle list
* It's ok to use the wagon pointers for non-trains as they will be ignored */
BuildDepotVehicleList(this->type, this->window_number, &this->vehicle_list, &this->wagon_list);
this->generate_list = false;
DepotSortList(&this->vehicle_list);
if (this->type == VEH_TRAIN) {
this->problematic_vehicles.clear();
TrainPlacement tp;
for (uint num = 0; num < this->vehicle_list.size(); ++num) {
const Vehicle *v = this->vehicle_list[num];
this->problematic_vehicles.push_back(!tp.CanFindAppropriatePlatform(Train::From(v), false));
}
}
uint new_unitnumber_digits = GetUnitNumberDigits(this->vehicle_list);
/* Only increase the size; do not decrease to prevent constant changes */
@ -742,8 +766,7 @@ struct DepotWindow : Window {
}
/* Setup disabled buttons. */
TileIndex tile = this->window_number;
this->SetWidgetsDisabledState(!IsTileOwner(tile, _local_company),
this->SetWidgetsDisabledState(this->owner != _local_company,
WID_D_STOP_ALL,
WID_D_START_ALL,
WID_D_SELL,
@ -759,6 +782,8 @@ struct DepotWindow : Window {
void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
{
TileIndex tile = Depot::Get(this->window_number)->xy;
switch (widget) {
case WID_D_MATRIX: // List
this->DepotClick(pt.x, pt.y);
@ -789,20 +814,25 @@ struct DepotWindow : Window {
if (_ctrl_pressed) {
ShowExtraViewportWindow(this->window_number);
} else {
ScrollMainWindowToTile(this->window_number);
ScrollMainWindowToTile(tile);
}
break;
case WID_D_RENAME: // Rename button
SetDParam(0, this->type);
SetDParam(1, Depot::GetByTile((TileIndex)this->window_number)->index);
SetDParam(1, this->window_number);
ShowQueryString(STR_DEPOT_NAME, STR_DEPOT_RENAME_DEPOT_CAPTION, MAX_LENGTH_DEPOT_NAME_CHARS, this, CS_ALPHANUMERAL, QSF_ENABLE_DEFAULT | QSF_LEN_IN_CHARS);
break;
case WID_D_HIGHLIGHT:
this->SetWidgetDirty(WID_D_HIGHLIGHT);
SetViewportHighlightDepot(this->window_number, !this->IsWidgetLowered(WID_D_HIGHLIGHT));
break;
case WID_D_STOP_ALL:
case WID_D_START_ALL: {
VehicleListIdentifier vli(VL_DEPOT_LIST, this->type, this->owner);
Command<CMD_MASS_START_STOP>::Post(this->window_number, widget == WID_D_START_ALL, false, vli);
Command<CMD_MASS_START_STOP>::Post(STR_ERROR_CAN_T_START_STOP_VEHICLES, tile, widget == WID_D_START_ALL, false, vli);
break;
}
@ -810,7 +840,7 @@ struct DepotWindow : Window {
/* Only open the confirmation window if there are anything to sell */
if (!this->vehicle_list.empty() || !this->wagon_list.empty()) {
SetDParam(0, this->type);
SetDParam(1, this->GetDepotIndex());
SetDParam(1, this->window_number);
ShowQuery(
STR_DEPOT_CAPTION,
STR_DEPOT_SELL_CONFIRMATION_TEXT,
@ -821,11 +851,11 @@ struct DepotWindow : Window {
break;
case WID_D_VEHICLE_LIST:
ShowVehicleListWindow(GetTileOwner(this->window_number), this->type, (TileIndex)this->window_number);
ShowVehicleListWindow(this->owner, this->type, this->window_number);
break;
case WID_D_AUTOREPLACE:
Command<CMD_DEPOT_MASS_AUTOREPLACE>::Post(this->window_number, this->type);
Command<CMD_DEPOT_MASS_AUTOREPLACE>::Post(STR_ERROR_CAN_T_REPLACE_VEHICLES, tile, this->type);
break;
}
@ -836,7 +866,7 @@ struct DepotWindow : Window {
if (!str.has_value()) return;
/* Do depot renaming */
Command<CMD_RENAME_DEPOT>::Post(STR_ERROR_CAN_T_RENAME_DEPOT, this->GetDepotIndex(), *str);
Command<CMD_RENAME_DEPOT>::Post(STR_ERROR_CAN_T_RENAME_DEPOT, this->window_number, *str);
}
bool OnRightClick([[maybe_unused]] Point pt, WidgetID widget) override
@ -902,10 +932,10 @@ struct DepotWindow : Window {
{
if (_ctrl_pressed) {
/* Share-clone, do not open new viewport, and keep tool active */
Command<CMD_CLONE_VEHICLE>::Post(STR_ERROR_CAN_T_BUY_TRAIN + v->type, this->window_number, v->index, true);
Command<CMD_CLONE_VEHICLE>::Post(STR_ERROR_CAN_T_BUY_TRAIN + v->type, Depot::Get(this->window_number)->xy, v->index, true);
} else {
/* Copy-clone, open viewport for new vehicle, and deselect the tool (assume player wants to change things on new vehicle) */
if (Command<CMD_CLONE_VEHICLE>::Post(STR_ERROR_CAN_T_BUY_TRAIN + v->type, CcCloneVehicle, this->window_number, v->index, false)) {
if (Command<CMD_CLONE_VEHICLE>::Post(STR_ERROR_CAN_T_BUY_TRAIN + v->type, CcCloneVehicle, Depot::Get(this->window_number)->xy, v->index, false)) {
ResetObjectToPlace();
}
}
@ -1111,43 +1141,34 @@ struct DepotWindow : Window {
return ES_NOT_HANDLED;
}
/**
* Gets the DepotID of the current window.
* In the case of airports, this is the station ID.
* @return Depot or station ID of this window.
*/
inline uint16_t GetDepotIndex() const
{
return (this->type == VEH_AIRCRAFT) ? ::GetStationIndex(this->window_number) : ::GetDepotIndex(this->window_number);
}
};
static void DepotSellAllConfirmationCallback(Window *win, bool confirmed)
{
if (confirmed) {
DepotWindow *w = (DepotWindow*)win;
TileIndex tile = w->window_number;
VehicleType vehtype = w->type;
Command<CMD_DEPOT_SELL_ALL_VEHICLES>::Post(tile, vehtype);
assert(Depot::IsValidID(win->window_number));
Depot *d = Depot::Get(win->window_number);
if (!d->IsInUse()) return;
Command<CMD_DEPOT_SELL_ALL_VEHICLES>::Post(d->xy, d->veh_type);
}
}
/**
* Opens a depot window
* @param tile The tile where the depot/hangar is located
* @param type The type of vehicles in the depot
* Opens a depot window.
* @param depot_id Index of the depot.
*/
void ShowDepotWindow(TileIndex tile, VehicleType type)
void ShowDepotWindow(DepotID depot_id)
{
if (BringWindowToFrontById(WC_VEHICLE_DEPOT, tile) != nullptr) return;
assert(Depot::IsValidID(depot_id));
if (BringWindowToFrontById(WC_VEHICLE_DEPOT, depot_id) != nullptr) return;
switch (type) {
Depot *d = Depot::Get(depot_id);
switch (d->veh_type) {
default: NOT_REACHED();
case VEH_TRAIN: new DepotWindow(_train_depot_desc, tile, type); break;
case VEH_ROAD: new DepotWindow(_road_depot_desc, tile, type); break;
case VEH_SHIP: new DepotWindow(_ship_depot_desc, tile, type); break;
case VEH_AIRCRAFT: new DepotWindow(_aircraft_depot_desc, tile, type); break;
case VEH_TRAIN: new DepotWindow(_train_depot_desc, depot_id); break;
case VEH_ROAD: new DepotWindow(_road_depot_desc, depot_id); break;
case VEH_SHIP: new DepotWindow(_ship_depot_desc, depot_id); break;
case VEH_AIRCRAFT: new DepotWindow(_aircraft_depot_desc, depot_id); break;
}
}
@ -1164,8 +1185,357 @@ void DeleteDepotHighlightOfVehicle(const Vehicle *v)
*/
if (_special_mouse_mode != WSM_DRAGDROP) return;
w = dynamic_cast<DepotWindow*>(FindWindowById(WC_VEHICLE_DEPOT, v->tile));
/* For shadows and rotors, do nothing. */
if (v->type == VEH_AIRCRAFT && !Aircraft::From(v)->IsNormalAircraft()) return;
assert(IsDepotTile(v->tile));
w = dynamic_cast<DepotWindow*>(FindWindowById(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile)));
if (w != nullptr) {
if (w->sel == v->index) ResetObjectToPlace();
}
}
static std::vector<DepotID> _depots_nearby_list;
/** Structure with user-data for AddNearbyDepot. */
struct AddNearbyDepotData {
TileArea search_area; ///< Search area.
VehicleType type; ///< Vehicle type of the searched depots.
};
/**
* Add depot on this tile to _depots_nearby_list if it's fully within the
* depot spread.
* @param tile Tile just being checked
* @param user_data Pointer to TileArea context
*/
static bool AddNearbyDepot(TileIndex tile, void *user_data)
{
AddNearbyDepotData *andd = (AddNearbyDepotData *)user_data;
/* Check if own depot and if we stay within station spread */
if (!IsDepotTile(tile)) return false;
Depot *dep = Depot::GetByTile(tile);
if (dep->owner != _local_company || dep->veh_type != andd->type ||
(find(_depots_nearby_list.begin(), _depots_nearby_list.end(), dep->index) != _depots_nearby_list.end())) {
return false;
}
CommandCost cost = dep->BeforeAddTiles(andd->search_area);
if (cost.Succeeded()) {
_depots_nearby_list.push_back(dep->index);
}
return false;
}
/**
* Circulate around the to-be-built depot to find depots we could join.
* Make sure that only depots are returned where joining wouldn't exceed
* depot spread and are our own depot.
* @param ta Base tile area of the to-be-built depot
* @param veh_type Vehicle type depots to look for
* @param distant_join Search for adjacent depots (false) or depots fully
* within depot spread
*/
static const Depot *FindDepotsNearby(TileArea ta, VehicleType veh_type, bool distant_join)
{
_depots_nearby_list.clear();
_depots_nearby_list.push_back(NEW_DEPOT);
/* Check the inside, to return, if we sit on another big depot */
Depot *depot;
for (TileIndex t : ta) {
if (!IsDepotTile(t)) continue;
depot = Depot::GetByTile(t);
if (depot->veh_type == veh_type && depot->owner == _current_company) return depot;
}
/* Only search tiles where we have a chance to stay within the depot spread.
* The complete check needs to be done in the callback as we don't know the
* extent of the found depot, yet. */
if (distant_join && std::min(ta.w, ta.h) >= _settings_game.depot.depot_spread) return nullptr;
uint max_dist = distant_join ? _settings_game.depot.depot_spread - std::min(ta.w, ta.h) : 1;
AddNearbyDepotData andd;
andd.search_area = ta;
andd.type = veh_type;
TileIndex tile = TileAddByDir(andd.search_area.tile, DIR_N);
CircularTileSearch(&tile, max_dist, ta.w, ta.h, AddNearbyDepot, &andd);
/* Add reusable depots. */
ta.Expand(8);
for (Depot *d : Depot::Iterate()) {
if (d->IsInUse()) continue;
if (d->veh_type != veh_type || d->owner != _current_company) continue;
if (!ta.Contains(d->xy)) continue;
_depots_nearby_list.push_back(d->index);
}
return nullptr;
}
/**
* Check whether we need to show the depot selection window.
* @param ta Tile area of the to-be-built depot.
* @param proc The procedure for the depot picker.
* @param veh_type the vehicle type of the depot.
* @return whether we need to show the depot selection window.
*/
static bool DepotJoinerNeeded(TileArea ta, VehicleType veh_type)
{
/* If a window is already opened and we didn't ctrl-click,
* return true (i.e. just flash the old window) */
Window *selection_window = FindWindowById(WC_SELECT_DEPOT, veh_type);
if (selection_window != nullptr) {
/* Abort current distant-join and start new one */
selection_window->Close();
UpdateTileSelection();
}
/* Only show the popup if we press ctrl. */
if (!_ctrl_pressed) return false;
/* Test for adjacent depot or depot below selection.
* If adjacent-stations is disabled and we are building next to a depot, do not show the selection window.
* but join the other depot immediately. */
const Depot *depot = FindDepotsNearby(ta, veh_type, false);
return depot == nullptr && (_settings_game.depot.adjacent_depots || std::any_of(std::begin(_depots_nearby_list), std::end(_depots_nearby_list), [](DepotID s) { return s != NEW_DEPOT; }));
}
/**
* Window for selecting depots to (distant) join to.
*/
struct SelectDepotWindow : Window {
DepotPickerCmdProc select_depot_proc; ///< The procedure params
TileArea area; ///< Location of new depot
Scrollbar *vscroll; ///< Vertical scrollbar for the window
SelectDepotWindow(WindowDesc &desc, TileArea ta, DepotPickerCmdProc& proc, VehicleType veh_type) :
Window(desc),
select_depot_proc(std::move(proc)),
area(ta)
{
this->CreateNestedTree();
this->vscroll = this->GetScrollbar(WID_JD_SCROLLBAR);
this->FinishInitNested(veh_type);
this->OnInvalidateData(0);
_thd.freeze = true;
}
~SelectDepotWindow()
{
SetViewportHighlightDepot(INVALID_DEPOT, true);
_thd.freeze = false;
}
void UpdateWidgetSize(int widget, Dimension &size, const Dimension &padding, [[maybe_unused]] Dimension &fill, Dimension &resize) override
{
if (widget != WID_JD_PANEL) return;
resize.height = GetCharacterHeight(FS_NORMAL);
size.height = 5 * resize.height + padding.height;
/* Determine the widest string. */
Dimension d = GetStringBoundingBox(STR_JOIN_DEPOT_CREATE_SPLITTED_DEPOT);
for (const auto &depot : _depots_nearby_list) {
if (depot == NEW_DEPOT) continue;
const Depot *dep = Depot::Get(depot);
SetDParam(0, this->window_number);
SetDParam(1, dep->index);
d = maxdim(d, GetStringBoundingBox(STR_DEPOT_LIST_DEPOT));
}
d.height = 5 * resize.height;
d.width += padding.width;
d.height += padding.height;
size = d;
}
void DrawWidget(const Rect &r, int widget) const override
{
if (widget != WID_JD_PANEL) return;
Rect tr = r.Shrink(WidgetDimensions::scaled.framerect);
auto [first, last] = this->vscroll->GetVisibleRangeIterators(_depots_nearby_list);
for (auto it = first; it != last; ++it, tr.top += this->resize.step_height) {
if (*it == NEW_DEPOT) {
DrawString(tr, STR_JOIN_DEPOT_CREATE_SPLITTED_DEPOT);
} else {
SetDParam(0, this->window_number);
SetDParam(1, *it);
[[maybe_unused]] Depot *depot = Depot::GetIfValid(*it);
assert(depot != nullptr);
DrawString(tr, STR_DEPOT_LIST_DEPOT);
}
}
}
void OnClick(Point pt, int widget, [[maybe_unused]] int click_count) override
{
if (widget != WID_JD_PANEL) return;
auto it = this->vscroll->GetScrolledItemFromWidget(_depots_nearby_list, pt.y, this, WID_JD_PANEL, WidgetDimensions::scaled.framerect.top);
if (it == _depots_nearby_list.end()) return;
/* Execute stored Command */
this->select_depot_proc(*it);
InvalidateWindowData(WC_SELECT_DEPOT, window_number);
this->Close();
}
void OnRealtimeTick([[maybe_unused]] uint delta_ms) override
{
if (_thd.dirty & 2) {
_thd.dirty &= ~2;
this->SetDirty();
}
}
void OnResize() override
{
this->vscroll->SetCapacityFromWidget(this, WID_JD_PANEL, WidgetDimensions::scaled.framerect.Vertical());
}
/**
* Some data on this window has become invalid.
* @param data Information about the changed data.
* @param gui_scope Whether the call is done from GUI scope. You may not do everything when not in GUI scope. See #InvalidateWindowData() for details.
*/
void OnInvalidateData([[maybe_unused]] int data = 0, bool gui_scope = true) override
{
if (!gui_scope) return;
FindDepotsNearby(this->area, (VehicleType)this->window_number, true);
this->vscroll->SetCount((uint)_depots_nearby_list.size());
this->SetDirty();
}
void OnMouseOver(Point pt, int widget) override
{
if (widget != WID_JD_PANEL) {
SetViewportHighlightDepot(INVALID_DEPOT, true);
return;
}
/* Highlight depot under cursor */
auto it = this->vscroll->GetScrolledItemFromWidget(_depots_nearby_list, pt.y, this, WID_JD_PANEL, WidgetDimensions::scaled.framerect.top);
SetViewportHighlightDepot(*it, true);
}
};
static const NWidgetPart _nested_select_depot_widgets[] = {
NWidget(NWID_HORIZONTAL),
NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
NWidget(WWT_CAPTION, COLOUR_DARK_GREEN, WID_JD_CAPTION), SetDataTip(STR_JOIN_DEPOT_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
NWidget(WWT_DEFSIZEBOX, COLOUR_DARK_GREEN),
EndContainer(),
NWidget(NWID_HORIZONTAL),
NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_JD_PANEL), SetResize(1, 0), SetScrollbar(WID_JD_SCROLLBAR), EndContainer(),
NWidget(NWID_VERTICAL),
NWidget(NWID_VSCROLLBAR, COLOUR_DARK_GREEN, WID_JD_SCROLLBAR),
NWidget(WWT_RESIZEBOX, COLOUR_DARK_GREEN),
EndContainer(),
EndContainer(),
};
static WindowDesc _select_depot_desc(
WDP_AUTO, "build_depot_join", 200, 180,
WC_SELECT_DEPOT, WC_NONE,
WDF_CONSTRUCTION,
_nested_select_depot_widgets
);
/**
* Show the depot selection window when needed. If not, build the depot.
* @param ta Area to build the depot in.
* @param proc Details of the procedure for the depot picker.
* @param veh_type Vehicle type of the depot to be built.
*/
void ShowSelectDepotIfNeeded(TileArea ta, DepotPickerCmdProc proc, VehicleType veh_type)
{
if (DepotJoinerNeeded(ta, veh_type)) {
if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace();
new SelectDepotWindow(_select_depot_desc, ta, proc, veh_type);
} else {
proc(INVALID_DEPOT);
}
}
/**
* Find depots adjacent to the current tile highlight area, so that all depot tiles
* can be highlighted.
* @param v_type Vehicle type to check.
*/
static void HighlightSingleAdjacentDepot(VehicleType v_type)
{
/* With distant join we don't know which depot will be selected, so don't show any */
if (_ctrl_pressed) {
SetViewportHighlightDepot(INVALID_DEPOT, true);
return;
}
/* Tile area for TileHighlightData */
TileArea location(TileVirtXY(_thd.pos.x, _thd.pos.y), _thd.size.x / TILE_SIZE - 1, _thd.size.y / TILE_SIZE - 1);
/* If the current tile is already a depot, then it must be the nearest depot. */
if (IsDepotTypeTile(location.tile, (TransportType)v_type) &&
GetTileOwner(location.tile) == _local_company) {
SetViewportHighlightDepot(GetDepotIndex(location.tile), true);
return;
}
/* Extended area by one tile */
uint x = TileX(location.tile);
uint y = TileY(location.tile);
int max_c = 1;
TileArea ta(TileXY(std::max<int>(0, x - max_c), std::max<int>(0, y - max_c)), TileXY(std::min<int>(Map::MaxX(), x + location.w + max_c), std::min<int>(Map::MaxY(), y + location.h + max_c)));
DepotID adjacent = INVALID_DEPOT;
for (TileIndex tile : ta) {
if (IsDepotTile(tile) && GetTileOwner(tile) == _local_company) {
Depot *depot = Depot::GetByTile(tile);
if (depot == nullptr) continue;
if (depot->veh_type != v_type) continue;
if (adjacent != INVALID_DEPOT && depot->index != adjacent) {
/* Multiple nearby, distant join is required. */
adjacent = INVALID_DEPOT;
break;
}
adjacent = depot->index;
}
}
SetViewportHighlightDepot(adjacent, true);
}
/**
* Check whether we need to redraw the depot highlight.
* If it is needed actually make the window for redrawing.
* @param w the window to check.
* @param veh_type vehicle type to check.
*/
void CheckRedrawDepotHighlight(const Window *w, VehicleType veh_type)
{
/* Test if ctrl state changed */
static bool _last_ctrl_pressed;
if (_ctrl_pressed != _last_ctrl_pressed) {
_thd.dirty = 0xff;
_last_ctrl_pressed = _ctrl_pressed;
}
if (_thd.dirty & 1) {
_thd.dirty &= ~1;
w->SetDirty();
if (_thd.drawstyle == HT_RECT) {
HighlightSingleAdjacentDepot(veh_type);
}
}
}

View File

@ -12,24 +12,25 @@
#include "station_map.h"
static const uint8_t DEPOT_TYPE = 0x02;
/**
* Check if a tile is a depot and it is a depot of the given type.
*/
inline bool IsDepotTypeTile(Tile tile, TransportType type)
{
if (type == TRANSPORT_AIR) return IsHangarTile(tile);
if (GB(tile.m5(), 6, 2) != DEPOT_TYPE) return false;
switch (type) {
default: NOT_REACHED();
case TRANSPORT_RAIL:
return IsRailDepotTile(tile);
return IsTileType(tile, MP_RAILWAY);
case TRANSPORT_ROAD:
return IsRoadDepotTile(tile);
return IsTileType(tile, MP_ROAD);
case TRANSPORT_WATER:
return IsShipDepotTile(tile);
case TRANSPORT_AIR:
return IsHangarTile(tile);
return IsTileType(tile, MP_WATER);
}
}
@ -40,19 +41,28 @@ inline bool IsDepotTypeTile(Tile tile, TransportType type)
*/
inline bool IsDepotTile(Tile tile)
{
return IsRailDepotTile(tile) || IsRoadDepotTile(tile) || IsShipDepotTile(tile) || IsHangarTile(tile);
TileType type = GetTileType(tile);
if (type == MP_STATION) return IsHangar(tile);
if (GB(tile.m5(), 6, 2) != DEPOT_TYPE) return false;
return type == MP_RAILWAY || type == MP_ROAD || type == MP_WATER;
}
extern DepotID GetHangarIndex(TileIndex t);
/**
* Get the index of which depot is attached to the tile.
* @param t the tile
* @pre IsRailDepotTile(t) || IsRoadDepotTile(t) || IsShipDepotTile(t)
* @pre IsDepotTile(t)
* @return DepotID
*/
inline DepotID GetDepotIndex(Tile t)
{
/* Hangars don't have a Depot class, thus store no DepotID. */
assert(IsRailDepotTile(t) || IsRoadDepotTile(t) || IsShipDepotTile(t));
assert(IsDepotTile(t));
/* Hangars don't store depot id on m2. */
if (IsTileType(t, MP_STATION)) return GetHangarIndex(t);
return t.m2();
}
@ -73,4 +83,98 @@ inline VehicleType GetDepotVehicleType(Tile t)
}
}
/** Return true if a tile belongs to an extended depot. */
static inline bool IsExtendedDepot(Tile tile) {
assert(IsValidTile(tile));
assert(IsDepotTile(tile));
if (IsAirportTile(tile)) return false;
return HasBit(tile.m5(), 5);
}
/** Return true if a tile belongs to an extended depot. */
static inline bool IsExtendedDepotTile(TileIndex tile) {
if (!IsValidTile(tile)) return false;
if (!IsDepotTile(tile)) return false;
return IsExtendedDepot(tile);
}
/**
* Has this depot some vehicle servicing or stopped inside?
* @param tile tile of the depot.
* @param south_dir In case of road transport, return reservation facing south if true.
* @return The type of reservation on this tile (empty, servicing or occupied).
* @pre is a depot tile
*/
static inline DepotReservation GetDepotReservation(Tile t, bool south_dir = false)
{
assert(IsDepotTile(t));
if (!IsExtendedDepot(t)) return DEPOT_RESERVATION_EMPTY;
if (south_dir) {
assert(GetDepotVehicleType(t) == VEH_ROAD);
return (DepotReservation)GB(t.m6(), 4, 2);
}
return (DepotReservation)GB(t.m4(), 6, 2);
}
/**
* Is this a platform/depot tile full with stopped vehicles?
* @param tile tile of the depot.
* @param south_dir In case of road transport, check reservation facing south if true.
* @return the type of reservation of the depot.
* @pre is a depot tile
*/
static inline bool IsDepotFullWithStoppedVehicles(TileIndex t, bool south_dir = false)
{
assert(IsDepotTile(t));
if (!IsExtendedDepot(t)) return false;
return GetDepotReservation(t, south_dir) == DEPOT_RESERVATION_FULL_STOPPED_VEH;
}
/**
* Has this depot tile/platform some vehicle inside?
* @param tile tile of the depot.
* @param south_dir In case of road transport, check reservation facing south if true.
* @return true iff depot tile/platform has no vehicle.
* @pre IsExtendedDepotTile
*/
static inline bool IsExtendedDepotEmpty(TileIndex t, bool south_dir = false)
{
assert(IsExtendedDepotTile(t));
return GetDepotReservation(t, south_dir) == DEPOT_RESERVATION_EMPTY;
}
/**
* Mark whether this depot has a ship inside.
* @param tile of the depot.
* @param reservation type of reservation
* @param south_dir Whether to set south direction reservation.
* @pre tile is an extended ship depot.
*/
static inline void SetDepotReservation(Tile t, DepotReservation reservation, bool south_dir = false)
{
assert(IsDepotTile(t));
if (!IsExtendedDepot(t)) return;
switch (GetTileType(t)) {
default: NOT_REACHED();
case MP_RAILWAY:
break;
case MP_ROAD:
if (south_dir) {
SB(t.m6(), 4, 2, reservation);
return;
}
break;
case MP_WATER:
assert(GetDepotReservation(t) == GetDepotReservation(GetOtherShipDepotTile(t)));
SB(Tile(GetOtherShipDepotTile(t)).m4(), 6, 2, reservation);
break;
case MP_STATION: return;
}
SB(t.m4(), 6, 2, reservation);
}
void UpdateExtendedDepotReservation(Vehicle *v, bool state);
#endif /* DEPOT_MAP_H */

View File

@ -14,7 +14,16 @@ typedef uint16_t DepotID; ///< Type for the unique identifier of depots.
struct Depot;
static const DepotID INVALID_DEPOT = UINT16_MAX;
static const DepotID NEW_DEPOT = INVALID_DEPOT - 1;
static const uint MAX_LENGTH_DEPOT_NAME_CHARS = 32; ///< The maximum length of a depot name in characters including '\0'
/** Type of reservation of extended ship depots. */
enum DepotReservation {
DEPOT_RESERVATION_EMPTY = 0, ///< No vehicle servicing/stopped on depot tile/platform.
DEPOT_RESERVATION_IN_USE = 1, ///< At least a vehicle is in the depot, but the depot tile is not full of stopped vehicles.
DEPOT_RESERVATION_FULL_STOPPED_VEH = 2, ///< The depot tile/platform is full with stopped vehicles.
DEPOT_RESERVATION_END
};
#endif /* DEPOT_TYPE_H */

View File

@ -276,4 +276,14 @@ inline bool IsDiagonalDirection(Direction dir)
return (dir & 1) != 0;
}
/**
* Checks if a given DiagDirection is facing south.
* @param diag_dir Diagonal direction to check
* @return true iff the diagonal direction is facing south.
*/
static inline bool IsDiagDirFacingSouth(DiagDirection diag_dir)
{
return diag_dir == DIAGDIR_SE || diag_dir == DIAGDIR_SW;
}
#endif /* DIRECTION_FUNC_H */

View File

@ -32,6 +32,7 @@
#include "waypoint_cmd.h"
#include "timer/timer.h"
#include "timer/timer_game_calendar.h"
#include "depot_func.h"
#include "widgets/dock_widget.h"
@ -112,6 +113,13 @@ struct BuildDocksToolbarWindow : Window {
{
if (_game_mode == GM_NORMAL && this->IsWidgetLowered(WID_DT_STATION)) SetViewportCatchmentStation(nullptr, true);
if (_settings_client.gui.link_terraform_toolbar) CloseWindowById(WC_SCEN_LAND_GEN, 0, false);
if (_game_mode == GM_NORMAL &&
((this->HasWidget(WID_DT_DEPOT) && this->IsWidgetLowered(WID_DT_DEPOT)) ||
(this->HasWidget(WID_DT_EXTENDED_DEPOT) && this->IsWidgetLowered(WID_DT_EXTENDED_DEPOT)))) {
SetViewportHighlightDepot(INVALID_DEPOT, true);
}
this->Window::Close();
}
@ -127,6 +135,7 @@ struct BuildDocksToolbarWindow : Window {
bool can_build = CanBuildVehicleInfrastructure(VEH_SHIP);
this->SetWidgetsDisabledState(!can_build,
WID_DT_DEPOT,
WID_DT_EXTENDED_DEPOT,
WID_DT_STATION,
WID_DT_BUOY);
if (!can_build) {
@ -137,11 +146,13 @@ struct BuildDocksToolbarWindow : Window {
if (_game_mode != GM_EDITOR) {
if (!can_build) {
/* Show in the tooltip why this button is disabled. */
this->GetWidget<NWidgetCore>(WID_DT_DEPOT)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE);
if (this->HasWidget(WID_DT_DEPOT)) this->GetWidget<NWidgetCore>(WID_DT_DEPOT)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE);
if (this->HasWidget(WID_DT_EXTENDED_DEPOT)) this->GetWidget<NWidgetCore>(WID_DT_EXTENDED_DEPOT)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE);
this->GetWidget<NWidgetCore>(WID_DT_STATION)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE);
this->GetWidget<NWidgetCore>(WID_DT_BUOY)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE);
} else {
this->GetWidget<NWidgetCore>(WID_DT_DEPOT)->SetToolTip(STR_WATERWAYS_TOOLBAR_BUILD_DEPOT_TOOLTIP);
if (this->HasWidget(WID_DT_DEPOT)) this->GetWidget<NWidgetCore>(WID_DT_DEPOT)->SetToolTip(STR_WATERWAYS_TOOLBAR_BUILD_DEPOT_TOOLTIP);
if (this->HasWidget(WID_DT_EXTENDED_DEPOT)) this->GetWidget<NWidgetCore>(WID_DT_EXTENDED_DEPOT)->SetToolTip(STR_WATERWAYS_TOOLBAR_BUILD_EXTENDED_DEPOT_TOOLTIP);
this->GetWidget<NWidgetCore>(WID_DT_STATION)->SetToolTip(STR_WATERWAYS_TOOLBAR_BUILD_DOCK_TOOLTIP);
this->GetWidget<NWidgetCore>(WID_DT_BUOY)->SetToolTip(STR_WATERWAYS_TOOLBAR_BUOY_TOOLTIP);
}
@ -164,7 +175,10 @@ struct BuildDocksToolbarWindow : Window {
break;
case WID_DT_DEPOT: // Build depot button
if (HandlePlacePushButton(this, WID_DT_DEPOT, SPR_CURSOR_SHIP_DEPOT, HT_RECT)) ShowBuildDocksDepotPicker(this);
case WID_DT_EXTENDED_DEPOT:
if (HandlePlacePushButton(this, widget, SPR_CURSOR_SHIP_DEPOT, HT_RECT)) {
ShowBuildDocksDepotPicker(this);
}
break;
case WID_DT_STATION: // Build station button
@ -205,8 +219,16 @@ struct BuildDocksToolbarWindow : Window {
break;
case WID_DT_DEPOT: // Build depot button
Command<CMD_BUILD_SHIP_DEPOT>::Post(STR_ERROR_CAN_T_BUILD_SHIP_DEPOT, CcBuildDocks, tile, _ship_depot_direction);
case WID_DT_EXTENDED_DEPOT: {
CloseWindowById(WC_SELECT_DEPOT, VEH_SHIP);
ViewportPlaceMethod vpm = _ship_depot_direction != AXIS_X ? VPM_LIMITED_X_FIXED_Y : VPM_LIMITED_Y_FIXED_X;
VpSetPlaceSizingLimit(_settings_game.depot.depot_spread);
VpStartPlaceSizing(tile, vpm, DDSP_BUILD_DEPOT);
/* Select tiles now to prevent selection from flickering. */
VpSelectTilesWithMethod(pt.x, pt.y, vpm);
break;
}
case WID_DT_STATION: { // Build station button
/* Determine the watery part of the dock. */
@ -260,6 +282,17 @@ struct BuildDocksToolbarWindow : Window {
case DDSP_CREATE_RIVER:
Command<CMD_BUILD_CANAL>::Post(STR_ERROR_CAN_T_PLACE_RIVERS, CcPlaySound_CONSTRUCTION_WATER, end_tile, start_tile, WATER_CLASS_RIVER, _ctrl_pressed);
break;
case DDSP_BUILD_DEPOT: {
bool adjacent = _ctrl_pressed;
bool extended = this->last_clicked_widget == WID_DT_EXTENDED_DEPOT;
auto proc = [=](DepotID join_to) -> bool {
return Command<CMD_BUILD_SHIP_DEPOT>::Post(STR_ERROR_CAN_T_BUILD_SHIP_DEPOT, CcBuildDocks, start_tile, _ship_depot_direction, adjacent, extended, join_to, end_tile);
};
ShowSelectDepotIfNeeded(TileArea(start_tile, end_tile), proc, VEH_SHIP);
break;
}
default: break;
}
@ -270,11 +303,18 @@ struct BuildDocksToolbarWindow : Window {
{
if (_game_mode != GM_EDITOR && this->IsWidgetLowered(WID_DT_STATION)) SetViewportCatchmentStation(nullptr, true);
if (_game_mode != GM_EDITOR &&
((this->HasWidget(WID_DT_DEPOT) && this->IsWidgetLowered(WID_DT_DEPOT)) ||
(this->HasWidget(WID_DT_EXTENDED_DEPOT) && this->IsWidgetLowered(WID_DT_EXTENDED_DEPOT)))) {
SetViewportHighlightDepot(INVALID_DEPOT, true);
}
this->RaiseButtons();
CloseWindowById(WC_BUILD_STATION, TRANSPORT_WATER);
CloseWindowById(WC_BUILD_DEPOT, TRANSPORT_WATER);
CloseWindowById(WC_SELECT_STATION, 0);
CloseWindowById(WC_SELECT_DEPOT, VEH_SHIP);
CloseWindowByClass(WC_BUILD_BRIDGE);
}
@ -322,6 +362,25 @@ struct BuildDocksToolbarWindow : Window {
}, DockToolbarGlobalHotkeys};
};
/**
* Add the depot icons depending on availability of construction.
* @return Panel with water depot buttons.
*/
static std::unique_ptr<NWidgetBase> MakeNWidgetWaterDepot()
{
auto hor = std::make_unique<NWidgetHorizontal>();
if (HasBit(_settings_game.depot.water_depot_types, 0)) {
hor->Add(std::make_unique<NWidgetLeaf>(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_DT_DEPOT, SPR_IMG_SHIP_DEPOT, STR_WATERWAYS_TOOLBAR_BUILD_DEPOT_TOOLTIP));
}
if (HasBit(_settings_game.depot.water_depot_types, 1)) {
hor->Add(std::make_unique<NWidgetLeaf>(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_DT_EXTENDED_DEPOT, SPR_IMG_SHIP_DEPOT, STR_WATERWAYS_TOOLBAR_BUILD_EXTENDED_DEPOT_TOOLTIP));
}
return hor;
}
/**
* Nested widget parts of docks toolbar, game version.
* Position of #WID_DT_RIVER widget has changed.
@ -337,7 +396,7 @@ static constexpr NWidgetPart _nested_build_docks_toolbar_widgets[] = {
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_DT_LOCK), SetMinimalSize(22, 22), SetFill(0, 1), SetDataTip(SPR_IMG_BUILD_LOCK, STR_WATERWAYS_TOOLBAR_BUILD_LOCKS_TOOLTIP),
NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetMinimalSize(5, 22), SetFill(1, 1), EndContainer(),
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_DT_DEMOLISH), SetMinimalSize(22, 22), SetFill(0, 1), SetDataTip(SPR_IMG_DYNAMITE, STR_TOOLTIP_DEMOLISH_BUILDINGS_ETC),
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_DT_DEPOT), SetMinimalSize(22, 22), SetFill(0, 1), SetDataTip(SPR_IMG_SHIP_DEPOT, STR_WATERWAYS_TOOLBAR_BUILD_DEPOT_TOOLTIP),
NWidgetFunction(MakeNWidgetWaterDepot),
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_DT_STATION), SetMinimalSize(22, 22), SetFill(0, 1), SetDataTip(SPR_IMG_SHIP_DOCK, STR_WATERWAYS_TOOLBAR_BUILD_DOCK_TOOLTIP),
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_DT_BUOY), SetMinimalSize(22, 22), SetFill(0, 1), SetDataTip(SPR_IMG_BUOY, STR_WATERWAYS_TOOLBAR_BUOY_TOOLTIP),
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_DT_BUILD_AQUEDUCT), SetMinimalSize(23, 22), SetFill(0, 1), SetDataTip(SPR_IMG_AQUEDUCT, STR_WATERWAYS_TOOLBAR_BUILD_AQUEDUCT_TOOLTIP),
@ -514,10 +573,13 @@ struct BuildDocksDepotWindow : public PickerWindowBase {
private:
static void UpdateDocksDirection()
{
VpSetPlaceFixedSize(2);
if (_ship_depot_direction != AXIS_X) {
SetTileSelectSize(1, 2);
_thd.select_method = VPM_LIMITED_X_FIXED_Y;
} else {
SetTileSelectSize(2, 1);
_thd.select_method = VPM_LIMITED_Y_FIXED_X;
}
}
@ -529,6 +591,13 @@ public:
UpdateDocksDirection();
}
void Close([[maybe_unused]] int data = 0) override
{
CloseWindowById(WC_SELECT_DEPOT, VEH_SHIP);
VpResetFixedSize();
this->PickerWindowBase::Close();
}
void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
{
switch (widget) {
@ -569,6 +638,7 @@ public:
switch (widget) {
case WID_BDD_X:
case WID_BDD_Y:
CloseWindowById(WC_SELECT_DEPOT, VEH_SHIP);
this->RaiseWidget(WID_BDD_X + _ship_depot_direction);
_ship_depot_direction = (widget == WID_BDD_X ? AXIS_X : AXIS_Y);
this->LowerWidget(WID_BDD_X + _ship_depot_direction);
@ -578,6 +648,11 @@ public:
break;
}
}
void OnRealtimeTick([[maybe_unused]] uint delta_ms) override
{
CheckRedrawDepotHighlight(this, VEH_SHIP);
}
};
static constexpr NWidgetPart _nested_build_docks_depot_widgets[] = {

View File

@ -55,6 +55,8 @@
#include "timer/timer.h"
#include "timer/timer_game_calendar.h"
#include "timer/timer_game_economy.h"
#include "depot_base.h"
#include "platform_func.h"
#include "table/strings.h"
#include "table/pricebase.h"
@ -374,6 +376,12 @@ void ChangeOwnershipOfCompanyItems(Owner old_owner, Owner new_owner)
}
if (new_owner == INVALID_OWNER) RebuildSubsidisedSourceAndDestinationCache();
for (Depot *dep : Depot::Iterate()) {
if (dep->owner == old_owner && new_owner != INVALID_OWNER) {
dep->owner = new_owner;
}
}
/* Take care of rating and transport rights in towns */
for (Town *t : Town::Iterate()) {
/* If a company takes over, give the ratings to that company. */
@ -1614,14 +1622,13 @@ static void ReserveConsist(Station *st, Vehicle *u, CargoArray *consist_capleft,
* Update the vehicle's load_unload_ticks, the time it will wait until it tries to load or unload
* again. Adjust for overhang of trains and set it at least to 1.
* @param front The vehicle to be updated.
* @param st The station the vehicle is loading at.
* @param ticks The time it would normally wait, based on cargo loaded and unloaded.
*/
static void UpdateLoadUnloadTicks(Vehicle *front, const Station *st, int ticks)
static void UpdateLoadUnloadTicks(Vehicle *front, int ticks)
{
if (front->type == VEH_TRAIN && _settings_game.order.station_length_loading_penalty) {
/* Each platform tile is worth 2 rail vehicles. */
int overhang = front->GetGroundVehicleCache()->cached_total_length - st->GetPlatformLength(front->tile) * TILE_SIZE;
int overhang = front->GetGroundVehicleCache()->cached_total_length - GetPlatformLength(front->tile) * TILE_SIZE;
if (overhang > 0) {
ticks <<= 1;
ticks += (overhang * ticks) / 8;
@ -1883,9 +1890,9 @@ static void LoadUnloadVehicle(Vehicle *front)
SetBit(front->vehicle_flags, VF_STOP_LOADING);
}
UpdateLoadUnloadTicks(front, st, new_load_unload_ticks);
UpdateLoadUnloadTicks(front, new_load_unload_ticks);
} else {
UpdateLoadUnloadTicks(front, st, 20); // We need the ticks for link refreshing.
UpdateLoadUnloadTicks(front, 20); // We need the ticks for link refreshing.
bool finished_loading = true;
if (front->current_order.GetLoadType() & OLFB_FULL_LOAD) {
if (front->current_order.GetLoadType() == OLF_FULL_LOAD_ANY) {

View File

@ -241,8 +241,6 @@ static const int INVALID_PRICE_MODIFIER = MIN_PRICE_MODIFIER - 1;
static const uint TUNNELBRIDGE_TRACKBIT_FACTOR = 4;
/** Multiplier for how many regular track bits a level crossing counts. */
static const uint LEVELCROSSING_TRACKBIT_FACTOR = 2;
/** Multiplier for how many regular track bits a road depot counts. */
static const uint ROAD_DEPOT_TRACKBIT_FACTOR = 2;
/** Multiplier for how many regular track bits a bay stop counts. */
static const uint ROAD_STOP_TRACKBIT_FACTOR = 2;
/** Multiplier for how many regular tiles a lock counts. */

View File

@ -197,7 +197,10 @@ bool GroundVehicle<T, Type>::IsChainInDepot() const
/* Check whether the rest is also already trying to enter the depot. */
for (; v != nullptr; v = v->Next()) {
if (!v->T::IsInDepot() || v->tile != this->tile) return false;
if (!v->T::IsInDepot()) return false;
assert(IsDepotTile(v->tile));
assert(IsDepotTile(this->tile));
assert(GetDepotIndex(this->tile) == GetDepotIndex(v->tile));
}
return true;

View File

@ -19,6 +19,7 @@
#include "core/pool_func.hpp"
#include "order_backup.h"
#include "group_cmd.h"
#include "depot_map.h"
#include "table/strings.h"
@ -579,7 +580,7 @@ std::tuple<CommandCost, GroupID> CmdAddVehicleGroup(DoCommandFlag flags, GroupID
}
}
SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
if (IsDepotTypeTile(v->tile, (TransportType)v->type)) SetWindowDirty(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile));
SetWindowDirty(WC_VEHICLE_VIEW, v->index);
SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
InvalidateWindowData(WC_VEHICLE_VIEW, v->index);

View File

@ -842,7 +842,7 @@ public:
break;
case WID_GL_AVAILABLE_VEHICLES:
ShowBuildVehicleWindow(INVALID_TILE, this->vli.vtype);
ShowBuildVehicleWindow(INVALID_DEPOT, this->vli.vtype);
break;
case WID_GL_MANAGE_VEHICLES_DROPDOWN: {

View File

@ -1654,6 +1654,7 @@ bool GenerateLandscape(uint8_t mode)
void OnTick_Town();
void OnTick_Trees();
void OnTick_Station();
void OnTick_Depot();
void OnTick_Industry();
void OnTick_Companies();
@ -1667,6 +1668,7 @@ void CallLandscapeTick()
OnTick_Town();
OnTick_Trees();
OnTick_Station();
OnTick_Depot();
OnTick_Industry();
}

View File

@ -278,6 +278,8 @@ STR_TOOLTIP_FILTER_CRITERIA :{BLACK}Select f
STR_BUTTON_SORT_BY :{BLACK}Sort by
STR_BUTTON_CATCHMENT :{BLACK}Coverage
STR_TOOLTIP_CATCHMENT :{BLACK}Toggle coverage area display
STR_BUTTON_HIGHLIGHT_DEPOT :{BLACK}Highlight
STR_TOOLTIP_HIGHLIGHT_DEPOT :{BLACK}Toggle highlight on viewport for this depot
STR_TOOLTIP_CLOSE_WINDOW :{BLACK}Close window
STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS :{BLACK}Window title - drag this to move window
@ -908,6 +910,8 @@ STR_NEWS_TRAIN_IS_STUCK :{WHITE}{VEHICLE
STR_NEWS_VEHICLE_IS_LOST :{WHITE}{VEHICLE} is lost
STR_NEWS_VEHICLE_UNPROFITABLE_YEAR :{WHITE}{VEHICLE}'s profit last year was {CURRENCY_LONG}
STR_NEWS_VEHICLE_UNPROFITABLE_PERIOD :{WHITE}{VEHICLE}'s profit last period was {CURRENCY_LONG}
STR_NEWS_VEHICLE_CAN_T_FIND_FREE_DEPOT :{WHITE}{VEHICLE} can't find a free depot
STR_NEWS_VEHICLE_TOO_LONG_FOR_SERVICING :{WHITE}{VEHICLE} couldn't service in short platform
STR_NEWS_AIRCRAFT_DEST_TOO_FAR :{WHITE}{VEHICLE} can't get to the next destination because it is out of range
STR_NEWS_ORDER_REFIT_FAILED :{WHITE}{VEHICLE} stopped because an ordered refit failed
@ -1462,6 +1466,7 @@ STR_CONFIG_SETTING_STOP_ON_TOWN_ROAD_HELPTEXT :Allow construct
STR_CONFIG_SETTING_STOP_ON_COMPETITOR_ROAD :Allow drive-through road stops on roads owned by competitors: {STRING2}
STR_CONFIG_SETTING_STOP_ON_COMPETITOR_ROAD_HELPTEXT :Allow construction of drive-through road stops on roads owned by other companies
STR_CONFIG_SETTING_DYNAMIC_ENGINES_EXISTING_VEHICLES :{WHITE}Changing this setting is not possible when there are vehicles
STR_CONFIG_SETTING_REPLACEMENTS_DIFF_TYPE :{WHITE}Disabling this setting is not possible during a game
STR_CONFIG_SETTING_INFRASTRUCTURE_MAINTENANCE :Infrastructure maintenance: {STRING2}
STR_CONFIG_SETTING_INFRASTRUCTURE_MAINTENANCE_HELPTEXT :When enabled, infrastructure causes maintenance costs. The cost grows over-proportional with the network size, thus affecting bigger companies more than smaller ones
@ -1615,6 +1620,28 @@ STR_CONFIG_SETTING_SE_FLAT_WORLD_HEIGHT :The height leve
STR_CONFIG_SETTING_EDGES_NOT_EMPTY :{WHITE}One or more tiles at the northern edge are not empty
STR_CONFIG_SETTING_EDGES_NOT_WATER :{WHITE}One or more tiles at one of the edges is not water
STR_CONFIG_SETTING_DISTANT_JOIN_DEPOTS :Allow to join depot parts not directly adjacent: {STRING2}
STR_CONFIG_SETTING_DISTANT_JOIN_DEPOTS_HELPTEXT :Allow adding parts to a depot without directly touching the existing parts. Needs Ctrl+Click while placing the new parts
STR_CONFIG_SETTING_DEPOT_SPREAD :Maximum depot spread: {STRING2}
STR_CONFIG_SETTING_DEPOT_SPREAD_HELPTEXT :Maximum area the parts of a single depot may be spread out on.
STR_CONFIG_SETTING_RAIL_DEPOT_TYPES :Rail depot types: {STRING2}
STR_CONFIG_SETTING_RAIL_DEPOT_TYPES_HELPTEXT :Available rail depot types for construction for human players.
STR_CONFIG_SETTING_ROAD_DEPOT_TYPES :Road depot types: {STRING2}
STR_CONFIG_SETTING_ROAD_DEPOT_TYPES_HELPTEXT :Available road depot types for construction for human players.
STR_CONFIG_SETTING_WATER_DEPOT_TYPES :Water depot types: {STRING2}
STR_CONFIG_SETTING_WATER_DEPOT_TYPES_HELPTEXT :Available water depot types for construction for human players.
###length 3
STR_CONFIG_SETTING_ONLY_STANDARD_DEPOT :Standard depots
STR_CONFIG_SETTING_ONLY_EXTENDED_DEPOT :Extended depots
STR_CONFIG_SETTING_BOTH_DEPOT_TYPES :Both depot types
###next-name-looks-similar
STR_CONFIG_SETTING_REPLACE_INCOMPATIBLE_RAIL :Allow replacing rail vehicles with incompatible rail types: {STRING2}
STR_CONFIG_SETTING_REPLACE_INCOMPATIBLE_RAIL_HELPTEXT :Allow replacing rail vehicles even if they are not compatible by rail type.
STR_CONFIG_SETTING_REPLACE_INCOMPATIBLE_ROAD :Allow replacing road vehicles with incompatible road types: {STRING2}
STR_CONFIG_SETTING_REPLACE_INCOMPATIBLE_ROAD_HELPTEXT :Allow replacing road vehicles even if they are not compatible by road type.
STR_CONFIG_SETTING_STATION_SPREAD :Maximum station spread: {STRING2}
STR_CONFIG_SETTING_STATION_SPREAD_HELPTEXT :Maximum area the parts of a single station may be spread out on. Note that high values will slow the game
@ -2137,6 +2164,7 @@ STR_CONFIG_SETTING_ENVIRONMENT_TREES :Trees
STR_CONFIG_SETTING_AI :Competitors
STR_CONFIG_SETTING_AI_NPC :Computer players
STR_CONFIG_SETTING_NETWORK :Network
STR_CONFIG_SETTING_DEPOTS :Depots
STR_CONFIG_SETTING_REVERSE_AT_SIGNALS :Automatic reversing at signals: {STRING2}
STR_CONFIG_SETTING_REVERSE_AT_SIGNALS_HELPTEXT :Allow trains to reverse on a signal, if they waited there a long time
@ -2763,6 +2791,11 @@ STR_JOIN_STATION_CREATE_SPLITTED_STATION :{YELLOW}Build a
STR_JOIN_WAYPOINT_CAPTION :{WHITE}Join waypoint
STR_JOIN_WAYPOINT_CREATE_SPLITTED_WAYPOINT :{YELLOW}Build a separate waypoint
# Join depot window
STR_JOIN_DEPOT_CAPTION :{WHITE}Join depot
STR_JOIN_DEPOT_CREATE_SPLITTED_DEPOT :{YELLOW}Build a separate depot
STR_DEPOT_LIST_DEPOT :{YELLOW}{DEPOT}
# Generic toolbar
STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE :{BLACK}Disabled as currently no vehicles are available for this infrastructure
@ -2774,7 +2807,8 @@ STR_RAIL_TOOLBAR_MAGLEV_CONSTRUCTION_CAPTION :Maglev Construc
STR_RAIL_TOOLBAR_TOOLTIP_BUILD_RAILROAD_TRACK :{BLACK}Build railway track. Ctrl+Click to remove railway track. Also press Shift to show cost estimate only
STR_RAIL_TOOLBAR_TOOLTIP_BUILD_AUTORAIL :{BLACK}Build railway track using the Autorail mode. Ctrl+Click to remove railway track. Also press Shift to show cost estimate only
STR_RAIL_TOOLBAR_TOOLTIP_BUILD_TRAIN_DEPOT_FOR_BUILDING :{BLACK}Build train depot (for buying and servicing trains). Also press Shift to show cost estimate only
STR_RAIL_TOOLBAR_TOOLTIP_BUILD_TRAIN_DEPOT :{BLACK}Build standard train depot (for buying and servicing trains). Ctrl+Click to select another depot to join. Also press Shift to show cost estimate only
STR_RAIL_TOOLBAR_TOOLTIP_BUILD_EXTENDED_TRAIN_DEPOT :{BLACK}Build extended train depot (for buying and servicing trains). Ctrl+Click to select another depot to join. Also press Shift to show cost estimate only
STR_RAIL_TOOLBAR_TOOLTIP_CONVERT_RAIL_TO_WAYPOINT :{BLACK}Build waypoint on railway. Ctrl+Click to select another waypoint to join. Also press Shift to show cost estimate only
STR_RAIL_TOOLBAR_TOOLTIP_BUILD_RAILROAD_STATION :{BLACK}Build railway station. Ctrl+Click to select another station to join. Also press Shift to show cost estimate only
STR_RAIL_TOOLBAR_TOOLTIP_BUILD_RAILROAD_SIGNALS :{BLACK}Build signal on railway. Ctrl+Click to build the alternate signal style{}Click+Drag to fill the selected section of rail with signals at the chosen spacing. Ctrl+Click+Drag to fill signals up to the next junction, station, or signal. Also press Shift to show cost estimate only
@ -2885,8 +2919,10 @@ STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_SECTION :{BLACK}Build ro
STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAMWAY_SECTION :{BLACK}Build tramway section. Ctrl+Click to remove tramway section. Also press Shift to show cost estimate only
STR_ROAD_TOOLBAR_TOOLTIP_BUILD_AUTOROAD :{BLACK}Build road section using the Autoroad mode. Ctrl+Click to remove road section. Also press Shift to show cost estimate only
STR_ROAD_TOOLBAR_TOOLTIP_BUILD_AUTOTRAM :{BLACK}Build tramway section using the Autotram mode. Ctrl+Click to remove tramway section. Also press Shift to show cost estimate only
STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_VEHICLE_DEPOT :{BLACK}Build road vehicle depot (for buying and servicing vehicles). Also press Shift to show cost estimate only
STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAM_VEHICLE_DEPOT :{BLACK}Build tram vehicle depot (for buying and servicing vehicles). Also press Shift to show cost estimate only
STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_VEHICLE_DEPOT :{BLACK}Build standard road vehicle depot (for buying and servicing vehicles). Ctrl+Click to select another depot to join. Also press Shift to show cost estimate only
STR_ROAD_TOOLBAR_TOOLTIP_BUILD_EXTENDED_ROAD_VEHICLE_DEPOT :{BLACK}Build extended road vehicle depot (for buying and servicing vehicles). Ctrl+Click to select another depot to join. Also press Shift to show cost estimate only
STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAM_VEHICLE_DEPOT :{BLACK}Build standard tram vehicle depot (for buying and servicing vehicles). Ctrl+Click to select another depot to join. Also press Shift to show cost estimate only
STR_ROAD_TOOLBAR_TOOLTIP_BUILD_EXTENDED_TRAM_VEHICLE_DEPOT :{BLACK}Build extended tram vehicle depot (for buying and servicing vehicles). Ctrl+Click to select another depot to join. Also press Shift to show cost estimate only
STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_ROAD_TO_WAYPOINT :{BLACK}Build waypoint on road. Ctrl+Click to select another waypoint to join. Also press Shift to show cost estimate only
STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_TRAM_TO_WAYPOINT :{BLACK}Build waypoint on tramway. Ctrl+Click to select another waypoint to join. Also press Shift to show cost estimate only
STR_ROAD_TOOLBAR_TOOLTIP_BUILD_BUS_STATION :{BLACK}Build bus station. Ctrl+Click to select another station to join. Also press Shift to show cost estimate only
@ -2927,7 +2963,8 @@ STR_WATERWAYS_TOOLBAR_CAPTION :{WHITE}Waterway
STR_WATERWAYS_TOOLBAR_CAPTION_SE :{WHITE}Waterways
STR_WATERWAYS_TOOLBAR_BUILD_CANALS_TOOLTIP :{BLACK}Build canals. Also press Shift to show cost estimate only
STR_WATERWAYS_TOOLBAR_BUILD_LOCKS_TOOLTIP :{BLACK}Build locks. Also press Shift to show cost estimate only
STR_WATERWAYS_TOOLBAR_BUILD_DEPOT_TOOLTIP :{BLACK}Build ship depot (for buying and servicing ships). Also press Shift to show cost estimate only
STR_WATERWAYS_TOOLBAR_BUILD_DEPOT_TOOLTIP :{BLACK}Build standard ship depot (for buying and servicing ships). Ctrl+Click to select another depot to join. Also press Shift to show cost estimate only
STR_WATERWAYS_TOOLBAR_BUILD_EXTENDED_DEPOT_TOOLTIP :{BLACK}Build extended ship depot (for buying and servicing ships). Ctrl+Click to select another depot to join. Also press Shift to show cost estimate only
STR_WATERWAYS_TOOLBAR_BUILD_DOCK_TOOLTIP :{BLACK}Build ship dock. Ctrl+Click to select another station to join. Also press Shift to show cost estimate only
STR_WATERWAYS_TOOLBAR_BUOY_TOOLTIP :{BLACK}Place a buoy which can be used as a waypoint. Also press Shift to show cost estimate only
STR_WATERWAYS_TOOLBAR_BUILD_AQUEDUCT_TOOLTIP :{BLACK}Build aqueduct. Also press Shift to show cost estimate only
@ -3140,12 +3177,14 @@ STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_NOENTRYSIGNALS :Railway track w
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_PBSSIGNALS :Railway track with combo- and path signals
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_NOENTRYSIGNALS :Railway track with combo- and one-way path signals
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBS_NOENTRYSIGNALS :Railway track with path and one-way path signals
STR_LAI_RAIL_DESCRIPTION_TRAIN_DEPOT :Railway train depot
STR_LAI_RAIL_DESCRIPTION_TRAIN_DEPOT :Standard railway train depot
STR_LAI_RAIL_DESCRIPTION_TRAIN_DEPOT_EXTENDED :Extended railway train depot
STR_LAI_ROAD_DESCRIPTION_ROAD :Road
STR_LAI_ROAD_DESCRIPTION_ROAD_WITH_STREETLIGHTS :Road with street lights
STR_LAI_ROAD_DESCRIPTION_TREE_LINED_ROAD :Tree-lined road
STR_LAI_ROAD_DESCRIPTION_ROAD_VEHICLE_DEPOT :Road vehicle depot
STR_LAI_ROAD_DESCRIPTION_ROAD_VEHICLE_DEPOT :Standard road vehicle depot
STR_LAI_ROAD_DESCRIPTION_ROAD_VEHICLE_DEPOT_EXTENDED :Extended road vehicle depot
STR_LAI_ROAD_DESCRIPTION_ROAD_RAIL_LEVEL_CROSSING :Road/rail level crossing
STR_LAI_ROAD_DESCRIPTION_TRAMWAY :Tramway
@ -3170,7 +3209,8 @@ STR_LAI_WATER_DESCRIPTION_CANAL :Canal
STR_LAI_WATER_DESCRIPTION_LOCK :Lock
STR_LAI_WATER_DESCRIPTION_RIVER :River
STR_LAI_WATER_DESCRIPTION_COAST_OR_RIVERBANK :Coast or riverbank
STR_LAI_WATER_DESCRIPTION_SHIP_DEPOT :Ship depot
STR_LAI_WATER_DESCRIPTION_SHIP_DEPOT :Standard ship depot
STR_LAI_WATER_DESCRIPTION_SHIP_DEPOT_EXTENDED :Extended ship depot
# Industries come directly from their industry names
@ -4407,6 +4447,7 @@ STR_VEHICLE_STATUS_STOPPED :{RED}Stopped
STR_VEHICLE_STATUS_TRAIN_STOPPING_VEL :{RED}{VELOCITY} - Stopping
STR_VEHICLE_STATUS_TRAIN_NO_POWER :{RED}No power
STR_VEHICLE_STATUS_TRAIN_STUCK :{ORANGE}Waiting for free path
STR_VEHICLE_STATUS_SERVICING :{LTBLUE}Servicing vehicle
STR_VEHICLE_STATUS_AIRCRAFT_TOO_FAR :{ORANGE}Too far to next destination
STR_VEHICLE_STATUS_HEADING_FOR_STATION_VEL :{LTBLUE}{1:VELOCITY} - Heading for {0:STATION}
@ -4894,6 +4935,8 @@ STR_PERCENT_DOWN_SMALL :{TINY_FONT}{WHI
STR_PERCENT_DOWN :{WHITE}{NUM}%{DOWN_ARROW}
STR_PERCENT_UP_DOWN_SMALL :{TINY_FONT}{WHITE}{NUM}%{UP_ARROW}{DOWN_ARROW}
STR_PERCENT_UP_DOWN :{WHITE}{NUM}%{UP_ARROW}{DOWN_ARROW}
STR_SERVICING_INDICATOR_SMALL :{TINY_FONT}{LTBLUE}Servicing ({NUM}{NBSP}%)
STR_SERVICING_INDICATOR :{LTBLUE}Servicing ({NUM}{NBSP}%)
STR_PERCENT_NONE_SMALL :{TINY_FONT}{WHITE}{NUM}%
STR_PERCENT_NONE :{WHITE}{NUM}%
@ -5110,10 +5153,18 @@ STR_ERROR_BUOY_IS_IN_USE :{WHITE}... buoy
# Depot related errors
STR_ERROR_CAN_T_BUILD_TRAIN_DEPOT :{WHITE}Can't build train depot here...
STR_ERROR_CAN_T_REMOVE_TRAIN_DEPOT :{WHITE}Can't remove train depot here...
STR_ERROR_CAN_T_BUILD_ROAD_DEPOT :{WHITE}Can't build road vehicle depot here...
STR_ERROR_CAN_T_BUILD_TRAM_DEPOT :{WHITE}Can't build tram vehicle depot here...
STR_ERROR_CAN_T_BUILD_SHIP_DEPOT :{WHITE}Can't build ship depot here...
STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING_DEPOT :{WHITE}... adjoins more than one existing depot
STR_ERROR_DEPOT_TYPE_NOT_AVAILABLE :{WHITE}... depot type not available
STR_ERROR_DEPOT_EXTENDING_PLATFORMS :{WHITE}Extending already reserved depot platforms
STR_ERROR_DEPOT_EXTENDED_RAIL_DEPOT_IS_NOT_FREE :{WHITE}Extended rail depot has a reserved tile and can't be converted
STR_ERROR_CAN_T_START_STOP_VEHICLES :{WHITE}Can't start at least one vehicle in the depot...
STR_ERROR_CAN_T_REPLACE_VEHICLES :{WHITE}Can't replace vehicles in the depot...
STR_ERROR_CAN_T_RENAME_DEPOT :{WHITE}Can't rename depot...
STR_ERROR_TRAIN_MUST_BE_STOPPED_INSIDE_DEPOT :{WHITE}... must be stopped inside a depot
@ -5123,6 +5174,7 @@ STR_ERROR_AIRCRAFT_MUST_BE_STOPPED_INSIDE_HANGAR :{WHITE}... must
STR_ERROR_TRAINS_CAN_ONLY_BE_ALTERED_INSIDE_A_DEPOT :{WHITE}Trains can only be altered when stopped inside a depot
STR_ERROR_TRAIN_TOO_LONG :{WHITE}Train too long
STR_ERROR_INCOMPATIBLE_RAILTYPES_WITH_DEPOT :{WHITE}Train chain is incompatible with any tile of this depot
STR_ERROR_CAN_T_REVERSE_DIRECTION_RAIL_VEHICLE :{WHITE}Can't reverse direction of vehicle...
STR_ERROR_CAN_T_REVERSE_DIRECTION_RAIL_VEHICLE_MULTIPLE_UNITS :{WHITE}... consists of multiple units
STR_ERROR_INCOMPATIBLE_RAIL_TYPES :Incompatible rail types
@ -5131,8 +5183,19 @@ STR_ERROR_CAN_T_MOVE_VEHICLE :{WHITE}Can't mo
STR_ERROR_REAR_ENGINE_FOLLOW_FRONT :{WHITE}The rear engine will always follow its front counterpart
STR_ERROR_UNABLE_TO_FIND_ROUTE_TO :{WHITE}Unable to find route to local depot
STR_ERROR_UNABLE_TO_FIND_LOCAL_DEPOT :{WHITE}Unable to find local depot
STR_ERROR_UNABLE_TO_FIND_APPROPRIATE_DEPOT_TILE :{WHITE}Unable to find appropriate depot tile
STR_ERROR_DEPOT_TOO_SPREAD_OUT :{WHITE}... depot too spread out
STR_ERROR_DEPOT_WRONG_DEPOT_TYPE :Wrong depot type
STR_ERROR_CAN_T_START_PLATFORM_TYPE :{WHITE}{VEHICLE} can't be started because there is no compatible platform in the depot for this type of train
STR_ERROR_CAN_T_START_PLATFORM_LONG :{WHITE}{VEHICLE} can't be started because compatible platforms are not long enough
STR_ERROR_DEPOT_FULL_DEPOT :There is no free depot compatible with this type of vehicle
###length 5
STR_ADVICE_PLATFORM_TYPE :{WHITE}{VEHICLE} can't leave depot because there is no compatible platform for this type of train
STR_ADVICE_PLATFORM_LONG :{WHITE}{VEHICLE} can't leave depot because compatible platforms are not long enough
STR_ADVICE_VEHICLE_HAS_NO_POWER :{WHITE}{VEHICLE} can't leave depot because it has no power in any tile of the depot
STR_ADVICE_PLATFORM_FREE_PLATFORM :{WHITE}{VEHICLE} can't leave depot because compatible platforms are occupied
STR_ADVICE_PLATFORM_SIGNALS :{WHITE}{VEHICLE} can't leave depot because the segments of the compatible free platforms are occupied. Check the signaling of those segments.
# Depot unbunching related errors
STR_ERROR_UNBUNCHING_ONLY_ONE_ALLOWED :{WHITE}... can only have one unbunching order
@ -5297,6 +5360,7 @@ STR_ERROR_TOO_MANY_VEHICLES_IN_GAME :{WHITE}Too many
STR_ERROR_CAN_T_CHANGE_SERVICING :{WHITE}Can't change servicing interval...
STR_ERROR_VEHICLE_IS_DESTROYED :{WHITE}... vehicle is destroyed
STR_ERROR_NO_FREE_DEPOT :{WHITE}... there is no free depot
STR_ERROR_CAN_T_CLONE_VEHICLE_LIST :{WHITE}... not all vehicles are identical
@ -5314,6 +5378,7 @@ STR_ERROR_NO_TOWN_ROADTYPES_AVAILABLE_YET_EXPLANATION :{WHITE}Start a
STR_ERROR_CAN_T_MAKE_TRAIN_PASS_SIGNAL :{WHITE}Can't make train pass signal at danger...
STR_ERROR_CAN_T_REVERSE_DIRECTION_TRAIN :{WHITE}Can't reverse direction of train...
STR_ERROR_TRAIN_START_NO_POWER :Train has no power
STR_ERROR_ROAD_VEHICLE_START_NO_POWER :Road vehicle has no power
STR_ERROR_CAN_T_MAKE_ROAD_VEHICLE_TURN :{WHITE}Can't make road vehicle turn around...
@ -5825,6 +5890,9 @@ STR_VIEWPORT_STATION_TINY :{TINY_FONT}{STA
STR_VIEWPORT_WAYPOINT :{WAYPOINT}
STR_VIEWPORT_WAYPOINT_TINY :{TINY_FONT}{WAYPOINT}
STR_VIEWPORT_DEPOT :{DEPOT}
STR_VIEWPORT_DEPOT_TINY :{TINY_FONT}{DEPOT}
# Simple strings to get specific types of data
STR_COMPANY_NAME :{COMPANY}
STR_COMPANY_NAME_COMPANY_NUM :{COMPANY} {COMPANY_NUM}

View File

@ -24,6 +24,7 @@
#include "newgrf_animation_base.h"
#include "newgrf_class_func.h"
#include "timer/timer_game_calendar.h"
#include "platform_func.h"
#include "safeguards.h"

View File

@ -19,6 +19,8 @@
#include "order_cmd.h"
#include "group_cmd.h"
#include "vehicle_func.h"
#include "depot_map.h"
#include "depot_base.h"
#include "safeguards.h"
@ -45,8 +47,9 @@ OrderBackup::~OrderBackup()
*/
OrderBackup::OrderBackup(const Vehicle *v, uint32_t user)
{
assert(IsDepotTile(v->tile));
this->user = user;
this->tile = v->tile;
this->depot_id = GetDepotIndex(v->tile);
this->group = v->group_id;
this->CopyConsistPropertiesFrom(v);
@ -123,8 +126,10 @@ void OrderBackup::DoRestore(Vehicle *v)
*/
/* static */ void OrderBackup::Restore(Vehicle *v, uint32_t user)
{
assert(IsDepotTile(v->tile));
DepotID depot_id_veh = GetDepotIndex(v->tile);
for (OrderBackup *ob : OrderBackup::Iterate()) {
if (v->tile != ob->tile || ob->user != user) continue;
if (depot_id_veh != ob->depot_id || ob->user != user) continue;
ob->DoRestore(v);
delete ob;
@ -133,28 +138,28 @@ void OrderBackup::DoRestore(Vehicle *v)
/**
* Reset an OrderBackup given a tile and user.
* @param tile The tile associated with the OrderBackup.
* @param depot_id The depot associated with the OrderBackup.
* @param user The user associated with the OrderBackup.
* @note Must not be used from the GUI!
*/
/* static */ void OrderBackup::ResetOfUser(TileIndex tile, uint32_t user)
/* static */ void OrderBackup::ResetOfUser(DepotID depot_id, uint32_t user)
{
for (OrderBackup *ob : OrderBackup::Iterate()) {
if (ob->user == user && (ob->tile == tile || tile == INVALID_TILE)) delete ob;
if (ob->user == user && (ob->depot_id == depot_id || depot_id == INVALID_DEPOT)) delete ob;
}
}
/**
* Clear an OrderBackup
* @param flags For command.
* @param tile Tile related to the to-be-cleared OrderBackup.
* @param depot_id Tile related to the to-be-cleared OrderBackup.
* @param user_id User that had the OrderBackup.
* @return The cost of this operation or an error.
*/
CommandCost CmdClearOrderBackup(DoCommandFlag flags, TileIndex tile, ClientID user_id)
CommandCost CmdClearOrderBackup(DoCommandFlag flags, DepotID depot_id, ClientID user_id)
{
/* No need to check anything. If the tile or user don't exist we just ignore it. */
if (flags & DC_EXEC) OrderBackup::ResetOfUser(tile == 0 ? INVALID_TILE : tile, user_id);
assert(Depot::IsValidID(depot_id));
if (flags & DC_EXEC) OrderBackup::ResetOfUser(depot_id, user_id);
return CommandCost();
}
@ -173,18 +178,18 @@ CommandCost CmdClearOrderBackup(DoCommandFlag flags, TileIndex tile, ClientID us
/* If it's not a backup of us, ignore it. */
if (ob->user != user) continue;
Command<CMD_CLEAR_ORDER_BACKUP>::Post(0, static_cast<ClientID>(user));
Command<CMD_CLEAR_ORDER_BACKUP>::Post(ob->depot_id, static_cast<ClientID>(user));
return;
}
}
/**
* Reset the OrderBackups from GUI/game logic.
* @param t The tile of the order backup.
* @param depot_id The index of the depot associated to the order backups.
* @param from_gui Whether the call came from the GUI, i.e. whether
* it must be synced over the network.
*/
/* static */ void OrderBackup::Reset(TileIndex t, bool from_gui)
/* static */ void OrderBackup::Reset(DepotID depot_id, bool from_gui)
{
/* The user has CLIENT_ID_SERVER as default when network play is not active,
* but compiled it. A network client has its own variable for the unique
@ -195,16 +200,16 @@ CommandCost CmdClearOrderBackup(DoCommandFlag flags, TileIndex tile, ClientID us
for (OrderBackup *ob : OrderBackup::Iterate()) {
/* If this is a GUI action, and it's not a backup of us, ignore it. */
if (from_gui && ob->user != user) continue;
/* If it's not for our chosen tile either, ignore it. */
if (t != INVALID_TILE && t != ob->tile) continue;
/* If it's not for our chosen depot either, ignore it. */
if (depot_id != INVALID_DEPOT && depot_id != ob->depot_id) continue;
if (from_gui) {
/* We need to circumvent the "prevention" from this command being executed
* while the game is paused, so use the internal method. Nor do we want
* this command to get its cost estimated when shift is pressed. */
Command<CMD_CLEAR_ORDER_BACKUP>::Unsafe<CommandCallback>(STR_NULL, nullptr, true, false, ob->tile, CommandTraits<CMD_CLEAR_ORDER_BACKUP>::Args{ ob->tile, static_cast<ClientID>(user) });
Command<CMD_CLEAR_ORDER_BACKUP>::Unsafe<CommandCallback>(STR_NULL, nullptr, true, false, ob->depot_id, CommandTraits<CMD_CLEAR_ORDER_BACKUP>::Args{ ob->depot_id, static_cast<ClientID>(user) });
} else {
/* The command came from the game logic, i.e. the clearing of a tile.
/* The command came from the game logic, i.e. the clearing of a depot.
* In that case we have no need to actually sync this, just do it. */
delete ob;
}
@ -246,18 +251,14 @@ CommandCost CmdClearOrderBackup(DoCommandFlag flags, TileIndex tile, ClientID us
* Removes an order from all vehicles. Triggers when, say, a station is removed.
* @param type The type of the order (OT_GOTO_[STATION|DEPOT|WAYPOINT]).
* @param destination The destination. Can be a StationID, DepotID or WaypointID.
* @param hangar Only used for airports in the destination.
* When false, remove airport and hangar orders.
* When true, remove either airport or hangar order.
*/
/* static */ void OrderBackup::RemoveOrder(OrderType type, DestinationID destination, bool hangar)
/* static */ void OrderBackup::RemoveOrder(OrderType type, DestinationID destination)
{
for (OrderBackup *ob : OrderBackup::Iterate()) {
for (Order *order = ob->orders; order != nullptr; order = order->next) {
OrderType ot = order->GetType();
if (ot == OT_GOTO_DEPOT && (order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) != 0) continue;
if (ot == OT_GOTO_DEPOT && hangar && !IsHangarTile(ob->tile)) continue; // Not an aircraft? Can't have a hangar order.
if (ot == OT_IMPLICIT || (IsHangarTile(ob->tile) && ot == OT_GOTO_DEPOT && !hangar)) ot = OT_GOTO_STATION;
if (ot == OT_IMPLICIT) ot = OT_GOTO_STATION;
if (ot == type && order->GetDestination() == destination) {
/* Remove the order backup! If a station/depot gets removed, we can't/shouldn't restore those broken orders. */
delete ob;

View File

@ -16,6 +16,7 @@
#include "vehicle_type.h"
#include "base_consist.h"
#include "saveload/saveload.h"
#include "depot_type.h"
/** Unique identifier for an order backup. */
typedef uint8_t OrderBackupID;
@ -35,7 +36,7 @@ private:
friend SaveLoadTable GetOrderBackupDescription(); ///< Saving and loading of order backups.
friend struct BKORChunkHandler; ///< Creating empty orders upon savegame loading.
uint32_t user; ///< The user that requested the backup.
TileIndex tile; ///< Tile of the depot where the order was changed.
DepotID depot_id; ///< Depot where the order was changed.
GroupID group; ///< The group the vehicle was part of.
const Vehicle *clone; ///< Vehicle this vehicle was a clone of.
@ -53,13 +54,13 @@ public:
static void Backup(const Vehicle *v, uint32_t user);
static void Restore(Vehicle *v, uint32_t user);
static void ResetOfUser(TileIndex tile, uint32_t user);
static void ResetOfUser(DepotID depot_id, uint32_t user);
static void ResetUser(uint32_t user);
static void Reset(TileIndex tile = INVALID_TILE, bool from_gui = true);
static void Reset(DepotID depot = INVALID_DEPOT, bool from_gui = true);
static void ClearGroup(GroupID group);
static void ClearVehicle(const Vehicle *v);
static void RemoveOrder(OrderType type, DestinationID destination, bool hangar);
static void RemoveOrder(OrderType type, DestinationID destination);
};
#endif /* ORDER_BACKUP_H */

View File

@ -225,6 +225,7 @@ public:
inline void SetMaxSpeed(uint16_t speed) { this->max_speed = speed; }
bool ShouldStopAtStation(const Vehicle *v, StationID station) const;
bool ShouldStopAtDepot(DepotID depot) const;
bool CanLoadOrUnload() const;
bool CanLeaveWithCargo(bool has_cargo) const;

View File

@ -650,7 +650,7 @@ TileIndex Order::GetLocation(const Vehicle *v, bool airport) const
case OT_GOTO_DEPOT:
if (this->GetDestination() == INVALID_DEPOT) return INVALID_TILE;
return (v->type == VEH_AIRCRAFT) ? Station::Get(this->GetDestination())->xy : Depot::Get(this->GetDestination())->xy;
return Depot::Get(this->GetDestination())->xy;
default:
return INVALID_TILE;
@ -684,6 +684,28 @@ uint GetOrderDistance(const Order *prev, const Order *cur, const Vehicle *v, int
return v->type == VEH_AIRCRAFT ? DistanceSquare(prev_tile, cur_tile) : DistanceManhattan(prev_tile, cur_tile);
}
/**
* Get the station or depot index associated to an order of a vehicle.
* For aircraft, it will return the index of the associated station, even for go to hangar orders.
* @param o Order to check.
* @param is_aircraft Whether the order is of an aircraft vehicle.
* @return index associated to a station or depot, or INVALID_STATION.
*/
DestinationID GetTargetDestination(const Order &o, bool is_aircraft)
{
DestinationID destination_id = o.GetDestination();
switch (o.GetType()) {
case OT_GOTO_STATION:
return destination_id;
case OT_GOTO_DEPOT:
assert(Depot::IsValidID(destination_id));
return is_aircraft ? GetStationIndex(Depot::Get(destination_id)->xy) : destination_id;
default:
return INVALID_STATION;
}
}
/**
* Add an order to the orderlist of a vehicle.
* @param flags operation to perform
@ -763,41 +785,15 @@ CommandCost CmdInsertOrder(DoCommandFlag flags, VehicleID veh, VehicleOrderID se
case OT_GOTO_DEPOT: {
if ((new_order.GetDepotActionType() & ODATFB_NEAREST_DEPOT) == 0) {
if (v->type == VEH_AIRCRAFT) {
const Station *st = Station::GetIfValid(new_order.GetDestination());
if (st == nullptr) return CMD_ERROR;
ret = CheckOwnership(st->owner);
if (ret.Failed()) return ret;
if (!CanVehicleUseStation(v, st) || !st->airport.HasHangar()) {
return CMD_ERROR;
}
} else {
const Depot *dp = Depot::GetIfValid(new_order.GetDestination());
if (dp == nullptr) return CMD_ERROR;
if (dp == nullptr || !dp->IsInUse()) return CMD_ERROR;
ret = CheckOwnership(GetTileOwner(dp->xy));
ret = CheckOwnership(dp->owner);
if (ret.Failed()) return ret;
switch (v->type) {
case VEH_TRAIN:
if (!IsRailDepotTile(dp->xy)) return CMD_ERROR;
break;
case VEH_ROAD:
if (!IsRoadDepotTile(dp->xy)) return CMD_ERROR;
break;
case VEH_SHIP:
if (!IsShipDepotTile(dp->xy)) return CMD_ERROR;
break;
default: return CMD_ERROR;
}
}
if (v->type != dp->veh_type) return CMD_ERROR;
if (v->type == VEH_AIRCRAFT && !CanVehicleUseStation(v, Station::GetByTile(dp->xy))) return CMD_ERROR;
}
if (new_order.GetNonStopType() != ONSF_STOP_EVERYWHERE && !v->IsGroundVehicle()) return CMD_ERROR;
@ -1780,24 +1776,11 @@ void CheckOrders(const Vehicle *v)
* Removes an order from all vehicles. Triggers when, say, a station is removed.
* @param type The type of the order (OT_GOTO_[STATION|DEPOT|WAYPOINT]).
* @param destination The destination. Can be a StationID, DepotID or WaypointID.
* @param hangar Only used for airports in the destination.
* When false, remove airport and hangar orders.
* When true, remove either airport or hangar order.
*/
void RemoveOrderFromAllVehicles(OrderType type, DestinationID destination, bool hangar)
void RemoveOrderFromAllVehicles(OrderType type, DestinationID destination)
{
/* Aircraft have StationIDs for depot orders and never use DepotIDs
* This fact is handled specially below
*/
/* Go through all vehicles */
for (Vehicle *v : Vehicle::Iterate()) {
if ((v->type == VEH_AIRCRAFT && v->current_order.IsType(OT_GOTO_DEPOT) && !hangar ? OT_GOTO_STATION : v->current_order.GetType()) == type &&
(!hangar || v->type == VEH_AIRCRAFT) && v->current_order.GetDestination() == destination) {
v->current_order.MakeDummy();
SetWindowDirty(WC_VEHICLE_VIEW, v->index);
}
/* Clear the order from the order-list */
int id = -1;
for (Order *order : v->Orders()) {
@ -1806,8 +1789,7 @@ restart:
OrderType ot = order->GetType();
if (ot == OT_GOTO_DEPOT && (order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) != 0) continue;
if (ot == OT_GOTO_DEPOT && hangar && v->type != VEH_AIRCRAFT) continue; // Not an aircraft? Can't have a hangar order.
if (ot == OT_IMPLICIT || (v->type == VEH_AIRCRAFT && ot == OT_GOTO_DEPOT && !hangar)) ot = OT_GOTO_STATION;
if (ot == OT_IMPLICIT) ot = OT_GOTO_STATION;
if (ot == type && order->GetDestination() == destination) {
/* We want to clear implicit orders, but we don't want to make them
* dummy orders. They should just vanish. Also check the actual order
@ -1841,7 +1823,7 @@ restart:
}
}
OrderBackup::RemoveOrder(type, destination, hangar);
OrderBackup::RemoveOrder(type, destination);
}
/**
@ -2051,10 +2033,11 @@ bool UpdateOrderDest(Vehicle *v, const Order *order, int conditional_depth, bool
v->IncrementRealOrderIndex();
} else {
if (v->type != VEH_AIRCRAFT) {
v->SetDestTile(Depot::Get(order->GetDestination())->xy);
v->SetDestTile(Depot::Get(order->GetDestination())->GetBestDepotTile(v));
} else {
Aircraft *a = Aircraft::From(v);
DestinationID destination = a->current_order.GetDestination();
DestinationID destination_depot = a->current_order.GetDestination();
StationID destination = GetStationIndex(Depot::Get(destination_depot)->xy);
if (a->targetairport != destination) {
/* The aircraft is now heading for a different hangar than the next in the orders */
a->SetDestTile(a->GetOrderStationLocation(destination));
@ -2232,6 +2215,17 @@ bool Order::ShouldStopAtStation(const Vehicle *v, StationID station) const
!(this->GetNonStopType() & (is_dest_station ? ONSF_NO_STOP_AT_DESTINATION_STATION : ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS));
}
/**
* Check whether the given vehicle should stop at the given depot.
* @param v the vehicle that might be stopping.
* @param depot the depot to stop at.
* @return true if the vehicle should stop.
*/
bool Order::ShouldStopAtDepot(DepotID depot) const
{
return this->IsType(OT_GOTO_DEPOT) && this->dest == depot;
}
bool Order::CanLoadOrUnload() const
{
return (this->IsType(OT_GOTO_STATION) || this->IsType(OT_IMPLICIT)) &&

View File

@ -21,7 +21,7 @@ CommandCost CmdInsertOrder(DoCommandFlag flags, VehicleID veh, VehicleOrderID se
CommandCost CmdOrderRefit(DoCommandFlag flags, VehicleID veh, VehicleOrderID order_number, CargoID cargo);
CommandCost CmdCloneOrder(DoCommandFlag flags, CloneOptions action, VehicleID veh_dst, VehicleID veh_src);
CommandCost CmdMoveOrder(DoCommandFlag flags, VehicleID veh, VehicleOrderID moving_order, VehicleOrderID target_order);
CommandCost CmdClearOrderBackup(DoCommandFlag flags, TileIndex tile, ClientID user_id);
CommandCost CmdClearOrderBackup(DoCommandFlag flags, DepotID depot_id, ClientID user_id);
DEF_CMD_TRAIT(CMD_MODIFY_ORDER, CmdModifyOrder, CMD_LOCATION, CMDT_ROUTE_MANAGEMENT)
DEF_CMD_TRAIT(CMD_SKIP_TO_ORDER, CmdSkipToOrder, CMD_LOCATION, CMDT_ROUTE_MANAGEMENT)

View File

@ -15,7 +15,7 @@
#include "company_type.h"
/* Functions */
void RemoveOrderFromAllVehicles(OrderType type, DestinationID destination, bool hangar = false);
void RemoveOrderFromAllVehicles(OrderType type, DestinationID destination);
void InvalidateVehicleOrder(const Vehicle *v, int data);
void CheckOrders(const Vehicle*);
void DeleteVehicleOrders(Vehicle *v, bool keep_orderlist = false, bool reset_order_indices = true);
@ -23,6 +23,7 @@ bool ProcessOrders(Vehicle *v);
bool UpdateOrderDest(Vehicle *v, const Order *order, int conditional_depth = 0, bool pbs_look_ahead = false);
VehicleOrderID ProcessConditionalOrder(const Order *order, const Vehicle *v);
uint GetOrderDistance(const Order *prev, const Order *cur, const Vehicle *v, int conditional_depth = 0);
DestinationID GetTargetDestination(const Order &o, bool is_aircraft);
void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int y, bool selected, bool timetable, int left, int middle, int right);

View File

@ -34,6 +34,7 @@
#include "error.h"
#include "order_cmd.h"
#include "company_cmd.h"
#include "depot_base.h"
#include "widgets/order_widget.h"
@ -309,7 +310,7 @@ void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int
/* Going to a specific depot. */
SetDParam(0, STR_ORDER_GO_TO_DEPOT_FORMAT);
SetDParam(2, v->type);
SetDParam(3, order->GetDestination());
SetDParam(3, GetTargetDestination(*order, v->type == VEH_AIRCRAFT));
}
if (order->GetDepotOrderType() & ODTFB_SERVICE) {
@ -384,8 +385,7 @@ static Order GetOrderCmdFromTile(const Vehicle *v, TileIndex tile)
/* check depot first */
if (IsDepotTypeTile(tile, (TransportType)(uint)v->type) && IsTileOwner(tile, _local_company)) {
order.MakeGoToDepot(v->type == VEH_AIRCRAFT ? GetStationIndex(tile) : GetDepotIndex(tile),
ODTFB_PART_OF_ORDERS,
order.MakeGoToDepot(GetDepotIndex(tile), ODTFB_PART_OF_ORDERS,
(_settings_client.gui.new_nonstop && v->IsGroundVehicle()) ? ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS : ONSF_STOP_EVERYWHERE);
if (_ctrl_pressed) {

View File

@ -18,6 +18,7 @@
#include "../tunnelbridge_map.h"
#include "../depot_map.h"
#include "pathfinder_func.h"
#include "../platform_func.h"
/**
* Track follower helper template class (can serve pathfinders and vehicle
@ -46,7 +47,8 @@ struct CFollowTrackT
bool m_is_tunnel; ///< last turn passed tunnel
bool m_is_bridge; ///< last turn passed bridge ramp
bool m_is_station; ///< last turn passed station
int m_tiles_skipped; ///< number of skipped tunnel or station tiles
bool m_is_extended_depot; ///< last turn passed depot
int m_tiles_skipped; ///< number of skipped tunnel, depot or station tiles
ErrorCode m_err;
RailTypes m_railtypes;
@ -80,7 +82,7 @@ struct CFollowTrackT
m_new_tile = INVALID_TILE;
m_new_td_bits = TRACKDIR_BIT_NONE;
m_exitdir = INVALID_DIAGDIR;
m_is_station = m_is_bridge = m_is_tunnel = false;
m_is_station = m_is_bridge = m_is_tunnel = m_is_extended_depot = false;
m_tiles_skipped = 0;
m_err = EC_NONE;
m_railtypes = railtype_override;
@ -99,7 +101,7 @@ struct CFollowTrackT
{
assert(IsTram()); // this function shouldn't be called in other cases
if (IsNormalRoadTile(tile)) {
if (IsNormalRoadTile(tile) || IsExtendedRoadDepotTile(tile)) {
RoadBits rb = GetRoadBits(tile, RTT_TRAM);
switch (rb) {
case ROAD_NW: return DIAGDIR_NW;
@ -170,11 +172,11 @@ struct CFollowTrackT
{
if (!DoTrackMasking()) return true;
if (m_is_station) {
/* Check skipped station tiles as well. */
if (m_is_station || m_is_extended_depot) {
/* Check skipped station and depot tiles as well. */
TileIndexDiff diff = TileOffsByDiagDir(m_exitdir);
for (TileIndex tile = m_new_tile - diff * m_tiles_skipped; tile != m_new_tile; tile += diff) {
if (HasStationReservation(tile)) {
if ((m_is_station && HasStationReservation(tile)) || (m_is_extended_depot && HasDepotReservation(tile))) {
m_new_td_bits = TRACKDIR_BIT_NONE;
m_err = EC_RESERVED;
return false;
@ -200,7 +202,7 @@ protected:
/** Follow the m_exitdir from m_old_tile and fill m_new_tile and m_tiles_skipped */
inline void FollowTileExit()
{
m_is_station = m_is_bridge = m_is_tunnel = false;
m_is_station = m_is_bridge = m_is_tunnel = m_is_extended_depot = false;
m_tiles_skipped = 0;
/* extra handling for tunnels and bridges in our direction */
@ -224,9 +226,13 @@ protected:
/* normal or station tile, do one step */
m_new_tile = TileAddByDiagDir(m_old_tile, m_exitdir);
/* special handling for stations */
if (IsRailTT() && HasStationTileRail(m_new_tile)) {
/* special handling for stations and multi-tile depots */
if (IsRailTT()) {
if (HasStationTileRail(m_new_tile)) {
m_is_station = true;
} else if (IsExtendedRailDepotTile(m_new_tile)) {
m_is_extended_depot = true;
}
} else if (IsRoadTT() && IsStationRoadStopTile(m_new_tile)) {
m_is_station = true;
}
@ -266,14 +272,16 @@ protected:
}
}
/* road depots can be also left in one direction only */
/* road depots can be also left in one direction sometimes */
if (IsRoadTT() && IsDepotTypeTile(m_old_tile, TT())) {
DiagDirection exitdir = GetRoadDepotDirection(m_old_tile);
if (exitdir != m_exitdir) {
RoadTramType rtt = IsTram() ? RTT_TRAM : RTT_ROAD;
RoadBits rb = GetRoadBits(m_old_tile, rtt);
if ((rb & DiagDirToRoadBits(m_exitdir)) == ROAD_NONE) {
m_err = EC_NO_WAY;
return false;
}
}
return true;
}
@ -300,18 +308,19 @@ protected:
/* road and rail depots can also be entered from one direction only */
if (IsRoadTT() && IsDepotTypeTile(m_new_tile, TT())) {
DiagDirection exitdir = GetRoadDepotDirection(m_new_tile);
if (ReverseDiagDir(exitdir) != m_exitdir) {
m_err = EC_NO_WAY;
return false;
}
/* don't try to enter other company's depots */
if (GetTileOwner(m_new_tile) != m_veh_owner) {
m_err = EC_OWNER;
return false;
}
RoadTramType rtt = IsTram() ? RTT_TRAM : RTT_ROAD;
RoadBits rb = GetRoadBits(m_new_tile, rtt);
if ((rb & DiagDirToRoadBits(ReverseDiagDir(m_exitdir))) == ROAD_NONE) {
m_err = EC_NO_WAY;
return false;
}
if (IsRailTT() && IsDepotTypeTile(m_new_tile, TT())) {
}
if (IsRailTT() && IsStandardRailDepotTile(m_new_tile)) {
DiagDirection exitdir = GetRailDepotDirection(m_new_tile);
if (ReverseDiagDir(exitdir) != m_exitdir) {
m_err = EC_NO_WAY;
@ -368,14 +377,14 @@ protected:
}
}
/* special handling for rail stations - get to the end of platform */
if (IsRailTT() && m_is_station) {
/* entered railway station
* get platform length */
uint length = BaseStation::GetByTile(m_new_tile)->GetPlatformLength(m_new_tile, TrackdirToExitdir(m_old_td));
/* how big step we must do to get to the last platform tile? */
m_tiles_skipped = length - 1;
/* move to the platform end */
/* special handling for rail platforms - get to the end of platform */
if (IsRailTT() && (m_is_station || m_is_extended_depot)) {
/* Entered a platform. */
assert(HasStationTileRail(m_new_tile) || IsExtendedRailDepotTile(m_new_tile));
/* How big step we must do to get to the last platform tile? */
m_tiles_skipped = GetPlatformLength(m_new_tile, TrackdirToExitdir(m_old_td)) - 1;
/* Move to the platform end. */
TileIndexDiff diff = TileOffsByDiagDir(m_exitdir);
diff *= m_tiles_skipped;
m_new_tile = TileAdd(m_new_tile, diff);
@ -390,14 +399,29 @@ protected:
{
/* rail and road depots cause reversing */
if (!IsWaterTT() && IsDepotTypeTile(m_old_tile, TT())) {
DiagDirection exitdir = IsRailTT() ? GetRailDepotDirection(m_old_tile) : GetRoadDepotDirection(m_old_tile);
DiagDirection exitdir;
switch (TT()) {
case TRANSPORT_AIR:
return false;
case TRANSPORT_RAIL:
if (IsExtendedRailDepot(m_old_tile)) return false;
exitdir = GetRailDepotDirection(m_old_tile);
break;
case TRANSPORT_ROAD: {
if (GetRoadBits(m_old_tile, IsTram() ? RTT_TRAM : RTT_ROAD) != DiagDirToRoadBits(m_exitdir)) return false;
exitdir = ReverseDiagDir(m_exitdir);
break;
}
default: NOT_REACHED();
}
if (exitdir != m_exitdir) {
/* reverse */
m_new_tile = m_old_tile;
m_new_td_bits = TrackdirToTrackdirBits(ReverseTrackdir(m_old_td));
m_exitdir = exitdir;
m_tiles_skipped = 0;
m_is_tunnel = m_is_bridge = m_is_station = false;
m_is_tunnel = m_is_bridge = m_is_station = m_is_extended_depot = false;
return true;
}
}

View File

@ -12,6 +12,7 @@
#include "../tile_cmd.h"
#include "../waypoint_base.h"
#include "../depot_base.h"
/**
* Calculates the tile of given station that is closest to a given tile
@ -47,6 +48,46 @@ inline TileIndex CalcClosestStationTile(StationID station, TileIndex tile, Stati
return TileXY(x, y);
}
/**
* Calculates the tile of a depot that is closest to a given tile.
* @param depot_id The depot to calculate the distance to.
* @param tile The tile from where to calculate the distance.
* @return The closest depot tile to the given tile.
*/
static inline TileIndex CalcClosestDepotTile(DepotID depot_id, TileIndex tile)
{
assert(Depot::IsValidID(depot_id));
const Depot *dep = Depot::Get(depot_id);
/* If tile area is empty, use the xy tile. */
if (dep->ta.tile == INVALID_TILE) {
assert(dep->xy != INVALID_TILE);
return dep->xy;
}
TileIndex best_tile = INVALID_TILE;
DepotReservation best_found_type = dep->veh_type == VEH_SHIP ? DEPOT_RESERVATION_END : DEPOT_RESERVATION_EMPTY;
uint best_distance = UINT_MAX;
for (auto const &depot_tile : dep->depot_tiles) {
uint new_distance = DistanceManhattan(depot_tile, tile);
bool check_south_direction = dep->veh_type == VEH_ROAD;
again:
DepotReservation depot_reservation = GetDepotReservation(depot_tile, check_south_direction);
if (((best_found_type == depot_reservation) && new_distance < best_distance) || (depot_reservation < best_found_type)) {
best_tile = depot_tile;
best_distance = new_distance;
best_found_type = depot_reservation;
}
if (check_south_direction) {
check_south_direction = false;
goto again;
}
}
return best_tile;
}
/**
* Wrapper around GetTileTrackStatus() and TrackStatusToTrackdirBits(), as for
* single tram bits GetTileTrackStatus() returns 0. The reason for this is

View File

@ -150,13 +150,24 @@ public:
return false;
}
/** Check for a reserved depot platform. */
inline bool IsAnyDepotTileReserved(TileIndex tile, Trackdir trackdir, int skipped)
{
TileIndexDiff diff = TileOffsByDiagDir(TrackdirToExitdir(ReverseTrackdir(trackdir)));
for (; skipped >= 0; skipped--, tile += diff) {
if (HasDepotReservation(tile)) return true;
}
return false;
}
/** The cost for reserved tiles, including skipped ones. */
inline int ReservationCost(Node &n, TileIndex tile, Trackdir trackdir, int skipped)
{
if (n.m_num_signals_passed >= m_sig_look_ahead_costs.size() / 2) return 0;
if (!IsPbsSignal(n.m_last_signal_type)) return 0;
if (IsRailStationTile(tile) && IsAnyStationTileReserved(tile, trackdir, skipped)) {
if ((IsRailStationTile(tile) && IsAnyStationTileReserved(tile, trackdir, skipped)) ||
(IsExtendedRailDepotTile(tile) && IsAnyDepotTileReserved(tile, trackdir, skipped))) {
return Yapf().PfGetSettings().rail_pbs_station_penalty * (skipped + 1);
} else if (TrackOverlapsTracks(GetReservedTrackbits(tile), TrackdirToTrack(trackdir))) {
int cost = Yapf().PfGetSettings().rail_pbs_cross_penalty;
@ -389,13 +400,12 @@ no_entry_cost: // jump here at the beginning if the node has no parent (it is th
/* Tests for 'potential target' reasons to close the segment. */
if (cur.tile == prev.tile) {
/* Penalty for reversing in a depot. */
assert(IsRailDepot(cur.tile));
assert(IsStandardRailDepot(cur.tile));
segment_cost += Yapf().PfGetSettings().rail_depot_reverse_penalty;
} else if (IsRailDepotTile(cur.tile)) {
} else if (IsStandardRailDepotTile(cur.tile)) {
/* We will end in this pass (depot is possible target) */
end_segment_reason |= ESRB_DEPOT;
} else if (cur.tile_type == MP_STATION && IsRailWaypoint(cur.tile)) {
if (v->current_order.IsType(OT_GOTO_WAYPOINT) &&
GetStationIndex(cur.tile) == v->current_order.GetDestination() &&
@ -440,14 +450,14 @@ no_entry_cost: // jump here at the beginning if the node has no parent (it is th
/* Waypoint is also a good reason to finish. */
end_segment_reason |= ESRB_WAYPOINT;
} else if (tf->m_is_station) {
} else if (tf->m_is_station || tf->m_is_extended_depot) {
/* Station penalties. */
uint platform_length = tf->m_tiles_skipped + 1;
/* We don't know yet if the station is our target or not. Act like
* if it is pass-through station (not our destination). */
segment_cost += Yapf().PfGetSettings().rail_station_penalty * platform_length;
/* We will end in this pass (station is possible target) */
end_segment_reason |= ESRB_STATION;
end_segment_reason |= ESRB_PLATFORM;
} else if (TrackFollower::DoTrackMasking() && cur.tile_type == MP_RAILWAY) {
/* Searching for a safe tile? */
@ -591,13 +601,21 @@ no_entry_cost: // jump here at the beginning if the node has no parent (it is th
}
}
/* Station platform-length penalty. */
if ((end_segment_reason & ESRB_STATION) != ESRB_NONE) {
const BaseStation *st = BaseStation::GetByTile(n.GetLastTile());
assert(st != nullptr);
uint platform_length = st->GetPlatformLength(n.GetLastTile(), ReverseDiagDir(TrackdirToExitdir(n.GetLastTrackdir())));
/* Reduce the extra cost caused by passing-station penalty (each station receives it in the segment cost). */
/* Platform-length penalty. */
if ((end_segment_reason & ESRB_PLATFORM) != ESRB_NONE) {
assert(HasStationTileRail(n.GetLastTile()) || IsExtendedRailDepotTile(n.GetLastTile()));
uint platform_length = GetPlatformLength(n.GetLastTile(), ReverseDiagDir(TrackdirToExitdir(n.GetLastTrackdir())));
/* Reduce the extra cost caused by passing-platform penalty (each platform receives it in the segment cost). */
extra_cost -= Yapf().PfGetSettings().rail_station_penalty * platform_length;
if (tf->m_is_extended_depot) {
DepotReservation depot_reservation = GetDepotReservation(n.GetLastTile());
if (depot_reservation == DEPOT_RESERVATION_FULL_STOPPED_VEH) {
extra_cost += YAPF_INFINITE_PENALTY;
} else {
extra_cost += (HasDepotReservation(n.GetLastTile()) ? 2 : 1) * platform_length * Yapf().PfGetSettings().rail_station_penalty;
}
}
/* Add penalty for the inappropriate platform length. */
extra_cost += PlatformLengthPenalty(platform_length);
}

View File

@ -118,6 +118,7 @@ protected:
TileIndex m_destTile;
TrackdirBits m_destTrackdirs;
StationID m_dest_station_id;
DepotID m_dest_depot_id;
bool m_any_depot;
/** to access inherited path finder */
@ -149,14 +150,25 @@ public:
break;
case OT_GOTO_DEPOT:
m_dest_station_id = INVALID_STATION;
if (v->current_order.GetDepotActionType() & ODATFB_NEAREST_DEPOT) {
m_any_depot = true;
m_dest_depot_id = INVALID_DEPOT;
m_destTile = v->dest_tile;
m_destTrackdirs = TrackStatusToTrackdirBits(GetTileTrackStatus(v->dest_tile, TRANSPORT_RAIL, 0));
} else {
m_dest_depot_id = v->current_order.GetDestination();
assert(Depot::IsValidID(m_dest_depot_id));
m_destTile = CalcClosestDepotTile(m_dest_depot_id, v->tile);
m_destTrackdirs = INVALID_TRACKDIR_BIT;
}
[[fallthrough]];
break;
default:
m_destTile = v->dest_tile;
m_dest_station_id = INVALID_STATION;
m_dest_depot_id = INVALID_DEPOT;
m_destTrackdirs = TrackStatusToTrackdirBits(GetTileTrackStatus(v->dest_tile, TRANSPORT_RAIL, 0));
break;
}
@ -176,6 +188,10 @@ public:
return HasStationTileRail(tile)
&& (GetStationIndex(tile) == m_dest_station_id)
&& (GetRailStationTrack(tile) == TrackdirToTrack(td));
} else if (m_dest_depot_id != INVALID_DEPOT) {
return IsRailDepotTile(tile)
&& (GetDepotIndex(tile) == m_dest_depot_id)
&& (GetRailDepotTrack(tile) == TrackdirToTrack(td));
}
if (m_any_depot) {

View File

@ -16,6 +16,7 @@
#include "yapf_destrail.hpp"
#include "../../viewport_func.h"
#include "../../newgrf_station.h"
#include "../../platform_func.h"
#include "../../safeguards.h"
@ -85,6 +86,23 @@ private:
return true;
}
/** Reserve a railway platform. Tile contains the failed tile on abort. */
bool ReserveRailDepotPlatform(TileIndex &tile, DiagDirection dir)
{
assert(IsExtendedRailDepotTile(tile));
TileIndex start = tile;
TileIndexDiff diff = TileOffsByDiagDir(dir);
do {
if (HasDepotReservation(tile)) return false;
SetDepotReservation(tile, true);
MarkTileDirtyByTile(tile);
tile = TileAdd(tile, diff);
} while (IsCompatibleTrainDepotTile(tile, start) && tile != m_origin_tile);
return true;
}
/** Try to reserve a single track/platform. */
bool ReserveSingleTrack(TileIndex tile, Trackdir td)
{
@ -94,6 +112,12 @@ private:
m_res_fail_tile = tile;
m_res_fail_td = td;
}
} else if (IsExtendedRailDepotTile(tile)) {
if (!ReserveRailDepotPlatform(tile, TrackdirToExitdir(ReverseTrackdir(td)))) {
/* Platform could not be reserved, undo. */
m_res_fail_tile = tile;
m_res_fail_td = td;
}
} else {
if (!TryReserveRailTrack(tile, TrackdirToTrack(td))) {
/* Tile couldn't be reserved, undo. */
@ -116,6 +140,13 @@ private:
SetRailStationReservation(tile, false);
tile = TileAdd(tile, diff);
}
} else if (IsExtendedRailDepotTile(tile)) {
TileIndex start = tile;
TileIndexDiff diff = TileOffsByDiagDir(TrackdirToExitdir(ReverseTrackdir(td)));
while ((tile != m_res_fail_tile || td != m_res_fail_td) && IsCompatibleTrainDepotTile(tile, start)) {
SetDepotReservation(tile, false);
tile = TileAdd(tile, diff);
}
} else if (tile != m_res_fail_tile || td != m_res_fail_td) {
UnreserveRailTrack(tile, TrackdirToTrack(td));
}
@ -646,7 +677,9 @@ bool YapfTrainFindNearestSafeTile(const Train *v, TileIndex tile, Trackdir td, b
/** if any track changes, this counter is incremented - that will invalidate segment cost cache */
int CSegmentCostCacheBase::s_rail_change_counter = 0;
extern void FixBigRailDepotSprites(Tile tile);
void YapfNotifyTrackLayoutChange(TileIndex tile, Track track)
{
FixBigRailDepotSprites(tile);
CSegmentCostCacheBase::NotifyTrackLayoutChange(tile, track);
}

View File

@ -66,6 +66,19 @@ protected:
/* Increase the cost for level crossings */
if (IsLevelCrossing(tile)) {
cost += Yapf().PfGetSettings().road_crossing_penalty;
} else if (IsRoadDepot(tile) && IsExtendedRoadDepot(tile)) {
switch (GetDepotReservation(tile, IsDiagDirFacingSouth(TrackdirToExitdir(trackdir)))) {
case DEPOT_RESERVATION_FULL_STOPPED_VEH:
cost += 16 * YAPF_TILE_LENGTH;
break;
case DEPOT_RESERVATION_IN_USE:
cost += 8 * YAPF_TILE_LENGTH;
break;
case DEPOT_RESERVATION_EMPTY:
cost += YAPF_TILE_LENGTH;
break;
default: NOT_REACHED();
}
}
break;
@ -135,7 +148,7 @@ public:
}
/* stop if we have just entered the depot */
if (IsRoadDepotTile(tile) && trackdir == DiagDirToDiagTrackdir(ReverseDiagDir(GetRoadDepotDirection(tile)))) {
if (IsRoadDepotTile(tile) && !IsExtendedRoadDepotTile(tile) && trackdir == DiagDirToDiagTrackdir(ReverseDiagDir(GetRoadDepotDirection(tile)))) {
/* next time we will reverse and leave the depot */
break;
}
@ -201,7 +214,7 @@ public:
/** Called by YAPF to detect if node ends in the desired destination */
inline bool PfDetectDestination(Node &n)
{
return IsRoadDepotTile(n.m_segment_last_tile);
return PfDetectDestinationTile(n.m_segment_last_tile, n.m_segment_last_td);
}
inline bool PfDetectDestinationTile(TileIndex tile, Trackdir)
@ -234,7 +247,9 @@ protected:
TileIndex m_destTile;
TrackdirBits m_destTrackdirs;
StationID m_dest_station;
DepotID m_dest_depot;
StationType m_station_type;
bool m_bus;
bool m_non_artic;
public:
@ -252,8 +267,14 @@ public:
m_destTile = CalcClosestStationTile(m_dest_station, v->tile, m_station_type);
m_non_artic = !v->HasArticulatedPart();
m_destTrackdirs = INVALID_TRACKDIR_BIT;
} else if (v->current_order.IsType(OT_GOTO_DEPOT)) {
m_dest_station = INVALID_STATION;
m_dest_depot = v->current_order.GetDestination();
m_destTile = CalcClosestDepotTile(m_dest_depot, v->tile);
m_destTrackdirs = INVALID_TRACKDIR_BIT;
} else {
m_dest_station = INVALID_STATION;
m_dest_depot = INVALID_DEPOT;
m_destTile = v->dest_tile;
m_destTrackdirs = TrackStatusToTrackdirBits(GetTileTrackStatus(v->dest_tile, TRANSPORT_ROAD, GetRoadTramType(v->roadtype)));
}
@ -287,6 +308,11 @@ public:
(m_non_artic || IsDriveThroughStopTile(tile));
}
if (m_dest_depot != INVALID_DEPOT) {
return IsRoadDepotTile(tile) &&
GetDepotIndex(tile) == m_dest_depot;
}
return tile == m_destTile && HasTrackdir(m_destTrackdirs, trackdir);
}

View File

@ -382,6 +382,10 @@ public:
c += count * 3 * YAPF_TILE_LENGTH;
}
if (IsShipDepotTile(n.GetTile())) {
if (IsExtendedDepot(n.GetTile()) && IsDepotFullWithStoppedVehicles(n.GetTile())) c += YAPF_INFINITE_PENALTY;
}
/* Skipped tile cost for aqueducts. */
c += YAPF_TILE_LENGTH * tf->m_tiles_skipped;

View File

@ -23,7 +23,7 @@ enum EndSegmentReason {
ESR_CHOICE_FOLLOWS, ///< the next tile contains a choice (the track splits to more than one segments)
ESR_DEPOT, ///< stop in the depot (could be a target next time)
ESR_WAYPOINT, ///< waypoint encountered (could be a target next time)
ESR_STATION, ///< station encountered (could be a target next time)
ESR_PLATFORM, ///< platform (station/extended depot) encountered (could be a target next time)
ESR_SAFE_TILE, ///< safe waiting position found (could be a target)
/* The following reasons are used only internally by PfCalcCost().
@ -47,7 +47,7 @@ enum EndSegmentReasonBits {
ESRB_CHOICE_FOLLOWS = 1 << ESR_CHOICE_FOLLOWS,
ESRB_DEPOT = 1 << ESR_DEPOT,
ESRB_WAYPOINT = 1 << ESR_WAYPOINT,
ESRB_STATION = 1 << ESR_STATION,
ESRB_PLATFORM = 1 << ESR_PLATFORM,
ESRB_SAFE_TILE = 1 << ESR_SAFE_TILE,
ESRB_PATH_TOO_LONG = 1 << ESR_PATH_TOO_LONG,
@ -58,10 +58,10 @@ enum EndSegmentReasonBits {
/* Additional (composite) values. */
/* What reasons mean that the target can be found and needs to be detected. */
ESRB_POSSIBLE_TARGET = ESRB_DEPOT | ESRB_WAYPOINT | ESRB_STATION | ESRB_SAFE_TILE,
ESRB_POSSIBLE_TARGET = ESRB_DEPOT | ESRB_WAYPOINT | ESRB_PLATFORM | ESRB_SAFE_TILE,
/* What reasons can be stored back into cached segment. */
ESRB_CACHED_MASK = ESRB_DEAD_END | ESRB_RAIL_TYPE | ESRB_INFINITE_LOOP | ESRB_SEGMENT_TOO_LONG | ESRB_CHOICE_FOLLOWS | ESRB_DEPOT | ESRB_WAYPOINT | ESRB_STATION | ESRB_SAFE_TILE,
ESRB_CACHED_MASK = ESRB_DEAD_END | ESRB_RAIL_TYPE | ESRB_INFINITE_LOOP | ESRB_SEGMENT_TOO_LONG | ESRB_CHOICE_FOLLOWS | ESRB_DEPOT | ESRB_WAYPOINT | ESRB_PLATFORM | ESRB_SAFE_TILE,
/* Reasons to abort pathfinding in this direction. */
ESRB_ABORT_PF_MASK = ESRB_DEAD_END | ESRB_PATH_TOO_LONG | ESRB_INFINITE_LOOP | ESRB_FIRST_TWO_WAY_RED,

View File

@ -12,6 +12,8 @@
#include "vehicle_func.h"
#include "newgrf_station.h"
#include "pathfinder/follow_track.hpp"
#include "platform_func.h"
#include "depot_map.h"
#include "safeguards.h"
@ -47,28 +49,6 @@ TrackBits GetReservedTrackbits(TileIndex t)
return TRACK_BIT_NONE;
}
/**
* Set the reservation for a complete station platform.
* @pre IsRailStationTile(start)
* @param start starting tile of the platform
* @param dir the direction in which to follow the platform
* @param b the state the reservation should be set to
*/
void SetRailStationPlatformReservation(TileIndex start, DiagDirection dir, bool b)
{
TileIndex tile = start;
TileIndexDiff diff = TileOffsByDiagDir(dir);
assert(IsRailStationTile(start));
assert(GetRailStationAxis(start) == DiagDirToAxis(dir));
do {
SetRailStationReservation(tile, b);
MarkTileDirtyByTile(tile);
tile = TileAdd(tile, diff);
} while (IsCompatibleTrainStationTile(tile, start));
}
/**
* Try to reserve a specific track on a tile
* @param tile the tile
@ -202,12 +182,12 @@ static PBSTileInfo FollowReservation(Owner o, RailTypes rts, TileIndex tile, Tra
/* No reservation --> path end found */
if (reserved == TRACKDIR_BIT_NONE) {
if (ft.m_is_station) {
if (ft.m_is_station || ft.m_is_extended_depot) {
/* Check skipped station tiles as well, maybe our reservation ends inside the station. */
TileIndexDiff diff = TileOffsByDiagDir(ft.m_exitdir);
while (ft.m_tiles_skipped-- > 0) {
ft.m_new_tile -= diff;
if (HasStationReservation(ft.m_new_tile)) {
if ((ft.m_is_station && HasStationReservation(ft.m_new_tile)) || (ft.m_is_extended_depot && HasDepotReservation(ft.m_new_tile))) {
tile = ft.m_new_tile;
trackdir = DiagDirToDiagTrackdir(ft.m_exitdir);
break;
@ -240,7 +220,7 @@ static PBSTileInfo FollowReservation(Owner o, RailTypes rts, TileIndex tile, Tra
if (tile == start_tile && trackdir == start_trackdir) break;
}
/* Depot tile? Can't continue. */
if (IsRailDepotTile(tile)) break;
if (IsStandardRailDepotTile(tile)) break;
/* Non-pbs signal? Reservation can't continue. */
if (IsTileType(tile, MP_RAILWAY) && HasSignalOnTrackdir(tile, trackdir) && !IsPbsSignal(GetSignalType(tile, TrackdirToTrack(trackdir)))) break;
}
@ -292,7 +272,7 @@ PBSTileInfo FollowTrainReservation(const Train *v, Vehicle **train_on_res)
TileIndex tile = v->tile;
Trackdir trackdir = v->GetVehicleTrackdir();
if (IsRailDepotTile(tile) && !GetDepotReservationTrackBits(tile)) return PBSTileInfo(tile, trackdir, false);
if (IsStandardRailDepotTile(tile) && !GetDepotReservationTrackBits(tile)) return PBSTileInfo(tile, trackdir, false);
FindTrainOnTrackInfo ftoti;
ftoti.res = FollowReservation(v->owner, GetRailTypeInfo(v->railtype)->compatible_railtypes, tile, trackdir);
@ -300,14 +280,14 @@ PBSTileInfo FollowTrainReservation(const Train *v, Vehicle **train_on_res)
if (train_on_res != nullptr) {
FindVehicleOnPos(ftoti.res.tile, &ftoti, FindTrainOnTrackEnum);
if (ftoti.best != nullptr) *train_on_res = ftoti.best->First();
if (*train_on_res == nullptr && IsRailStationTile(ftoti.res.tile)) {
/* The target tile is a rail station. The track follower
if (*train_on_res == nullptr && (IsRailStationTile(ftoti.res.tile) || IsExtendedRailDepotTile(ftoti.res.tile))) {
/* The target tile is a rail station or extended depot. The track follower
* has stopped on the last platform tile where we haven't
* found a train. Also check all previous platform tiles
* for a possible train. */
TileIndexDiff diff = TileOffsByDiagDir(TrackdirToExitdir(ReverseTrackdir(ftoti.res.trackdir)));
for (TileIndex st_tile = ftoti.res.tile + diff; *train_on_res == nullptr && IsCompatibleTrainStationTile(st_tile, ftoti.res.tile); st_tile += diff) {
FindVehicleOnPos(st_tile, &ftoti, FindTrainOnTrackEnum);
for (TileIndex pt_tile = ftoti.res.tile + diff; *train_on_res == nullptr && IsCompatiblePlatformTile(pt_tile, ftoti.res.tile); pt_tile += diff) {
FindVehicleOnPos(pt_tile, &ftoti, FindTrainOnTrackEnum);
if (ftoti.best != nullptr) *train_on_res = ftoti.best->First();
}
}
@ -348,11 +328,11 @@ Train *GetTrainForReservation(TileIndex tile, Track track)
FindVehicleOnPos(ftoti.res.tile, &ftoti, FindTrainOnTrackEnum);
if (ftoti.best != nullptr) return ftoti.best;
/* Special case for stations: check the whole platform for a vehicle. */
if (IsRailStationTile(ftoti.res.tile)) {
/* Special case for stations and extended depots: check the whole platform for a vehicle. */
if (IsRailStationTile(ftoti.res.tile) || IsExtendedRailDepotTile(ftoti.res.tile)) {
TileIndexDiff diff = TileOffsByDiagDir(TrackdirToExitdir(ReverseTrackdir(ftoti.res.trackdir)));
for (TileIndex st_tile = ftoti.res.tile + diff; IsCompatibleTrainStationTile(st_tile, ftoti.res.tile); st_tile += diff) {
FindVehicleOnPos(st_tile, &ftoti, FindTrainOnTrackEnum);
for (TileIndex pt_tile = ftoti.res.tile + diff; IsCompatiblePlatformTile(pt_tile, ftoti.res.tile); pt_tile += diff) {
FindVehicleOnPos(pt_tile, &ftoti, FindTrainOnTrackEnum);
if (ftoti.best != nullptr) return ftoti.best;
}
}
@ -379,7 +359,7 @@ Train *GetTrainForReservation(TileIndex tile, Track track)
*/
bool IsSafeWaitingPosition(const Train *v, TileIndex tile, Trackdir trackdir, bool include_line_end, bool forbid_90deg)
{
if (IsRailDepotTile(tile)) return true;
if (IsStandardRailDepotTile(tile)) return true;
if (IsTileType(tile, MP_RAILWAY)) {
/* For non-pbs signals, stop on the signal tile. */
@ -432,7 +412,7 @@ bool IsWaitingPositionFree(const Train *v, TileIndex tile, Trackdir trackdir, bo
if (TrackOverlapsTracks(reserved, track)) return false;
/* Not reserved and depot or not a pbs signal -> free. */
if (IsRailDepotTile(tile)) return true;
if (IsStandardRailDepotTile(tile)) return true;
if (IsTileType(tile, MP_RAILWAY) && HasSignalOnTrackdir(tile, trackdir) && !IsPbsSignal(GetSignalType(tile, track))) return true;
/* Check the next tile, if it's a PBS signal, it has to be free as well. */
@ -446,3 +426,26 @@ bool IsWaitingPositionFree(const Train *v, TileIndex tile, Trackdir trackdir, bo
return !HasReservedTracks(ft.m_new_tile, TrackdirBitsToTrackBits(ft.m_new_td_bits));
}
/**
* Fix the sprites of depots to show it opened or closed depending on its neighbours.
* @param t Tile that has changed.
*/
void FixBigRailDepotSprites(Tile t)
{
if (t == INVALID_TILE) return;
/* Expand tile area to check. */
TileArea ta = TileArea(t).Expand(1);
for (Tile tile : ta) {
if (!IsExtendedRailDepotTile(tile)) continue;
CFollowTrackRail ft(GetTileOwner(tile), GetRailTypeInfo(GetTileRailType(tile))->compatible_railtypes);
Track track = GetRailDepotTrack(tile);
Trackdir trackdir = TrackToTrackdir(track);
if (track == TRACK_X) trackdir = ReverseTrackdir(trackdir);
bool opened = ft.Follow(tile, trackdir);
if (track == TRACK_Y) opened = !opened;
SB(tile.m5(), 1, 1, opened);
}
}

406
src/platform.cpp 100644
View File

@ -0,0 +1,406 @@
/*
* 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 platform.cpp Implementation of platform functions. */
#include "stdafx.h"
#include "station_map.h"
#include "platform_func.h"
#include "viewport_func.h"
#include "depot_base.h"
#include "vehicle_base.h"
#include "engine_base.h"
/**
* Set the reservation for a complete station platform.
* @pre IsRailStationTile(start)
* @param start starting tile of the platform
* @param dir the direction in which to follow the platform
* @param b the state the reservation should be set to
*/
void SetRailStationPlatformReservation(TileIndex start, DiagDirection dir, bool b)
{
TileIndex tile = start;
TileIndexDiff diff = TileOffsByDiagDir(dir);
assert(IsRailStationTile(start));
assert(GetRailStationAxis(start) == DiagDirToAxis(dir));
do {
SetRailStationReservation(tile, b);
MarkTileDirtyByTile(tile);
tile = TileAdd(tile, diff);
} while (IsCompatibleTrainStationTile(tile, start));
}
/**
* Set the reservation for a complete depot platform.
* @pre IsExtendedRailDepotTile(start)
* @param start starting tile of the platform
* @param dir the direction in which to follow the platform
* @param b the state the reservation should be set to
*/
void SetRailDepotPlatformReservation(TileIndex start, DiagDirection dir, bool b)
{
TileIndex tile = start;
TileIndexDiff diff = TileOffsByDiagDir(dir);
assert(IsExtendedRailDepotTile(start));
assert(GetRailDepotTrack(start) == DiagDirToDiagTrack(dir));
do {
SetDepotReservation(tile, b);
MarkTileDirtyByTile(tile);
tile = TileAdd(tile, diff);
} while (IsCompatibleTrainDepotTile(tile, start));
}
/**
* Set the reservation for a complete platform in a given direction.
* @param start starting tile of the platform
* @param dir the direction in which to follow the platform
* @param b the state the reservation should be set to
*/
void SetPlatformReservation(TileIndex start, DiagDirection dir, bool b)
{
switch (GetPlatformType(start)) {
case PT_RAIL_STATION:
SetRailStationPlatformReservation(start, dir, b);
return;
case PT_RAIL_WAYPOINT:
SetRailStationReservation(start, b);
return;
case PT_RAIL_DEPOT:
SetRailDepotPlatformReservation(start, dir, b);
return;
default: NOT_REACHED();
}
}
/**
* Set the reservation for a complete platform.
* @param start A tile of the platform
* @param b the state the reservation should be set to
*/
void SetPlatformReservation(TileIndex start, bool b)
{
DiagDirection dir;
switch (GetPlatformType(start)) {
case PT_RAIL_STATION:
NOT_REACHED();
case PT_RAIL_WAYPOINT:
NOT_REACHED();
case PT_RAIL_DEPOT:
assert(IsExtendedRailDepotTile(start));
dir = GetRailDepotDirection(start);
SetRailDepotPlatformReservation(start, dir, b);
SetRailDepotPlatformReservation(start, ReverseDiagDir(dir), b);
return;
default: NOT_REACHED();
}
}
/**
* Get the length of a rail station platform.
* @pre IsRailStationTile(tile)
* @param tile Tile to check
* @return The length of the platform in tile length.
*/
uint GetRailStationPlatformLength(TileIndex tile)
{
assert(IsRailStationTile(tile));
TileIndexDiff delta = (GetRailStationAxis(tile) == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1));
TileIndex t = tile;
uint len = 0;
do {
t -= delta;
len++;
} while (IsCompatibleTrainStationTile(t, tile));
t = tile;
do {
t += delta;
len++;
} while (IsCompatibleTrainStationTile(t, tile));
return len - 1;
}
/**
* Get the length of a rail station platform in a given direction.
* @pre IsRailStationTile(tile)
* @param tile Tile to check
* @param dir Direction to check
* @return The length of the platform in tile length in the given direction.
*/
uint GetRailStationPlatformLength(TileIndex tile, DiagDirection dir)
{
TileIndex start_tile = tile;
uint length = 0;
assert(IsRailStationTile(tile));
assert(dir < DIAGDIR_END);
do {
length++;
tile += TileOffsByDiagDir(dir);
} while (IsCompatibleTrainStationTile(tile, start_tile));
return length;
}
/**
* Get the length of a rail depot platform.
* @pre IsDepotTypeTile(tile, TRANSPORT_RAIL)
* @param tile Tile to check
* @return The length of the platform in tile length.
*/
uint GetRailDepotPlatformLength(TileIndex tile)
{
assert(IsExtendedRailDepotTile(tile));
TileIndexDiff delta = (GetRailDepotTrack(tile) == TRACK_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1));
TileIndex t = tile;
uint len = 0;
do {
t -= delta;
len++;
} while (IsCompatibleTrainDepotTile(t, tile));
t = tile;
do {
t += delta;
len++;
} while (IsCompatibleTrainDepotTile(t, tile));
return len - 1;
}
/**
* Get the length of a road depot platform.
* @pre IsDepotTypeTile(tile, TRANSPORT_ROAD)
* @param tile Tile to check
* @param rtt Whether to check for road or tram type.
* @return The length of the platform in tile length.
*/
uint GetRoadDepotPlatformLength(TileIndex tile, RoadTramType rtt)
{
assert(IsExtendedRoadDepotTile(tile));
DiagDirection dir = GetRoadDepotDirection(tile);
TileIndexDiff delta = TileOffsByDiagDir(dir);
TileIndex t = tile;
uint len = 0;
do {
len++;
if ((GetRoadBits(t, rtt) & DiagDirToRoadBits(dir)) == ROAD_NONE) break;
t -= delta;
} while (IsCompatibleRoadDepotTile(t, tile, rtt));
t = tile;
dir = ReverseDiagDir(dir);
do {
len++;
if ((GetRoadBits(t, rtt) & DiagDirToRoadBits(dir)) == ROAD_NONE) break;
t += delta;
} while (IsCompatibleRoadDepotTile(t, tile, rtt));
return len - 1;
}
/**
* Get the length of a rail depot platform in a given direction.
* @pre IsRailDepotTile(tile)
* @param tile Tile to check
* @param dir Direction to check
* @return The length of the platform in tile length in the given direction.
*/
uint GetRailDepotPlatformLength(TileIndex tile, DiagDirection dir)
{
TileIndex start_tile = tile;
uint length = 0;
assert(IsExtendedRailDepotTile(tile));
assert(dir < DIAGDIR_END);
do {
length++;
tile += TileOffsByDiagDir(dir);
} while (IsCompatibleTrainDepotTile(tile, start_tile));
return length;
}
/**
* Get the length of a road depot platform in a given direction.
* @pre IsRoadDepotTile(tile)
* @param tile Tile to check
* @param dir Direction to check
* @param rtt Whether to check for road or tram type.
* @return The length of the platform in tile length in the given direction.
*/
uint GetRoadDepotPlatformLength(TileIndex tile, DiagDirection dir, RoadTramType rtt)
{
TileIndex start_tile = tile;
uint length = 0;
assert(IsExtendedRoadDepotTile(tile));
assert(dir < DIAGDIR_END);
do {
length++;
if ((GetRoadBits(tile, rtt) & DiagDirToRoadBits(dir)) == ROAD_NONE) break;
tile += TileOffsByDiagDir(dir);
} while (IsCompatibleRoadDepotTile(tile, start_tile, rtt));
return length;
}
/**
* Get the length of a platform.
* @param tile Tile to check
* @param rtt Whether to check for road or tram type (only for road transport).
* @return The length of the platform in tile length.
*/
uint GetPlatformLength(TileIndex tile, RoadTramType rtt)
{
switch (GetPlatformType(tile)) {
case PT_RAIL_STATION:
return GetRailStationPlatformLength(tile);
case PT_RAIL_WAYPOINT:
return 1;
case PT_RAIL_DEPOT:
return GetRailDepotPlatformLength(tile);
case PT_ROAD_DEPOT:
return GetRoadDepotPlatformLength(tile, rtt);
default: NOT_REACHED();
}
}
/**
* Get the length of a rail depot platform in a given direction.
* @pre IsRailDepotTile(tile)
* @param tile Tile to check
* @param dir Direction to check
* @param rtt Whether to check for road or tram type (only for road transport).
* @return The length of the platform in tile length in the given direction.
*/
uint GetPlatformLength(TileIndex tile, DiagDirection dir, RoadTramType rtt)
{
switch (GetPlatformType(tile)) {
case PT_RAIL_STATION:
return GetRailStationPlatformLength(tile, dir);
case PT_RAIL_WAYPOINT:
return 1;
case PT_RAIL_DEPOT:
return GetRailDepotPlatformLength(tile, dir);
case PT_ROAD_DEPOT:
return GetRoadDepotPlatformLength(tile, dir, rtt);
default: NOT_REACHED();
}
}
/**
* Get a tile where a rail station platform begins or ends.
* @pre IsRailStationTile(tile)
* @param tile Tile to check
* @param dir The diagonal direction to check
* @return The last tile of the platform seen from tile with direction dir.
*/
TileIndex GetRailStationExtreme(TileIndex tile, DiagDirection dir)
{
assert(IsRailStationTile(tile));
assert(GetRailStationAxis(tile) == DiagDirToAxis(dir));
TileIndexDiff delta = TileOffsByDiagDir(dir);
TileIndex t = tile;
do {
t -= delta;
} while (IsCompatibleTrainStationTile(t, tile));
return t + delta;
}
/**
* Get a tile where a depot platform begins or ends.
* @pre IsExtendedDepotTile(tile)
* @param tile Tile to check
* @param dir The diagonal direction to check
* @return The last tile of the platform seen from tile with direction dir.
*/
TileIndex GetRailDepotExtreme(TileIndex tile, DiagDirection dir)
{
assert(IsExtendedDepotTile(tile));
assert(GetRailDepotTrack(tile) == DiagDirToDiagTrack(dir));
TileIndexDiff delta = TileOffsByDiagDir(dir);
TileIndex t = tile;
do {
t -= delta;
} while (IsCompatibleTrainDepotTile(t, tile));
return t + delta;
}
/**
* Get a tile where a platform begins or ends.
* @param tile Tile to check
* @param dir Direction to check
* @return The last tile of the platform seen from tile with direction dir.
*/
TileIndex GetPlatformExtremeTile(TileIndex tile, DiagDirection dir)
{
switch (GetPlatformType(tile)) {
case PT_RAIL_STATION:
return GetRailStationExtreme(tile, dir);
case PT_RAIL_WAYPOINT:
return tile;
case PT_RAIL_DEPOT:
return GetRailDepotExtreme(tile, dir);
default: NOT_REACHED();
}
}
/**
* Get the tiles belonging to a platform.
* @param tile Tile of a platform
* @return the tile area of the platform
*/
TileArea GetPlatformTileArea(TileIndex tile)
{
switch (GetPlatformType(tile)) {
case PT_RAIL_STATION: {
assert(IsRailStationTile(tile));
DiagDirection dir = AxisToDiagDir(GetRailStationAxis(tile));
return TileArea(GetRailStationExtreme(tile, dir), GetRailStationExtreme(tile, ReverseDiagDir(dir)));
}
case PT_RAIL_WAYPOINT:
return TileArea(tile);
case PT_RAIL_DEPOT: {
assert(IsExtendedRailDepotTile(tile));
DiagDirection dir = GetRailDepotDirection(tile);
return TileArea(GetRailDepotExtreme(tile, dir), GetRailDepotExtreme(tile, ReverseDiagDir(dir)));
}
default: NOT_REACHED();
}
}
/**
* Check whether this tile is an extreme of a platform.
* @param tile Tile to check
* @return Whether the tile is the extreme of a platform.
*/
bool IsAnyStartPlatformTile(TileIndex tile)
{
assert(IsExtendedRailDepotTile(tile));
DiagDirection dir = GetRailDepotDirection(tile);
return tile == GetPlatformExtremeTile(tile, dir) || tile == GetPlatformExtremeTile(tile, ReverseDiagDir(dir));
}

171
src/platform_func.h 100644
View File

@ -0,0 +1,171 @@
/*
* 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 platform_func.h Functions related with platforms (tiles in a row that are connected somehow). */
#ifndef PLATFORM_FUNC_H
#define PLATFORM_FUNC_H
#include "station_map.h"
#include "depot_map.h"
#include "platform_type.h"
#include "road_map.h"
/**
* Check if a tile is a valid continuation to a railstation tile.
* The tile \a test_tile is a valid continuation to \a station_tile, if all of the following are true:
* \li \a test_tile is a rail station tile
* \li the railtype of \a test_tile is compatible with the railtype of \a station_tile
* \li the tracks on \a test_tile and \a station_tile are in the same direction
* \li both tiles belong to the same station
* \li \a test_tile is not blocked (@see IsStationTileBlocked)
* @param test_tile Tile to test
* @param station_tile Station tile to compare with
* @pre IsRailStationTile(station_tile)
* @return true if the two tiles are compatible
*/
static inline bool IsCompatibleTrainStationTile(TileIndex test_tile, TileIndex station_tile)
{
assert(IsRailStationTile(station_tile));
return IsRailStationTile(test_tile) && !IsStationTileBlocked(test_tile) &&
IsCompatibleRail(GetRailType(test_tile), GetRailType(station_tile)) &&
GetRailStationAxis(test_tile) == GetRailStationAxis(station_tile) &&
GetStationIndex(test_tile) == GetStationIndex(station_tile);
}
/**
* Check if a tile is a valid continuation to an extended rail depot tile.
* The tile \a test_tile is a valid continuation to \a depot_tile, if all of the following are true:
* \li \a test_tile is an extended depot tile
* \li \a test_tile and \a depot_tile have the same rail type
* \li the tracks on \a test_tile and \a depot_tile are in the same direction
* \li both tiles belong to the same depot
* @param test_tile Tile to test
* @param depot_tile Depot tile to compare with
* @pre IsExtendedRailDepotTile(depot_tile)
* @return true if the two tiles are compatible
*/
static inline bool IsCompatibleTrainDepotTile(TileIndex test_tile, TileIndex depot_tile)
{
assert(IsExtendedRailDepotTile(depot_tile));
return IsExtendedRailDepotTile(test_tile) &&
GetRailType(test_tile) == GetRailType(depot_tile) &&
GetRailDepotTrack(test_tile) == GetRailDepotTrack(depot_tile) &&
GetDepotIndex(test_tile) == GetDepotIndex(depot_tile);
}
/**
* Check if a tile is a valid continuation to an extended road depot tile.
* The tile \a test_tile is a valid continuation to \a depot_tile, if all of the following are true:
* \li \a test_tile is an extended depot tile
* \li \a test_tile and \a depot_tile have the same road type and appropriate road bits
* \li the tracks on \a test_tile and \a depot_tile are in the same direction
* \li both tiles belong to the same depot
* @param test_tile Tile to test
* @param depot_tile Depot tile to compare with
* @param rtt Whether road or tram type.
* @pre IsExtendedRoadDepotTile(depot_tile)
* @return true if the two tiles are compatible
*/
static inline bool IsCompatibleRoadDepotTile(TileIndex test_tile, TileIndex depot_tile, RoadTramType rtt)
{
assert(IsExtendedRoadDepotTile(depot_tile));
if (!IsExtendedRoadDepotTile(test_tile)) return false;
if (GetDepotIndex(test_tile) != GetDepotIndex(depot_tile)) return false;
if (GetRoadType(depot_tile, rtt) != GetRoadType(test_tile, rtt)) return false;
DiagDirection dir = DiagdirBetweenTiles(test_tile, depot_tile);
assert(dir != INVALID_DIAGDIR);
return (GetRoadBits(test_tile, rtt) & DiagDirToRoadBits(dir)) != ROAD_NONE;
}
/**
* Returns the type of platform of a given tile.
* @param tile Tile to check
* @return the type of platform (rail station, rail waypoint...)
*/
static inline PlatformType GetPlatformType(TileIndex tile)
{
switch (GetTileType(tile)) {
case MP_STATION:
if (IsRailStation(tile)) return PT_RAIL_STATION;
if (IsRailWaypoint(tile)) return PT_RAIL_WAYPOINT;
break;
case MP_RAILWAY:
if (IsExtendedRailDepotTile(tile)) return PT_RAIL_DEPOT;
break;
case MP_ROAD:
if (IsExtendedRoadDepotTile(tile)) return PT_ROAD_DEPOT;
break;
default: break;
}
return INVALID_PLATFORM_TYPE;
}
/**
* Check whether a tile is a known platform type.
* @param tile to check
* @return whether the tile is a known platform type.
*/
static inline bool IsPlatformTile(TileIndex tile)
{
return GetPlatformType(tile) != INVALID_PLATFORM_TYPE;
}
/**
* Check whether a platform tile is reserved.
* @param tile to check
* @return whether the platform tile is reserved
*/
static inline bool HasPlatformReservation(TileIndex tile)
{
switch(GetPlatformType(tile)) {
case PT_RAIL_STATION:
case PT_RAIL_WAYPOINT:
return HasStationReservation(tile);
case PT_RAIL_DEPOT:
return HasDepotReservation(tile);
default: NOT_REACHED();
}
}
/**
* Check whether two tiles are compatible platform tiles: they must have the same
* platform type and (depending on the platform type) its railtype or other specs.
* @param test_tile the tile to check
* @param orig_tile the tile with the platform type we are interested in
* @param rtt Whether to check road or tram types (only for road transport);
* @return whether the two tiles are compatible tiles for defining a platform
*/
static inline bool IsCompatiblePlatformTile(TileIndex test_tile, TileIndex orig_tile, RoadTramType rtt = RTT_ROAD)
{
switch (GetPlatformType(orig_tile)) {
case PT_RAIL_STATION:
return IsCompatibleTrainStationTile(test_tile, orig_tile);
case PT_RAIL_WAYPOINT:
return test_tile == orig_tile;
case PT_RAIL_DEPOT:
return IsCompatibleTrainDepotTile(test_tile, orig_tile);
case PT_ROAD_DEPOT:
return IsCompatibleRoadDepotTile(test_tile, orig_tile, rtt);
default: NOT_REACHED();
}
}
void SetPlatformReservation(TileIndex start, DiagDirection dir, bool b);
void SetPlatformReservation(TileIndex start, bool b);
uint GetPlatformLength(TileIndex tile, RoadTramType rtt = RTT_ROAD);
uint GetPlatformLength(TileIndex tile, DiagDirection dir, RoadTramType rtt = RTT_ROAD);
TileIndex GetPlatformExtremeTile(TileIndex tile, DiagDirection dir);
TileArea GetPlatformTileArea(TileIndex tile);
bool IsAnyStartPlatformTile(TileIndex tile);
#endif /* PLATFORM_FUNC_H */

View File

@ -0,0 +1,24 @@
/*
* 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 platform_type.h Types related to platforms. */
#ifndef PLATFORM_TYPE_H
#define PLATFORM_TYPE_H
#include "core/enum_type.hpp"
enum PlatformType {
PT_RAIL_STATION,
PT_RAIL_WAYPOINT,
PT_RAIL_DEPOT,
PT_ROAD_DEPOT,
PT_END,
INVALID_PLATFORM_TYPE = PT_END,
};
#endif /* PLATFORM_TYPE_H */

View File

@ -337,6 +337,19 @@ inline bool HasPowerOnRail(RailType enginetype, RailType tiletype)
return HasBit(GetRailTypeInfo(enginetype)->powered_railtypes, tiletype);
}
/**
* Checks if an engine with a given \a enginetype is powered on \a rail_types.
* This would normally just be an equality check,
* but for electric rails (which also support non-electric vehicles).
* @param enginetype The RailType of the engine we are considering.
* @param rail_types The RailTypes we are considering.
* @return Whether the engine got power on this tile.
*/
static inline bool HasPowerOnRails(RailType enginetype, RailTypes rail_types)
{
return (GetRailTypeInfo(enginetype)->powered_railtypes & rail_types) != 0;
}
/**
* Test if a RailType disallows build of level crossings.
* @param rt The RailType to check.

View File

@ -33,6 +33,7 @@
#include "object_map.h"
#include "rail_cmd.h"
#include "landscape_cmd.h"
#include "platform_func.h"
#include "table/strings.h"
#include "table/railtypes.h"
@ -952,81 +953,163 @@ CommandCost CmdRemoveRailroadTrack(DoCommandFlag flags, TileIndex end_tile, Tile
/**
* Build a train depot
* @param flags operation to perform
* @param tile position of the train depot
* @param tile first position of the train depot
* @param railtype rail type
* @param dir entrance direction
* @param adjacent allow adjacent depots
* @param extended build extended depots
* @param join_to depot to join to
* @param end_tile end tile of the area to be built
* @return the cost of this operation or an error
*
* @todo When checking for the tile slope,
* distinguish between "Flat land required" and "land sloped in wrong direction"
*/
CommandCost CmdBuildTrainDepot(DoCommandFlag flags, TileIndex tile, RailType railtype, DiagDirection dir)
CommandCost CmdBuildTrainDepot(DoCommandFlag flags, TileIndex tile, RailType railtype, DiagDirection dir, bool adjacent, bool extended, DepotID join_to, TileIndex end_tile)
{
/* check railtype and valid direction for depot (0 through 3), 4 in total */
if (!ValParamRailType(railtype) || !IsValidDiagDirection(dir)) return CMD_ERROR;
Slope tileh = GetTileSlope(tile);
if (Company::IsValidHumanID(_current_company) && !HasBit(_settings_game.depot.rail_depot_types, extended)) return_cmd_error(STR_ERROR_DEPOT_TYPE_NOT_AVAILABLE);
CommandCost cost(EXPENSES_CONSTRUCTION);
TileArea ta(tile, end_tile);
Depot *depot = nullptr;
/* Create a new depot or find a depot to join to. */
CommandCost ret = FindJoiningDepot(ta, VEH_TRAIN, join_to, depot, adjacent, flags);
if (ret.Failed()) return ret;
Axis axis = DiagDirToAxis(dir);
/* Do not allow extending already occupied platforms. */
if (extended && join_to != NEW_DEPOT) {
TileArea ta_ext = TileArea(ta.tile, ta.w, ta.h).Expand(1);
uint max_coord;
uint min_coord;
if (axis == AXIS_X) {
min_coord = TileY(ta.tile);
max_coord = min_coord + ta.h;
} else {
min_coord = TileX(ta.tile);
max_coord = min_coord + ta.w;
}
for (Tile t : ta_ext) {
if (!IsExtendedRailDepotTile(t)) continue;
if (GetDepotIndex(t) != depot->index) continue;
if (GetRailType(t) != railtype) continue;
if (!HasDepotReservation(t)) continue;
if (DiagDirToAxis(GetRailDepotDirection(t)) != axis) continue;
uint current = (axis == AXIS_X) ? TileY(t) : TileX(t);
if (!IsInsideMM(current, min_coord, max_coord)) continue;
return_cmd_error(STR_ERROR_DEPOT_EXTENDING_PLATFORMS);
}
}
uint8_t num_new_depot_tiles = 0;
uint8_t num_overbuilt_depot_tiles = 0;
/* Prohibit construction if
* The tile is non-flat AND
* 1) build-on-slopes is disabled
* 2) the tile is steep i.e. spans two height levels
* 3) the exit points in the wrong direction
* 4) the tile is not an already built depot (or it is a compatible single rail tile for building extended depots)
*/
for (Tile t : ta) {
if (IsBridgeAbove(t)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
Slope tileh = GetTileSlope(t);
if (tileh != SLOPE_FLAT) {
if (!_settings_game.construction.build_on_slopes || !CanBuildDepotByTileh(dir, tileh)) {
if (!_settings_game.construction.build_on_slopes ||
!CanBuildDepotByTileh(dir, tileh)) {
return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED);
}
if (extended && !CanBuildDepotByTileh(ReverseDiagDir(dir), tileh)) {
return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED);
}
cost.AddCost(_price[PR_BUILD_FOUNDATION]);
}
/* Allow the user to rotate the depot instead of having to destroy it and build it again */
bool rotate_existing_depot = false;
if (IsRailDepotTile(tile) && railtype == GetRailType(tile)) {
CommandCost ret = CheckTileOwnership(tile);
if (ret.Failed()) return ret;
if (extended) {
if (IsPlainRailTile(t) && !HasSignals(t) && GetRailType(t) == railtype) {
/* Allow overbuilding if the tile:
* - has rail, but no signals
* - it has exactly one track
* - the track is in line with the depot
* - the current rail type is the same as the to-be-built
*/
TrackBits tracks = GetTrackBits(t);
Track track = RemoveFirstTrack(&tracks);
uint invalid_dirs = 5 << DiagDirToAxis(dir);
Track expected_track = HasBit(invalid_dirs, DIAGDIR_NE) ? TRACK_X : TRACK_Y;
if (dir == GetRailDepotDirection(tile)) return CommandCost();
ret = EnsureNoVehicleOnGround(tile);
if (ret.Failed()) return ret;
rotate_existing_depot = true;
if (tracks == TRACK_BIT_NONE && track == expected_track) {
cost.AddCost(Command<CMD_REMOVE_SINGLE_RAIL>::Do(flags, t, track).GetCost());
/* With flags & ~DC_EXEC CmdLandscapeClear would fail since the rail still exists */
if (cost.Failed()) return cost;
goto new_depot_tile;
}
}
if (!rotate_existing_depot) {
cost.AddCost(Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile));
/* Skip already existing and compatible extended depots. */
if (IsRailDepotTile(t) && IsExtendedRailDepotTile(t) &&
GetDepotIndex(t) == join_to && railtype == GetRailType(t)) {
if (axis == DiagDirToAxis(GetRailDepotDirection(t))) continue;
}
} else {
/* Check whether this is a standard depot tile and it needs to be rotated. */
if (IsRailDepotTile(t) && IsStandardRailDepotTile(t) &&
GetDepotIndex(t) == join_to && railtype == GetRailType(t)) {
if (dir == GetRailDepotDirection(t)) continue;
ret = EnsureNoVehicleOnGround(t);
if (ret.Failed()) return ret;
num_overbuilt_depot_tiles++;
if (flags & DC_EXEC) {
SetRailDepotExitDirection(t, dir);
AddSideToSignalBuffer(t, INVALID_DIAGDIR, _current_company);
YapfNotifyTrackLayoutChange(t, DiagDirToDiagTrack(dir));
MarkTileDirtyByTile(t);
}
continue;
}
}
cost.AddCost(Command<CMD_LANDSCAPE_CLEAR>::Do(flags, t));
if (cost.Failed()) return cost;
if (IsBridgeAbove(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
if (!Depot::CanAllocateItem()) return CMD_ERROR;
}
new_depot_tile:
num_new_depot_tiles++;
if (flags & DC_EXEC) {
if (rotate_existing_depot) {
SetRailDepotExitDirection(tile, dir);
MakeRailDepot(t, _current_company, depot->index, dir, railtype);
SB(t.m5(), 5, 1, extended);
if (extended) {
AddTrackToSignalBuffer(t, DiagDirToDiagTrack(dir), _current_company);
} else {
Depot *d = new Depot(tile);
d->build_date = TimerGameCalendar::date;
AddSideToSignalBuffer(t, INVALID_DIAGDIR, _current_company);
}
YapfNotifyTrackLayoutChange(t, DiagDirToDiagTrack(dir));
MarkTileDirtyByTile(t);
}
}
MakeRailDepot(tile, _current_company, d->index, dir, railtype);
MakeDefaultName(d);
if (num_new_depot_tiles + num_overbuilt_depot_tiles == 0) return CommandCost();
Company::Get(_current_company)->infrastructure.rail[railtype]++;
cost.AddCost(_price[PR_BUILD_DEPOT_TRAIN] * (num_new_depot_tiles + num_overbuilt_depot_tiles));
cost.AddCost(RailBuildCost(railtype) * (num_new_depot_tiles + num_overbuilt_depot_tiles));
if (flags & DC_EXEC) {
Company::Get(_current_company)->infrastructure.rail[railtype] += num_new_depot_tiles;
DirtyCompanyInfrastructureWindows(_current_company);
depot->AfterAddRemove(ta, true);
if (join_to == NEW_DEPOT) MakeDefaultName(depot);
}
MarkTileDirtyByTile(tile);
AddSideToSignalBuffer(tile, INVALID_DIAGDIR, _current_company);
YapfNotifyTrackLayoutChange(tile, DiagDirToDiagTrack(dir));
}
cost.AddCost(_price[PR_BUILD_DEPOT_TRAIN]);
cost.AddCost(RailBuildCost(railtype));
return cost;
}
@ -1540,6 +1623,56 @@ static Vehicle *UpdateTrainPowerProc(Vehicle *v, void *data)
return nullptr;
}
/**
* Returns whether a depot has an extended depot
* tile which is reserved.
* @param Depot pointer to a depot
* @return true iff \a dep has an extended depot tile reserved.
*/
bool HasAnyExtendedDepotReservedTile(Depot *dep)
{
assert(dep != nullptr);
for (TileIndex tile : dep->ta) {
if (!IsExtendedDepotTile(tile)) continue;
if (GetDepotIndex(tile) != dep->index) continue;
if (HasDepotReservation(tile)) return true;
}
return false;
}
CommandCost ConvertExtendedDepot(DoCommandFlag flags, Depot *dep, RailType rail_type)
{
CommandCost cost(EXPENSES_CONSTRUCTION);
assert(dep->owner == _current_company);
Company *c = Company::Get(dep->owner);
for (TileIndex tile : dep->ta) {
if (!IsDepotTile(tile)) continue;
if (GetDepotIndex(tile) != dep->index) continue;
assert(!HasDepotReservation(tile));
assert(dep->owner == GetTileOwner(tile));
/* Original railtype we are converting from */
RailType type = GetRailType(tile);
if (type == rail_type || (_settings_game.vehicle.disable_elrails && rail_type == RAILTYPE_RAIL && type == RAILTYPE_ELECTRIC)) continue;
cost.AddCost(RailConvertCost(type, rail_type));
if (flags & DC_EXEC) {
c->infrastructure.rail[type]--;
c->infrastructure.rail[rail_type]++;
SetRailType(tile, rail_type);
MarkTileDirtyByTile(tile);
YapfNotifyTrackLayoutChange(tile, GetRailDepotTrack(tile));
DirtyCompanyInfrastructureWindows(c->index);
}
}
return cost;
}
/**
* Convert one rail type to the other. You can convert normal rail to
* monorail/maglev easily or vice-versa.
@ -1558,6 +1691,7 @@ CommandCost CmdConvertRail(DoCommandFlag flags, TileIndex tile, TileIndex area_s
if (area_start >= Map::Size()) return CMD_ERROR;
TrainList affected_trains;
std::vector<DepotID> affected_depots;
CommandCost cost(EXPENSES_CONSTRUCTION);
CommandCost error = CommandCost(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK); // by default, there is no track to convert.
@ -1647,21 +1781,19 @@ CommandCost CmdConvertRail(DoCommandFlag flags, TileIndex tile, TileIndex area_s
switch (tt) {
case MP_RAILWAY:
switch (GetRailTileType(tile)) {
case RAIL_TILE_DEPOT:
found_convertible_track = true;
if (GetRailTileType(tile) == RAIL_TILE_DEPOT) {
if (flags & DC_EXEC) {
/* notify YAPF about the track layout change */
YapfNotifyTrackLayoutChange(tile, GetRailDepotTrack(tile));
/* Update build vehicle window related to this depot */
InvalidateWindowData(WC_VEHICLE_DEPOT, tile);
InvalidateWindowData(WC_BUILD_VEHICLE, tile);
}
found_convertible_track = true;
cost.AddCost(RailConvertCost(type, totype));
break;
default: // RAIL_TILE_NORMAL, RAIL_TILE_SIGNALS
if (find(affected_depots.begin(), affected_depots.end(), (tile)) == affected_depots.end()) {
affected_depots.push_back(GetDepotIndex(tile));
}
cost.AddCost(RailConvertCost(type, totype));
} else { // RAIL_TILE_NORMAL, RAIL_TILE_SIGNALS
if (flags & DC_EXEC) {
/* notify YAPF about the track layout change */
TrackBits tracks = GetTrackBits(tile);
@ -1669,9 +1801,7 @@ CommandCost CmdConvertRail(DoCommandFlag flags, TileIndex tile, TileIndex area_s
YapfNotifyTrackLayoutChange(tile, RemoveFirstTrack(&tracks));
}
}
found_convertible_track = true;
cost.AddCost(RailConvertCost(type, totype) * CountBits(GetTrackBits(tile)));
break;
}
break;
@ -1753,6 +1883,17 @@ CommandCost CmdConvertRail(DoCommandFlag flags, TileIndex tile, TileIndex area_s
}
}
/* Update affected depots. */
for (auto &depot_tile : affected_depots) {
Depot *dep = Depot::Get(depot_tile);
if (HasAnyExtendedDepotReservedTile(dep)) cost.MakeError(STR_ERROR_DEPOT_EXTENDED_RAIL_DEPOT_IS_NOT_FREE);
if (flags & DC_EXEC) {
dep->RescanDepotTiles();
InvalidateWindowData(WC_VEHICLE_DEPOT, dep->index);
}
}
if (flags & DC_EXEC) {
/* Railtype changed, update trains as when entering different track */
for (Train *v : affected_trains) {
@ -1763,8 +1904,10 @@ CommandCost CmdConvertRail(DoCommandFlag flags, TileIndex tile, TileIndex area_s
return found_convertible_track ? cost : error;
}
static CommandCost RemoveTrainDepot(TileIndex tile, DoCommandFlag flags)
static CommandCost RemoveTrainDepot(TileIndex tile, DoCommandFlag flags, bool keep_rail)
{
assert(IsRailDepotTile(tile));
if (_current_company != OWNER_WATER) {
CommandCost ret = CheckTileOwnership(tile);
if (ret.Failed()) return ret;
@ -1773,10 +1916,21 @@ static CommandCost RemoveTrainDepot(TileIndex tile, DoCommandFlag flags)
CommandCost ret = EnsureNoVehicleOnGround(tile);
if (ret.Failed()) return ret;
if (HasDepotReservation(tile)) return CMD_ERROR;
CommandCost total_cost(EXPENSES_CONSTRUCTION);
if (keep_rail) {
/* Don't refund the 'steel' of the track when we keep the rail. */
total_cost.AddCost(-_price[PR_CLEAR_RAIL]);
}
if (flags & DC_EXEC) {
/* read variables before the depot is removed */
Depot *depot = Depot::GetByTile(tile);
Company *c = Company::GetIfValid(depot->owner);
assert(c != nullptr);
DiagDirection dir = GetRailDepotDirection(tile);
Owner owner = GetTileOwner(tile);
Train *v = nullptr;
if (HasDepotReservation(tile)) {
@ -1784,17 +1938,57 @@ static CommandCost RemoveTrainDepot(TileIndex tile, DoCommandFlag flags)
if (v != nullptr) FreeTrainTrackReservation(v);
}
Company::Get(owner)->infrastructure.rail[GetRailType(tile)]--;
DirtyCompanyInfrastructureWindows(owner);
Track track = GetRailDepotTrack(tile);
RailType rt = GetRailType(tile);
bool is_extended_depot = IsExtendedDepot(tile);
delete Depot::GetByTile(tile);
DoClearSquare(tile);
AddSideToSignalBuffer(tile, dir, owner);
YapfNotifyTrackLayoutChange(tile, DiagDirToDiagTrack(dir));
if (v != nullptr) TryPathReserve(v, true);
if (keep_rail) {
MakeRailNormal(tile, depot->owner, TrackToTrackBits(track), rt);
} else {
c->infrastructure.rail[GetRailType(tile)]--;
DirtyCompanyInfrastructureWindows(c->index);
}
return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_TRAIN]);
if (is_extended_depot) {
AddTrackToSignalBuffer(tile, DiagDirToDiagTrack(dir), c->index);
} else {
AddSideToSignalBuffer(tile, dir, c->index);
}
YapfNotifyTrackLayoutChange(tile, DiagDirToDiagTrack(dir));
if (v != nullptr) TryPathReserve(v, true);
depot->AfterAddRemove(TileArea(tile), false);
}
total_cost.AddCost(_price[PR_CLEAR_DEPOT_TRAIN]);
return total_cost;
}
/**
* Remove train depots from an area
* @param flags operation to perform
* @param start_tile start tile of the area
* @param end_tile end tile of the area
* @return the cost of this operation or an error
*/
CommandCost CmdRemoveTrainDepot(DoCommandFlag flags, TileIndex start_tile, TileIndex end_tile)
{
assert(IsValidTile(start_tile));
assert(IsValidTile(end_tile));
CommandCost cost(EXPENSES_CONSTRUCTION);
TileArea ta(start_tile, end_tile);
for (TileIndex t : ta) {
if (!IsRailDepotTile(t)) continue;
CommandCost ret = RemoveTrainDepot(t, flags, IsExtendedDepot(t));
if (ret.Failed()) return ret;
cost.AddCost(ret);
}
return cost;
}
static CommandCost ClearTile_Track(TileIndex tile, DoCommandFlag flags)
@ -1845,7 +2039,7 @@ static CommandCost ClearTile_Track(TileIndex tile, DoCommandFlag flags)
}
case RAIL_TILE_DEPOT:
return RemoveTrainDepot(tile, flags);
return RemoveTrainDepot(tile, flags, false);
default:
return CMD_ERROR;
@ -2072,7 +2266,12 @@ static inline void DrawTrackSprite(SpriteID sprite, PaletteID pal, const TileInf
static void DrawTrackBitsOverlay(TileInfo *ti, TrackBits track, const RailTypeInfo *rti)
{
RailGroundType rgt = GetRailGroundType(ti->tile);
Foundation f = GetRailFoundation(ti->tileh, track);
Foundation f = FOUNDATION_NONE;
if (IsRailDepot(ti->tile)) {
if (ti->tileh != SLOPE_FLAT) f = FOUNDATION_LEVELED;
} else {
f = GetRailFoundation(ti->tileh, track);
}
Corner halftile_corner = CORNER_INVALID;
if (IsNonContinuousFoundation(f)) {
@ -2112,7 +2311,18 @@ static void DrawTrackBitsOverlay(TileInfo *ti, TrackBits track, const RailTypeIn
bool no_combine = ti->tileh == SLOPE_FLAT && HasBit(rti->flags, RTF_NO_SPRITE_COMBINE);
SpriteID overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY);
SpriteID ground = GetCustomRailSprite(rti, ti->tile, no_combine ? RTSG_GROUND_COMPLETE : RTSG_GROUND);
TrackBits pbs = _settings_client.gui.show_track_reservation ? GetRailReservationTrackBits(ti->tile) : TRACK_BIT_NONE;
TrackBits pbs = TRACK_BIT_NONE;
if (_settings_client.gui.show_track_reservation) {
if (IsPlainRail(ti->tile)) {
pbs = GetRailReservationTrackBits(ti->tile);
} else {
assert(IsRailDepot(ti->tile));
if (HasDepotReservation(ti->tile)) {
pbs = track;
}
}
}
if (track == TRACK_BIT_NONE) {
/* Half-tile foundation, no track here? */
@ -2342,7 +2552,14 @@ static void DrawTrackBits(TileInfo *ti, TrackBits track)
/* PBS debugging, draw reserved tracks darker */
if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation) {
/* Get reservation, but mask track on halftile slope */
TrackBits pbs = GetRailReservationTrackBits(ti->tile) & track;
TrackBits pbs = TRACK_BIT_NONE;
if (IsPlainRail(ti->tile)) {
pbs = GetRailReservationTrackBits(ti->tile) & track;
} else {
assert(IsRailDepot(ti->tile));
if (HasDepotReservation(ti->tile)) pbs = track;
}
if (pbs & TRACK_BIT_X) {
if (ti->tileh == SLOPE_FLAT || ti->tileh == SLOPE_ELEVATED) {
DrawGroundSprite(rti->base_sprites.single_x, PALETTE_CRASH);
@ -2425,124 +2642,40 @@ static void DrawTile_Track(TileInfo *ti)
_drawtile_track_palette = COMPANY_SPRITE_COLOUR(GetTileOwner(ti->tile));
TrackBits rails = TRACK_BIT_NONE;
if (IsPlainRail(ti->tile)) {
TrackBits rails = GetTrackBits(ti->tile);
rails = GetTrackBits(ti->tile);
} else {
assert(IsRailDepot(ti->tile));
DiagDirection dir = GetRailDepotDirection(ti->tile);
if (IsDiagDirFacingSouth(dir) || IsTransparencySet(TO_BUILDINGS)) {
rails = TrackToTrackBits(GetRailDepotTrack(ti->tile));
}
}
DrawTrackBits(ti, rails);
if (HasBit(_display_opt, DO_FULL_DETAIL)) DrawTrackDetails(ti, rti);
if (IsPlainRail(ti->tile) && HasBit(_display_opt, DO_FULL_DETAIL)) DrawTrackDetails(ti, rti);
if (HasRailCatenaryDrawn(GetRailType(ti->tile))) DrawRailCatenary(ti);
if (HasSignals(ti->tile)) DrawSignals(ti->tile, rails, rti);
} else {
if (IsRailDepot(ti->tile) && !IsInvisibilitySet(TO_BUILDINGS)) {
/* draw depot */
const DrawTileSprites *dts;
PaletteID pal = PAL_NONE;
SpriteID relocation;
if (ti->tileh != SLOPE_FLAT) DrawFoundation(ti, FOUNDATION_LEVELED);
if (IsInvisibilitySet(TO_BUILDINGS)) {
/* Draw rail instead of depot */
dts = &_depot_invisible_gfx_table[GetRailDepotDirection(ti->tile)];
} else {
dts = &_depot_gfx_table[GetRailDepotDirection(ti->tile)];
}
SpriteID image;
if (rti->UsesOverlay()) {
image = SPR_FLAT_GRASS_TILE;
} else {
image = dts->ground.sprite;
if (image != SPR_FLAT_GRASS_TILE) image += rti->GetRailtypeSpriteOffset();
}
/* Adjust ground tile for desert and snow. */
if (IsSnowRailGround(ti->tile)) {
if (image != SPR_FLAT_GRASS_TILE) {
image += rti->snow_offset; // tile with tracks
} else {
image = SPR_FLAT_SNOW_DESERT_TILE; // flat ground
}
}
DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, _drawtile_track_palette));
if (rti->UsesOverlay()) {
SpriteID ground = GetCustomRailSprite(rti, ti->tile, RTSG_GROUND);
switch (GetRailDepotDirection(ti->tile)) {
case DIAGDIR_NE:
if (!IsInvisibilitySet(TO_BUILDINGS)) break;
[[fallthrough]];
case DIAGDIR_SW:
DrawGroundSprite(ground + RTO_X, PAL_NONE);
break;
case DIAGDIR_NW:
if (!IsInvisibilitySet(TO_BUILDINGS)) break;
[[fallthrough]];
case DIAGDIR_SE:
DrawGroundSprite(ground + RTO_Y, PAL_NONE);
break;
default:
break;
}
if (_settings_client.gui.show_track_reservation && HasDepotReservation(ti->tile)) {
SpriteID overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY);
switch (GetRailDepotDirection(ti->tile)) {
case DIAGDIR_NE:
if (!IsInvisibilitySet(TO_BUILDINGS)) break;
[[fallthrough]];
case DIAGDIR_SW:
DrawGroundSprite(overlay + RTO_X, PALETTE_CRASH);
break;
case DIAGDIR_NW:
if (!IsInvisibilitySet(TO_BUILDINGS)) break;
[[fallthrough]];
case DIAGDIR_SE:
DrawGroundSprite(overlay + RTO_Y, PALETTE_CRASH);
break;
default:
break;
}
}
} else {
/* PBS debugging, draw reserved tracks darker */
if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasDepotReservation(ti->tile)) {
switch (GetRailDepotDirection(ti->tile)) {
case DIAGDIR_NE:
if (!IsInvisibilitySet(TO_BUILDINGS)) break;
[[fallthrough]];
case DIAGDIR_SW:
DrawGroundSprite(rti->base_sprites.single_x, PALETTE_CRASH);
break;
case DIAGDIR_NW:
if (!IsInvisibilitySet(TO_BUILDINGS)) break;
[[fallthrough]];
case DIAGDIR_SE:
DrawGroundSprite(rti->base_sprites.single_y, PALETTE_CRASH);
break;
default:
break;
}
}
}
const DrawTileSprites *dts = &_depot_gfx_table[GetRailDepotDirection(ti->tile)];
int depot_sprite = GetCustomRailSprite(rti, ti->tile, RTSG_DEPOT);
relocation = depot_sprite != 0 ? depot_sprite - SPR_RAIL_DEPOT_SE_1 : rti->GetRailtypeSpriteOffset();
if (HasRailCatenaryDrawn(GetRailType(ti->tile))) DrawRailCatenary(ti);
SpriteID relocation = depot_sprite != 0 ? depot_sprite - SPR_RAIL_DEPOT_SE_1 : rti->GetRailtypeSpriteOffset();
DrawRailTileSeq(ti, dts, TO_BUILDINGS, relocation, 0, _drawtile_track_palette);
}
if (HasSignals(ti->tile)) DrawSignals(ti->tile, rails, rti);
DrawBridgeMiddle(ti);
}
void DrawTrainDepotSprite(int x, int y, int dir, RailType railtype)
{
const DrawTileSprites *dts = &_depot_gfx_table[dir];
const DrawTileSprites *dts = &_depot_gfx_gui_table[dir];
const RailTypeInfo *rti = GetRailTypeInfo(railtype);
SpriteID image = rti->UsesOverlay() ? SPR_FLAT_GRASS_TILE : dts->ground.sprite;
uint32_t offset = rti->GetRailtypeSpriteOffset();
@ -2758,6 +2891,13 @@ static TrackStatus GetTileTrackStatus_Track(TileIndex tile, TransportType mode,
}
case RAIL_TILE_DEPOT: {
if (IsExtendedRailDepot(tile)) {
Track track = GetRailDepotTrack(tile);
trackbits = TrackToTrackBits(track);
break;
}
/* Small depot. */
DiagDirection dir = GetRailDepotDirection(tile);
if (side != INVALID_DIAGDIR && side != dir) break;
@ -2774,7 +2914,7 @@ static bool ClickTile_Track(TileIndex tile)
{
if (!IsRailDepot(tile)) return false;
ShowDepotWindow(tile, VEH_TRAIN);
ShowDepotWindow(GetDepotIndex(tile));
return true;
}
@ -2855,7 +2995,7 @@ static void GetTileDesc_Track(TileIndex tile, TileDesc *td)
}
case RAIL_TILE_DEPOT:
td->str = STR_LAI_RAIL_DESCRIPTION_TRAIN_DEPOT;
td->str = IsExtendedDepot(tile) ? STR_LAI_RAIL_DESCRIPTION_TRAIN_DEPOT_EXTENDED : STR_LAI_RAIL_DESCRIPTION_TRAIN_DEPOT;
if (_settings_game.vehicle.train_acceleration_model != AM_ORIGINAL) {
if (td->rail_speed > 0) {
td->rail_speed = std::min<uint16_t>(td->rail_speed, 61);
@ -2915,6 +3055,7 @@ static const int8_t _deltacoord_leaveoffset[8] = {
*/
int TicksToLeaveDepot(const Train *v)
{
assert(IsStandardRailDepotTile(v->tile));
DiagDirection dir = GetRailDepotDirection(v->tile);
int length = v->CalcNextVehicleOffset();
@ -2936,6 +3077,38 @@ static VehicleEnterTileStatus VehicleEnter_Track(Vehicle *u, TileIndex tile, int
/* This routine applies only to trains in depot tiles. */
if (u->type != VEH_TRAIN || !IsRailDepotTile(tile)) return VETSB_CONTINUE;
Train *v = Train::From(u);
if (IsExtendedRailDepot(tile)) {
DepotID depot_id = GetDepotIndex(tile);
if (!v->current_order.ShouldStopAtDepot(depot_id)) return VETSB_CONTINUE;
/* Stop position on platform is half the front vehicle length of the train. */
int stop_pos = v->gcache.cached_veh_length / 2;
int depot_ahead = (GetPlatformLength(tile, DirToDiagDir(v->direction)) - 1) * TILE_SIZE;
if (depot_ahead > stop_pos) return VETSB_CONTINUE;
DiagDirection dir = DirToDiagDir(v->direction);
x &= 0xF;
y &= 0xF;
if (DiagDirToAxis(dir) != AXIS_X) Swap(x, y);
if (y == TILE_SIZE / 2) {
if (dir == DIAGDIR_SE || dir == DIAGDIR_SW) x = TILE_SIZE - 1 - x;
if (stop_pos == x) {
return VETSB_ENTERED_DEPOT_PLATFORM;
} else if (stop_pos < x) {
v->vehstatus |= VS_TRAIN_SLOWING;
uint16_t spd = std::max(0, stop_pos * 20 - 15);
if (spd < v->cur_speed) v->cur_speed = spd;
}
}
return VETSB_CONTINUE;
}
/* Depot direction. */
DiagDirection dir = GetRailDepotDirection(tile);
@ -2944,8 +3117,6 @@ static VehicleEnterTileStatus VehicleEnter_Track(Vehicle *u, TileIndex tile, int
/* Make sure a train is not entering the tile from behind. */
if (_fractcoords_behind[dir] == fract_coord) return VETSB_CANNOT_ENTER;
Train *v = Train::From(u);
/* Leaving depot? */
if (v->direction == DiagDirToDir(dir)) {
/* Calculate the point where the following wagon should be activated. */
@ -2970,10 +3141,10 @@ static VehicleEnterTileStatus VehicleEnter_Track(Vehicle *u, TileIndex tile, int
v->track = TRACK_BIT_DEPOT,
v->vehstatus |= VS_HIDDEN;
v->direction = ReverseDir(v->direction);
if (v->Next() == nullptr) VehicleEnterDepot(v->First());
if (v->Next() == nullptr) HandleTrainEnterDepot(v->First());
v->tile = tile;
InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile));
return VETSB_ENTERED_WORMHOLE;
}
@ -3080,6 +3251,14 @@ static CommandCost TerraformTile_Track(TileIndex tile, DoCommandFlag flags, int
return CommandCost(EXPENSES_CONSTRUCTION, was_water ? _price[PR_CLEAR_WATER] : (Money)0);
} else if (_settings_game.construction.build_on_slopes && AutoslopeEnabled() &&
AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, GetRailDepotDirection(tile))) {
if (IsExtendedRailDepotTile(tile) && GetTileMaxZ(tile) == z_new + GetSlopeMaxZ(tileh_new)) {
DiagDirection direction = GetRailDepotDirection(tile);
if (!AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, direction) ||
!AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, ReverseDiagDir(direction))) {
return Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile);
}
}
return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
}
return Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile);

View File

@ -19,7 +19,8 @@ CommandCost CmdBuildRailroadTrack(DoCommandFlag flags, TileIndex end_tile, TileI
CommandCost CmdRemoveRailroadTrack(DoCommandFlag flags, TileIndex end_tile, TileIndex start_tile, Track track);
CommandCost CmdBuildSingleRail(DoCommandFlag flags, TileIndex tile, RailType railtype, Track track, bool auto_remove_signals);
CommandCost CmdRemoveSingleRail(DoCommandFlag flags, TileIndex tile, Track track);
CommandCost CmdBuildTrainDepot(DoCommandFlag flags, TileIndex tile, RailType railtype, DiagDirection dir);
CommandCost CmdBuildTrainDepot(DoCommandFlag flags, TileIndex tile, RailType railtype, DiagDirection dir, bool adjacent, bool extended, DepotID depot_id, TileIndex end_tile);
CommandCost CmdRemoveTrainDepot(DoCommandFlag flags, TileIndex start_tile, TileIndex end_tile);
CommandCost CmdBuildSingleSignal(DoCommandFlag flags, TileIndex tile, Track track, SignalType sigtype, SignalVariant sigvar, bool convert_signal, bool skip_existing_signals, bool ctrl_pressed, SignalType cycle_start, SignalType cycle_stop, uint8_t num_dir_cycle, uint8_t signals_copy);
CommandCost CmdRemoveSingleSignal(DoCommandFlag flags, TileIndex tile, Track track);
CommandCost CmdConvertRail(DoCommandFlag flags, TileIndex tile, TileIndex area_start, RailType totype, bool diagonal);
@ -31,6 +32,7 @@ DEF_CMD_TRAIT(CMD_REMOVE_RAILROAD_TRACK, CmdRemoveRailroadTrack, CMD_AUTO,
DEF_CMD_TRAIT(CMD_BUILD_SINGLE_RAIL, CmdBuildSingleRail, CMD_AUTO | CMD_NO_WATER, CMDT_LANDSCAPE_CONSTRUCTION)
DEF_CMD_TRAIT(CMD_REMOVE_SINGLE_RAIL, CmdRemoveSingleRail, CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION)
DEF_CMD_TRAIT(CMD_BUILD_TRAIN_DEPOT, CmdBuildTrainDepot, CMD_AUTO | CMD_NO_WATER, CMDT_LANDSCAPE_CONSTRUCTION)
DEF_CMD_TRAIT(CMD_REMOVE_TRAIN_DEPOT, CmdRemoveTrainDepot, CMD_AUTO | CMD_NO_WATER, CMDT_LANDSCAPE_CONSTRUCTION)
DEF_CMD_TRAIT(CMD_BUILD_SINGLE_SIGNAL, CmdBuildSingleSignal, CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION)
DEF_CMD_TRAIT(CMD_REMOVE_SINGLE_SIGNAL, CmdRemoveSingleSignal, CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION)
DEF_CMD_TRAIT(CMD_CONVERT_RAIL, CmdConvertRail, 0, CMDT_LANDSCAPE_CONSTRUCTION)
@ -40,6 +42,6 @@ DEF_CMD_TRAIT(CMD_REMOVE_SIGNAL_TRACK, CmdRemoveSignalTrack, CMD_AUTO,
CommandCallback CcPlaySound_CONSTRUCTION_RAIL;
CommandCallback CcStation;
CommandCallback CcBuildRailTunnel;
void CcRailDepot(Commands cmd, const CommandCost &result, TileIndex tile, RailType rt, DiagDirection dir);
void CcRailDepot(Commands cmd, const CommandCost &result, TileIndex tile, RailType rt, DiagDirection dir, bool adjacent, bool extended, DepotID join_to, TileIndex end_tile);
#endif /* RAIL_CMD_H */

View File

@ -41,6 +41,7 @@
#include "timer/timer.h"
#include "timer/timer_game_calendar.h"
#include "picker_gui.h"
#include "depot_func.h"
#include "station_map.h"
#include "tunnelbridge_map.h"
@ -72,7 +73,7 @@ static StationPickerSelection _station_gui; ///< Settings of the station picker.
static void HandleStationPlacement(TileIndex start, TileIndex end);
static void ShowBuildTrainDepotPicker(Window *parent);
static void ShowBuildTrainDepotPicker(Window *parent, bool extended_depot);
static void ShowBuildWaypointPicker(Window *parent);
static Window *ShowStationBuilder(Window *parent);
static void ShowSignalBuilder(Window *parent);
@ -116,7 +117,7 @@ static void GenericPlaceRail(TileIndex tile, Track track)
*/
static void PlaceExtraDepotRail(TileIndex tile, DiagDirection dir, Track track)
{
if (GetRailTileType(tile) == RAIL_TILE_DEPOT) return;
if (IsRailDepot(tile)) return;
if (GetRailTileType(tile) == RAIL_TILE_SIGNALS && !_settings_client.gui.auto_remove_signals) return;
if ((GetTrackBits(tile) & DiagdirReachesTracks(dir)) == 0) return;
@ -137,25 +138,28 @@ static const DiagDirection _place_depot_extra_dir[12] = {
DIAGDIR_NW, DIAGDIR_NE, DIAGDIR_NW, DIAGDIR_NE,
};
void CcRailDepot(Commands, const CommandCost &result, TileIndex tile, RailType, DiagDirection dir)
void CcRailDepot(Commands, const CommandCost &result, TileIndex start_tile, RailType, DiagDirection dir, bool, bool extended, DepotID, TileIndex end_tile)
{
if (result.Failed()) return;
if (extended) return;
if (_settings_client.sound.confirm) SndPlayTileFx(SND_20_CONSTRUCTION_RAIL, tile);
if (_settings_client.sound.confirm) SndPlayTileFx(SND_20_CONSTRUCTION_RAIL, start_tile);
if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace();
tile += TileOffsByDiagDir(dir);
TileArea ta(start_tile, end_tile);
for (TileIndex t : ta) {
TileIndex tile = t + TileOffsByDiagDir(dir);
if (IsTileType(tile, MP_RAILWAY)) {
PlaceExtraDepotRail(tile, _place_depot_extra_dir[dir], _place_depot_extra_track[dir]);
/* Don't place the rail straight out of the depot of there is another depot across from it. */
Tile double_depot_tile = tile + TileOffsByDiagDir(dir);
bool is_double_depot = IsValidTile(double_depot_tile) && IsRailDepotTile(double_depot_tile);
if (!is_double_depot) PlaceExtraDepotRail(tile, _place_depot_extra_dir[dir + 4], _place_depot_extra_track[dir + 4]);
PlaceExtraDepotRail(tile, _place_depot_extra_dir[dir + 8], _place_depot_extra_track[dir + 8]);
}
}
}
/**
@ -317,6 +321,7 @@ void CcBuildRailTunnel(Commands, const CommandCost &result, TileIndex tile)
static void ToggleRailButton_Remove(Window *w)
{
CloseWindowById(WC_SELECT_STATION, 0);
CloseWindowById(WC_SELECT_DEPOT, VEH_TRAIN);
w->ToggleWidgetLoweredState(WID_RAT_REMOVE);
w->SetWidgetDirty(WID_RAT_REMOVE);
_remove_button_clicked = w->IsWidgetLowered(WID_RAT_REMOVE);
@ -334,7 +339,7 @@ static bool RailToolbar_CtrlChanged(Window *w)
/* allow ctrl to switch remove mode only for these widgets */
for (WidgetID i = WID_RAT_BUILD_NS; i <= WID_RAT_BUILD_STATION; i++) {
if ((i <= WID_RAT_AUTORAIL || i >= WID_RAT_BUILD_WAYPOINT) && w->IsWidgetLowered(i)) {
if ((i <= WID_RAT_AUTORAIL || i >= WID_RAT_BUILD_DEPOT) && w->HasWidget(i) && w->IsWidgetLowered(i)) {
ToggleRailButton_Remove(w);
return true;
}
@ -454,6 +459,11 @@ struct BuildRailToolbarWindow : Window {
if (this->IsWidgetLowered(WID_RAT_BUILD_WAYPOINT)) SetViewportCatchmentWaypoint(nullptr, true);
if (_settings_client.gui.link_terraform_toolbar) CloseWindowById(WC_SCEN_LAND_GEN, 0, false);
CloseWindowById(WC_SELECT_STATION, 0);
if ((this->HasWidget(WID_RAT_BUILD_DEPOT) && this->IsWidgetLowered(WID_RAT_BUILD_DEPOT)) ||
(this->HasWidget(WID_RAT_BUILD_EXTENDED_DEPOT) && this->IsWidgetLowered(WID_RAT_BUILD_EXTENDED_DEPOT))) {
SetViewportHighlightDepot(INVALID_DEPOT, true);
}
this->Window::Close();
}
@ -505,7 +515,8 @@ struct BuildRailToolbarWindow : Window {
this->GetWidget<NWidgetCore>(WID_RAT_BUILD_EW)->widget_data = rti->gui_sprites.build_ew_rail;
this->GetWidget<NWidgetCore>(WID_RAT_BUILD_Y)->widget_data = rti->gui_sprites.build_y_rail;
this->GetWidget<NWidgetCore>(WID_RAT_AUTORAIL)->widget_data = rti->gui_sprites.auto_rail;
this->GetWidget<NWidgetCore>(WID_RAT_BUILD_DEPOT)->widget_data = rti->gui_sprites.build_depot;
if (this->HasWidget(WID_RAT_BUILD_DEPOT)) this->GetWidget<NWidgetCore>(WID_RAT_BUILD_DEPOT)->widget_data = rti->gui_sprites.build_depot;
if (this->HasWidget(WID_RAT_BUILD_EXTENDED_DEPOT)) this->GetWidget<NWidgetCore>(WID_RAT_BUILD_EXTENDED_DEPOT)->widget_data = rti->gui_sprites.build_depot;
this->GetWidget<NWidgetCore>(WID_RAT_CONVERT_RAIL)->widget_data = rti->gui_sprites.convert_rail;
this->GetWidget<NWidgetCore>(WID_RAT_BUILD_TUNNEL)->widget_data = rti->gui_sprites.build_tunnel;
}
@ -533,6 +544,8 @@ struct BuildRailToolbarWindow : Window {
case WID_RAT_BUILD_EW:
case WID_RAT_BUILD_Y:
case WID_RAT_AUTORAIL:
case WID_RAT_BUILD_DEPOT:
case WID_RAT_BUILD_EXTENDED_DEPOT:
case WID_RAT_BUILD_WAYPOINT:
case WID_RAT_BUILD_STATION:
case WID_RAT_BUILD_SIGNALS:
@ -601,8 +614,9 @@ struct BuildRailToolbarWindow : Window {
break;
case WID_RAT_BUILD_DEPOT:
if (HandlePlacePushButton(this, WID_RAT_BUILD_DEPOT, GetRailTypeInfo(_cur_railtype)->cursor.depot, HT_RECT)) {
ShowBuildTrainDepotPicker(this);
case WID_RAT_BUILD_EXTENDED_DEPOT:
if (HandlePlacePushButton(this, widget, GetRailTypeInfo(_cur_railtype)->cursor.depot, HT_RECT)) {
ShowBuildTrainDepotPicker(this, widget == WID_RAT_BUILD_EXTENDED_DEPOT);
this->last_user_action = widget;
}
break;
@ -689,8 +703,17 @@ struct BuildRailToolbarWindow : Window {
break;
case WID_RAT_BUILD_DEPOT:
Command<CMD_BUILD_TRAIN_DEPOT>::Post(STR_ERROR_CAN_T_BUILD_TRAIN_DEPOT, CcRailDepot, tile, _cur_railtype, _build_depot_direction);
case WID_RAT_BUILD_EXTENDED_DEPOT: {
CloseWindowById(WC_SELECT_DEPOT, VEH_TRAIN);
ViewportPlaceMethod vpm = VPM_X_AND_Y_LIMITED;
if (this->last_user_action == WID_RAT_BUILD_DEPOT) {
vpm = (DiagDirToAxis(_build_depot_direction) == 0) ? VPM_X_LIMITED : VPM_Y_LIMITED;
}
VpStartPlaceSizing(tile, vpm, _remove_button_clicked ? DDSP_REMOVE_DEPOT : DDSP_BUILD_DEPOT);
VpSetPlaceSizingLimit(_settings_game.depot.depot_spread);
break;
}
case WID_RAT_BUILD_WAYPOINT:
PlaceRail_Waypoint(tile);
@ -786,6 +809,22 @@ struct BuildRailToolbarWindow : Window {
}
}
break;
case DDSP_BUILD_DEPOT:
case DDSP_REMOVE_DEPOT:
if (_remove_button_clicked) {
Command<CMD_REMOVE_TRAIN_DEPOT>::Post(STR_ERROR_CAN_T_REMOVE_TRAIN_DEPOT, CcPlaySound_CONSTRUCTION_RAIL, start_tile, end_tile);
} else {
bool adjacent = _ctrl_pressed;
bool extended = this->last_user_action == WID_RAT_BUILD_EXTENDED_DEPOT;
auto proc = [=](DepotID join_to) -> bool {
return Command<CMD_BUILD_TRAIN_DEPOT>::Post(STR_ERROR_CAN_T_BUILD_TRAIN_DEPOT, CcRailDepot, start_tile, _cur_railtype, _build_depot_direction, adjacent, extended, join_to, end_tile);
};
ShowSelectDepotIfNeeded(TileArea(start_tile, end_tile), proc, VEH_TRAIN);
}
break;
}
}
}
@ -795,6 +834,11 @@ struct BuildRailToolbarWindow : Window {
if (this->IsWidgetLowered(WID_RAT_BUILD_STATION)) SetViewportCatchmentStation(nullptr, true);
if (this->IsWidgetLowered(WID_RAT_BUILD_WAYPOINT)) SetViewportCatchmentWaypoint(nullptr, true);
if ((this->HasWidget(WID_RAT_BUILD_DEPOT) && this->IsWidgetLowered(WID_RAT_BUILD_DEPOT)) ||
(this->HasWidget(WID_RAT_BUILD_EXTENDED_DEPOT) && this->IsWidgetLowered(WID_RAT_BUILD_EXTENDED_DEPOT))) {
SetViewportHighlightDepot(INVALID_DEPOT, true);
}
this->RaiseButtons();
this->DisableWidget(WID_RAT_REMOVE);
this->SetWidgetDirty(WID_RAT_REMOVE);
@ -804,6 +848,7 @@ struct BuildRailToolbarWindow : Window {
CloseWindowById(WC_BUILD_DEPOT, TRANSPORT_RAIL);
CloseWindowById(WC_BUILD_WAYPOINT, TRANSPORT_RAIL);
CloseWindowById(WC_SELECT_STATION, 0);
CloseWindowById(WC_SELECT_DEPOT, VEH_TRAIN);
CloseWindowByClass(WC_BUILD_BRIDGE);
}
@ -815,8 +860,12 @@ struct BuildRailToolbarWindow : Window {
EventState OnCTRLStateChange() override
{
/* do not toggle Remove button by Ctrl when placing station */
if (!this->IsWidgetLowered(WID_RAT_BUILD_STATION) && !this->IsWidgetLowered(WID_RAT_BUILD_WAYPOINT) && RailToolbar_CtrlChanged(this)) return ES_HANDLED;
/* do not toggle Remove button by Ctrl when placing station or depot */
if (!this->IsWidgetLowered(WID_RAT_BUILD_STATION) &&
!this->IsWidgetLowered(WID_RAT_BUILD_WAYPOINT) &&
!(this->HasWidget(WID_RAT_BUILD_DEPOT) && this->IsWidgetLowered(WID_RAT_BUILD_DEPOT)) &&
!(this->HasWidget(WID_RAT_BUILD_EXTENDED_DEPOT) && this->IsWidgetLowered(WID_RAT_BUILD_EXTENDED_DEPOT)) &&
RailToolbar_CtrlChanged(this)) return ES_HANDLED;
return ES_NOT_HANDLED;
}
@ -857,6 +906,27 @@ struct BuildRailToolbarWindow : Window {
}, RailToolbarGlobalHotkeys};
};
/**
* Add the depot icons depending on availability of construction.
* @return Panel with rail depot buttons.
*/
static std::unique_ptr<NWidgetBase> MakeNWidgetRailDepot()
{
auto hor = std::make_unique<NWidgetHorizontal>();
if (HasBit(_settings_game.depot.rail_depot_types, 0)) {
/* Add the widget for building standard rail depot. */
hor->Add(std::make_unique<NWidgetLeaf>(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_RAT_BUILD_DEPOT, SPR_IMG_DEPOT_RAIL, STR_RAIL_TOOLBAR_TOOLTIP_BUILD_TRAIN_DEPOT));
}
if (HasBit(_settings_game.depot.rail_depot_types, 1)) {
/* Add the widget for building extended rail depot. */
hor->Add(std::make_unique<NWidgetLeaf>(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_RAT_BUILD_EXTENDED_DEPOT, SPR_IMG_DEPOT_RAIL, STR_RAIL_TOOLBAR_TOOLTIP_BUILD_EXTENDED_TRAIN_DEPOT));
}
return hor;
}
static constexpr NWidgetPart _nested_build_rail_widgets[] = {
NWidget(NWID_HORIZONTAL),
NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
@ -879,8 +949,7 @@ static constexpr NWidgetPart _nested_build_rail_widgets[] = {
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_RAT_DEMOLISH),
SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_DYNAMITE, STR_TOOLTIP_DEMOLISH_BUILDINGS_ETC),
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_RAT_BUILD_DEPOT),
SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_DEPOT_RAIL, STR_RAIL_TOOLBAR_TOOLTIP_BUILD_TRAIN_DEPOT_FOR_BUILDING),
NWidgetFunction(MakeNWidgetRailDepot),
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_RAT_BUILD_WAYPOINT),
SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_WAYPOINT, STR_RAIL_TOOLBAR_TOOLTIP_CONVERT_RAIL_TO_WAYPOINT),
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_RAT_BUILD_STATION),
@ -1699,12 +1768,33 @@ static void ShowSignalBuilder(Window *parent)
}
struct BuildRailDepotWindow : public PickerWindowBase {
BuildRailDepotWindow(WindowDesc &desc, Window *parent) : PickerWindowBase(desc, parent)
BuildRailDepotWindow(WindowDesc &desc, Window *parent, bool extended_depot) : PickerWindowBase(desc, parent)
{
this->InitNested(TRANSPORT_RAIL);
/* Fix direction for extended depots. */
if (extended_depot) {
switch ((BuildRailDepotWidgets)_build_depot_direction) {
case WID_BRAD_DEPOT_NE:
_build_depot_direction++;
break;
case WID_BRAD_DEPOT_NW:
_build_depot_direction--;
break;
default: break;
}
}
this->LowerWidget(WID_BRAD_DEPOT_NE + _build_depot_direction);
}
void Close([[maybe_unused]] int data = 0) override
{
CloseWindowById(WC_SELECT_DEPOT, VEH_TRAIN);
this->PickerWindowBase::Close();
}
void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
{
if (!IsInsideMM(widget, WID_BRAD_DEPOT_NE, WID_BRAD_DEPOT_NW + 1)) return;
@ -1734,6 +1824,7 @@ struct BuildRailDepotWindow : public PickerWindowBase {
case WID_BRAD_DEPOT_SE:
case WID_BRAD_DEPOT_SW:
case WID_BRAD_DEPOT_NW:
CloseWindowById(WC_SELECT_DEPOT, VEH_TRAIN);
this->RaiseWidget(WID_BRAD_DEPOT_NE + _build_depot_direction);
_build_depot_direction = (DiagDirection)(widget - WID_BRAD_DEPOT_NE);
this->LowerWidget(WID_BRAD_DEPOT_NE + _build_depot_direction);
@ -1742,6 +1833,11 @@ struct BuildRailDepotWindow : public PickerWindowBase {
break;
}
}
void OnRealtimeTick([[maybe_unused]] uint delta_ms) override
{
CheckRedrawDepotHighlight(this, VEH_TRAIN);
}
};
/** Nested widget definition of the build rail depot window */
@ -1771,9 +1867,30 @@ static WindowDesc _build_depot_desc(
_nested_build_depot_widgets
);
static void ShowBuildTrainDepotPicker(Window *parent)
/** Nested widget definition of the build extended rail depot window */
static const NWidgetPart _nested_build_extended_depot_widgets[] = {
NWidget(NWID_HORIZONTAL),
NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetDataTip(STR_BUILD_DEPOT_TRAIN_ORIENTATION_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
EndContainer(),
NWidget(WWT_PANEL, COLOUR_DARK_GREEN),
NWidget(NWID_HORIZONTAL_LTR), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), SetPIPRatio(1, 0, 1), SetPadding(WidgetDimensions::unscaled.picker),
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAD_DEPOT_SW), SetMinimalSize(66, 50), SetDataTip(0x0, STR_BUILD_DEPOT_TRAIN_ORIENTATION_TOOLTIP),
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAD_DEPOT_SE), SetMinimalSize(66, 50), SetDataTip(0x0, STR_BUILD_DEPOT_TRAIN_ORIENTATION_TOOLTIP),
EndContainer(),
EndContainer(),
};
static WindowDesc _build_extended_depot_desc(
WDP_AUTO, nullptr, 0, 0,
WC_BUILD_DEPOT, WC_BUILD_TOOLBAR,
WDF_CONSTRUCTION,
_nested_build_extended_depot_widgets
);
static void ShowBuildTrainDepotPicker(Window *parent, bool extended_depot)
{
new BuildRailDepotWindow(_build_depot_desc, parent);
new BuildRailDepotWindow(extended_depot ? _build_extended_depot_desc : _build_depot_desc, parent, extended_depot);
}
class WaypointPickerCallbacks : public PickerCallbacksNewGRFClass<StationClass> {

View File

@ -23,7 +23,7 @@
enum RailTileType {
RAIL_TILE_NORMAL = 0, ///< Normal rail tile without signals
RAIL_TILE_SIGNALS = 1, ///< Normal rail tile with signals
RAIL_TILE_DEPOT = 3, ///< Depot (one entrance)
RAIL_TILE_DEPOT = 2, ///< Depot
};
/**
@ -107,6 +107,50 @@ debug_inline static bool IsRailDepotTile(Tile t)
return IsTileType(t, MP_RAILWAY) && IsRailDepot(t);
}
/**
* Is this rail depot tile an extended depot?
* @param t the tile to get the information from
* @pre IsRailDepotTile(t)
* @return true if and only if the tile is an extended rail depot
*/
static inline bool IsExtendedRailDepot(Tile t)
{
assert(IsRailDepotTile(t));
return HasBit(t.m5(), 5);
}
/**
* Is this rail tile a standard rail depot?
* @param t the tile to get the information from
* @pre IsTileType(t, MP_RAILWAY)
* @return true if and only if the tile is a standard rail depot
*/
static inline bool IsStandardRailDepot(Tile t)
{
assert(IsTileType(t, MP_RAILWAY));
return IsRailDepot(t) && !IsExtendedRailDepot(t);
}
/**
* Is this tile a standard rail depot?
* @param t the tile to get the information from
* @return true if and only if the tile is a standard rail depot
*/
static inline bool IsStandardRailDepotTile(TileIndex t)
{
return IsTileType(t, MP_RAILWAY) && IsStandardRailDepot(t);
}
/**
* Is this tile rail tile and an extended rail depot?
* @param t the tile to get the information from
* @return true if and only if the tile is an extended rail depot
*/
static inline bool IsExtendedRailDepotTile(TileIndex t)
{
return IsTileType(t, MP_RAILWAY) && IsRailDepotTile(t) && IsExtendedRailDepot(t);
}
/**
* Gets the rail type of the given tile
* @param t the tile to get the rail type from

View File

@ -244,6 +244,19 @@ inline bool HasPowerOnRoad(RoadType enginetype, RoadType tiletype)
return HasBit(GetRoadTypeInfo(enginetype)->powered_roadtypes, tiletype);
}
/**
* Checks if an engine with a given \a enginetype is powered on \a road_types.
* This would normally just be an equality check,
* but for electrified roads (which also support non-electric vehicles).
* @param enginetype The RoadType of the engine we are considering.
* @param rail_types The RoadTypes we are considering.
* @return Whether the engine got power on this tile.
*/
static inline bool HasPowerOnRoads(RoadType enginetype, RoadTypes road_types)
{
return (GetRoadTypeInfo(enginetype)->powered_roadtypes & road_types) != 0;
}
/**
* Returns the cost of building the specified roadtype.
* @param roadtype The roadtype being built.

View File

@ -42,6 +42,7 @@
#include "road_cmd.h"
#include "landscape_cmd.h"
#include "rail_cmd.h"
#include "platform_func.h"
#include "table/strings.h"
#include "table/roadtypes.h"
@ -313,6 +314,22 @@ CommandCost CheckAllowRemoveRoad(TileIndex tile, RoadBits remove, Owner owner, R
return CommandCost();
}
void UpdateRoadDepotDir(TileIndex tile)
{
assert(IsExtendedRoadDepot(tile));
RoadBits rb = GetAllRoadBits(tile);
DiagDirection dir = DIAGDIR_NE;
if (rb & ROAD_SE) {
dir = DIAGDIR_SE;
} else if (rb & ROAD_SW) {
dir = DIAGDIR_SW;
} else if (rb & ROAD_NW) {
dir = DIAGDIR_NW;
} else {
assert(rb & ROAD_NE);
}
SetRoadDepotDirection(tile, dir);
}
/**
* Delete a piece of road.
@ -524,9 +541,33 @@ static CommandCost RemoveRoad(TileIndex tile, DoCommandFlag flags, RoadBits piec
return CommandCost(EXPENSES_CONSTRUCTION, RoadClearCost(existing_rt) * 2);
}
default:
case ROAD_TILE_DEPOT:
return CMD_ERROR;
case ROAD_TILE_DEPOT: {
/* Depot must have at least one road bit. */
RoadBits new_rb = (GetRoadBits(tile, rtt) & ~pieces);
if (new_rb == ROAD_NONE && GetRoadType(tile, OtherRoadTramType(rtt)) == INVALID_ROADTYPE) return CMD_ERROR;
uint num_removed_bits = CountBits(pieces & GetRoadBits(tile, rtt));
CommandCost cost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_ROAD] * num_removed_bits);
if ((flags & DC_EXEC) && num_removed_bits != 0) {
SetRoadBits(tile, new_rb, rtt);
Company *c = Company::GetIfValid(GetTileOwner(tile));
c->infrastructure.road[GetRoadType(tile, rtt)] -= num_removed_bits;
DirtyCompanyInfrastructureWindows(c->index);
if (new_rb == ROAD_NONE) {
SetRoadType(tile, rtt, INVALID_ROADTYPE);
Depot::GetByTile(tile)->AfterAddRemove(TileArea(tile), false);
}
if (IsExtendedRoadDepot(tile)) UpdateRoadDepotDir(tile);
MarkTileDirtyByTile(tile);
}
return cost;
}
default: NOT_REACHED();
}
}
@ -711,8 +752,34 @@ CommandCost CmdBuildRoad(DoCommandFlag flags, TileIndex tile, RoadBits pieces, R
if (HasTileRoadType(tile, rtt)) return_cmd_error(STR_ERROR_ALREADY_BUILT);
break;
case ROAD_TILE_DEPOT:
if ((GetAnyRoadBits(tile, rtt) & pieces) == pieces) return_cmd_error(STR_ERROR_ALREADY_BUILT);
case ROAD_TILE_DEPOT: {
Owner owner = GetRoadOwner(tile, rtt);
if (owner != OWNER_NONE) {
CommandCost ret = CheckOwnership(owner, tile);
if (ret.Failed()) return ret;
}
if (IsExtendedRoadDepot(tile)) {
RoadType tile_rt = GetRoadType(tile, rtt);
if (tile_rt != INVALID_ROADTYPE && rt != tile_rt) return CMD_ERROR;
Axis axis = DiagDirToAxis(GetRoadDepotDirection(tile));
RoadBits rb = (axis == AXIS_X ? ROAD_X : ROAD_Y) & pieces;
if (rb != pieces) return CMD_ERROR;
existing = GetRoadBits(tile, rtt);
if ((rb & ~existing) == ROAD_NONE) return_cmd_error(STR_ERROR_ALREADY_BUILT);
cost.AddCost(_price[PR_BUILD_DEPOT_ROAD] * CountBits(rb & ~existing));
break;
} else if (GetRoadBits(tile, OtherRoadTramType(rtt)) == pieces) {
/* Check if we can add a new road/tram type if none present. */
if (HasTileRoadType(tile, rtt)) {
return_cmd_error(STR_ERROR_ALREADY_BUILT);
}
/* We may add a new road type. */
cost.AddCost(_price[PR_BUILD_DEPOT_ROAD]);
break;
}
}
goto do_clear;
default: NOT_REACHED();
@ -886,7 +953,18 @@ do_clear:;
switch (GetTileType(tile)) {
case MP_ROAD: {
RoadTileType rttype = GetRoadTileType(tile);
if (existing == ROAD_NONE || rttype == ROAD_TILE_CROSSING) {
if (rttype == ROAD_TILE_DEPOT) {
SetRoadType(tile, rtt, rt);
if (IsExtendedRoadDepot(tile)) {
SetRoadBits(tile, pieces | GetRoadBits(tile, rtt), rtt);
/* Do not add or remove to company infrastructure for depots. Already acounted for. */
UpdateRoadDepotDir(tile);
} else {
SetRoadBits(tile, GetRoadBits(tile, OtherRoadTramType(rtt)), rtt);
}
Depot::GetByTile(tile)->AfterAddRemove(TileArea(tile), true);
break;
} else if (existing == ROAD_NONE || rttype == ROAD_TILE_CROSSING) {
SetRoadType(tile, rtt, rt);
SetRoadOwner(tile, rtt, company);
if (rtt == RTT_ROAD) SetTownIndex(tile, town_id);
@ -1140,67 +1218,184 @@ std::tuple<CommandCost, Money> CmdRemoveLongRoad(DoCommandFlag flags, TileIndex
* @param tile tile where to build the depot
* @param flags operation to perform
* @param rt road type
* @param dir entrance direction
* @param orig_dir entrance direction
* @param adjacent allow adjacent depots
* @param extended build extended depot
* @param half_start build only one trackbit in start tile if building an extended depot
* @param half_end build only one trackbit in end tile if building an extended depot
* @param depot_id depot to join to
* @param end_tile end tile of the depot to be built
* @return the cost of this operation or an error
*
* @todo When checking for the tile slope,
* distinguish between "Flat land required" and "land sloped in wrong direction"
*/
CommandCost CmdBuildRoadDepot(DoCommandFlag flags, TileIndex tile, RoadType rt, DiagDirection dir)
CommandCost CmdBuildRoadDepot(DoCommandFlag flags, TileIndex tile, RoadType rt, DiagDirection orig_dir, bool adjacent, bool extended, bool half_start, bool half_end, DepotID join_to, TileIndex end_tile)
{
if (!ValParamRoadType(rt) || !IsValidDiagDirection(dir)) return CMD_ERROR;
if (!ValParamRoadType(rt) || !IsValidDiagDirection(orig_dir)) return CMD_ERROR;
if (Company::IsValidHumanID(_current_company) && !HasBit(_settings_game.depot.road_depot_types, extended)) {
return_cmd_error(STR_ERROR_DEPOT_TYPE_NOT_AVAILABLE);
}
TileArea ta(tile, end_tile);
assert(extended || ta.w == 1 || ta.h == 1);
Axis axis = DiagDirToAxis(orig_dir);
RoadTramType rtt = GetRoadTramType(rt);
uint start_coord = 0;
uint end_coord = 0;
DiagDirection dir = orig_dir;
if (extended) {
start_coord = axis == AXIS_X ? TileX(tile) : TileY(tile);
end_coord = axis == AXIS_X ? TileX(end_tile) : TileY(end_tile);
dir = AxisToDiagDir(axis);
/* Swap direction, also the half-tile drag var (bit 0 and 1) */
if (start_coord > end_coord || start_coord == end_coord) {
dir = ReverseDiagDir(dir);
half_start = !half_start;
half_end = !half_end;
}
}
/* Create a new depot or find a depot to join to. */
Depot *depot = nullptr;
CommandCost ret = FindJoiningDepot(ta, VEH_ROAD, join_to, depot, adjacent, flags);
if (ret.Failed()) return ret;
uint8_t num_new_depot_tiles = 0;
uint8_t num_overbuilt_depot_tiles = 0;
CommandCost cost(EXPENSES_CONSTRUCTION);
int allowed_z = -1;
uint num_new_pieces = 0;
uint invalid_dirs = extended ? 5 << axis : 1 << dir;
for (Tile t : ta) {
RoadBits rb = ROAD_NONE;
if (IsBridgeAbove(t)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
auto [tileh, z] = GetTileSlopeZ(t);
int flat_z = z + GetSlopeMaxZ(tileh);
Slope tileh = GetTileSlope(tile);
if (tileh != SLOPE_FLAT) {
if (!_settings_game.construction.build_on_slopes || !CanBuildDepotByTileh(dir, tileh)) {
return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED);
}
if (extended && IsSteepSlope(tileh)) return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED);
/* Forbid building if the tile faces a slope in a invalid direction. */
for (DiagDirection dir = DIAGDIR_BEGIN; dir != DIAGDIR_END; dir++) {
if (HasBit(invalid_dirs, dir) && !CanBuildDepotByTileh(dir, tileh)) {
return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED);
}
}
cost.AddCost(_price[PR_BUILD_FOUNDATION]);
}
/* Allow the user to rotate the depot instead of having to destroy it and build it again */
bool rotate_existing_depot = false;
if (IsRoadDepotTile(tile) && (HasRoadTypeTram(tile) ? rt == GetRoadTypeTram(tile) : rt == GetRoadTypeRoad(tile)))
{
CommandCost ret = CheckTileOwnership(tile);
if (ret.Failed()) return ret;
if (dir == GetRoadDepotDirection(tile)) return CommandCost();
ret = EnsureNoVehicleOnGround(tile);
if (ret.Failed()) return ret;
rotate_existing_depot = true;
/* The level of this tile must be equal to allowed_z. */
if (allowed_z < 0) {
/* First tile. */
allowed_z = flat_z;
} else if (allowed_z != flat_z) {
return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED);
}
if (!rotate_existing_depot) {
cost.AddCost(Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile));
/* Check whether this is a compatible depot tile. */
if (IsRoadDepotTile(t) && GetDepotIndex(t) == join_to && rt == GetRoadType(t, rtt)) {
if (extended) {
if (IsExtendedRoadDepotTile(t) &&
axis == DiagDirToAxis(GetRoadDepotDirection(t))) {
/* Already exists and has the right axis: Check new roadbits. */
goto rb_for_extended_depot;
}
} else {
if (!IsExtendedRoadDepotTile(t)) {
if (dir == GetRoadDepotDirection(t)) continue;
/* If another roadtype exists (road/tram), depot cannot be rotated. */
if (GetRoadTypeRoad(t) != INVALID_ROADTYPE && GetRoadTypeTram(t) != INVALID_ROADTYPE) {
return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
}
/* Overbuild the depot tile and change its exit direction. */
num_overbuilt_depot_tiles++;
if (flags & DC_EXEC) {
rb = DiagDirToRoadBits(orig_dir);
SetRoadBits(t, rb, rtt);
SetRoadDepotDirection(t, orig_dir);
MarkTileDirtyByTile(t);
}
continue;
}
}
}
cost.AddCost(Command<CMD_LANDSCAPE_CLEAR>::Do(flags, t));
if (cost.Failed()) return cost;
if (IsBridgeAbove(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
if (!Depot::CanAllocateItem()) return CMD_ERROR;
/* Check which road bits to build. */
if (extended) {
rb_for_extended_depot:
uint axis_coord = axis == AXIS_X ? TileX(t) : TileY(t);
/* Road parts only have to be built at the start tile or at the end tile. */
if (!half_end && axis_coord == end_coord) {
rb = DiagDirToRoadBits(ReverseDiagDir(dir));
}
if (half_start && axis_coord == start_coord) {
rb = DiagDirToRoadBits(dir);
}
if (rb == ROAD_NONE) {
rb = AxisToRoadBits(axis);
}
assert(rb != ROAD_NONE);
if (IsRoadDepotTile(t)) {
RoadType old_rt = GetRoadType(t, rtt);
if (old_rt != INVALID_ROADTYPE && old_rt != rt) return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
RoadBits old_rb = GetAllRoadBits(t);
if ((old_rb & AxisToRoadBits(axis)) != old_rb) return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
old_rb = GetRoadBits(t, rtt);
if ((rb & ~old_rb) == ROAD_NONE) return_cmd_error(STR_ERROR_ALREADY_BUILT);
num_new_pieces += CountBits(rb & ~old_rb);
num_overbuilt_depot_tiles++;
rb |= old_rb;
} else {
num_new_pieces += CountBits(rb);
num_new_depot_tiles++;
}
} else {
rb = DiagDirToRoadBits(orig_dir);
num_new_pieces += 1;
num_new_depot_tiles++;
}
if (flags & DC_EXEC) {
if (rotate_existing_depot) {
SetRoadDepotExitDirection(tile, dir);
} else {
Depot *dep = new Depot(tile);
dep->build_date = TimerGameCalendar::date;
MakeRoadDepot(tile, _current_company, dep->index, dir, rt);
MakeDefaultName(dep);
/* A road depot has two road bits. */
UpdateCompanyRoadInfrastructure(rt, _current_company, ROAD_DEPOT_TRACKBIT_FACTOR);
if (!IsRoadDepotTile(t)) MakeRoadDepot(t, _current_company, depot->index, orig_dir, rt);
if (GetRoadType(t, rtt) == INVALID_ROADTYPE) SetRoadType(t, rtt, rt);
SetRoadBits(t, rb, rtt);
if (extended) {
SB(t.m5(), 5, 1, true);
UpdateRoadDepotDir(t);
}
MarkTileDirtyByTile(tile);
MarkTileDirtyByTile(t);
}
}
if (num_new_depot_tiles + num_overbuilt_depot_tiles == 0) return CommandCost();
cost.AddCost(_price[PR_BUILD_DEPOT_ROAD] * (num_new_depot_tiles + num_overbuilt_depot_tiles));
if (flags & DC_EXEC) {
UpdateCompanyRoadInfrastructure(rt, _current_company, num_new_pieces);
depot->AfterAddRemove(ta, true);
if (join_to == NEW_DEPOT) MakeDefaultName(depot);
}
cost.AddCost(_price[PR_BUILD_DEPOT_ROAD]);
return cost;
}
@ -1214,21 +1409,27 @@ static CommandCost RemoveRoadDepot(TileIndex tile, DoCommandFlag flags)
CommandCost ret = EnsureNoVehicleOnGround(tile);
if (ret.Failed()) return ret;
if (flags & DC_EXEC) {
Company *c = Company::GetIfValid(GetTileOwner(tile));
if (c != nullptr) {
/* A road depot has two road bits. */
CommandCost cost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_ROAD]);
RoadType rt = GetRoadTypeRoad(tile);
if (rt == INVALID_ROADTYPE) rt = GetRoadTypeTram(tile);
c->infrastructure.road[rt] -= ROAD_DEPOT_TRACKBIT_FACTOR;
RoadType tt = GetRoadTypeTram(tile);
if (rt != INVALID_ROADTYPE) cost.AddCost(_price[PR_CLEAR_DEPOT_ROAD]);
if (tt != INVALID_ROADTYPE) cost.AddCost(_price[PR_CLEAR_DEPOT_ROAD]);
if (flags & DC_EXEC) {
Depot *depot = Depot::GetByTile(tile);
Company *c = Company::GetIfValid(depot->owner);
if (c != nullptr) {
/* A road depot has two road types. */
if (rt != INVALID_ROADTYPE) c->infrastructure.road[rt] -= CountBits(GetRoadBits(tile, RTT_ROAD));
if (tt != INVALID_ROADTYPE) c->infrastructure.road[tt] -= CountBits(GetRoadBits(tile, RTT_TRAM));
DirtyCompanyInfrastructureWindows(c->index);
}
delete Depot::GetByTile(tile);
DoClearSquare(tile);
depot->AfterAddRemove(TileArea(tile), false);
}
return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_ROAD]);
return cost;
}
static CommandCost ClearTile_Road(TileIndex tile, DoCommandFlag flags)
@ -1454,11 +1655,16 @@ void DrawRoadCatenary(const TileInfo *ti)
RoadBits tram = ROAD_NONE;
if (IsTileType(ti->tile, MP_ROAD)) {
if (IsNormalRoad(ti->tile)) {
switch (GetRoadTileType(ti->tile)) {
case ROAD_TILE_NORMAL:
case ROAD_TILE_DEPOT:
road = GetRoadBits(ti->tile, RTT_ROAD);
tram = GetRoadBits(ti->tile, RTT_TRAM);
} else if (IsLevelCrossing(ti->tile)) {
break;
case ROAD_TILE_CROSSING:
tram = road = (GetCrossingRailAxis(ti->tile) == AXIS_Y ? ROAD_X : ROAD_Y);
break;
default: NOT_REACHED();
}
} else if (IsTileType(ti->tile, MP_STATION)) {
if (IsAnyRoadStop(ti->tile)) {
@ -1821,17 +2027,30 @@ static void DrawTile_Road(TileInfo *ti)
case ROAD_TILE_DEPOT: {
if (ti->tileh != SLOPE_FLAT) DrawFoundation(ti, FOUNDATION_LEVELED);
PaletteID palette = COMPANY_SPRITE_COLOUR(GetTileOwner(ti->tile));
RoadType road_rt = GetRoadTypeRoad(ti->tile);
RoadType tram_rt = GetRoadTypeTram(ti->tile);
const RoadTypeInfo *rti = GetRoadTypeInfo(road_rt == INVALID_ROADTYPE ? tram_rt : road_rt);
assert(road_rt != INVALID_ROADTYPE || tram_rt != INVALID_ROADTYPE);
const RoadTypeInfo *road_rti = road_rt != INVALID_ROADTYPE ? GetRoadTypeInfo(road_rt) : nullptr;
const RoadTypeInfo *tram_rti = tram_rt != INVALID_ROADTYPE ? GetRoadTypeInfo(tram_rt) : nullptr;
const RoadTypeInfo *main_rti = tram_rti != nullptr ? tram_rti : road_rti;
int relocation = GetCustomRoadSprite(rti, ti->tile, ROTSG_DEPOT);
DiagDirection dir = GetRoadDepotDirection(ti->tile);
uint road_offset = GetRoadSpriteOffset(SLOPE_FLAT, GetRoadBits(ti->tile, RTT_ROAD));
uint tram_offset = GetRoadSpriteOffset(SLOPE_FLAT, GetRoadBits(ti->tile, RTT_TRAM));
PaletteID pal = PAL_NONE;
const DrawTileSprites *dts = &_road_depot[dir];
SpriteID image = SPR_ROAD_Y + (road_rti == nullptr ? tram_offset : road_offset) - 19;
DrawGroundSprite(image, pal);
DrawRoadOverlays(ti, pal, road_rti, tram_rti, road_offset, tram_offset);
int relocation = GetCustomRoadSprite(main_rti, ti->tile, ROTSG_DEPOT);
bool default_gfx = relocation == 0;
if (default_gfx) {
if (HasBit(rti->flags, ROTF_CATENARY)) {
if (_loaded_newgrf_features.tram == TRAMWAY_REPLACE_DEPOT_WITH_TRACK && road_rt == INVALID_ROADTYPE && !rti->UsesOverlay()) {
if (HasBit(main_rti->flags, ROTF_CATENARY)) {
if (_loaded_newgrf_features.tram == TRAMWAY_REPLACE_DEPOT_WITH_TRACK && road_rt == INVALID_ROADTYPE && !main_rti->UsesOverlay()) {
/* Sprites with track only work for default tram */
relocation = SPR_TRAMWAY_DEPOT_WITH_TRACK - SPR_ROAD_DEPOT;
default_gfx = false;
@ -1844,21 +2063,11 @@ static void DrawTile_Road(TileInfo *ti)
relocation -= SPR_ROAD_DEPOT;
}
DiagDirection dir = GetRoadDepotDirection(ti->tile);
const DrawTileSprites *dts = &_road_depot[dir];
DrawGroundSprite(dts->ground.sprite, PAL_NONE);
/* Draw road, tram catenary */
DrawRoadCatenary(ti);
if (default_gfx) {
uint offset = GetRoadSpriteOffset(SLOPE_FLAT, DiagDirToRoadBits(dir));
if (rti->UsesOverlay()) {
SpriteID ground = GetCustomRoadSprite(rti, ti->tile, ROTSG_OVERLAY);
if (ground != 0) DrawGroundSprite(ground + offset, PAL_NONE);
} else if (road_rt == INVALID_ROADTYPE) {
DrawGroundSprite(SPR_TRAMWAY_OVERLAY + offset, PAL_NONE);
}
}
DrawRailTileSeq(ti, dts, TO_BUILDINGS, relocation, 0, COMPANY_SPRITE_COLOUR(GetTileOwner(ti->tile)));
DrawRailTileSeq(ti, dts, TO_BUILDINGS, relocation, 0, palette);
break;
}
}
@ -1876,7 +2085,9 @@ void DrawRoadDepotSprite(int x, int y, DiagDirection dir, RoadType rt)
{
PaletteID palette = COMPANY_SPRITE_COLOUR(_local_company);
RoadTramType rtt = GetRoadTramType(rt);
const RoadTypeInfo *rti = GetRoadTypeInfo(rt);
uint road_offset = GetRoadSpriteOffset(SLOPE_FLAT, DiagDirToRoadBits(dir));
int relocation = GetCustomRoadSprite(rti, INVALID_TILE, ROTSG_DEPOT);
bool default_gfx = relocation == 0;
if (default_gfx) {
@ -1897,6 +2108,18 @@ void DrawRoadDepotSprite(int x, int y, DiagDirection dir, RoadType rt)
const DrawTileSprites *dts = &_road_depot[dir];
DrawSprite(dts->ground.sprite, PAL_NONE, x, y);
if (rti->UsesOverlay()) {
SpriteID ground = GetCustomRoadSprite(rti, INVALID_TILE, ROTSG_GROUND);
DrawSprite(ground + road_offset, PAL_NONE, x, y);
ground = GetCustomRoadSprite(rti, INVALID_TILE, ROTSG_OVERLAY);
if (ground != 0) DrawSprite(ground + road_offset, PAL_NONE, x, y);
} else if (rtt == RTT_TRAM) {
DrawSprite(SPR_TRAMWAY_TRAM + road_offset, PAL_NONE, x, y);
DrawSprite(SPR_TRAMWAY_OVERLAY + road_offset, PAL_NONE, x, y);
} else {
DrawSprite(SPR_ROAD_Y + road_offset - 19, PAL_NONE, x, y);
}
if (default_gfx) {
uint offset = GetRoadSpriteOffset(SLOPE_FLAT, DiagDirToRoadBits(dir));
if (rti->UsesOverlay()) {
@ -2081,7 +2304,7 @@ static bool ClickTile_Road(TileIndex tile)
{
if (!IsRoadDepot(tile)) return false;
ShowDepotWindow(tile, VEH_ROAD);
ShowDepotWindow(GetDepotIndex(tile));
return true;
}
@ -2153,11 +2376,11 @@ static TrackStatus GetTileTrackStatus_Road(TileIndex tile, TransportType mode, u
default:
case ROAD_TILE_DEPOT: {
DiagDirection dir = GetRoadDepotDirection(tile);
Axis axis = DiagDirToAxis(GetRoadDepotDirection(tile));
if (side != INVALID_DIAGDIR && side != dir) break;
if (side != INVALID_DIAGDIR && axis != DiagDirToAxis(side)) break;
trackdirbits = TrackBitsToTrackdirBits(DiagDirToDiagTrackBits(dir));
trackdirbits = TrackBitsToTrackdirBits(AxisToTrackBits(axis));
break;
}
}
@ -2214,7 +2437,7 @@ static void GetTileDesc_Road(TileIndex tile, TileDesc *td)
}
case ROAD_TILE_DEPOT:
td->str = STR_LAI_ROAD_DESCRIPTION_ROAD_VEHICLE_DEPOT;
td->str = IsExtendedDepot(tile) ? STR_LAI_ROAD_DESCRIPTION_ROAD_VEHICLE_DEPOT_EXTENDED : STR_LAI_ROAD_DESCRIPTION_ROAD_VEHICLE_DEPOT;
td->build_date = Depot::GetByTile(tile)->build_date;
break;
@ -2254,12 +2477,40 @@ static const uint8_t _roadveh_enter_depot_dir[4] = {
TRACKDIR_X_SW, TRACKDIR_Y_NW, TRACKDIR_X_NE, TRACKDIR_Y_SE
};
static VehicleEnterTileStatus VehicleEnter_Road(Vehicle *v, TileIndex tile, int, int)
static VehicleEnterTileStatus VehicleEnter_Road(Vehicle *v, TileIndex tile, int x, int y)
{
switch (GetRoadTileType(tile)) {
case ROAD_TILE_DEPOT: {
if (v->type != VEH_ROAD) break;
if (GetRoadTileType(tile) != ROAD_TILE_DEPOT || v->type != VEH_ROAD) return VETSB_CONTINUE;
if (IsExtendedRoadDepot(tile)) {
v = v->First();
if (!IsExtendedRoadDepotTile(v->tile)) return VETSB_CONTINUE;
DepotID depot_id = GetDepotIndex(v->tile);
if (!v->current_order.IsType(OT_GOTO_DEPOT) ||
v->current_order.GetDestination() != depot_id) {
return VETSB_CONTINUE;
}
for (Vehicle *u = v; u != nullptr; u = u->Next()) {
if (!IsExtendedRoadDepotTile(u->tile) || GetDepotIndex(u->tile) != depot_id) return VETSB_CONTINUE;
if (!IsDiagonalDirection(u->direction)) return VETSB_CONTINUE;
if (DiagDirToAxis(DirToDiagDir(u->direction)) !=
DiagDirToAxis(GetRoadDepotDirection(v->tile))) {
return VETSB_CONTINUE;
}
}
/* Stop position on platform is half the front vehicle length of the road vehicle. */
int stop_pos = RoadVehicle::From(v)->gcache.cached_veh_length / 2;
DiagDirection dir = DirToDiagDir(v->direction);
int depot_ahead = (GetPlatformLength(tile, dir, GetRoadTramType(RoadVehicle::From(v)->roadtype)) - 1) * TILE_SIZE;
if (depot_ahead > stop_pos) return VETSB_CONTINUE;
x = v->x_pos & 0xF;
y = v->y_pos & 0xF;
if (DiagDirToAxis(dir) != AXIS_X) Swap(x, y);
if (dir == DIAGDIR_SE || dir == DIAGDIR_SW) x = TILE_SIZE - x;
if (abs(stop_pos - x) <= 1) return VETSB_ENTERED_DEPOT_PLATFORM;
} else {
RoadVehicle *rv = RoadVehicle::From(v);
if (rv->frame == RVC_DEPOT_STOP_FRAME &&
_roadveh_enter_depot_dir[GetRoadDepotDirection(tile)] == rv->state) {
@ -2268,15 +2519,11 @@ static VehicleEnterTileStatus VehicleEnter_Road(Vehicle *v, TileIndex tile, int,
rv->direction = ReverseDir(rv->direction);
if (rv->Next() == nullptr) VehicleEnterDepot(rv->First());
rv->tile = tile;
InvalidateWindowData(WC_VEHICLE_DEPOT, rv->tile);
InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(rv->tile));
return VETSB_ENTERED_WORMHOLE;
}
break;
}
default: break;
}
return VETSB_CONTINUE;
}
@ -2288,14 +2535,14 @@ static void ChangeTileOwner_Road(TileIndex tile, Owner old_owner, Owner new_owne
if (new_owner == INVALID_OWNER) {
Command<CMD_LANDSCAPE_CLEAR>::Do(DC_EXEC | DC_BANKRUPT, tile);
} else {
/* A road depot has two road bits. No need to dirty windows here, we'll redraw the whole screen anyway. */
RoadType rt = GetRoadTypeRoad(tile);
if (rt == INVALID_ROADTYPE) rt = GetRoadTypeTram(tile);
Company::Get(old_owner)->infrastructure.road[rt] -= 2;
Company::Get(new_owner)->infrastructure.road[rt] += 2;
SetTileOwner(tile, new_owner);
for (RoadTramType rtt : _roadtramtypes) {
RoadType rt = GetRoadTypeRoad(tile);
if (rt != INVALID_ROADTYPE) {
uint pieces = CountBits(GetRoadBits(tile, rtt));
Company::Get(old_owner)->infrastructure.road[rt] -= pieces;
Company::Get(new_owner)->infrastructure.road[rt] += pieces;
}
if (GetRoadOwner(tile, rtt) == old_owner) {
SetRoadOwner(tile, rtt, new_owner);
}
@ -2344,7 +2591,10 @@ static CommandCost TerraformTile_Road(TileIndex tile, DoCommandFlag flags, int z
break;
case ROAD_TILE_DEPOT:
if (AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, GetRoadDepotDirection(tile))) return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
if (AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, GetRoadDepotDirection(tile)) &&
(!IsExtendedRoadDepot(tile) || AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, ReverseDiagDir(GetRoadDepotDirection(tile))))) {
return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
}
break;
case ROAD_TILE_NORMAL: {
@ -2521,8 +2771,6 @@ CommandCost CmdConvertRoad(DoCommandFlag flags, TileIndex tile, TileIndex area_s
uint num_pieces = CountBits(GetAnyRoadBits(tile, rtt));
if (tt == MP_STATION && IsBayRoadStopTile(tile)) {
num_pieces *= ROAD_STOP_TRACKBIT_FACTOR;
} else if (tt == MP_ROAD && IsRoadDepot(tile)) {
num_pieces *= ROAD_DEPOT_TRACKBIT_FACTOR;
}
found_convertible_road = true;
@ -2543,8 +2791,8 @@ CommandCost CmdConvertRoad(DoCommandFlag flags, TileIndex tile, TileIndex area_s
if (IsRoadDepotTile(tile)) {
/* Update build vehicle window related to this depot */
InvalidateWindowData(WC_VEHICLE_DEPOT, tile);
InvalidateWindowData(WC_BUILD_VEHICLE, tile);
InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(tile));
InvalidateWindowData(WC_BUILD_VEHICLE, GetDepotIndex(tile));
}
}
} else {

View File

@ -13,6 +13,7 @@
#include "direction_type.h"
#include "road_type.h"
#include "command_type.h"
#include "depot_type.h"
enum RoadStopClassID : uint16_t;
@ -22,7 +23,7 @@ void UpdateNearestTownForRoadTiles(bool invalidate);
CommandCost CmdBuildLongRoad(DoCommandFlag flags, TileIndex end_tile, TileIndex start_tile, RoadType rt, Axis axis, DisallowedRoadDirections drd, bool start_half, bool end_half, bool is_ai);
std::tuple<CommandCost, Money> CmdRemoveLongRoad(DoCommandFlag flags, TileIndex end_tile, TileIndex start_tile, RoadType rt, Axis axis, bool start_half, bool end_half);
CommandCost CmdBuildRoad(DoCommandFlag flags, TileIndex tile, RoadBits pieces, RoadType rt, DisallowedRoadDirections toggle_drd, TownID town_id);
CommandCost CmdBuildRoadDepot(DoCommandFlag flags, TileIndex tile, RoadType rt, DiagDirection dir);
CommandCost CmdBuildRoadDepot(DoCommandFlag flags, TileIndex tile, RoadType rt, DiagDirection dir, bool adjacent, bool extended, bool half_start, bool half_end, DepotID join_to, TileIndex end_tile);
CommandCost CmdConvertRoad(DoCommandFlag flags, TileIndex tile, TileIndex area_start, RoadType to_type);
DEF_CMD_TRAIT(CMD_BUILD_LONG_ROAD, CmdBuildLongRoad, CMD_AUTO | CMD_NO_WATER | CMD_DEITY, CMDT_LANDSCAPE_CONSTRUCTION)
@ -33,7 +34,7 @@ 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 CcRoadDepot(Commands cmd, const CommandCost &result, TileIndex start_tile, RoadType rt, DiagDirection dir, bool adjacent, bool extended, bool half_start, bool half_end, DepotID join_to, TileIndex end_tile);
void CcRoadStop(Commands cmd, const CommandCost &result, TileIndex tile, uint8_t width, uint8_t length, RoadStopType, bool is_drive_through, DiagDirection dir, RoadType, RoadStopClassID spec_class, uint16_t spec_index, StationID, bool);
#endif /* ROAD_CMD_H */

View File

@ -43,6 +43,7 @@
#include "picker_gui.h"
#include "timer/timer.h"
#include "timer/timer_game_calendar.h"
#include "depot_func.h"
#include "widgets/road_widget.h"
@ -51,7 +52,7 @@
#include "safeguards.h"
static void ShowRVStationPicker(Window *parent, RoadStopType rs);
static void ShowRoadDepotPicker(Window *parent);
static void ShowRoadDepotPicker(Window *parent, bool extended_depot);
static void ShowBuildRoadWaypointPicker(Window *parent);
static bool _remove_button_clicked;
@ -170,13 +171,46 @@ void ConnectRoadToStructure(TileIndex tile, DiagDirection direction)
}
}
void CcRoadDepot(Commands, const CommandCost &result, TileIndex tile, RoadType, DiagDirection dir)
void CcRoadDepot(Commands , const CommandCost &result, TileIndex start_tile, RoadType, DiagDirection orig_dir, bool, bool extended, bool half_start, bool half_end, DepotID, TileIndex end_tile)
{
if (result.Failed()) return;
if (_settings_client.sound.confirm) SndPlayTileFx(SND_1F_CONSTRUCTION_OTHER, tile);
if (_settings_client.sound.confirm) SndPlayTileFx(SND_1F_CONSTRUCTION_OTHER, start_tile);
if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace();
Axis axis = DiagDirToAxis(orig_dir);
uint start_coord;
uint end_coord;
bool build_start = true;
bool build_end = false;
DiagDirection dir = orig_dir;
if (extended) {
build_start = half_end;
build_end = !half_start;
start_coord = axis == AXIS_X ? TileX(start_tile) : TileY(start_tile);
end_coord = axis == AXIS_X ? TileX(end_tile) : TileY(end_tile);
dir = AxisToDiagDir(axis);
/* Swap direction, also the half-tile drag var (bit 0 and 1) */
if (start_coord > end_coord || start_coord == end_coord) {
dir = ReverseDiagDir(dir);
build_start = !build_start;
build_end = !build_end;
}
}
TileArea ta(start_tile, end_tile);
for (TileIndex tile : ta) {
if (build_start && !ta.Contains(tile + TileOffsByDiagDir(dir))) {
ConnectRoadToStructure(tile, dir);
}
if (build_end && !ta.Contains(tile + TileOffsByDiagDir(ReverseDiagDir(dir)))) {
ConnectRoadToStructure(tile, ReverseDiagDir(dir));
}
}
}
/**
@ -368,6 +402,13 @@ struct BuildRoadToolbarWindow : Window {
{
if (_game_mode == GM_NORMAL && (this->IsWidgetLowered(WID_ROT_BUS_STATION) || this->IsWidgetLowered(WID_ROT_TRUCK_STATION))) SetViewportCatchmentStation(nullptr, true);
if (_settings_client.gui.link_terraform_toolbar) CloseWindowById(WC_SCEN_LAND_GEN, 0, false);
if (_game_mode == GM_NORMAL &&
((this->HasWidget(WID_ROT_DEPOT) && this->IsWidgetLowered(WID_ROT_DEPOT)) ||
(this->HasWidget(WID_ROT_EXTENDED_DEPOT) && this->IsWidgetLowered(WID_ROT_EXTENDED_DEPOT)))) {
SetViewportHighlightDepot(INVALID_DEPOT, true);
}
this->Window::Close();
}
@ -384,6 +425,7 @@ struct BuildRoadToolbarWindow : Window {
bool can_build = CanBuildVehicleInfrastructure(VEH_ROAD, rtt);
this->SetWidgetsDisabledState(!can_build,
WID_ROT_DEPOT,
WID_ROT_EXTENDED_DEPOT,
WID_ROT_BUILD_WAYPOINT,
WID_ROT_BUS_STATION,
WID_ROT_TRUCK_STATION);
@ -397,12 +439,14 @@ struct BuildRoadToolbarWindow : Window {
if (_game_mode != GM_EDITOR) {
if (!can_build) {
/* Show in the tooltip why this button is disabled. */
this->GetWidget<NWidgetCore>(WID_ROT_DEPOT)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE);
if (this->HasWidget(WID_ROT_DEPOT)) this->GetWidget<NWidgetCore>(WID_ROT_DEPOT)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE);
if (this->HasWidget(WID_ROT_EXTENDED_DEPOT)) this->GetWidget<NWidgetCore>(WID_ROT_EXTENDED_DEPOT)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE);
this->GetWidget<NWidgetCore>(WID_ROT_BUILD_WAYPOINT)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE);
this->GetWidget<NWidgetCore>(WID_ROT_BUS_STATION)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE);
this->GetWidget<NWidgetCore>(WID_ROT_TRUCK_STATION)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE);
} else {
this->GetWidget<NWidgetCore>(WID_ROT_DEPOT)->SetToolTip(rtt == RTT_ROAD ? STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_VEHICLE_DEPOT : STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAM_VEHICLE_DEPOT);
if (this->HasWidget(WID_ROT_DEPOT)) this->GetWidget<NWidgetCore>(WID_ROT_DEPOT)->SetToolTip(rtt == RTT_ROAD ? STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_VEHICLE_DEPOT : STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAM_VEHICLE_DEPOT);
if (this->HasWidget(WID_ROT_EXTENDED_DEPOT)) this->GetWidget<NWidgetCore>(WID_ROT_EXTENDED_DEPOT)->SetToolTip(rtt == RTT_ROAD ? STR_ROAD_TOOLBAR_TOOLTIP_BUILD_EXTENDED_ROAD_VEHICLE_DEPOT : STR_ROAD_TOOLBAR_TOOLTIP_BUILD_EXTENDED_TRAM_VEHICLE_DEPOT);
this->GetWidget<NWidgetCore>(WID_ROT_BUILD_WAYPOINT)->SetToolTip(rtt == RTT_ROAD ? STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_ROAD_TO_WAYPOINT : STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_TRAM_TO_WAYPOINT);
this->GetWidget<NWidgetCore>(WID_ROT_BUS_STATION)->SetToolTip(rtt == RTT_ROAD ? STR_ROAD_TOOLBAR_TOOLTIP_BUILD_BUS_STATION : STR_ROAD_TOOLBAR_TOOLTIP_BUILD_PASSENGER_TRAM_STATION);
this->GetWidget<NWidgetCore>(WID_ROT_TRUCK_STATION)->SetToolTip(rtt == RTT_ROAD ? STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRUCK_LOADING_BAY : STR_ROAD_TOOLBAR_TOOLTIP_BUILD_CARGO_TRAM_STATION);
@ -427,7 +471,8 @@ struct BuildRoadToolbarWindow : Window {
this->GetWidget<NWidgetCore>(WID_ROT_ROAD_Y)->widget_data = rti->gui_sprites.build_y_road;
this->GetWidget<NWidgetCore>(WID_ROT_AUTOROAD)->widget_data = rti->gui_sprites.auto_road;
if (_game_mode != GM_EDITOR) {
this->GetWidget<NWidgetCore>(WID_ROT_DEPOT)->widget_data = rti->gui_sprites.build_depot;
if (this->HasWidget(WID_ROT_DEPOT)) this->GetWidget<NWidgetCore>(WID_ROT_DEPOT)->widget_data = rti->gui_sprites.build_depot;
if (this->HasWidget(WID_ROT_EXTENDED_DEPOT)) this->GetWidget<NWidgetCore>(WID_ROT_EXTENDED_DEPOT)->widget_data = rti->gui_sprites.build_depot;
}
this->GetWidget<NWidgetCore>(WID_ROT_CONVERT_ROAD)->widget_data = rti->gui_sprites.convert_road;
this->GetWidget<NWidgetCore>(WID_ROT_BUILD_TUNNEL)->widget_data = rti->gui_sprites.build_tunnel;
@ -538,8 +583,9 @@ struct BuildRoadToolbarWindow : Window {
break;
case WID_ROT_DEPOT:
if (HandlePlacePushButton(this, WID_ROT_DEPOT, this->rti->cursor.depot, HT_RECT)) {
ShowRoadDepotPicker(this);
case WID_ROT_EXTENDED_DEPOT:
if (HandlePlacePushButton(this, widget, this->rti->cursor.depot, HT_RECT)) {
ShowRoadDepotPicker(this, widget == WID_ROT_EXTENDED_DEPOT);
this->last_started_action = widget;
}
break;
@ -636,9 +682,19 @@ struct BuildRoadToolbarWindow : Window {
break;
case WID_ROT_DEPOT:
Command<CMD_BUILD_ROAD_DEPOT>::Post(this->rti->strings.err_depot, CcRoadDepot,
tile, _cur_roadtype, _road_depot_orientation);
case WID_ROT_EXTENDED_DEPOT: {
CloseWindowById(WC_SELECT_DEPOT, VEH_ROAD);
_place_road_dir = DiagDirToAxis(_road_depot_orientation);
_place_road_start_half_x = (_place_road_dir == AXIS_X) && (_tile_fract_coords.x >= 8);
_place_road_start_half_y = (_place_road_dir == AXIS_Y) && (_tile_fract_coords.y >= 8);
VpSetPlaceSizingLimit(_settings_game.depot.depot_spread);
ViewportPlaceMethod vpm = VPM_X_AND_Y_LIMITED;
if (this->last_started_action == WID_ROT_DEPOT) vpm = (DiagDirToAxis(_road_depot_orientation) == AXIS_X) ? VPM_X_LIMITED : VPM_Y_LIMITED;
VpStartPlaceSizing(tile, vpm, DDSP_BUILD_DEPOT);
break;
}
case WID_ROT_BUILD_WAYPOINT:
PlaceRoad_Waypoint(tile);
@ -673,6 +729,12 @@ struct BuildRoadToolbarWindow : Window {
{
if (_game_mode != GM_EDITOR && (this->IsWidgetLowered(WID_ROT_BUS_STATION) || this->IsWidgetLowered(WID_ROT_TRUCK_STATION))) SetViewportCatchmentStation(nullptr, true);
if (_game_mode == GM_NORMAL &&
((this->HasWidget(WID_ROT_DEPOT) && this->IsWidgetLowered(WID_ROT_DEPOT)) ||
(this->HasWidget(WID_ROT_EXTENDED_DEPOT) && this->IsWidgetLowered(WID_ROT_EXTENDED_DEPOT)))) {
SetViewportHighlightDepot(INVALID_DEPOT, true);
}
this->RaiseButtons();
this->SetWidgetDisabledState(WID_ROT_REMOVE, true);
this->SetWidgetDirty(WID_ROT_REMOVE);
@ -687,6 +749,7 @@ struct BuildRoadToolbarWindow : Window {
CloseWindowById(WC_BUILD_DEPOT, TRANSPORT_ROAD);
CloseWindowById(WC_BUILD_WAYPOINT, TRANSPORT_ROAD);
CloseWindowById(WC_SELECT_STATION, 0);
CloseWindowById(WC_SELECT_DEPOT, VEH_ROAD);
CloseWindowByClass(WC_BUILD_BRIDGE);
}
@ -719,7 +782,14 @@ struct BuildRoadToolbarWindow : Window {
_place_road_dir = AXIS_Y;
_place_road_end_half = pt.y & 8;
}
break;
case DDSP_BUILD_DEPOT:
if (_place_road_dir == AXIS_X) {
_place_road_end_half = pt.x & 8;
} else {
_place_road_end_half = pt.y & 8;
}
break;
default:
@ -805,6 +875,21 @@ struct BuildRoadToolbarWindow : Window {
}
break;
case DDSP_BUILD_DEPOT: {
StringID error_string = this->rti->strings.err_depot;
bool adjacent = _ctrl_pressed;
bool extended = last_started_action == WID_ROT_EXTENDED_DEPOT;
bool half_start = _place_road_start_half_x || _place_road_start_half_y;
bool half_end = _place_road_end_half;
auto proc = [=](DepotID join_to) -> bool {
return Command<CMD_BUILD_ROAD_DEPOT>::Post(error_string, CcRoadDepot, start_tile, _cur_roadtype, _road_depot_orientation, adjacent, extended, half_start, half_end, join_to, end_tile);
};
ShowSelectDepotIfNeeded(TileArea(start_tile, end_tile), proc, VEH_ROAD);
break;
}
case DDSP_CONVERT_ROAD:
Command<CMD_CONVERT_ROAD>::Post(rti->strings.err_convert_road, CcPlaySound_CONSTRUCTION_OTHER, end_tile, start_tile, _cur_roadtype);
break;
@ -900,6 +985,27 @@ struct BuildRoadToolbarWindow : Window {
}, TramToolbarGlobalHotkeys};
};
/**
* Add the depot icons depending on availability of construction.
* @return Panel with road depot buttons.
*/
static std::unique_ptr<NWidgetBase> MakeNWidgetRoadDepot()
{
auto hor = std::make_unique<NWidgetHorizontal>();
if (HasBit(_settings_game.depot.road_depot_types, 0)) {
/* Add the widget for building standard road depots. */
hor->Add(std::make_unique<NWidgetLeaf>(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_DEPOT, SPR_IMG_ROAD_DEPOT, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_VEHICLE_DEPOT));
}
if (HasBit(_settings_game.depot.road_depot_types, 1)) {
/* Add the widget for building extended road depots. */
hor->Add(std::make_unique<NWidgetLeaf>(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_EXTENDED_DEPOT, SPR_IMG_ROAD_DEPOT, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_EXTENDED_ROAD_VEHICLE_DEPOT));
}
return hor;
}
static constexpr NWidgetPart _nested_build_road_widgets[] = {
NWidget(NWID_HORIZONTAL),
NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
@ -915,8 +1021,7 @@ static constexpr NWidgetPart _nested_build_road_widgets[] = {
SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_AUTOROAD, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_AUTOROAD),
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_DEMOLISH),
SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_DYNAMITE, STR_TOOLTIP_DEMOLISH_BUILDINGS_ETC),
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_DEPOT),
SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_ROAD_DEPOT, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_VEHICLE_DEPOT),
NWidgetFunction(MakeNWidgetRoadDepot),
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_BUILD_WAYPOINT),
SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_WAYPOINT, STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_ROAD_TO_WAYPOINT),
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_BUS_STATION),
@ -960,8 +1065,7 @@ static constexpr NWidgetPart _nested_build_tramway_widgets[] = {
SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_AUTOTRAM, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_AUTOTRAM),
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_DEMOLISH),
SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_DYNAMITE, STR_TOOLTIP_DEMOLISH_BUILDINGS_ETC),
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_DEPOT),
SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_ROAD_DEPOT, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAM_VEHICLE_DEPOT),
NWidgetFunction(MakeNWidgetRoadDepot),
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_BUILD_WAYPOINT),
SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_WAYPOINT, STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_TRAM_TO_WAYPOINT),
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_BUS_STATION),
@ -1091,14 +1195,28 @@ Window *ShowBuildRoadScenToolbar(RoadType roadtype)
}
struct BuildRoadDepotWindow : public PickerWindowBase {
BuildRoadDepotWindow(WindowDesc &desc, Window *parent) : PickerWindowBase(desc, parent)
BuildRoadDepotWindow(WindowDesc &desc, Window *parent, bool extended_depot) : PickerWindowBase(desc, parent)
{
this->CreateNestedTree();
/* Fix direction for extended depots. */
if (extended_depot) {
switch (_road_depot_orientation) {
case DIAGDIR_NE:
_road_depot_orientation++;
break;
case DIAGDIR_NW:
_road_depot_orientation--;
break;
default: break;
}
}
this->LowerWidget(WID_BROD_DEPOT_NE + _road_depot_orientation);
if (RoadTypeIsTram(_cur_roadtype)) {
this->GetWidget<NWidgetCore>(WID_BROD_CAPTION)->widget_data = STR_BUILD_DEPOT_TRAM_ORIENTATION_CAPTION;
for (WidgetID i = WID_BROD_DEPOT_NE; i <= WID_BROD_DEPOT_NW; i++) {
if (!this->HasWidget(i)) continue;
this->GetWidget<NWidgetCore>(i)->tool_tip = STR_BUILD_DEPOT_TRAM_ORIENTATION_SELECT_TOOLTIP;
}
}
@ -1106,6 +1224,12 @@ struct BuildRoadDepotWindow : public PickerWindowBase {
this->FinishInitNested(TRANSPORT_ROAD);
}
void Close([[maybe_unused]] int data = 0) override
{
CloseWindowById(WC_SELECT_DEPOT, VEH_ROAD);
this->PickerWindowBase::Close();
}
void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
{
if (!IsInsideMM(widget, WID_BROD_DEPOT_NE, WID_BROD_DEPOT_NW + 1)) return;
@ -1135,6 +1259,7 @@ struct BuildRoadDepotWindow : public PickerWindowBase {
case WID_BROD_DEPOT_NE:
case WID_BROD_DEPOT_SW:
case WID_BROD_DEPOT_SE:
CloseWindowById(WC_SELECT_DEPOT, VEH_ROAD);
this->RaiseWidget(WID_BROD_DEPOT_NE + _road_depot_orientation);
_road_depot_orientation = (DiagDirection)(widget - WID_BROD_DEPOT_NE);
this->LowerWidget(WID_BROD_DEPOT_NE + _road_depot_orientation);
@ -1146,6 +1271,11 @@ struct BuildRoadDepotWindow : public PickerWindowBase {
break;
}
}
void OnRealtimeTick([[maybe_unused]] uint delta_ms) override
{
CheckRedrawDepotHighlight(this, VEH_ROAD);
}
};
static constexpr NWidgetPart _nested_build_road_depot_widgets[] = {
@ -1174,9 +1304,29 @@ static WindowDesc _build_road_depot_desc(
_nested_build_road_depot_widgets
);
static void ShowRoadDepotPicker(Window *parent)
static const NWidgetPart _nested_build_extended_road_depot_widgets[] = {
NWidget(NWID_HORIZONTAL),
NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
NWidget(WWT_CAPTION, COLOUR_DARK_GREEN, WID_BROD_CAPTION), SetDataTip(STR_BUILD_DEPOT_ROAD_ORIENTATION_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
EndContainer(),
NWidget(WWT_PANEL, COLOUR_DARK_GREEN),
NWidget(NWID_HORIZONTAL_LTR), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), SetPIPRatio(1, 0, 1), SetPadding(WidgetDimensions::unscaled.picker),
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BROD_DEPOT_SW), SetMinimalSize(66, 50), SetFill(0, 0), SetDataTip(0x0, STR_BUILD_DEPOT_ROAD_ORIENTATION_SELECT_TOOLTIP),
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BROD_DEPOT_SE), SetMinimalSize(66, 50), SetFill(0, 0), SetDataTip(0x0, STR_BUILD_DEPOT_ROAD_ORIENTATION_SELECT_TOOLTIP),
EndContainer(),
EndContainer(),
};
static WindowDesc _build_extended_road_depot_desc(
WDP_AUTO, nullptr, 0, 0,
WC_BUILD_DEPOT, WC_BUILD_TOOLBAR,
WDF_CONSTRUCTION,
_nested_build_extended_road_depot_widgets
);
static void ShowRoadDepotPicker(Window *parent, bool extended_depot)
{
new BuildRoadDepotWindow(_build_road_depot_desc, parent);
new BuildRoadDepotWindow(extended_depot ? _build_extended_road_depot_desc : _build_road_depot_desc, parent, extended_depot);
}
template <RoadStopType roadstoptype>

View File

@ -38,9 +38,11 @@ RoadBits GetAnyRoadBits(Tile tile, RoadTramType rtt, bool straight_tunnel_bridge
case MP_ROAD:
switch (GetRoadTileType(tile)) {
default:
case ROAD_TILE_NORMAL: return GetRoadBits(tile, rtt);
case ROAD_TILE_CROSSING: return GetCrossingRoadBits(tile);
case ROAD_TILE_DEPOT: return DiagDirToRoadBits(GetRoadDepotDirection(tile));
case ROAD_TILE_NORMAL:
case ROAD_TILE_DEPOT:
return GetRoadBits(tile, rtt);
case ROAD_TILE_CROSSING:
return GetCrossingRoadBits(tile);
}
case MP_STATION:

View File

@ -118,16 +118,38 @@ debug_inline static bool IsRoadDepotTile(Tile t)
return IsTileType(t, MP_ROAD) && IsRoadDepot(t);
}
/**
* Return whether a road depot tile is an extended one.
* @param t Tile to query.
* @return True if extended road depot tile.
*/
static inline bool IsExtendedRoadDepot(Tile t)
{
assert(IsTileType(t, MP_ROAD));
assert(IsRoadDepot(t));
return HasBit(t.m5(), 5);
}
/**
* Return whether a tile is an extended road depot tile.
* @param t Tile to query.
* @return True if extended road depot tile.
*/
static inline bool IsExtendedRoadDepotTile(Tile t)
{
return IsTileType(t, MP_ROAD) && IsRoadDepot(t) && IsExtendedRoadDepot(t);
}
/**
* Get the present road bits for a specific road type.
* @param t The tile to query.
* @param rt Road type.
* @pre IsNormalRoad(t)
* @param rtt Road tram type.
* @pre IsNormalRoad(t) || IsRoadDepotTile(t)
* @return The present road bits for the road type.
*/
inline RoadBits GetRoadBits(Tile t, RoadTramType rtt)
{
assert(IsNormalRoad(t));
assert(IsNormalRoad(t) || IsRoadDepotTile(t));
if (rtt == RTT_TRAM) return (RoadBits)GB(t.m3(), 0, 4);
return (RoadBits)GB(t.m5(), 0, 4);
}
@ -152,7 +174,7 @@ inline RoadBits GetAllRoadBits(Tile tile)
*/
inline void SetRoadBits(Tile t, RoadBits r, RoadTramType rtt)
{
assert(IsNormalRoad(t)); // XXX incomplete
assert(IsNormalRoad(t) || IsRoadDepotTile(t));
if (rtt == RTT_TRAM) {
SB(t.m3(), 0, 4, r);
} else {
@ -556,19 +578,28 @@ inline void TerminateRoadWorks(Tile t)
SB(t.m7(), 0, 4, 0);
}
/**
* Set the direction of the exit of a road depot.
* @param t The tile to query.
* @return Diagonal direction of the depot exit.
*/
static inline void SetRoadDepotDirection(Tile t, DiagDirection dir)
{
assert(IsRoadDepot(t));
SB(t.m6(), 6, 2, dir);
}
/**
* Get the direction of the exit of a road depot.
* Get the direction of the exit of a road depot (or the image of the depot for extended road depots).
* @param t The tile to query.
* @return Diagonal direction of the depot exit.
*/
inline DiagDirection GetRoadDepotDirection(Tile t)
{
assert(IsRoadDepot(t));
return (DiagDirection)GB(t.m5(), 0, 2);
return (DiagDirection)GB(t.m6(), 6, 2);
}
RoadBits GetAnyRoadBits(Tile tile, RoadTramType rtt, bool straight_tunnel_bridge_entrance = false);
/**
@ -680,7 +711,7 @@ inline void MakeRoadCrossing(Tile t, Owner road, Owner tram, Owner rail, Axis ro
inline void SetRoadDepotExitDirection(Tile tile, DiagDirection dir)
{
assert(IsRoadDepotTile(tile));
SB(tile.m5(), 0, 2, dir);
SB(tile.m6(), 6, 2, dir);
}
/**
@ -698,8 +729,9 @@ inline void MakeRoadDepot(Tile tile, Owner owner, DepotID depot_id, DiagDirectio
tile.m2() = depot_id;
tile.m3() = 0;
tile.m4() = INVALID_ROADTYPE;
tile.m5() = ROAD_TILE_DEPOT << 6 | dir;
SB(tile.m6(), 2, 4, 0);
tile.m5() = ROAD_TILE_DEPOT << 6;
SB(tile.m6(), 0, 6, 0);
SB(tile.m6(), 6, 2, dir);
tile.m7() = owner;
tile.m8() = INVALID_ROADTYPE << 6;
SetRoadType(tile, GetRoadTramType(rt), rt);

View File

@ -37,6 +37,7 @@
#include "framerate_type.h"
#include "roadveh_cmd.h"
#include "road_cmd.h"
#include "depot_base.h"
#include "table/strings.h"
@ -250,6 +251,62 @@ void RoadVehUpdateCache(RoadVehicle *v, bool same_length)
v->vcache.cached_max_speed = (max_speed != 0) ? max_speed * 4 : RoadVehInfo(v->engine_type)->max_speed;
}
/**
* Find an adequate tile for placing an engine.
* @param[in,out] tile A tile of the depot.
* @param[in,out] is_exit_facing_south Whether the depot tile is facing south.
* @param e Engine to be built.
* @param already_built Whether the vehicle already exists (for vehicle replacement).
* @return CommandCost() or an error message if the depot has no appropriate tiles.
*/
CommandCost FindDepotTileForPlacingEngine(TileIndex &tile, bool &is_exit_facing_south, const Engine *e, bool already_built)
{
assert(IsRoadDepotTile(tile));
Depot *dep = Depot:: GetByTile(tile);
/* Check that the vehicle can drive on some tile of the depot */
RoadType rt = e->u.road.roadtype;
const RoadTypeInfo *rti = GetRoadTypeInfo(rt);
if ((dep->r_types.road_types & rti->powered_roadtypes) == 0) return_cmd_error(STR_ERROR_DEPOT_WRONG_DEPOT_TYPE);
/* Use same tile if possible when replacing or trying to leave the depot. */
if (HasTileAnyRoadType(tile, rti->powered_roadtypes) && already_built) return CommandCost();
for (auto t : dep->depot_tiles) {
if (!HasTileAnyRoadType(t, rti->powered_roadtypes)) continue;
if (!IsExtendedDepot(t)) return CommandCost();
if (GetDepotReservation(t, is_exit_facing_south) == DEPOT_RESERVATION_EMPTY) {
tile = t;
return CommandCost();
} else if (GetDepotReservation(t, !is_exit_facing_south) == DEPOT_RESERVATION_EMPTY) {
is_exit_facing_south = !is_exit_facing_south;
tile = t;
return CommandCost();
}
}
return_cmd_error(STR_ERROR_DEPOT_FULL_DEPOT);
}
DiagDirection GetRoadDepotExit(TileIndex tile, RoadTramType rtt, DiagDirection dir)
{
assert(IsRoadDepot(tile));
RoadBits rb = GetRoadBits(tile, rtt);
if ((rb & DiagDirToRoadBits(dir)) != ROAD_NONE) return dir;
if (rb & ROAD_SE) return DIAGDIR_SE;
if (rb & ROAD_SW) return DIAGDIR_SW;
if (rb & ROAD_NE) return DIAGDIR_NE;
if (rb & ROAD_NW) return DIAGDIR_NW;
return INVALID_DIAGDIR;
}
struct RoadDriveEntry {
uint8_t x, y;
};
#include "table/roadveh_movement.h"
/**
* Build a road vehicle.
* @param flags type of operation.
@ -260,25 +317,39 @@ void RoadVehUpdateCache(RoadVehicle *v, bool same_length)
*/
CommandCost CmdBuildRoadVehicle(DoCommandFlag flags, TileIndex tile, const Engine *e, Vehicle **ret)
{
/* Check that the vehicle can drive on the road in question */
assert(IsRoadDepotTile(tile));
RoadType rt = e->u.road.roadtype;
const RoadTypeInfo *rti = GetRoadTypeInfo(rt);
if (!HasTileAnyRoadType(tile, rti->powered_roadtypes)) return_cmd_error(STR_ERROR_DEPOT_WRONG_DEPOT_TYPE);
DiagDirection dir = GetRoadDepotExit(tile, RoadTramType(rt), GetRoadDepotDirection(tile));
bool facing_south = IsValidDiagDirection(dir) ? IsDiagDirFacingSouth(dir) : false;
if ((flags & DC_AUTOREPLACE) == 0) {
CommandCost check = FindDepotTileForPlacingEngine(tile, facing_south, e, false);
if (check.Failed()) return check;
dir = GetRoadDepotExit(tile, RoadTramType(rt), GetRoadDepotDirection(tile));
}
if (IsExtendedRoadDepotTile(tile) && facing_south != IsDiagDirFacingSouth(dir)) dir = ReverseDiagDir(dir);
if (flags & DC_EXEC) {
const RoadVehicleInfo *rvi = &e->u.road;
RoadVehicle *v = new RoadVehicle();
*ret = v;
v->direction = DiagDirToDir(GetRoadDepotDirection(tile));
v->direction = DiagDirToDir(dir);
v->owner = _current_company;
v->tile = tile;
int x = TileX(tile) * TILE_SIZE + TILE_SIZE / 2;
int y = TileY(tile) * TILE_SIZE + TILE_SIZE / 2;
v->x_pos = x;
v->y_pos = y;
v->z_pos = GetSlopePixelZ(x, y, true);
if (IsExtendedRoadDepotTile(tile)) {
const RoadDriveEntry *rdp = _road_drive_data[GetRoadTramType(rt)][(_settings_game.vehicle.road_side << RVS_DRIVE_SIDE) + DiagDirToDiagTrackdir(dir)];
v->frame = RVC_DEPOT_START_FRAME;
v->x_pos = TileX(v->tile) * TILE_SIZE + (rdp[RVC_DEPOT_START_FRAME].x & 0xF);
v->y_pos = TileY(v->tile) * TILE_SIZE + (rdp[RVC_DEPOT_START_FRAME].y & 0xF);
} else {
v->x_pos = TileX(tile) * TILE_SIZE + TILE_SIZE / 2;
v->y_pos = TileY(tile) * TILE_SIZE + TILE_SIZE / 2;
}
v->z_pos = GetSlopePixelZ(v->x_pos, v->y_pos, true);
v->state = RVSB_IN_DEPOT;
v->vehstatus = VS_HIDDEN | VS_STOPPED | VS_DEFPAL;
@ -322,6 +393,7 @@ CommandCost CmdBuildRoadVehicle(DoCommandFlag flags, TileIndex tile, const Engin
for (RoadVehicle *u = v; u != nullptr; u = u->Next()) {
u->cargo_cap = u->GetEngine()->DetermineCapacity(u);
u->refit_cap = 0;
u->state = RVSB_IN_DEPOT;
v->InvalidateNewGRFCache();
u->InvalidateNewGRFCache();
}
@ -331,6 +403,12 @@ CommandCost CmdBuildRoadVehicle(DoCommandFlag flags, TileIndex tile, const Engin
v->UpdatePosition();
if (IsExtendedDepot(v->tile) && (flags & DC_AUTOREPLACE) == 0) {
v->vehstatus &= ~VS_HIDDEN;
UpdateExtendedDepotReservation(v, true);
v->UpdateViewport(true, true);
}
CheckConsistencyOfArticulatedVehicle(v);
}
@ -616,34 +694,56 @@ struct RoadVehFindData {
static Vehicle *EnumCheckRoadVehClose(Vehicle *v, void *data)
{
static const int8_t dist_x[] = { -4, -8, -4, -1, 4, 8, 4, 1 };
static const int8_t dist_y[] = { -4, -1, 4, 8, 4, 1, -4, -8 };
if (v->type != VEH_ROAD || (v->vehstatus & VS_HIDDEN) == 0) return nullptr;
RoadVehFindData *rvf = (RoadVehFindData*)data;
if (abs(v->z_pos - rvf->veh->z_pos) >= 6 ||
v->direction != rvf->dir ||
rvf->veh->First() == v->First()) return nullptr;
static const int8_t dist_x[] = { -4, -8, -4, -1, 4, 8, 4, 1 };
static const int8_t dist_y[] = { -4, -1, 4, 8, 4, 1, -4, -8 };
short x_diff = v->x_pos - rvf->x;
short y_diff = v->y_pos - rvf->y;
if (v->type == VEH_ROAD &&
!v->IsInDepot() &&
abs(v->z_pos - rvf->veh->z_pos) < 6 &&
v->direction == rvf->dir &&
rvf->veh->First() != v->First() &&
(dist_x[v->direction] >= 0 || (x_diff > dist_x[v->direction] && x_diff <= 0)) &&
(dist_x[v->direction] <= 0 || (x_diff < dist_x[v->direction] && x_diff >= 0)) &&
(dist_y[v->direction] >= 0 || (y_diff > dist_y[v->direction] && y_diff <= 0)) &&
(dist_y[v->direction] <= 0 || (y_diff < dist_y[v->direction] && y_diff >= 0))) {
/* Check if vehicle is not close. */
if ((dist_x[v->direction] < 0 && (x_diff > 0 || x_diff <= dist_x[v->direction]))) return nullptr;
if ((dist_x[v->direction] > 0 && (x_diff < 0 || x_diff >= dist_x[v->direction]))) return nullptr;
if ((dist_y[v->direction] < 0 && (y_diff > 0 || y_diff <= dist_y[v->direction]))) return nullptr;
if ((dist_y[v->direction] > 0 && (y_diff < 0 || y_diff >= dist_y[v->direction]))) return nullptr;
uint diff = abs(x_diff) + abs(y_diff);
if (diff < rvf->best_diff || (diff == rvf->best_diff && v->index < rvf->best->index)) {
rvf->best = v;
rvf->best_diff = diff;
}
}
return nullptr;
}
/**
* Hide a stopped and visible road vehicle in an extended depot.
* @param v The road vehicle
* @pre v->IsStoppedInDepot() && IsExtendedRoadDepotTile(v->tile)
*/
static void LiftRoadVehicleInDepot(RoadVehicle *v)
{
assert(v->IsStoppedInDepot());
assert(IsExtendedRoadDepotTile(v->tile));
for (RoadVehicle *rv = v; rv != nullptr; rv = rv->Next()) {
rv->vehstatus |= VS_HIDDEN;
rv->tile = v->tile;
rv->direction = v->direction;
rv->x_pos = v->x_pos;
rv->y_pos = v->y_pos;
rv->UpdatePosition();
rv->Vehicle::UpdateViewport(true);
}
UpdateExtendedDepotReservation(v, false);
}
static RoadVehicle *RoadVehFindCloseTo(RoadVehicle *v, int x, int y, Direction dir, bool update_blocked_ctr = true)
{
RoadVehFindData rvf;
@ -675,6 +775,17 @@ static RoadVehicle *RoadVehFindCloseTo(RoadVehicle *v, int x, int y, Direction d
if (update_blocked_ctr && ++front->blocked_ctr > 1480) return nullptr;
rvf.best = rvf.best->First();
/* If the best vehicle is a road vehicle stopped in an extended depot,
* it is in the way of the moving vehicle. Hide the stopped vehicle
* inside the depot. */
if (rvf.best->IsStoppedInDepot()) {
assert(IsExtendedRoadDepotTile(rvf.best->tile));
LiftRoadVehicleInDepot(RoadVehicle::From(rvf.best));
return nullptr;
}
return RoadVehicle::From(rvf.best);
}
@ -772,7 +883,7 @@ static Vehicle *EnumFindVehBlockingOvertake(Vehicle *v, void *data)
{
const OvertakeData *od = (OvertakeData*)data;
return (v->type == VEH_ROAD && v->First() == v && v != od->u && v != od->v) ? v : nullptr;
return (v->type == VEH_ROAD && v->First() == v && v != od->u && v != od->v && ((v->vehstatus & VS_HIDDEN) == 0)) ? v : nullptr;
}
/**
@ -809,6 +920,9 @@ static void RoadVehCheckOvertake(RoadVehicle *v, RoadVehicle *u)
/* Don't overtake in stations */
if (IsTileType(v->tile, MP_STATION) || IsTileType(u->tile, MP_STATION)) return;
/* Don't overtake in road depot platforms. */
if (IsExtendedRoadDepotTile(v->tile)) return;
/* For now, articulated road vehicles can't overtake anything. */
if (v->HasArticulatedPart()) return;
@ -890,9 +1004,17 @@ static Trackdir RoadFindPathToDest(RoadVehicle *v, TileIndex tile, DiagDirection
TrackdirBits trackdirs = TrackStatusToTrackdirBits(ts);
if (IsTileType(tile, MP_ROAD)) {
if (IsRoadDepot(tile) && (!IsTileOwner(tile, v->owner) || GetRoadDepotDirection(tile) == enterdir)) {
/* Road depot owned by another company or with the wrong orientation */
if (IsRoadDepot(tile)) {
if (!IsTileOwner(tile, v->owner)) {
trackdirs = TRACKDIR_BIT_NONE;
} else if (IsExtendedRoadDepotTile(tile)) {
if (tile != v->tile) {
RoadBits rb = GetRoadBits(tile, GetRoadTramType(v->roadtype)) & DiagDirToRoadBits(ReverseDiagDir(enterdir));
if (rb == ROAD_NONE) trackdirs = TRACKDIR_BIT_NONE;
}
} else if (GetRoadDepotDirection(tile) == enterdir) { // Standard depot
trackdirs = TRACKDIR_BIT_NONE;
}
}
} else if (IsTileType(tile, MP_STATION) && IsBayRoadStopTile(tile)) {
/* Standard road stop (drive-through stops are treated as normal road) */
@ -994,58 +1116,118 @@ found_best_track:;
return best_track;
}
struct RoadDriveEntry {
uint8_t x, y;
};
void HandleRoadVehicleEnterDepot(RoadVehicle *v)
{
assert(IsRoadDepotTile(v->tile));
#include "table/roadveh_movement.h"
if (IsExtendedRoadDepot(v->tile)) {
assert(v == v->First());
for (RoadVehicle *u = v; u != nullptr; u = u->Next()) {
assert(u->direction == v->direction);
assert(IsExtendedRoadDepotTile(u->tile));
assert(GetDepotIndex(u->tile) == GetDepotIndex(v->tile));
u->state = RVSB_IN_DEPOT;
u->cur_speed = 0;
u->UpdateViewport(true, true); // revise: probably unneded
}
v->StartService();
UpdateExtendedDepotReservation(v, true);
SetWindowClassesDirty(WC_ROADVEH_LIST);
SetWindowDirty(WC_VEHICLE_VIEW, v->index);
InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile));
} else {
VehicleEnterDepot(v);
}
}
bool RoadVehLeaveDepot(RoadVehicle *v, bool first)
{
/* Don't leave unless v and following wagons are in the depot. */
for (const RoadVehicle *u = v; u != nullptr; u = u->Next()) {
if (u->state != RVSB_IN_DEPOT || u->tile != v->tile) return false;
if (!u->IsInDepot()) return false;
}
DiagDirection dir = GetRoadDepotDirection(v->tile);
v->direction = DiagDirToDir(dir);
bool visible_vehicle = first && (v->vehstatus & VS_HIDDEN) == 0;
Trackdir tdir = DiagDirToDiagTrackdir(dir);
if (first && (v->vehstatus & VS_HIDDEN) != 0) {
TileIndex new_tile = v->tile;
bool facing_south = IsDiagDirFacingSouth(DirToDiagDir(v->direction));
if (FindDepotTileForPlacingEngine(new_tile, facing_south, Engine::Get(v->engine_type), true).Failed()) return false;
if (IsExtendedDepot(v->tile)) {
UpdateExtendedDepotReservation(v, false);
v->tile = new_tile;
UpdateExtendedDepotReservation(v, true);
}
DiagDirection dir = GetRoadDepotExit(v->tile, RoadTramType(v->roadtype), DirToDiagDir(v->direction));
if (facing_south != IsDiagDirFacingSouth(dir)) dir = ReverseDiagDir(dir);
assert(dir != INVALID_DIAGDIR);
for (Vehicle *u = v; u != nullptr; u = u->Next()) {
u->direction = DiagDirToDir(dir);
u->tile = v->tile;
}
}
int x = v->x_pos;
int y = v->y_pos;
Trackdir tdir = v->GetVehicleTrackdir();
if ((v->vehstatus & VS_HIDDEN) != 0) {
const RoadDriveEntry *rdp = _road_drive_data[GetRoadTramType(v->roadtype)][(_settings_game.vehicle.road_side << RVS_DRIVE_SIDE) + tdir];
int x = TileX(v->tile) * TILE_SIZE + (rdp[RVC_DEPOT_START_FRAME].x & 0xF);
int y = TileY(v->tile) * TILE_SIZE + (rdp[RVC_DEPOT_START_FRAME].y & 0xF);
x = TileX(v->tile) * TILE_SIZE + (rdp[RVC_DEPOT_START_FRAME].x & 0xF);
y = TileY(v->tile) * TILE_SIZE + (rdp[RVC_DEPOT_START_FRAME].y & 0xF);
}
if (first) {
/* We are leaving a depot, but have to go to the exact same one; re-enter */
if (v->current_order.IsType(OT_GOTO_DEPOT) && v->tile == v->dest_tile) {
if (v->current_order.IsType(OT_GOTO_DEPOT) &&
IsRoadDepotTile(v->tile) &&
v->current_order.GetDestination() == GetDepotIndex(v->tile)) {
if (IsExtendedRoadDepot(v->tile)) {
v->StartService();
} else {
VehicleEnterDepot(v);
}
return true;
}
if (RoadVehFindCloseTo(v, x, y, v->direction, false) != nullptr) return true;
if ((v->vehstatus & VS_HIDDEN) != 0 && RoadVehFindCloseTo(v, x, y, v->direction, false) != nullptr) return true;
VehicleServiceInDepot(v);
v->LeaveUnbunchingDepot();
StartRoadVehSound(v);
/* Vehicle is about to leave a depot */
/* Vehicle is about to leave a depot. */
v->cur_speed = 0;
}
v->vehstatus &= ~VS_HIDDEN;
v->state = tdir;
v->frame = RVC_DEPOT_START_FRAME;
if ((v->vehstatus & VS_HIDDEN) != 0) {
v->vehstatus &= ~VS_HIDDEN;
v->x_pos = x;
v->y_pos = y;
v->frame = RVC_DEPOT_START_FRAME;
} else if (v->Next() != nullptr && (v->Next()->vehstatus & VS_HIDDEN) == 0){
for (RoadVehicle *u = v->Next(); u != nullptr; u = u->Next()) {
u->state = DiagDirToDiagTrackdir(DirToDiagDir(u->direction));
u->UpdatePosition();
u->UpdateInclination(true, true);
}
}
v->UpdatePosition();
v->UpdateInclination(true, true);
InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
if (first && IsExtendedDepot(v->tile)) {
UpdateExtendedDepotReservation(v, false);
}
return true;
InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile));
return !visible_vehicle;
}
static Trackdir FollowPreviousRoadVehicle(const RoadVehicle *v, const RoadVehicle *prev, TileIndex tile, DiagDirection entry_dir, bool already_reversed)
@ -1065,7 +1247,7 @@ static Trackdir FollowPreviousRoadVehicle(const RoadVehicle *v, const RoadVehicl
if (IsTileType(tile, MP_TUNNELBRIDGE)) {
diag_dir = GetTunnelBridgeDirection(tile);
} else if (IsRoadDepotTile(tile)) {
diag_dir = ReverseDiagDir(GetRoadDepotDirection(tile));
diag_dir = ReverseDiagDir(IsExtendedRoadDepot(tile) ? DirToDiagDir(v->direction) : GetRoadDepotDirection(tile));
}
if (diag_dir == INVALID_DIAGDIR) return INVALID_TRACKDIR;
@ -1133,6 +1315,53 @@ static bool CanBuildTramTrackOnTile(CompanyID c, TileIndex t, RoadType rt, RoadB
return ret.Succeeded();
}
/** Check whether there is a close vehicle ahead and act as needed.
* @param v Moving vehicle
* @param x x coordinate to check
* @param y y coordinate to check
* @param dir direction of the vehicle
* @return whether a close vehicle is found.
*/
bool CheckCloseVehicle(RoadVehicle *v, int x, int y, Direction dir)
{
if (!v->IsFrontEngine() || IsInsideMM(v->state, RVSB_IN_ROAD_STOP, RVSB_IN_ROAD_STOP_END)) return false;
/* Vehicle is not in a road stop.
* Check for another vehicle to overtake */
RoadVehicle *u = RoadVehFindCloseTo(v, x, y, dir);
if (u == nullptr) return false;
assert(u == u->First());
/* There is a vehicle in front overtake it if possible */
if (v->overtaking == 0) RoadVehCheckOvertake(v, u);
if (v->overtaking == 0) v->cur_speed = u->cur_speed;
/* In case we are in a road depot platform, why not try to start servicing? */
if (IsExtendedRoadDepotTile(v->tile) && v->current_order.IsType(OT_GOTO_DEPOT)) {
DepotID depot_id = GetDepotIndex(v->tile);
if (v->current_order.GetDestination() != depot_id) return true;
if (!u->IsInDepot() || GetDepotIndex(u->tile) != depot_id) return true;
for (u = v; u != nullptr; u = u->Next()) {
if (!IsExtendedRoadDepotTile(u->tile) || GetDepotIndex(u->tile) != depot_id) return true;
if (v->direction != u->direction) return true;
}
HandleRoadVehicleEnterDepot(v);
return true;
}
/* In case an RV is stopped in a road stop, why not try to load? */
if (v->cur_speed == 0 && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END) &&
v->current_order.ShouldStopAtStation(v, GetStationIndex(v->tile)) &&
v->owner == GetTileOwner(v->tile) && !v->current_order.IsType(OT_LEAVESTATION) &&
GetRoadStopType(v->tile) == (v->IsBus() ? ROADSTOP_BUS : ROADSTOP_TRUCK)) {
Station *st = Station::GetByTile(v->tile);
v->last_station_visited = st->index;
RoadVehArrivesAt(v, st);
v->BeginLoading();
}
return true;
}
bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev)
{
if (v->overtaking != 0) {
@ -1161,7 +1390,8 @@ bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev)
if (v->IsFrontEngine()) {
const Vehicle *u = RoadVehFindCloseTo(v, gp.x, gp.y, v->direction);
if (u != nullptr) {
v->cur_speed = u->First()->cur_speed;
assert(u == u->First());
v->cur_speed = u->cur_speed;
return false;
}
}
@ -1190,18 +1420,21 @@ bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev)
(_settings_game.vehicle.road_side << RVS_DRIVE_SIDE)) ^ v->overtaking][v->frame + 1];
if (rd.x & RDE_NEXT_TILE) {
TileIndex tile = v->tile + TileOffsByDiagDir((DiagDirection)(rd.x & 3));
DiagDirection diag_dir = (DiagDirection)(rd.x & 3);
TileIndex tile = v->tile + TileOffsByDiagDir(diag_dir);
Trackdir dir;
bool extended_depot_turn = IsExtendedRoadDepotTile(v->tile) &&
(GetRoadBits(v->tile, GetRoadTramType(v->roadtype)) & DiagDirToRoadBits(diag_dir)) == ROAD_NONE;
if (v->IsFrontEngine()) {
/* If this is the front engine, look for the right path. */
if (HasTileAnyRoadType(tile, v->compatible_roadtypes)) {
dir = RoadFindPathToDest(v, tile, (DiagDirection)(rd.x & 3));
if (HasTileAnyRoadType(tile, v->compatible_roadtypes) && !extended_depot_turn) {
dir = RoadFindPathToDest(v, tile, diag_dir);
} else {
dir = _road_reverse_table[(DiagDirection)(rd.x & 3)];
dir = _road_reverse_table[diag_dir];
}
} else {
dir = FollowPreviousRoadVehicle(v, prev, tile, (DiagDirection)(rd.x & 3), false);
dir = FollowPreviousRoadVehicle(v, prev, tile, diag_dir, false);
}
if (dir == INVALID_TRACKDIR) {
@ -1228,7 +1461,10 @@ again:
case TRACKDIR_RVREV_SW: needed = ROAD_NE; break;
case TRACKDIR_RVREV_NW: needed = ROAD_SE; break;
}
if ((v->Previous() != nullptr && v->Previous()->tile == tile) ||
if (extended_depot_turn) {
tile = v->tile;
start_frame = RVC_TURN_AROUND_START_FRAME_SHORT_TRAM;
} else if ((v->Previous() != nullptr && v->Previous()->tile == tile) ||
(v->IsFrontEngine() && IsNormalRoadTile(tile) && !HasRoadWorks(tile) &&
HasTileAnyRoadType(tile, v->compatible_roadtypes) &&
(needed & GetRoadBits(tile, RTT_TRAM)) != ROAD_NONE)) {
@ -1245,8 +1481,9 @@ again:
} else if (!v->IsFrontEngine() || !CanBuildTramTrackOnTile(v->owner, tile, v->roadtype, needed) || ((~needed & GetAnyRoadBits(v->tile, RTT_TRAM, false)) == ROAD_NONE)) {
/*
* Taking the 'small' corner for trams only happens when:
* - We are not the from vehicle of an articulated tram.
* - We are not the front vehicle of an articulated tram.
* - Or when the company cannot build on the next tile.
* - Or when the extended depot doesn't have the appropriate tram bit to continue.
*
* The 'small' corner means that the vehicle is on the end of a
* tram track and needs to start turning there. To do this properly
@ -1279,7 +1516,8 @@ again:
if (v->IsFrontEngine()) {
const Vehicle *u = RoadVehFindCloseTo(v, x, y, new_dir);
if (u != nullptr) {
v->cur_speed = u->First()->cur_speed;
assert(u == u->First());
v->cur_speed = u->cur_speed;
/* We might be blocked, prevent pathfinding rerun as we already know where we are heading to. */
v->path.tile.push_front(tile);
v->path.td.push_front(dir);
@ -1288,6 +1526,11 @@ again:
}
uint32_t r = VehicleEnterTile(v, tile, x, y);
if (HasBit(r, VETS_ENTERED_DEPOT_PLATFORM) && v->Next() == nullptr && v == v->First()) {
HandleRoadVehicleEnterDepot(RoadVehicle::From(v));
return false;
}
if (HasBit(r, VETS_CANNOT_ENTER)) {
if (!IsTileType(tile, MP_TUNNELBRIDGE)) {
v->cur_speed = 0;
@ -1344,6 +1587,9 @@ again:
}
v->x_pos = x;
v->y_pos = y;
if (prev != nullptr && prev->IsInDepot() && (prev->vehstatus & VS_HIDDEN) == 0) {
v->state = RVSB_IN_DEPOT;
}
v->UpdatePosition();
RoadZPosAffectSpeed(v, v->UpdateInclination(true, true));
return true;
@ -1354,7 +1600,7 @@ again:
Trackdir dir;
uint turn_around_start_frame = RVC_TURN_AROUND_START_FRAME;
if (RoadTypeIsTram(v->roadtype) && !IsRoadDepotTile(v->tile) && HasExactlyOneBit(GetAnyRoadBits(v->tile, RTT_TRAM, true))) {
if (RoadTypeIsTram(v->roadtype) && !IsRoadDepotTile(v->tile) && !IsExtendedRoadDepotTile(v->tile) && HasExactlyOneBit(GetAnyRoadBits(v->tile, RTT_TRAM, true))) {
/*
* The tram is turning around with one tram 'roadbit'. This means that
* it is using the 'big' corner 'drive data'. However, to support the
@ -1395,7 +1641,8 @@ again:
if (v->IsFrontEngine()) {
const Vehicle *u = RoadVehFindCloseTo(v, x, y, new_dir);
if (u != nullptr) {
v->cur_speed = u->First()->cur_speed;
assert(u == u->First());
v->cur_speed = u->cur_speed;
/* We might be blocked, prevent pathfinding rerun as we already know where we are heading to. */
v->path.tile.push_front(v->tile);
v->path.td.push_front(dir);
@ -1419,6 +1666,9 @@ again:
v->x_pos = x;
v->y_pos = y;
if (prev != nullptr && prev->IsInDepot() && (prev->vehstatus & VS_HIDDEN) == 0) {
v->state = RVSB_IN_DEPOT;
}
v->UpdatePosition();
RoadZPosAffectSpeed(v, v->UpdateInclination(true, true));
return true;
@ -1428,10 +1678,12 @@ again:
* it's on a depot tile, check if it's time to activate the next vehicle in
* the chain yet. */
if (v->Next() != nullptr && IsRoadDepotTile(v->tile)) {
if ((v->Next()->vehstatus & VS_HIDDEN)) {
if (v->frame == v->gcache.cached_veh_length + RVC_DEPOT_START_FRAME) {
RoadVehLeaveDepot(v->Next(), false);
}
}
}
/* Calculate new position for the vehicle */
int x = (v->x_pos & ~15) + (rd.x & 15);
@ -1439,30 +1691,7 @@ again:
Direction new_dir = RoadVehGetSlidingDirection(v, x, y);
if (v->IsFrontEngine() && !IsInsideMM(v->state, RVSB_IN_ROAD_STOP, RVSB_IN_ROAD_STOP_END)) {
/* Vehicle is not in a road stop.
* Check for another vehicle to overtake */
RoadVehicle *u = RoadVehFindCloseTo(v, x, y, new_dir);
if (u != nullptr) {
u = u->First();
/* There is a vehicle in front overtake it if possible */
if (v->overtaking == 0) RoadVehCheckOvertake(v, u);
if (v->overtaking == 0) v->cur_speed = u->cur_speed;
/* In case an RV is stopped in a road stop, why not try to load? */
if (v->cur_speed == 0 && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END) &&
v->current_order.ShouldStopAtStation(v, GetStationIndex(v->tile)) &&
v->owner == GetTileOwner(v->tile) && !v->current_order.IsType(OT_LEAVESTATION) &&
GetRoadStopType(v->tile) == (v->IsBus() ? ROADSTOP_BUS : ROADSTOP_TRUCK)) {
Station *st = Station::GetByTile(v->tile);
v->last_station_visited = st->index;
RoadVehArrivesAt(v, st);
v->BeginLoading();
}
return false;
}
}
if (CheckCloseVehicle(v, x, y, new_dir)) return false;
Direction old_dir = v->direction;
if (new_dir != old_dir) {
@ -1558,6 +1787,16 @@ again:
v->y_pos = y;
v->UpdatePosition();
RoadZPosAffectSpeed(v, v->UpdateInclination(false, true));
/* After updating the position, check whether the vehicle can stop in a depot platform. */
if (IsExtendedRoadDepotTile(v->tile) && v->Next() == nullptr) {
RoadVehicle *first = RoadVehicle::From(v)->First();
if (HasBit(VehicleEnterTile(first, first->tile, first->x_pos, first->y_pos), VETS_ENTERED_DEPOT_PLATFORM)) {
HandleRoadVehicleEnterDepot(first);
return false;
}
}
return true;
}
@ -1579,6 +1818,8 @@ static bool RoadVehController(RoadVehicle *v)
return true;
}
if (v->ContinueServicing()) return true;
ProcessOrders(v);
v->HandleLoading();
@ -1740,6 +1981,9 @@ Trackdir RoadVehicle::GetVehicleTrackdir() const
if (this->vehstatus & VS_CRASHED) return INVALID_TRACKDIR;
if (this->IsInDepot()) {
if (IsExtendedRoadDepot(this->tile)) {
return DiagDirToDiagTrackdir(DirToDiagDir(this->direction));
}
/* We'll assume the road vehicle is facing outwards */
return DiagDirToDiagTrackdir(GetRoadDepotDirection(this->tile));
}

View File

@ -11,6 +11,7 @@
#include "../void_map.h"
#include "../signs_base.h"
#include "../depot_base.h"
#include "../depot_func.h"
#include "../fios.h"
#include "../gamelog_internal.h"
#include "../network/network.h"
@ -222,6 +223,7 @@ static inline RailType UpdateRailType(RailType rt, RailType min)
void UpdateAllVirtCoords()
{
UpdateAllStationVirtCoords();
UpdateAllDepotVirtCoords();
UpdateAllSignVirtCoords();
UpdateAllTownVirtCoords();
UpdateAllTextEffectVirtCoords();
@ -294,6 +296,10 @@ static void InitializeWindowsAndCaches()
}
}
for (Depot *dep : Depot::Iterate()) {
dep->RescanDepotTiles();
}
RecomputePrices();
GroupStatistics::UpdateAfterLoad();
@ -641,6 +647,15 @@ bool AfterLoadGame()
}
}
if (IsSavegameVersionBefore(SLV_DEPOTS_ALIGN_RAIL_DEPOT_BITS)) {
for (auto t : Map::Iterate()) {
if (IsTileType(t, MP_RAILWAY) && GetRailTileType(t) == 3) {
/* Change the rail type for depots from old value 3 to new value 2. */
SB(t.m5(), 6, 2, RAIL_TILE_DEPOT);
}
}
}
/* in version 2.1 of the savegame, town owner was unified. */
if (IsSavegameVersionBefore(SLV_2, 1)) ConvertTownOwner();
@ -795,6 +810,24 @@ bool AfterLoadGame()
_settings_game.linkgraph.recalc_time *= CalendarTime::SECONDS_PER_DAY;
}
if (IsSavegameVersionBefore(SLV_DEPOT_SPREAD)) {
_settings_game.depot.depot_spread = 1;
_settings_game.depot.adjacent_depots = true;
_settings_game.depot.distant_join_depots = true;
}
if (IsSavegameVersionBefore(SLV_ALLOW_INCOMPATIBLE_REPLACEMENTS)) {
_settings_game.depot.allow_no_comp_railtype_replacements = false;
_settings_game.depot.allow_no_comp_roadtype_replacements = false;
}
if (IsSavegameVersionBefore(SLV_EXTENDED_DEPOTS)) {
/* Set standard depots as the only available depots. */
_settings_game.depot.rail_depot_types = 1;
_settings_game.depot.road_depot_types = 1;
_settings_game.depot.water_depot_types = 1;
}
/* Load the sprites */
GfxLoadSprites();
LoadStringWidthTable();
@ -2425,28 +2458,6 @@ bool AfterLoadGame()
for (Depot *d : Depot::Iterate()) d->build_date = TimerGameCalendar::date;
}
/* In old versions it was possible to remove an airport while a plane was
* taking off or landing. This gives all kind of problems when building
* another airport in the same station so we don't allow that anymore.
* For old savegames with such aircraft we just throw them in the air and
* treat the aircraft like they were flying already. */
if (IsSavegameVersionBefore(SLV_146)) {
for (Aircraft *v : Aircraft::Iterate()) {
if (!v->IsNormalAircraft()) continue;
Station *st = GetTargetAirportIfValid(v);
if (st == nullptr && v->state != FLYING) {
v->state = FLYING;
UpdateAircraftCache(v);
AircraftNextAirportPos_and_Order(v);
/* get aircraft back on running altitude */
if ((v->vehstatus & VS_CRASHED) == 0) {
GetAircraftFlightLevelBounds(v, &v->z_pos, nullptr);
SetAircraftPosition(v, v->x_pos, v->y_pos, GetAircraftFlightLevel(v));
}
}
}
}
/* Move the animation frame to the same location (m7) for all objects. */
if (IsSavegameVersionBefore(SLV_147)) {
for (auto t : Map::Iterate()) {
@ -2792,6 +2803,112 @@ bool AfterLoadGame()
}
}
if (IsSavegameVersionBefore(SLV_ADD_DEPOTS_TO_HANGARS)) {
for (Station *st : Station::Iterate()) {
if ((st->facilities & FACIL_AIRPORT) && st->airport.HasHangar()) {
/* Add a built-in hangar for some airport types. */
assert(Depot::CanAllocateItem());
st->airport.AddHangar();
} else {
/* If airport has no hangar, remove old go to hangar orders
* that could remain from removing an airport with a hangar
* and rebuilding it with an airport with no hangar. */
RemoveOrderFromAllVehicles(OT_GOTO_DEPOT, st->index);
}
}
}
if (IsSavegameVersionBefore(SLV_DEPOTID_IN_HANGAR_ORDERS)) {
/* Update go to hangar orders so they store the DepotID instead of StationID. */
for (Aircraft *a : Aircraft::Iterate()) {
if (!a->IsNormalAircraft()) continue;
/* Update current order. */
if (a->current_order.IsType(OT_GOTO_DEPOT)) {
Depot *dep = Station::Get(a->current_order.GetDestination())->airport.hangar;
if (dep == nullptr) {
/* Aircraft heading to a removed hangar. */
a->current_order.MakeDummy();
} else {
a->current_order.SetDestination(dep->index);
}
}
/* Update each aircraft order list once. */
if (a->orders == nullptr) continue;
if (a->orders->GetFirstSharedVehicle() != a) continue;
for (Order *order : a->Orders()) {
if (!order->IsType(OT_GOTO_DEPOT)) continue;
StationID station_id = order->GetDestination();
Station *st = Station::Get(station_id);
order->SetDestination(st->airport.hangar->index);
}
}
}
if (IsSavegameVersionBefore(SLV_ADD_MEMBERS_TO_DEPOT_STRUCT)) {
for (Depot *depot : Depot::Iterate()) {
if (!IsDepotTile(depot->xy) || GetDepotIndex(depot->xy) != depot->index) {
/* It can happen there is no depot here anymore (TTO/TTD savegames) */
depot->veh_type = VEH_INVALID;
depot->owner = INVALID_OWNER;
depot->Disuse();
delete depot;
continue;
}
depot->owner = GetTileOwner(depot->xy);
depot->veh_type = GetDepotVehicleType(depot->xy);
switch (depot->veh_type) {
case VEH_SHIP:
depot->AfterAddRemove(TileArea(depot->xy, 2, 2), true);
break;
case VEH_ROAD:
case VEH_TRAIN:
depot->AfterAddRemove(TileArea(depot->xy, 1, 1), true);
break;
case VEH_AIRCRAFT:
assert(IsHangarTile(depot->xy));
depot->station = Station::GetByTile(depot->xy);
break;
default:
break;
}
}
for (auto t : Map::Iterate()) {
if (!IsRoadDepotTile(t)) continue;
DiagDirection dir = (DiagDirection)GB(t.m5(), 0, 2);
SB(t.m5(), 0, 6, 0);
RoadBits rb = DiagDirToRoadBits(dir);
SetRoadBits(t, rb, HasRoadTypeRoad(t) ? RTT_ROAD : RTT_TRAM);
SB(t.m6(), 6, 2, dir);
}
}
/* In old versions it was possible to remove an airport while a plane was
* taking off or landing. This gives all kind of problems when building
* another airport in the same station so we don't allow that anymore.
* For old savegames with such aircraft we just throw them in the air and
* treat the aircraft like they were flying already. */
if (IsSavegameVersionBefore(SLV_146)) {
for (Aircraft *v : Aircraft::Iterate()) {
if (!v->IsNormalAircraft()) continue;
Station *st = GetTargetAirportIfValid(v);
if (st == nullptr && v->state != FLYING) {
v->state = FLYING;
UpdateAircraftCache(v);
AircraftNextAirportPos_and_Order(v);
/* get aircraft back on running altitude */
if ((v->vehstatus & VS_CRASHED) == 0) {
GetAircraftFlightLevelBounds(v, &v->z_pos, nullptr);
SetAircraftPosition(v, v->x_pos, v->y_pos, GetAircraftFlightLevel(v));
}
}
}
}
/* This triggers only when old snow_lines were copied into the snow_line_height. */
if (IsSavegameVersionBefore(SLV_164) && _settings_game.game_creation.snow_line_height >= MIN_SNOWLINE_HEIGHT * TILE_HEIGHT) {
_settings_game.game_creation.snow_line_height /= TILE_HEIGHT;

View File

@ -134,8 +134,8 @@ void AfterLoadCompanyStats()
RoadType rt = GetRoadType(tile, rtt);
if (rt == INVALID_ROADTYPE) continue;
c = Company::GetIfValid(IsRoadDepot(tile) ? GetTileOwner(tile) : GetRoadOwner(tile, rtt));
/* A level crossings and depots have two road bits. */
if (c != nullptr) c->infrastructure.road[rt] += IsNormalRoad(tile) ? CountBits(GetRoadBits(tile, rtt)) : 2;
/* Level crossings have two road bits. */
if (c != nullptr) c->infrastructure.road[rt] += (IsNormalRoad(tile) || IsRoadDepot(tile)) ? CountBits(GetRoadBits(tile, rtt)) : 2;
}
break;
}

View File

@ -108,6 +108,7 @@ const SaveLoadCompat _station_normal_sl_compat[] = {
SLC_VAR("airport.layout"),
SLC_VAR("airport.flags"),
SLC_VAR("airport.rotation"),
SLC_VAR("airport.hangar"),
SLC_VAR("storage"),
SLC_VAR("airport.psa"),
SLC_VAR("indtype"),

View File

@ -27,6 +27,13 @@ static const SaveLoad _depot_desc[] = {
SLE_CONDVAR(Depot, town_cn, SLE_UINT16, SLV_141, SL_MAX_VERSION),
SLE_CONDSSTR(Depot, name, SLE_STR, SLV_141, SL_MAX_VERSION),
SLE_CONDVAR(Depot, build_date, SLE_INT32, SLV_142, SL_MAX_VERSION),
SLE_CONDVAR(Depot, owner, SLE_UINT8, SLV_ADD_MEMBERS_TO_DEPOT_STRUCT, SL_MAX_VERSION),
SLE_CONDVAR(Depot, veh_type, SLE_UINT8, SLV_ADD_MEMBERS_TO_DEPOT_STRUCT, SL_MAX_VERSION),
SLE_CONDVAR(Depot, ta.tile, SLE_UINT32, SLV_ADD_MEMBERS_TO_DEPOT_STRUCT, SL_MAX_VERSION),
SLE_CONDVAR(Depot, ta.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_ADD_MEMBERS_TO_DEPOT_STRUCT, SL_MAX_VERSION),
SLE_CONDVAR(Depot, ta.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_ADD_MEMBERS_TO_DEPOT_STRUCT, SL_MAX_VERSION),
SLE_CONDREF(Depot, station, REF_STATION, SLV_ADD_MEMBERS_TO_DEPOT_STRUCT, SL_MAX_VERSION),
SLE_CONDVAR(Depot, delete_ctr, SLE_UINT8, SLV_KEEP_REMOVED_DEPOTS, SL_MAX_VERSION),
};
struct DEPTChunkHandler : ChunkHandler {

View File

@ -15,6 +15,7 @@
#include "../map_func.h"
#include "../core/bitmath_func.hpp"
#include "../fios.h"
#include "../tile_map.h"
#include "../safeguards.h"
@ -243,6 +244,16 @@ struct MAP5ChunkHandler : ChunkHandler {
SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8);
for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) Tile(i++).m5() = buf[j];
}
if (IsSavegameVersionBefore(SLV_ALIGN_WATER_BITS)) {
/* Move some bits for alignment purposes. */
for (TileIndex i = 0; i != size; i++) {
if (IsTileType(i, MP_WATER)) {
SB(Tile(i).m5(), 6, 1, GB(Tile(i).m5(), 4, 1));
SB(Tile(i).m5(), 4, 1, 0);
}
}
}
}
void Save() const override

View File

@ -690,6 +690,8 @@ static bool LoadOldDepot(LoadgameState *ls, int num)
if (d->xy != 0) {
d->town = RemapTown(d->xy);
} else {
d->owner = INVALID_OWNER;
d->veh_type = VEH_INVALID;
delete d;
}

View File

@ -16,6 +16,7 @@
#include "../order_backup.h"
#include "../settings_type.h"
#include "../network/network.h"
#include "../depot_map.h"
#include "../safeguards.h"
@ -243,11 +244,14 @@ struct ORDLChunkHandler : ChunkHandler {
}
};
static TileIndex _tile;
SaveLoadTable GetOrderBackupDescription()
{
static const SaveLoad _order_backup_desc[] = {
SLE_VAR(OrderBackup, user, SLE_UINT32),
SLE_VAR(OrderBackup, tile, SLE_UINT32),
SLEG_CONDVAR("tile", _tile, SLE_UINT32, SL_MIN_VERSION, SLV_DEPOTID_BACKUP_ORDERS),
SLE_CONDVAR(OrderBackup, depot_id, SLE_UINT16, SLV_DEPOTID_BACKUP_ORDERS, SL_MAX_VERSION),
SLE_VAR(OrderBackup, group, SLE_UINT16),
SLE_CONDVAR(OrderBackup, service_interval, SLE_FILE_U32 | SLE_VAR_U16, SL_MIN_VERSION, SLV_192),
SLE_CONDVAR(OrderBackup, service_interval, SLE_UINT16, SLV_192, SL_MAX_VERSION),
@ -297,6 +301,8 @@ struct BKORChunkHandler : ChunkHandler {
OrderBackup *ob = new (index) OrderBackup();
SlObject(ob, slt);
}
if (IsSavegameVersionBefore(SLV_DEPOTID_BACKUP_ORDERS)) _order_backup_pool.CleanPool();
}
void FixPointers() const override

View File

@ -23,6 +23,7 @@
#include "../stdafx.h"
#include "../debug.h"
#include "../station_base.h"
#include "../depot_base.h"
#include "../thread.h"
#include "../town.h"
#include "../network/network.h"
@ -1121,6 +1122,7 @@ static size_t ReferenceToInt(const void *obj, SLRefType rt)
case REF_STORAGE: return ((const PersistentStorage*)obj)->index + 1;
case REF_LINK_GRAPH: return ((const LinkGraph*)obj)->index + 1;
case REF_LINK_GRAPH_JOB: return ((const LinkGraphJob*)obj)->index + 1;
case REF_DEPOT: return ((const Depot*)obj)->index + 1;
default: NOT_REACHED();
}
}
@ -1202,6 +1204,10 @@ static void *IntToReference(size_t index, SLRefType rt)
if (LinkGraphJob::IsValidID(index)) return LinkGraphJob::Get(index);
SlErrorCorrupt("Referencing invalid LinkGraphJob");
case REF_DEPOT:
if (Depot::IsValidID(index)) return Depot::Get(index);
SlErrorCorrupt("Referencing invalid Depot");
default: NOT_REACHED();
}
}

View File

@ -383,7 +383,24 @@ enum SaveLoadVersion : uint16_t {
SLV_GROUP_NUMBERS, ///< 336 PR#12297 Add per-company group numbers.
SLV_INCREASE_STATION_TYPE_FIELD_SIZE, ///< 337 PR#12572 Increase size of StationType field in map array
SLV_ROAD_WAYPOINTS, ///< 338 PR#12572 Road waypoints
SLV_ADD_DEPOTS_TO_HANGARS, ///< XXX PR#10691 Add depots to airports that have a hangar.
SLV_DEPOTID_IN_HANGAR_ORDERS, ///< 320 PR#10691 Go to hangar orders store the DepotID instead of StationID.
SLV_DEPOTID_BACKUP_ORDERS, ///< 314 PR#XXXXX Backup orders are indexed through DepotIDs.
SLV_ALIGN_WATER_BITS, ///< 315 PR#XXXXX Align some water bits in the map array.
SLV_DEPOTS_ALIGN_RAIL_DEPOT_BITS, ///< 316 PR#XXXXX Align one bit for rail depots.
SLV_ADD_MEMBERS_TO_DEPOT_STRUCT, ///< 317 PR#XXXXX Add some members to depot struct.
SLV_DEPOT_SPREAD, ///< 318 PR#XXXXX Add a setting for max depot spread.
SLV_ALLOW_INCOMPATIBLE_REPLACEMENTS, ///< 319 PR#XXXXX Allow incompatible vehicle replacements.
SLV_KEEP_REMOVED_DEPOTS, ///< 320 PR#XXXXX Keep remove depots for a while.
SLV_EXTENDED_DEPOTS, ///< 321 PR#8480 Extended depots for rail, road and water transport.
SLV_PATCHED = UINT16_MAX - 6, ///< Make it difficult to load any savegame made with
// this patched version in any other version of OpenTTD (unless it uses the same saveload version trick).
SL_MAX_VERSION, ///< Highest possible saveload version
};
@ -598,6 +615,7 @@ enum SLRefType {
REF_STORAGE = 9, ///< Load/save a reference to a persistent storage.
REF_LINK_GRAPH = 10, ///< Load/save a reference to a link graph.
REF_LINK_GRAPH_JOB = 11, ///< Load/save a reference to a link graph job.
REF_DEPOT = 12, ///< Load/save a reference to a depot.
};
/**

View File

@ -610,6 +610,7 @@ public:
SLE_CONDVAR(Station, airport.layout, SLE_UINT8, SLV_145, SL_MAX_VERSION),
SLE_VAR(Station, airport.flags, SLE_UINT64),
SLE_CONDVAR(Station, airport.rotation, SLE_UINT8, SLV_145, SL_MAX_VERSION),
SLE_CONDREF(Station, airport.hangar, REF_DEPOT, SLV_ADD_DEPOTS_TO_HANGARS, SL_MAX_VERSION),
SLEG_CONDARR("storage", _old_st_persistent_storage.storage, SLE_UINT32, 16, SLV_145, SLV_161),
SLE_CONDREF(Station, airport.psa, REF_STORAGE, SLV_161, SL_MAX_VERSION),

View File

@ -665,6 +665,7 @@ public:
SLE_VAR(Vehicle, progress, SLE_UINT8),
SLE_VAR(Vehicle, vehstatus, SLE_UINT8),
SLE_CONDVAR(Vehicle, wait_counter, SLE_UINT16, SLV_EXTENDED_DEPOTS, SL_MAX_VERSION),
SLE_CONDVAR(Vehicle, last_station_visited, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_5),
SLE_CONDVAR(Vehicle, last_station_visited, SLE_UINT16, SLV_5, SL_MAX_VERSION),
SLE_CONDVAR(Vehicle, last_loading_station, SLE_UINT16, SLV_182, SL_MAX_VERSION),
@ -793,7 +794,7 @@ public:
SLE_CONDVAR(Train, flags, SLE_FILE_U8 | SLE_VAR_U16, SLV_2, SLV_100),
SLE_CONDVAR(Train, flags, SLE_UINT16, SLV_100, SL_MAX_VERSION),
SLE_CONDVAR(Train, wait_counter, SLE_UINT16, SLV_136, SL_MAX_VERSION),
SLE_CONDVAR(Train, wait_counter, SLE_UINT16, SLV_136, SLV_EXTENDED_DEPOTS),
SLE_CONDVAR(Train, gv_flags, SLE_UINT16, SLV_139, SL_MAX_VERSION),
};
inline const static SaveLoadCompatTable compat_description = _vehicle_train_sl_compat;
@ -1072,6 +1073,7 @@ struct VEHSChunkHandler : ChunkHandler {
default: SlErrorCorrupt("Invalid vehicle type");
}
if (IsSavegameVersionBefore(SLV_EXTENDED_DEPOTS)) assert(v->type == VEH_TRAIN || v->wait_counter == 0);
SlObject(v, slt);
if (_cargo_count != 0 && IsCompanyBuildableVehicleType(v) && CargoPacket::CanAllocateItem()) {

View File

@ -17,33 +17,16 @@
ScriptDepotList::ScriptDepotList(ScriptTile::TransportType transport_type)
{
EnforceDeityOrCompanyModeValid_Void();
::TileType tile_type;
switch (transport_type) {
default: return;
static_assert(VEH_TRAIN == (int)ScriptTile::TRANSPORT_RAIL);
static_assert(VEH_ROAD == (int)ScriptTile::TRANSPORT_ROAD);
static_assert(VEH_SHIP == (int)ScriptTile::TRANSPORT_WATER);
case ScriptTile::TRANSPORT_ROAD: tile_type = ::MP_ROAD; break;
case ScriptTile::TRANSPORT_RAIL: tile_type = ::MP_RAILWAY; break;
case ScriptTile::TRANSPORT_WATER: tile_type = ::MP_WATER; break;
case ScriptTile::TRANSPORT_AIR: {
/* Hangars are not seen as real depots by the depot code. */
bool is_deity = ScriptCompanyMode::IsDeity();
CompanyID owner = ScriptObject::GetCompany();
for (const Station *st : Station::Iterate()) {
if (is_deity || st->owner == owner) {
for (uint i = 0; i < st->airport.GetNumHangars(); i++) {
this->AddItem(st->airport.GetHangarTile(i).base());
}
}
}
return;
}
}
/* Handle 'standard' depots. */
bool is_deity = ScriptCompanyMode::IsDeity();
CompanyID owner = ScriptObject::GetCompany();
for (const Depot *depot : Depot::Iterate()) {
if ((is_deity || ::GetTileOwner(depot->xy) == owner) && ::IsTileType(depot->xy, tile_type)) this->AddItem(depot->xy.base());
if (!depot->IsInUse() || depot->veh_type != (VehicleType)transport_type ||
(!is_deity && ::GetTileOwner(depot->xy) != owner)) continue;
this->AddItem(depot->xy.base());
}
}

View File

@ -83,7 +83,7 @@
EnforcePrecondition(false, ::IsValidTile(front));
EnforcePrecondition(false, (::TileX(front) == ::TileX(tile)) != (::TileY(front) == ::TileY(tile)));
return ScriptObject::Command<CMD_BUILD_SHIP_DEPOT>::Do(tile, ::TileX(front) == ::TileX(tile) ? AXIS_Y : AXIS_X);
return ScriptObject::Command<CMD_BUILD_SHIP_DEPOT>::Do(tile, ::TileX(front) == ::TileX(tile) ? AXIS_Y : AXIS_X, false, false, INVALID_DEPOT, tile);
}
/* static */ bool ScriptMarine::BuildDock(TileIndex tile, StationID station_id)

View File

@ -245,18 +245,13 @@ static int ScriptOrderPositionToRealOrderPosition(VehicleID vehicle_id, ScriptOr
const Order *order = ::ResolveOrder(vehicle_id, order_position);
if (order == nullptr || order->GetType() == OT_CONDITIONAL) return INVALID_TILE;
const Vehicle *v = ::Vehicle::Get(vehicle_id);
switch (order->GetType()) {
case OT_GOTO_DEPOT: {
/* We don't know where the nearest depot is... (yet) */
if (order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) return INVALID_TILE;
if (v->type != VEH_AIRCRAFT) return ::Depot::Get(order->GetDestination())->xy;
/* Aircraft's hangars are referenced by StationID, not DepotID */
const Station *st = ::Station::Get(order->GetDestination());
if (!st->airport.HasHangar()) return INVALID_TILE;
return st->airport.GetHangarTile(0);
return ::Depot::Get(order->GetDestination())->xy;
}
case OT_GOTO_STATION: {
@ -490,11 +485,10 @@ static int ScriptOrderPositionToRealOrderPosition(VehicleID vehicle_id, ScriptOr
* to a depot (other vehicle types). */
if (::Vehicle::Get(vehicle_id)->type == VEH_AIRCRAFT) {
if (!::IsTileType(destination, MP_STATION)) return false;
order.MakeGoToDepot(::GetStationIndex(destination), odtf, onsf, odaf);
} else {
if (::IsTileType(destination, MP_STATION)) return false;
order.MakeGoToDepot(::GetDepotIndex(destination), odtf, onsf, odaf);
}
order.MakeGoToDepot(::GetDepotIndex(destination), odtf, onsf, odaf);
}
break;
}

View File

@ -145,7 +145,16 @@
DiagDirection entrance_dir = (::TileX(tile) == ::TileX(front)) ? (::TileY(tile) < ::TileY(front) ? DIAGDIR_SE : DIAGDIR_NW) : (::TileX(tile) < ::TileX(front) ? DIAGDIR_SW : DIAGDIR_NE);
return ScriptObject::Command<CMD_BUILD_TRAIN_DEPOT>::Do(tile, (::RailType)ScriptObject::GetRailType(), entrance_dir);
return ScriptObject::Command<CMD_BUILD_TRAIN_DEPOT>::Do(tile, (::RailType)ScriptObject::GetRailType(), entrance_dir, false, false, INVALID_DEPOT, tile);
}
/* static */ bool ScriptRail::RemoveRailDepot(TileIndex start_tile, TileIndex end_tile)
{
EnforceCompanyModeValid(false);
EnforcePrecondition(false, ::IsValidTile(start_tile));
EnforcePrecondition(false, ::IsValidTile(end_tile));
return ScriptObject::Command<CMD_REMOVE_TRAIN_DEPOT>::Do(start_tile, end_tile);
}
/* static */ bool ScriptRail::BuildRailStation(TileIndex tile, RailTrack direction, SQInteger num_platforms, SQInteger platform_length, StationID station_id)

View File

@ -240,6 +240,18 @@ public:
*/
static bool BuildRailDepot(TileIndex tile, TileIndex front);
/**
* Removes rail depots from an area.
* @param start_tile Start tile of the area.
* @param end_tile End tile of the area.
* @pre ScriptMap::IsValidTile(start_tile).
* @pre ScriptMap::IsValidTile(end_tile).
* @game @pre Valid ScriptCompanyMode active in scope.
* @exception ScriptError::ERR_FLAT_LAND_REQUIRED
* @return Whether all depot tiles of the owner in the area have been/can be cleared or not.
*/
static bool RemoveRailDepot(TileIndex start_tile, TileIndex end_tile);
/**
* Build a rail station.
* @param tile Place to build the station.

View File

@ -535,7 +535,7 @@ static bool NeighbourHasReachableRoad(::RoadType rt, TileIndex start_tile, DiagD
DiagDirection entrance_dir = (::TileX(tile) == ::TileX(front)) ? (::TileY(tile) < ::TileY(front) ? DIAGDIR_SE : DIAGDIR_NW) : (::TileX(tile) < ::TileX(front) ? DIAGDIR_SW : DIAGDIR_NE);
return ScriptObject::Command<CMD_BUILD_ROAD_DEPOT>::Do(tile, ScriptObject::GetRoadType(), entrance_dir);
return ScriptObject::Command<CMD_BUILD_ROAD_DEPOT>::Do(tile, ScriptObject::GetRoadType(), entrance_dir, false, false, false, false, INVALID_DEPOT, tile);
}
/* static */ bool ScriptRoad::_BuildRoadStationInternal(TileIndex tile, TileIndex front, RoadVehicleType road_veh_type, bool drive_through, StationID station_id)

View File

@ -60,7 +60,7 @@ ScriptVehicleList_Depot::ScriptVehicleList_Depot(TileIndex tile)
case MP_STATION: // Aircraft
if (!IsAirport(tile)) return;
type = VEH_AIRCRAFT;
dest = GetStationIndex(tile);
dest = GetDepotIndex(tile);
break;
case MP_RAILWAY:

View File

@ -2145,6 +2145,19 @@ static SettingsContainer &GetSettingsTree()
SettingsPage *limitations = main->Add(new SettingsPage(STR_CONFIG_SETTING_LIMITATIONS));
{
SettingsPage *depots = limitations->Add(new SettingsPage(STR_CONFIG_SETTING_DEPOTS));
{
depots->Add(new SettingEntry("depot.depot_spread"));
depots->Add(new SettingEntry("depot.distant_join_depots"));
depots->Add(new SettingEntry("depot.rail_depot_types"));
depots->Add(new SettingEntry("depot.road_depot_types"));
depots->Add(new SettingEntry("depot.water_depot_types"));
depots->Add(new SettingEntry("depot.allow_no_comp_railtype_replacements"));
depots->Add(new SettingEntry("depot.allow_no_comp_roadtype_replacements"));
}
limitations->Add(new SettingEntry("construction.command_pause_level"));
limitations->Add(new SettingEntry("construction.autoslope"));
limitations->Add(new SettingEntry("construction.extra_dynamite"));

View File

@ -380,6 +380,27 @@ static void SpriteZoomMinChanged(int32_t)
MarkWholeScreenDirty();
}
static bool CheckDifferentRailRoadTypesReplacements(int32_t &new_value)
{
if (_game_mode == GM_NORMAL) {
if (new_value == 0) {
ShowErrorMessage(STR_CONFIG_SETTING_REPLACEMENTS_DIFF_TYPE, INVALID_STRING_ID, WL_ERROR);
return false;
}
}
return true;
}
static void InvalidateReplacementWindows(int32_t)
{
InvalidateWindowClassesData(WC_REPLACE_VEHICLE);
}
static void DepotSettingsChanged(int32_t)
{
CloseWindowByClass(WC_BUILD_TOOLBAR);
}
/**
* Update any possible saveload window and delete any newgrf dialogue as
* its widget parts might change. Reinit all windows as it allows access to the

View File

@ -570,6 +570,26 @@ struct StationSettings {
uint8_t station_spread; ///< amount a station may spread
};
enum DepotTypes : uint8_t {
ONLY_STANDARD_DEPOT_TYPE = 1,
ONLY_EXTENDED_DEPOT_TYPE = 2,
BOTH_DEPOT_TYPES = 3,
};
/** Settings related to depots. */
struct DepotSettings {
uint8_t depot_spread; ///< amount a depot may spread
bool adjacent_depots; ///< allow depots to be built directly adjacent to other depots
bool distant_join_depots; ///< allow to join non-adjacent depots
uint8_t rail_depot_types; ///< allowed rail depot types for contruction
uint8_t road_depot_types; ///< allowed road depot types for contruction
uint8_t water_depot_types; ///< allowed water depot types for contruction
bool allow_no_comp_railtype_replacements; ///< allow replacing rail vehicles even if rail type is not compatible
bool allow_no_comp_roadtype_replacements; ///< allow replacing road vehicles even if road type is not compatible
};
/** Default settings for vehicles. */
struct VehicleDefaultSettings {
bool servint_ispercent; ///< service intervals are in percents
@ -603,6 +623,7 @@ struct GameSettings {
EconomySettings economy; ///< settings to change the economy
LinkGraphSettings linkgraph; ///< settings for link graph calculations
StationSettings station; ///< settings related to station management
DepotSettings depot; ///< settings related to depot management
LocaleSettings locale; ///< settings related to used currency/unit system in the current game
};

View File

@ -31,7 +31,7 @@ struct Ship final : public SpecializedVehicle<Ship, VEH_SHIP> {
/** We don't want GCC to zero our struct! It already is zeroed and has an index! */
Ship() : SpecializedVehicleBase() {}
/** We want to 'destruct' the right class. */
virtual ~Ship() { this->PreDestructor(); }
virtual ~Ship();
void MarkDirty() override;
void UpdateDeltaXY() override;
@ -43,7 +43,7 @@ struct Ship final : public SpecializedVehicle<Ship, VEH_SHIP> {
int GetDisplayMaxSpeed() const override { return this->vcache.cached_max_speed / 2; }
int GetCurrentMaxSpeed() const override { return std::min<int>(this->vcache.cached_max_speed, this->current_order.GetMaxSpeed() * 2); }
Money GetRunningCost() const override;
bool IsInDepot() const override { return this->state == TRACK_BIT_DEPOT; }
bool IsInDepot() const override { return HasBit((uint8_t)this->state, TRACK_DEPOT); }
bool Tick() override;
void OnNewCalendarDay() override;
void OnNewEconomyDay() override;

View File

@ -36,6 +36,7 @@
#include "industry.h"
#include "industry_map.h"
#include "ship_cmd.h"
#include "command_func.h"
#include "table/strings.h"
@ -187,8 +188,9 @@ static const Depot *FindClosestShipDepot(const Vehicle *v, uint max_distance)
const Depot *best_depot = nullptr;
uint best_dist_sq = std::numeric_limits<uint>::max();
for (const Depot *depot : Depot::Iterate()) {
if (depot->veh_type != VEH_SHIP || depot->owner != v->owner || !depot->IsInUse()) continue;
const TileIndex tile = depot->xy;
if (IsShipDepotTile(tile) && IsTileOwner(tile, v->owner)) {
const uint dist_sq = DistanceSquare(tile, v->tile);
if (dist_sq < best_dist_sq && dist_sq <= max_distance * max_distance &&
visited_patch_hashes.count(CalculateWaterRegionPatchHash(GetWaterRegionPatchInfo(tile))) > 0) {
@ -196,7 +198,6 @@ static const Depot *FindClosestShipDepot(const Vehicle *v, uint max_distance)
best_depot = depot;
}
}
}
return best_depot;
}
@ -222,7 +223,7 @@ static void CheckIfShipNeedsService(Vehicle *v)
}
v->current_order.MakeGoToDepot(depot->index, ODTFB_SERVICE);
v->SetDestTile(depot->xy);
v->SetDestTile(depot->GetBestDepotTile(v));
SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
}
@ -290,11 +291,12 @@ Trackdir Ship::GetVehicleTrackdir() const
if (this->vehstatus & VS_CRASHED) return INVALID_TRACKDIR;
if (this->IsInDepot()) {
/* We'll assume the ship is facing outwards */
return DiagDirToDiagTrackdir(GetShipDepotDirection(this->tile));
/* Only old depots need it. */
/* We'll assume the ship is facing outwards. */
if (this->state == TRACK_BIT_DEPOT) return DiagDirToDiagTrackdir(GetShipDepotDirection(this->tile));
}
if (this->state == TRACK_BIT_WORMHOLE) {
if (this->state == TRACK_BIT_WORMHOLE || this->IsInDepot()) {
/* ship on aqueduct, so just use its direction and assume a diagonal track */
return DiagDirToDiagTrackdir(DirToDiagDir(this->direction));
}
@ -302,6 +304,15 @@ Trackdir Ship::GetVehicleTrackdir() const
return TrackDirectionToTrackdir(FindFirstTrack(this->state), this->direction);
}
Ship::~Ship()
{
if (CleaningPool()) return;
if (this->IsInDepot()) SetDepotReservation(this->tile, DEPOT_RESERVATION_EMPTY);
this->PreDestructor();
}
void Ship::MarkDirty()
{
this->colourmap = PAL_NONE;
@ -373,6 +384,32 @@ static bool CheckReverseShip(const Ship *v, Trackdir *trackdir = nullptr)
return YapfShipCheckReverse(v, trackdir);
}
static bool CheckPlaceShipOnDepot(TileIndex tile)
{
assert(IsShipDepotTile(tile));
return !IsExtendedDepot(tile) || IsExtendedDepotEmpty(tile);
}
void HandleShipEnterDepot(Ship *v)
{
assert(IsShipDepotTile(v->tile));
if (IsExtendedDepot(v->tile)) {
SetDepotReservation(v->tile, DEPOT_RESERVATION_IN_USE);
v->state |= TRACK_BIT_DEPOT;
v->cur_speed = 0;
v->UpdateCache();
v->UpdateViewport(true, true);
SetWindowClassesDirty(WC_SHIPS_LIST);
SetWindowDirty(WC_VEHICLE_VIEW, v->index);
InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile));
v->StartService();
} else {
VehicleEnterDepot(v);
}
}
static bool CheckShipLeaveDepot(Ship *v)
{
if (!v->IsChainInDepot()) return false;
@ -383,19 +420,23 @@ static bool CheckShipLeaveDepot(Ship *v)
/* We are leaving a depot, but have to go to the exact same one; re-enter */
if (v->current_order.IsType(OT_GOTO_DEPOT) &&
IsShipDepotTile(v->tile) && GetDepotIndex(v->tile) == v->current_order.GetDestination()) {
VehicleEnterDepot(v);
HandleShipEnterDepot(v);
return true;
}
/* Don't leave depot if no destination set */
if (v->dest_tile == 0) return true;
if (IsExtendedDepot(v->tile)) {
SetDepotReservation(v->tile, DEPOT_RESERVATION_EMPTY);
} else {
/* Don't leave depot if another vehicle is already entering/leaving */
/* This helps avoid CPU load if many ships are set to start at the same time */
if (HasVehicleOnPos(v->tile, nullptr, &EnsureNoMovingShipProc)) return true;
TileIndex tile = v->tile;
Axis axis = GetShipDepotAxis(tile);
bool reverse = false;
DiagDirection north_dir = ReverseDiagDir(AxisToDiagDir(axis));
TileIndex north_neighbour = TileAdd(tile, TileOffsByDiagDir(north_dir));
@ -422,14 +463,23 @@ static bool CheckShipLeaveDepot(Ship *v)
v->state = AxisToTrackBits(axis);
v->vehstatus &= ~VS_HIDDEN;
/* Leave towards south if reverse. */
v->rotation = v->direction = DiagDirToDir(reverse ? south_dir : north_dir);
v->state = AxisToTrackBits(axis);
v->vehstatus &= ~VS_HIDDEN;
}
v->state &= ~TRACK_BIT_DEPOT;
v->cur_speed = 0;
v->UpdateViewport(true, true);
SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
DepotID depot_id = GetDepotIndex(v->tile);
SetWindowDirty(WC_VEHICLE_DEPOT, depot_id);
VehicleServiceInDepot(v);
v->LeaveUnbunchingDepot();
v->PlayLeaveStationSound();
InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
InvalidateWindowData(WC_VEHICLE_DEPOT, depot_id);
SetWindowClassesDirty(WC_SHIPS_LIST);
return false;
@ -494,6 +544,13 @@ static void ShipArrivesAt(const Vehicle *v, Station *st)
*/
static Track ChooseShipTrack(Ship *v, TileIndex tile, TrackBits tracks)
{
/* Before choosing a track, if close to the destination station or depot (not an oil rig)... */
if (DistanceManhattan(v->dest_tile, tile) <= 5 && (v->current_order.IsType(OT_GOTO_DEPOT) &&
(!IsShipDepotTile(v->dest_tile) || (IsExtendedDepotTile(v->dest_tile) && !IsExtendedDepotEmpty(v->dest_tile))))) {
/* Try to get a depot tile. */
v->dest_tile = Depot::Get(v->current_order.GetDestination())->GetBestDepotTile(v);
}
bool path_found = true;
Track track;
@ -705,6 +762,8 @@ static void ShipController(Ship *v)
if (v->vehstatus & VS_STOPPED) return;
if (v->ContinueServicing()) return;
if (ProcessOrders(v) && CheckReverseShip(v)) return ReverseShip(v);
v->HandleLoading();
@ -764,13 +823,6 @@ static void ShipController(Ship *v)
UpdateVehicleTimetable(v, true);
v->IncrementRealOrderIndex();
v->current_order.MakeDummy();
} else if (v->current_order.IsType(OT_GOTO_DEPOT) &&
v->dest_tile == gp.new_tile) {
/* Depot orders really need to reach the tile */
if ((gp.x & 0xF) == 8 && (gp.y & 0xF) == 8) {
VehicleEnterDepot(v);
return;
}
} else if (v->current_order.IsType(OT_GOTO_STATION) && IsDockingTile(gp.new_tile)) {
/* Process station in the orderlist. */
Station *st = Station::Get(v->current_order.GetDestination());
@ -791,6 +843,18 @@ static void ShipController(Ship *v)
/* New tile */
if (!IsValidTile(gp.new_tile)) return ReverseShip(v);
if (v->current_order.IsType(OT_GOTO_DEPOT) &&
IsShipDepotTile(gp.new_tile) &&
GetOtherShipDepotTile(gp.new_tile) == gp.old_tile &&
v->current_order.GetDestination() == GetDepotIndex(gp.new_tile)) {
if (CheckPlaceShipOnDepot(v->tile)) {
HandleShipEnterDepot(v);
v->UpdatePosition();
v->UpdateViewport(true, true);
return;
}
}
const DiagDirection diagdir = DiagdirBetweenTiles(gp.old_tile, gp.new_tile);
assert(diagdir != INVALID_DIAGDIR);
const TrackBits tracks = GetAvailShipTracks(gp.new_tile, diagdir);
@ -894,10 +958,22 @@ void Ship::SetDestTile(TileIndex tile)
*/
CommandCost CmdBuildShip(DoCommandFlag flags, TileIndex tile, const Engine *e, Vehicle **ret)
{
tile = GetShipDepotNorthTile(tile);
assert(IsShipDepotTile(tile));
if (!(flags & DC_AUTOREPLACE)) {
std::vector<TileIndex> *depot_tiles = &(Depot::GetByTile(tile)->depot_tiles);
tile = INVALID_TILE;
for (std::vector<TileIndex>::iterator it = depot_tiles->begin(); it != depot_tiles->end(); ++it) {
if (CheckPlaceShipOnDepot(*it)) {
tile = *it;
break;
}
}
if (tile == INVALID_TILE) return_cmd_error(STR_ERROR_NO_FREE_DEPOT);
}
if (flags & DC_EXEC) {
int x;
int y;
bool is_extended_depot = IsExtendedDepot(tile);
TileIndexDiffC offset = TileIndexDiffCByDiagDir(ReverseDiagDir(GetShipDepotDirection(tile)));
const ShipVehicleInfo *svi = &e->u.ship;
@ -906,14 +982,22 @@ CommandCost CmdBuildShip(DoCommandFlag flags, TileIndex tile, const Engine *e, V
v->owner = _current_company;
v->tile = tile;
x = TileX(tile) * TILE_SIZE + TILE_SIZE / 2;
y = TileY(tile) * TILE_SIZE + TILE_SIZE / 2;
v->x_pos = x;
v->y_pos = y;
v->z_pos = GetSlopePixelZ(x, y);
v->x_pos = TileX(tile) * TILE_SIZE + TILE_SIZE / 2 + offset.x * (TILE_SIZE / 2 - 1);
v->y_pos = TileY(tile) * TILE_SIZE + TILE_SIZE / 2 + offset.y * (TILE_SIZE / 2 - 1);
v->z_pos = GetSlopePixelZ(v->x_pos, v->y_pos);
v->state = TRACK_BIT_DEPOT;
if (is_extended_depot) {
v->state |= AxisToTrackBits(GetShipDepotAxis(tile));
v->direction = AxisToDirection(GetShipDepotAxis(v->tile));
SetDepotReservation(v->tile, DEPOT_RESERVATION_FULL_STOPPED_VEH);
} else {
v->vehstatus |= VS_HIDDEN;
}
v->rotation = v->direction;
v->UpdateDeltaXY();
v->vehstatus = VS_HIDDEN | VS_STOPPED | VS_DEFPAL;
v->vehstatus |= VS_STOPPED | VS_DEFPAL;
v->spritenum = svi->image_index;
v->cargo_type = e->GetDefaultCargoType();
@ -929,8 +1013,6 @@ CommandCost CmdBuildShip(DoCommandFlag flags, TileIndex tile, const Engine *e, V
v->reliability_spd_dec = e->reliability_spd_dec;
v->max_age = e->GetLifeLengthInDays();
v->state = TRACK_BIT_DEPOT;
v->SetServiceInterval(Company::Get(_current_company)->settings.vehicle.servint_ships);
v->date_of_last_service = TimerGameEconomy::date;
v->date_of_last_service_newgrf = TimerGameCalendar::date;
@ -951,6 +1033,8 @@ CommandCost CmdBuildShip(DoCommandFlag flags, TileIndex tile, const Engine *e, V
v->InvalidateNewGRFCacheOfChain();
v->UpdatePosition();
if (is_extended_depot) v->MarkDirty();
}
return CommandCost();
@ -961,5 +1045,5 @@ ClosestDepot Ship::FindClosestDepot()
const Depot *depot = FindClosestShipDepot(this, MAX_SHIP_DEPOT_SEARCH_DISTANCE);
if (depot == nullptr) return ClosestDepot();
return ClosestDepot(depot->xy, depot->index);
return ClosestDepot(depot->GetBestDepotTile(this), depot->index);
}

View File

@ -278,6 +278,13 @@ static SigFlags ExploreSegment(Owner owner)
if (GetTileOwner(tile) != owner) continue; // do not propagate signals on others' tiles (remove for tracksharing)
if (IsRailDepot(tile)) {
if (IsExtendedRailDepot(tile)) {
assert(enterdir != INVALID_DIAGDIR);
if (DiagDirToDiagTrack(enterdir) != GetRailDepotTrack(tile)) continue; // different axis
if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, nullptr, &TrainOnTileEnum)) flags |= SF_TRAIN;
tile += TileOffsByDiagDir(exitdir);
break;
}
if (enterdir == INVALID_DIAGDIR) { // from 'inside' - train just entered or left the depot
if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, nullptr, &TrainOnTileEnum)) flags |= SF_TRAIN;
exitdir = GetRailDepotDirection(tile);
@ -496,8 +503,14 @@ static SigSegState UpdateSignalsInBuffer(Owner owner)
case MP_RAILWAY:
if (IsRailDepot(tile)) {
/* 'optimization assert' do not try to update signals in other cases */
if (IsExtendedRailDepot(tile)) {
dir = GetRailDepotDirection(tile);
_tbdset.Add(tile, dir);
_tbdset.Add(tile + TileOffsByDiagDir(dir), ReverseDiagDir(dir));
} else {
assert(dir == INVALID_DIAGDIR || dir == GetRailDepotDirection(tile));
_tbdset.Add(tile, INVALID_DIAGDIR); // start from depot inside
}
break;
}
[[fallthrough]];

View File

@ -26,6 +26,7 @@
#include "core/random_func.hpp"
#include "linkgraph/linkgraph.h"
#include "linkgraph/linkgraphschedule.h"
#include "depot_base.h"
#include "table/strings.h"
@ -268,43 +269,6 @@ void Station::MarkTilesDirty(bool cargo_change) const
}
}
/* virtual */ uint Station::GetPlatformLength(TileIndex tile) const
{
assert(this->TileBelongsToRailStation(tile));
TileIndexDiff delta = (GetRailStationAxis(tile) == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1));
TileIndex t = tile;
uint len = 0;
do {
t -= delta;
len++;
} while (IsCompatibleTrainStationTile(t, tile));
t = tile;
do {
t += delta;
len++;
} while (IsCompatibleTrainStationTile(t, tile));
return len - 1;
}
/* virtual */ uint Station::GetPlatformLength(TileIndex tile, DiagDirection dir) const
{
TileIndex start_tile = tile;
uint length = 0;
assert(IsRailStationTile(tile));
assert(dir < DIAGDIR_END);
do {
length++;
tile += TileOffsByDiagDir(dir);
} while (IsCompatibleTrainStationTile(tile, start_tile));
return length;
}
/**
* Get the catchment size of an individual station tile.
* @param tile Station tile to get catchment size of.
@ -727,6 +691,56 @@ Money AirportMaintenanceCost(Owner owner)
return total_cost >> 3;
}
/**
* Create a hangar on the airport.
*/
void Airport::AddHangar()
{
assert(this->hangar == nullptr);
assert(Depot::CanAllocateItem());
assert(this->GetNumHangars() > 0);
Station *st = Station::GetByTile(this->GetHangarTile(0));
this->hangar = new Depot(this->GetHangarTile(0), VEH_AIRCRAFT, st->owner, st);
this->hangar->build_date = st->build_date;
this->hangar->town = st->town;
this->hangar->ta.tile = st->airport.tile;
this->hangar->ta.w = st->airport.w;
this->hangar->ta.h = st->airport.h;
for (uint i = 0; i < this->GetNumHangars(); i++) {
this->hangar->depot_tiles.push_back(this->GetHangarTile(i));
}
}
/**
* Delete the hangar on the airport.
*/
void Airport::RemoveHangar()
{
if (this->hangar == nullptr) return;
/* TODO Check this. */
RemoveOrderFromAllVehicles(OT_GOTO_DEPOT, this->hangar->index);
for (Aircraft *a : Aircraft::Iterate()) {
if (!a->IsNormalAircraft()) continue;
if (!a->current_order.IsType(OT_GOTO_DEPOT)) continue;
if (a->current_order.GetDestination() != this->hangar->index) continue;
a->current_order.MakeDummy();
}
this->hangar->Disuse();
delete this->hangar;
this->hangar = nullptr;
}
DepotID GetHangarIndex(TileIndex t) {
assert(IsAirportTile(t));
assert(Station::GetByTile(t)->airport.hangar != nullptr);
return Station::GetByTile(t)->airport.hangar->index;
}
bool StationCompare::operator() (const Station *lhs, const Station *rhs) const
{
return lhs->index < rhs->index;

View File

@ -18,6 +18,7 @@
#include "linkgraph/linkgraph_type.h"
#include "newgrf_storage.h"
#include "bitmap_type.h"
#include "depot_type.h"
static const uint8_t INITIAL_STATION_RATING = 175;
static const uint8_t MAX_STATION_RATING = 255;
@ -294,6 +295,7 @@ struct Airport : public TileArea {
uint8_t type; ///< Type of this airport, @see AirportTypes
uint8_t layout; ///< Airport layout number.
Direction rotation; ///< How this airport is rotated.
Depot *hangar; ///< The corresponding hangar of this airport, if any.
PersistentStorage *psa; ///< Persistent storage for NewGRF airports.
@ -404,6 +406,9 @@ struct Airport : public TileArea {
return num;
}
void AddHangar();
void RemoveHangar();
private:
/**
* Retrieve hangar information of a hangar at a given tile.
@ -483,9 +488,6 @@ public:
void MoveSign(TileIndex new_xy) override;
void AfterStationTileSetChange(bool adding, StationType type);
uint GetPlatformLength(TileIndex tile, DiagDirection dir) const override;
uint GetPlatformLength(TileIndex tile) const override;
void RecomputeCatchment(bool no_clear_nearby_lists = false);
static void RecomputeCatchmentForAll();

View File

@ -67,6 +67,8 @@
#include "timer/timer_game_tick.h"
#include "cheat_type.h"
#include "road_func.h"
#include "depot_base.h"
#include "platform_func.h"
#include "widgets/station_widget.h"
@ -1239,9 +1241,10 @@ CommandCost FindJoiningWaypoint(StationID existing_waypoint, StationID waypoint_
static void FreeTrainReservation(Train *v)
{
FreeTrainTrackReservation(v);
if (IsRailStationTile(v->tile)) SetRailStationPlatformReservation(v->tile, TrackdirToExitdir(v->GetVehicleTrackdir()), false);
if (IsPlatformTile(v->tile)) SetPlatformReservation(v->tile, TrackdirToExitdir(v->GetVehicleTrackdir()), false);
v = v->Last();
if (IsRailStationTile(v->tile)) SetRailStationPlatformReservation(v->tile, TrackdirToExitdir(ReverseTrackdir(v->GetVehicleTrackdir())), false);
if (IsPlatformTile(v->tile)) SetPlatformReservation(v->tile, TrackdirToExitdir(ReverseTrackdir(v->GetVehicleTrackdir())), false);
}
/**
@ -1250,10 +1253,10 @@ static void FreeTrainReservation(Train *v)
*/
static void RestoreTrainReservation(Train *v)
{
if (IsRailStationTile(v->tile)) SetRailStationPlatformReservation(v->tile, TrackdirToExitdir(v->GetVehicleTrackdir()), true);
if (IsPlatformTile(v->tile)) SetPlatformReservation(v->tile, TrackdirToExitdir(v->GetVehicleTrackdir()), true);
TryPathReserve(v, true, true);
v = v->Last();
if (IsRailStationTile(v->tile)) SetRailStationPlatformReservation(v->tile, TrackdirToExitdir(ReverseTrackdir(v->GetVehicleTrackdir())), true);
if (IsPlatformTile(v->tile)) SetPlatformReservation(v->tile, TrackdirToExitdir(ReverseTrackdir(v->GetVehicleTrackdir())), true);
}
/**
@ -1521,21 +1524,21 @@ CommandCost CmdBuildRailStation(DoCommandFlag flags, TileIndex tile_org, RailTyp
TileIndex platform_end = tile;
/* We can only account for tiles that are reachable from this tile, so ignore primarily blocked tiles while finding the platform begin and end. */
for (TileIndex next_tile = platform_begin - tile_offset; IsCompatibleTrainStationTile(next_tile, platform_begin); next_tile -= tile_offset) {
for (TileIndex next_tile = platform_begin - tile_offset; IsCompatiblePlatformTile(next_tile, platform_begin); next_tile -= tile_offset) {
platform_begin = next_tile;
}
for (TileIndex next_tile = platform_end + tile_offset; IsCompatibleTrainStationTile(next_tile, platform_end); next_tile += tile_offset) {
for (TileIndex next_tile = platform_end + tile_offset; IsCompatiblePlatformTile(next_tile, platform_end); next_tile += tile_offset) {
platform_end = next_tile;
}
/* If there is at least on reservation on the platform, we reserve the whole platform. */
bool reservation = false;
for (TileIndex t = platform_begin; !reservation && t <= platform_end; t += tile_offset) {
reservation = HasStationReservation(t);
reservation = HasPlatformReservation(t);
}
if (reservation) {
SetRailStationPlatformReservation(platform_begin, dir, true);
SetPlatformReservation(platform_begin, dir, true);
}
}
@ -2539,6 +2542,8 @@ CommandCost CmdBuildAirport(DoCommandFlag flags, TileIndex tile, uint8_t airport
/* Check if a valid, buildable airport was chosen for construction */
const AirportSpec *as = AirportSpec::Get(airport_type);
if (!as->depots.empty() && !Depot::CanAllocateItem()) return CMD_ERROR;
if (!as->IsAvailable() || layout >= as->layouts.size()) return CMD_ERROR;
if (!as->IsWithinMapBounds(layout, tile)) return CMD_ERROR;
@ -2632,6 +2637,8 @@ CommandCost CmdBuildAirport(DoCommandFlag flags, TileIndex tile, uint8_t airport
AirportTileAnimationTrigger(st, iter, AAT_BUILT);
}
if (!as->depots.empty()) st->airport.AddHangar();
UpdateAirplanesOnNewStation(st);
Company::Get(st->owner)->infrastructure.airport++;
@ -2674,11 +2681,7 @@ static CommandCost RemoveAirport(TileIndex tile, DoCommandFlag flags)
}
if (flags & DC_EXEC) {
for (uint i = 0; i < st->airport.GetNumHangars(); ++i) {
TileIndex tile_cur = st->airport.GetHangarTile(i);
OrderBackup::Reset(tile_cur, false);
CloseWindowById(WC_VEHICLE_DEPOT, tile_cur);
}
st->airport.RemoveHangar();
/* The noise level is the noise from the airport and reduce it to account for the distance to the town center.
* And as for construction, always remove it, even if the setting is not set, in order to avoid the
@ -3692,8 +3695,8 @@ static bool ClickTile_Station(TileIndex tile)
if (bst->facilities & FACIL_WAYPOINT) {
ShowWaypointWindow(Waypoint::From(bst));
} else if (IsHangar(tile)) {
const Station *st = Station::From(bst);
ShowDepotWindow(st->airport.GetHangarTile(st->airport.GetHangarNum(tile)), VEH_AIRCRAFT);
assert(Station::From(bst)->airport.HasHangar());
ShowDepotWindow(Station::From(bst)->airport.hangar->index);
} else {
ShowStationViewWindow(bst->index);
}

View File

@ -521,28 +521,6 @@ inline TrackBits GetRailStationTrackBits(Tile t)
return AxisToTrackBits(GetRailStationAxis(t));
}
/**
* Check if a tile is a valid continuation to a railstation tile.
* The tile \a test_tile is a valid continuation to \a station_tile, if all of the following are true:
* \li \a test_tile is a rail station tile
* \li the railtype of \a test_tile is compatible with the railtype of \a station_tile
* \li the tracks on \a test_tile and \a station_tile are in the same direction
* \li both tiles belong to the same station
* \li \a test_tile is not blocked (@see IsStationTileBlocked)
* @param test_tile Tile to test
* @param station_tile Station tile to compare with
* @pre IsRailStationTile(station_tile)
* @return true if the two tiles are compatible
*/
inline bool IsCompatibleTrainStationTile(Tile test_tile, Tile station_tile)
{
assert(IsRailStationTile(station_tile));
return IsRailStationTile(test_tile) && !IsStationTileBlocked(test_tile) &&
IsCompatibleRail(GetRailType(test_tile), GetRailType(station_tile)) &&
GetRailStationAxis(test_tile) == GetRailStationAxis(station_tile) &&
GetStationIndex(test_tile) == GetStationIndex(station_tile);
}
/**
* Get the reservation state of the rail station
* @pre HasStationRail(t)

View File

@ -21,6 +21,9 @@ static bool CheckRoadSide(int32_t &new_value);
static bool CheckDynamicEngines(int32_t &new_value);
static void StationCatchmentChanged(int32_t new_value);
static void MaxVehiclesChanged(int32_t new_value);
static bool CheckDifferentRailRoadTypesReplacements(int32_t &new_value);
static void InvalidateReplacementWindows(int32_t new_value);
static void DepotSettingsChanged(int32_t new_value);
static const SettingVariant _game_settings_table[] = {
[post-amble]
@ -145,6 +148,98 @@ str = STR_CONFIG_SETTING_DISTANT_JOIN_STATIONS
strhelp = STR_CONFIG_SETTING_DISTANT_JOIN_STATIONS_HELPTEXT
post_cb = [](auto) { CloseWindowById(WC_SELECT_STATION, 0); }
[SDT_VAR]
var = depot.rail_depot_types
type = SLE_UINT8
from = SLV_EXTENDED_DEPOTS
flags = SF_GUI_DROPDOWN
def = 1
min = 1
max = 3
interval = 1
str = STR_CONFIG_SETTING_RAIL_DEPOT_TYPES
strhelp = STR_CONFIG_SETTING_RAIL_DEPOT_TYPES_HELPTEXT
strval = STR_CONFIG_SETTING_ONLY_STANDARD_DEPOT
post_cb = DepotSettingsChanged
cat = SC_EXPERT
[SDT_VAR]
var = depot.road_depot_types
type = SLE_UINT8
from = SLV_EXTENDED_DEPOTS
flags = SF_GUI_DROPDOWN
def = 1
min = 1
max = 3
interval = 1
str = STR_CONFIG_SETTING_ROAD_DEPOT_TYPES
strhelp = STR_CONFIG_SETTING_ROAD_DEPOT_TYPES_HELPTEXT
strval = STR_CONFIG_SETTING_ONLY_STANDARD_DEPOT
post_cb = DepotSettingsChanged
cat = SC_EXPERT
[SDT_VAR]
var = depot.water_depot_types
type = SLE_UINT8
from = SLV_EXTENDED_DEPOTS
flags = SF_GUI_DROPDOWN
def = 1
min = 1
max = 3
interval = 1
str = STR_CONFIG_SETTING_WATER_DEPOT_TYPES
strhelp = STR_CONFIG_SETTING_WATER_DEPOT_TYPES_HELPTEXT
strval = STR_CONFIG_SETTING_ONLY_STANDARD_DEPOT
post_cb = DepotSettingsChanged
cat = SC_EXPERT
[SDT_BOOL]
var = depot.adjacent_depots
from = SLV_DEPOT_SPREAD
def = true
cat = SC_EXPERT
[SDT_BOOL]
var = depot.distant_join_depots
from = SLV_DEPOT_SPREAD
def = true
str = STR_CONFIG_SETTING_DISTANT_JOIN_DEPOTS
strhelp = STR_CONFIG_SETTING_DISTANT_JOIN_DEPOTS_HELPTEXT
post_cb = [](auto) { CloseWindowByClass(WC_SELECT_DEPOT); }
[SDT_VAR]
var = depot.depot_spread
type = SLE_UINT8
from = SLV_DEPOT_SPREAD
def = 1
min = 1
max = 64
str = STR_CONFIG_SETTING_DEPOT_SPREAD
strhelp = STR_CONFIG_SETTING_DEPOT_SPREAD_HELPTEXT
strval = STR_CONFIG_SETTING_TILE_LENGTH
post_cb = [](auto) { CloseWindowByClass(WC_SELECT_DEPOT); }
cat = SC_BASIC
[SDT_BOOL]
var = depot.allow_no_comp_railtype_replacements
from = SLV_ALLOW_INCOMPATIBLE_REPLACEMENTS
def = false
str = STR_CONFIG_SETTING_REPLACE_INCOMPATIBLE_RAIL
strhelp = STR_CONFIG_SETTING_REPLACE_INCOMPATIBLE_RAIL_HELPTEXT
pre_cb = CheckDifferentRailRoadTypesReplacements
post_cb = InvalidateReplacementWindows
cat = SC_EXPERT
[SDT_BOOL]
var = depot.allow_no_comp_roadtype_replacements
from = SLV_ALLOW_INCOMPATIBLE_REPLACEMENTS
def = false
str = STR_CONFIG_SETTING_REPLACE_INCOMPATIBLE_ROAD
strhelp = STR_CONFIG_SETTING_REPLACE_INCOMPATIBLE_ROAD_HELPTEXT
pre_cb = CheckDifferentRailRoadTypesReplacements
post_cb = InvalidateReplacementWindows
cat = SC_EXPERT
[SDT_OMANY]
var = vehicle.road_side
type = SLE_UINT8

View File

@ -33,18 +33,18 @@ static const DrawTileSeqStruct _depot_gfx_NW[] = {
TILE_SEQ_END()
};
static const DrawTileSprites _depot_gfx_table[] = {
static const DrawTileSprites _depot_gfx_gui_table[] = {
{ {SPR_FLAT_GRASS_TILE, PAL_NONE}, _depot_gfx_NE },
{ {SPR_RAIL_TRACK_Y, PAL_NONE}, _depot_gfx_SE },
{ {SPR_RAIL_TRACK_X, PAL_NONE}, _depot_gfx_SW },
{ {SPR_FLAT_GRASS_TILE, PAL_NONE}, _depot_gfx_NW }
};
static const DrawTileSprites _depot_invisible_gfx_table[] = {
{ {SPR_RAIL_TRACK_X, PAL_NONE}, _depot_gfx_NE },
{ {SPR_RAIL_TRACK_Y, PAL_NONE}, _depot_gfx_SE },
{ {SPR_RAIL_TRACK_X, PAL_NONE}, _depot_gfx_SW },
{ {SPR_RAIL_TRACK_Y, PAL_NONE}, _depot_gfx_NW }
static const DrawTileSprites _depot_gfx_table[] = {
{ {SPR_FLAT_GRASS_TILE, PAL_NONE}, _depot_gfx_NE },
{ {SPR_FLAT_GRASS_TILE, PAL_NONE}, _depot_gfx_SE },
{ {SPR_FLAT_GRASS_TILE, PAL_NONE}, _depot_gfx_SW },
{ {SPR_FLAT_GRASS_TILE, PAL_NONE}, _depot_gfx_NW }
};
#undef TILE_SEQ_LINE

View File

@ -19,9 +19,10 @@
/** The returned bits of VehicleEnterTile. */
enum VehicleEnterTileStatus {
VETS_ENTERED_STATION = 1, ///< The vehicle entered a station
VETS_ENTERED_WORMHOLE = 2, ///< The vehicle either entered a bridge, tunnel or depot tile (this includes the last tile of the bridge/tunnel)
VETS_CANNOT_ENTER = 3, ///< The vehicle cannot enter the tile
VETS_ENTERED_STATION = 1, ///< The vehicle entered a station.
VETS_ENTERED_WORMHOLE = 2, ///< The vehicle either entered a bridge, tunnel or depot tile (this includes the last tile of the bridge/tunnel).
VETS_CANNOT_ENTER = 3, ///< The vehicle cannot enter the tile.
VETS_ENTERED_DEPOT_PLATFORM = 4, ///< The vehicle entered a depot platform.
/**
* Shift the VehicleEnterTileStatus this many bits
@ -32,10 +33,11 @@ enum VehicleEnterTileStatus {
VETS_STATION_MASK = 0xFFFF << VETS_STATION_ID_OFFSET,
/** Bit sets of the above specified bits */
VETSB_CONTINUE = 0, ///< The vehicle can continue normally
VETSB_ENTERED_STATION = 1 << VETS_ENTERED_STATION, ///< The vehicle entered a station
VETSB_ENTERED_WORMHOLE = 1 << VETS_ENTERED_WORMHOLE, ///< The vehicle either entered a bridge, tunnel or depot tile (this includes the last tile of the bridge/tunnel)
VETSB_CANNOT_ENTER = 1 << VETS_CANNOT_ENTER, ///< The vehicle cannot enter the tile
VETSB_CONTINUE = 0, ///< The vehicle can continue normally.
VETSB_ENTERED_STATION = 1 << VETS_ENTERED_STATION, ///< The vehicle entered a station.
VETSB_ENTERED_WORMHOLE = 1 << VETS_ENTERED_WORMHOLE, ///< The vehicle either entered a bridge, tunnel or depot tile (this includes the last tile of the bridge/tunnel).
VETSB_CANNOT_ENTER = 1 << VETS_CANNOT_ENTER, ///< The vehicle cannot enter the tile.
VETSB_ENTERED_DEPOT_PLATFORM = 1 << VETS_ENTERED_DEPOT_PLATFORM, ///< The vehicle entered a depot platform.
};
DECLARE_ENUM_AS_BIT_SET(VehicleEnterTileStatus)

View File

@ -26,6 +26,8 @@ void VpStartDragging(ViewportDragDropSelectionProcess process);
void VpStartPlaceSizing(TileIndex tile, ViewportPlaceMethod method, ViewportDragDropSelectionProcess process);
void VpSetPresizeRange(TileIndex from, TileIndex to);
void VpSetPlaceSizingLimit(int limit);
void VpSetPlaceFixedSize(uint8_t fixed_size);
void VpResetFixedSize();
void UpdateTileSelection();

View File

@ -60,6 +60,7 @@ struct TileHighlightData {
Point selstart; ///< The location where the dragging started.
Point selend; ///< The location where the drag currently ends.
uint8_t sizelimit; ///< Whether the selection is limited in length, and what the maximum length is.
uint8_t fixed_size; ///< The fixed length for one of the sides.
HighLightStyle drawstyle; ///< Lower bits 0-3 are reserved for detailed highlight information.
HighLightStyle next_drawstyle; ///< Queued, but not yet drawn style.

View File

@ -78,6 +78,7 @@ public:
static constexpr TimerGameTick::Ticks STATION_RATING_TICKS = 185; ///< Cycle duration for updating station rating.
static constexpr TimerGameTick::Ticks STATION_ACCEPTANCE_TICKS = 250; ///< Cycle duration for updating station acceptance.
static constexpr TimerGameTick::Ticks STATION_LINKGRAPH_TICKS = 504; ///< Cycle duration for cleaning dead links.
static constexpr TimerGameTick::Ticks DEPOT_REMOVAL_TICKS = 250; ///< Cycle duration for cleaning demolished depots.
static constexpr TimerGameTick::Ticks CARGO_AGING_TICKS = 185; ///< Cycle duration for aging cargo.
static constexpr TimerGameTick::Ticks INDUSTRY_PRODUCE_TICKS = 256; ///< Cycle duration for industry production.
static constexpr TimerGameTick::Ticks TOWN_GROWTH_TICKS = 70; ///< Cycle duration for towns trying to grow (this originates from the size of the town array in TTD).

View File

@ -44,6 +44,7 @@
#include "core/random_func.hpp"
#include "core/backup_type.hpp"
#include "depot_base.h"
#include "depot_func.h"
#include "object_map.h"
#include "object_base.h"
#include "ai/ai.hpp"
@ -1254,7 +1255,7 @@ static bool CanRoadContinueIntoNextTile(const Town *t, const TileIndex tile, con
/* If the next tile is a road depot, allow if it's facing the right way. */
if (IsTileType(next_tile, MP_ROAD)) {
return IsRoadDepot(next_tile) && GetRoadDepotDirection(next_tile) == ReverseDiagDir(road_dir);
return IsRoadDepot(next_tile) && (GetRoadBits(next_tile, RTT_ROAD) & DiagDirToRoadBits(ReverseDiagDir(road_dir))) != ROAD_NONE;
}
/* If the next tile is a railroad track, check if towns are allowed to build level crossings.
@ -3005,6 +3006,8 @@ CommandCost CmdRenameTown(DoCommandFlag flags, TownID town_id, const std::string
ClearAllStationCachedNames();
ClearAllIndustryCachedNames();
UpdateAllStationVirtCoords();
UpdateAllDepotVirtCoords();
RebuildViewportKdtree();
}
return CommandCost();
}

View File

@ -14,7 +14,8 @@
/**
* These are used to specify a single track.
* Can be translated to a trackbit with TrackToTrackbit
* Can be translated to a trackbit with TrackToTrackbit.
* TRACK_WORMHOLE and TRACK_DEPOT do not represent single tracks but states; they cannot be translated to trackbits.
*/
enum Track : uint8_t {
TRACK_BEGIN = 0, ///< Used for iterations
@ -24,7 +25,9 @@ enum Track : uint8_t {
TRACK_LOWER = 3, ///< Track in the lower corner of the tile (south)
TRACK_LEFT = 4, ///< Track in the left corner of the tile (west)
TRACK_RIGHT = 5, ///< Track in the right corner of the tile (east)
TRACK_END, ///< Used for iterations
TRACK_END = 6, ///< Used for iterations
TRACK_WORMHOLE = TRACK_END,
TRACK_DEPOT = 7,
INVALID_TRACK = 0xFF, ///< Flag for an invalid track
};
@ -49,8 +52,8 @@ enum TrackBits : uint8_t {
TRACK_BIT_3WAY_NW = TRACK_BIT_Y | TRACK_BIT_UPPER | TRACK_BIT_LEFT, ///< "Arrow" to the north-west
TRACK_BIT_ALL = TRACK_BIT_CROSS | TRACK_BIT_HORZ | TRACK_BIT_VERT, ///< All possible tracks
TRACK_BIT_MASK = 0x3FU, ///< Bitmask for the first 6 bits
TRACK_BIT_WORMHOLE = 0x40U, ///< Bitflag for a wormhole (used for tunnels)
TRACK_BIT_DEPOT = 0x80U, ///< Bitflag for a depot
TRACK_BIT_WORMHOLE = 1U << TRACK_WORMHOLE, ///< Bitflag for a wormhole (used for tunnels)
TRACK_BIT_DEPOT = 1U << TRACK_DEPOT, ///< Bitflag for a depot
INVALID_TRACK_BIT = 0xFF, ///< Flag for an invalid trackbits value
};
DECLARE_ENUM_AS_BIT_SET(TrackBits)

View File

@ -21,6 +21,9 @@
struct Train;
static const uint8_t _vehicle_initial_x_fract[4] = {10, 8, 4, 8};
static const uint8_t _vehicle_initial_y_fract[4] = { 8, 4, 8, 10};
/** Rail vehicle flags. */
enum VehicleRailFlags {
VRF_REVERSING = 0,
@ -66,7 +69,7 @@ int GetTrainStopLocation(StationID station_id, TileIndex tile, const Train *v, i
void GetTrainSpriteSize(EngineID engine, uint &width, uint &height, int &xoffs, int &yoffs, EngineImageType image_type);
bool TrainOnCrossing(TileIndex tile);
void NormalizeTrainVehInDepot(const Train *u);
void NormalizeTrainVehInDepot(const Train *u, DoCommandFlag flags = DC_EXEC);
/** Variables that are cached to improve performance and such */
struct TrainCache {
@ -120,7 +123,7 @@ struct Train final : public GroundVehicle<Train, VEH_TRAIN> {
Money GetRunningCost() const override;
int GetCursorImageOffset() const;
int GetDisplayImageWidth(Point *offset = nullptr) const;
bool IsInDepot() const override { return this->track == TRACK_BIT_DEPOT; }
bool IsInDepot() const override { return HasBit((uint8_t)this->track, TRACK_DEPOT); }
bool Tick() override;
void OnNewCalendarDay() override;
void OnNewEconomyDay() override;
@ -353,4 +356,8 @@ protected: // These functions should not be called outside acceleration code.
}
};
bool HasCompatibleDepotTile(TileIndex tile, const Train *t);
bool HandleTrainEnterDepot(Train *v);
bool CheckReverseTrain(const Train *v);
#endif /* TRAIN_H */

View File

@ -38,6 +38,10 @@
#include "misc_cmd.h"
#include "timer/timer_game_calendar.h"
#include "timer/timer_game_economy.h"
#include "depot_base.h"
#include "platform_func.h"
#include "depot_map.h"
#include "train_placement.h"
#include "table/strings.h"
#include "table/train_sprites.h"
@ -51,9 +55,6 @@ static TileIndex TrainApproachingCrossingTile(const Train *v);
static void CheckIfTrainNeedsService(Train *v);
static void CheckNextTrainTile(Train *v);
static const uint8_t _vehicle_initial_x_fract[4] = {10, 8, 4, 8};
static const uint8_t _vehicle_initial_y_fract[4] = { 8, 4, 8, 10};
template <>
bool IsValidImageIndex<VEH_TRAIN>(uint8_t image_index)
{
@ -262,9 +263,9 @@ void Train::ConsistChanged(ConsistChangeFlags allowed_changes)
*/
int GetTrainStopLocation(StationID station_id, TileIndex tile, const Train *v, int *station_ahead, int *station_length)
{
const Station *st = Station::Get(station_id);
*station_ahead = st->GetPlatformLength(tile, DirToDiagDir(v->direction)) * TILE_SIZE;
*station_length = st->GetPlatformLength(tile) * TILE_SIZE;
assert(IsRailStationTile(tile));
*station_ahead = GetPlatformLength(tile, DirToDiagDir(v->direction)) * TILE_SIZE;
*station_length = GetPlatformLength(tile) * TILE_SIZE;
/* Default to the middle of the station for stations stops that are not in
* the order list like intermediate stations when non-stop is disabled */
@ -604,6 +605,65 @@ void GetTrainSpriteSize(EngineID engine, uint &width, uint &height, int &xoffs,
}
}
/**
* Check if a train chain is compatible with a depot tile.
* @param tile Tile to check.
* @param t Train chain to check.
* @return Whether the full train chain is compatible with this tile.
*/
bool IsVehicleCompatibleWithDepotTile(TileIndex tile, const Train *t)
{
assert(IsRailDepotTile(tile));
for (const Train *u = t; u != nullptr; u = u->Next()) {
RailType rail_type = Engine::Get(u->engine_type)->u.rail.railtype;
if (!IsCompatibleRail(rail_type, GetRailType(tile))) return false;
}
return true;
}
/**
* Check if a depot has a tile where a train chain can be stored.
* @param tile A tile of the depot.
* @param t The train to check.
* @return True iff the depot has a tile compatible with the chain.
*/
bool HasCompatibleDepotTile(TileIndex tile, const Train *t)
{
assert(IsRailDepotTile(tile));
Depot *dep = Depot::GetByTile(tile);
for (auto &depot_tile : dep->depot_tiles) {
if (IsVehicleCompatibleWithDepotTile(depot_tile, t)) return true;
}
return false;
}
/**
* Find a tile of a depot compatible with the rail type of a rail vehicle.
* @param depot_id Index of the depot.
* @param rail_type Rail type of the new vehicle.
* @param is_engine Whether the vehicle is an engine.
* @return A compatible tile of the depot or INVALID_TILE if no compatible tile is found.
*/
TileIndex FindCompatibleDepotTile(DepotID depot_id, RailType rail_type, bool is_engine)
{
assert(Depot::IsValidID(depot_id));
Depot *depot = Depot::Get(depot_id);
for (auto &dep_tile : depot->depot_tiles) {
if (is_engine) {
if (HasPowerOnRail(rail_type, GetRailType(dep_tile))) return dep_tile;
} else {
if (IsCompatibleRail(rail_type, GetRailType(dep_tile))) return dep_tile;
}
}
return INVALID_TILE;
}
/**
* Build a railroad wagon.
* @param flags type of operation.
@ -615,9 +675,12 @@ void GetTrainSpriteSize(EngineID engine, uint &width, uint &height, int &xoffs,
static CommandCost CmdBuildRailWagon(DoCommandFlag flags, TileIndex tile, const Engine *e, Vehicle **ret)
{
const RailVehicleInfo *rvi = &e->u.rail;
assert(IsRailDepotTile(tile));
DepotID depot_id = GetDepotIndex(tile);
/* Check that the wagon can drive on the track in question */
if (!IsCompatibleRail(rvi->railtype, GetRailType(tile))) return CMD_ERROR;
/* Find a good tile to place the wagon. */
tile = FindCompatibleDepotTile(depot_id, rvi->railtype, false);
if (tile == INVALID_TILE) return CMD_ERROR;
if (flags & DC_EXEC) {
Train *v = new Train();
@ -645,7 +708,7 @@ static CommandCost CmdBuildRailWagon(DoCommandFlag flags, TileIndex tile, const
v->SetWagon();
v->SetFreeWagon();
InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
InvalidateWindowData(WC_VEHICLE_DEPOT, depot_id);
v->cargo_type = e->GetDefaultCargoType();
assert(IsValidCargoID(v->cargo_type));
@ -673,12 +736,13 @@ static CommandCost CmdBuildRailWagon(DoCommandFlag flags, TileIndex tile, const
/* Try to connect the vehicle to one of free chains of wagons. */
for (Train *w : Train::Iterate()) {
if (w->tile == tile && ///< Same depot
if (!IsRailDepotTile(w->tile)) continue;
if (GetDepotIndex(w->tile) == depot_id && ///< Same depot
w->IsFreeWagon() && ///< A free wagon chain
w->engine_type == e->index && ///< Same type
w->First() != v && ///< Don't connect to ourself
!(w->vehstatus & VS_CRASHED)) { ///< Not crashed/flooded
if (Command<CMD_MOVE_RAIL_VEHICLE>::Do(DC_EXEC, v->index, w->Last()->index, true).Succeeded()) {
if (Command<CMD_MOVE_RAIL_VEHICLE>::Do(flags | DC_EXEC, v->index, w->Last()->index, true).Succeeded()) {
break;
}
}
@ -689,13 +753,16 @@ static CommandCost CmdBuildRailWagon(DoCommandFlag flags, TileIndex tile, const
}
/** Move all free vehicles in the depot to the train */
void NormalizeTrainVehInDepot(const Train *u)
void NormalizeTrainVehInDepot(const Train *u, DoCommandFlag flags)
{
assert(u->IsEngine());
assert(flags & DC_EXEC);
DepotID dep_id = GetDepotIndex(u->tile);
for (const Train *v : Train::Iterate()) {
if (v->IsFreeWagon() && v->tile == u->tile &&
v->track == TRACK_BIT_DEPOT) {
if (Command<CMD_MOVE_RAIL_VEHICLE>::Do(DC_EXEC, v->index, u->index, true).Failed()) {
if (v->IsFreeWagon() && v->IsInDepot() &&
GetDepotIndex(v->tile) == dep_id) {
if (Command<CMD_MOVE_RAIL_VEHICLE>::Do(flags, v->index, u->index, true).Failed()) {
break;
}
}
@ -748,13 +815,14 @@ static void AddRearEngineToMultiheadedTrain(Train *v)
*/
CommandCost CmdBuildRailVehicle(DoCommandFlag flags, TileIndex tile, const Engine *e, Vehicle **ret)
{
assert(IsRailDepotTile(tile));
const RailVehicleInfo *rvi = &e->u.rail;
if (rvi->railveh_type == RAILVEH_WAGON) return CmdBuildRailWagon(flags, tile, e, ret);
/* Check if depot and new engine uses the same kind of tracks *
* We need to see if the engine got power on the tile to avoid electric engines in non-electric depots */
if (!HasPowerOnRail(rvi->railtype, GetRailType(tile))) return CMD_ERROR;
/* Find a good tile to place the engine and get power on it. */
tile = FindCompatibleDepotTile(GetDepotIndex(tile), rvi->railtype, true);
if (tile == INVALID_TILE) return CMD_ERROR;
if (flags & DC_EXEC) {
DiagDirection dir = GetRailDepotDirection(tile);
@ -816,6 +884,10 @@ CommandCost CmdBuildRailVehicle(DoCommandFlag flags, TileIndex tile, const Engin
UpdateTrainGroupID(v);
CheckConsistencyOfArticulatedVehicle(v);
TrainPlacement train_placement;
train_placement.LiftTrain(v, flags);
train_placement.PlaceTrain(v, flags);
}
return CommandCost();
@ -824,10 +896,10 @@ CommandCost CmdBuildRailVehicle(DoCommandFlag flags, TileIndex tile, const Engin
static Train *FindGoodVehiclePos(const Train *src)
{
EngineID eng = src->engine_type;
TileIndex tile = src->tile;
DepotID dep_id = GetDepotIndex(src->tile);
for (Train *dst : Train::Iterate()) {
if (dst->IsFreeWagon() && dst->tile == tile && !(dst->vehstatus & VS_CRASHED)) {
if (dst->IsFreeWagon() && !(dst->vehstatus & VS_CRASHED) && GetDepotIndex(dst->tile) == dep_id) {
/* check so all vehicles in the line have the same engine. */
Train *t = dst;
while (t->engine_type == eng) {
@ -994,6 +1066,10 @@ static CommandCost CheckTrainAttachment(Train *t)
/* No multi-part train, no need to check. */
if (t == nullptr || t->Next() == nullptr) return CommandCost();
TrainPlacement tp;
tp.LookForPlaceInDepot(t, false);
if (tp.info == PI_FAILED_PLATFORM_TYPE) return_cmd_error(STR_ERROR_INCOMPATIBLE_RAILTYPES_WITH_DEPOT);
/* The maximum length for a train. For each part we decrease this by one
* and if the result is negative the train is simply too long. */
int allowed_len = _settings_game.vehicle.max_train_length * TILE_SIZE - t->gcache.cached_veh_length;
@ -1229,7 +1305,7 @@ CommandCost CmdMoveRailVehicle(DoCommandFlag flags, VehicleID src_veh, VehicleID
Train *dst_head;
if (dst != nullptr) {
dst_head = dst->First();
if (dst_head->tile != src_head->tile) return CMD_ERROR;
if (GetDepotIndex(dst_head->tile) != GetDepotIndex(src_head->tile)) return CMD_ERROR;
/* Now deal with articulated part of destination wagon */
dst = dst->GetLastEnginePart();
} else {
@ -1270,6 +1346,13 @@ CommandCost CmdMoveRailVehicle(DoCommandFlag flags, VehicleID src_veh, VehicleID
bool original_src_head_front_engine = original_src_head->IsFrontEngine();
bool original_dst_head_front_engine = original_dst_head != nullptr && original_dst_head->IsFrontEngine();
TrainPlacement train_placement_src;
TrainPlacement train_placement_dst;
train_placement_src.LiftTrain(src_head, flags);
train_placement_dst.LiftTrain(dst_head, flags);
assert(src_head != nullptr);
/* (Re)arrange the trains in the wanted arrangement. */
ArrangeTrains(&dst_head, dst, &src_head, src, move_chain);
@ -1282,6 +1365,8 @@ CommandCost CmdMoveRailVehicle(DoCommandFlag flags, VehicleID src_veh, VehicleID
/* Restore the train we had. */
RestoreTrainBackup(original_src);
RestoreTrainBackup(original_dst);
train_placement_src.PlaceTrain(original_src_head, flags & ~DC_EXEC);
if (src_head != dst_head) train_placement_dst.PlaceTrain(original_dst_head, flags & ~DC_EXEC);
return ret;
}
}
@ -1362,13 +1447,21 @@ CommandCost CmdMoveRailVehicle(DoCommandFlag flags, VehicleID src_veh, VehicleID
if (src_head != nullptr) src_head->First()->MarkDirty();
if (dst_head != nullptr) dst_head->First()->MarkDirty();
bool reverse_emplacement_order = !train_placement_src.placed && train_placement_dst.placed;
if (!reverse_emplacement_order) train_placement_src.PlaceTrain(src_head, flags);
if (src_head != dst_head) train_placement_dst.PlaceTrain(dst_head, flags);
if (reverse_emplacement_order) train_placement_src.PlaceTrain(src_head, flags);
/* We are undoubtedly changing something in the depot and train list. */
InvalidateWindowData(WC_VEHICLE_DEPOT, src->tile);
InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(src->tile));
InvalidateWindowClassesData(WC_TRAINS_LIST, 0);
} else {
/* We don't want to execute what we're just tried. */
RestoreTrainBackup(original_src);
RestoreTrainBackup(original_dst);
train_placement_src.PlaceTrain(original_src_head, flags);
if (src_head != dst_head) train_placement_dst.PlaceTrain(original_dst_head, flags);
}
return CommandCost();
@ -1393,6 +1486,9 @@ CommandCost CmdSellRailWagon(DoCommandFlag flags, Vehicle *t, bool sell_chain, b
if (v->IsRearDualheaded()) return_cmd_error(STR_ERROR_REAR_ENGINE_FOLLOW_FRONT);
TrainPlacement train_placement;
train_placement.LiftTrain(first, flags);
/* First make a backup of the order of the train. That way we can do
* whatever we want with the order and later on easily revert. */
TrainList original;
@ -1410,12 +1506,14 @@ CommandCost CmdSellRailWagon(DoCommandFlag flags, Vehicle *t, bool sell_chain, b
if (ret.Failed()) {
/* Restore the train we had. */
RestoreTrainBackup(original);
train_placement.PlaceTrain(first, flags);
return ret;
}
if (first->orders == nullptr && !OrderList::CanAllocateItem()) {
/* Restore the train we had. */
RestoreTrainBackup(original);
train_placement.PlaceTrain(first, flags);
return_cmd_error(STR_ERROR_NO_MORE_SPACE_FOR_ORDERS);
}
@ -1445,16 +1543,18 @@ CommandCost CmdSellRailWagon(DoCommandFlag flags, Vehicle *t, bool sell_chain, b
/* We need to update the information about the train. */
NormaliseTrainHead(new_head);
train_placement.PlaceTrain(new_head, flags);
/* We are undoubtedly changing something in the depot and train list. */
InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile));
InvalidateWindowClassesData(WC_TRAINS_LIST, 0);
/* Actually delete the sold 'goods' */
delete sell_head;
} else {
/* We don't want to execute what we're just tried. */
/* We don't want to execute what we have just tried. */
RestoreTrainBackup(original);
train_placement.PlaceTrain(first, flags);
}
return cost;
@ -1965,8 +2065,12 @@ static bool IsWholeTrainInsideDepot(const Train *v)
void ReverseTrainDirection(Train *v)
{
if (IsRailDepotTile(v->tile)) {
if (IsExtendedDepot(v->tile)) {
if ((v->track & TRACK_BIT_DEPOT) != 0 && (v->track & ~TRACK_BIT_DEPOT) != 0) return;
} else {
if (IsWholeTrainInsideDepot(v)) return;
InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
}
InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile));
}
/* Clear path reservation in front if train is not stuck. */
@ -1989,7 +2093,7 @@ void ReverseTrainDirection(Train *v)
AdvanceWagonsAfterSwap(v);
if (IsRailDepotTile(v->tile)) {
InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile));
}
ToggleBit(v->flags, VRF_TOGGLE_REVERSE);
@ -2030,9 +2134,9 @@ void ReverseTrainDirection(Train *v)
!IsPbsSignal(GetSignalType(v->tile, FindFirstTrack(v->track))));
/* If we are on a depot tile facing outwards, do not treat the current tile as safe. */
if (IsRailDepotTile(v->tile) && TrackdirToExitdir(v->GetVehicleTrackdir()) == GetRailDepotDirection(v->tile)) first_tile_okay = false;
if (IsStandardRailDepotTile(v->tile) && TrackdirToExitdir(v->GetVehicleTrackdir()) == GetRailDepotDirection(v->tile)) first_tile_okay = false;
if (IsRailStationTile(v->tile)) SetRailStationPlatformReservation(v->tile, TrackdirToExitdir(v->GetVehicleTrackdir()), true);
if (IsPlatformTile(v->tile)) SetPlatformReservation(v->tile, TrackdirToExitdir(v->GetVehicleTrackdir()), true);
if (TryPathReserve(v, false, first_tile_okay)) {
/* Do a look-ahead now in case our current tile was already a safe tile. */
CheckNextTrainTile(v);
@ -2079,7 +2183,14 @@ CommandCost CmdReverseTrainDirection(DoCommandFlag flags, VehicleID veh_id, bool
ToggleBit(v->flags, VRF_REVERSE_DIRECTION);
front->ConsistChanged(CCF_ARRANGE);
SetWindowDirty(WC_VEHICLE_DEPOT, front->tile);
if (IsRailDepotTile(front->tile)) {
if (IsExtendedDepot(front->tile)) {
TrainPlacement tp;
tp.LiftTrain(front, flags);
tp.PlaceTrain(front, flags & ~DC_EXEC);
}
SetWindowDirty(WC_VEHICLE_DEPOT, GetDepotIndex(front->tile));
}
SetWindowDirty(WC_VEHICLE_DETAILS, front->index);
SetWindowDirty(WC_VEHICLE_VIEW, front->index);
SetWindowClassesDirty(WC_TRAINS_LIST);
@ -2089,6 +2200,9 @@ CommandCost CmdReverseTrainDirection(DoCommandFlag flags, VehicleID veh_id, bool
if (!v->IsPrimaryVehicle()) return CMD_ERROR;
if ((v->vehstatus & VS_CRASHED) || v->breakdown_ctr != 0) return CMD_ERROR;
/* Do not reverse while servicing. */
if (IsExtendedRailDepotTile(v->tile) && (v->track & TRACK_BIT_DEPOT) != 0 && (v->track & ~TRACK_BIT_DEPOT) != 0) return CMD_ERROR;
if (flags & DC_EXEC) {
/* Properly leave the station if we are loading and won't be loading anymore */
if (v->current_order.IsType(OT_LOADING)) {
@ -2212,7 +2326,7 @@ static void CheckNextTrainTile(Train *v)
switch (v->current_order.GetType()) {
/* Exit if we reached our destination depot. */
case OT_GOTO_DEPOT:
if (v->tile == v->dest_tile) return;
if (IsRailDepotTile(v->tile) && v->current_order.ShouldStopAtDepot(GetDepotIndex(v->tile))) return;
break;
case OT_GOTO_WAYPOINT:
@ -2258,6 +2372,53 @@ static void CheckNextTrainTile(Train *v)
}
}
bool HandleTrainEnterDepot(Train *v)
{
assert(IsRailDepotTile(v->tile));
if (IsExtendedRailDepot(v->tile)) {
v->cur_speed = 0;
Train *t = Train::From(v);
for (Train *u = t; u != nullptr; u = u->Next()) {
if (!IsCompatibleTrainDepotTile(u->tile, t->tile)) {
SetDParam(0, v->index);
AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_TOO_LONG_FOR_SERVICING, v->index);
return false;
}
}
for (Train *u = t; u != nullptr; u = u->Next()) u->track |= TRACK_BIT_DEPOT;
t->force_proceed = TFP_NONE;
ClrBit(t->flags, VRF_TOGGLE_REVERSE);
UpdateExtendedDepotReservation(t, true);
v->UpdateViewport(true, true);
SetWindowClassesDirty(WC_TRAINS_LIST);
SetWindowDirty(WC_VEHICLE_VIEW, v->index);
InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile));
v->StartService();
} else {
/* Clear path reservation */
SetDepotReservation(v->tile, false);
VehicleEnterDepot(v);
}
return true;
}
bool CheckReverseTrain(const Train *v)
{
if (_settings_game.difficulty.line_reverse_mode != 0 ||
v->track == TRACK_BIT_DEPOT || v->track == TRACK_BIT_WORMHOLE ||
!(v->direction & 1)) {
return false;
}
assert(v->track != TRACK_BIT_NONE);
return YapfTrainCheckReverse(v);
}
/**
* Will the train stay in the depot the next tick?
* @param v %Train to check.
@ -2266,16 +2427,81 @@ static void CheckNextTrainTile(Train *v)
static bool CheckTrainStayInDepot(Train *v)
{
/* bail out if not all wagons are in the same depot or not in a depot at all */
if (!v->IsInDepot()) return false;
assert(IsRailDepotTile(v->tile));
DepotID depot_id = GetDepotIndex(v->tile);
if (IsExtendedRailDepot(v->tile)) {
/* If not placed, try it. If not possible, exit. */
if (CheckIfTrainNeedsPlacement(v)) {
/* If stuck, wait a little bit, so we can avoid
* trying placing it as much as we can. */
bool already_stuck = false;
bool send_message = false;
if (HasBit(v->flags, VRF_TRAIN_STUCK)) {
already_stuck = true;
v->wait_counter++;
if (v->wait_counter % (1 << 7) != 0) {
return true;
} else if (v->wait_counter % (1 << 11) == 0) {
send_message = true;
}
ClrBit(v->flags, VRF_TRAIN_STUCK);
}
TrainPlacement train_placement;
train_placement.LiftTrain(v, DC_EXEC);
train_placement.LookForPlaceInDepot(v, true);
if (train_placement.info < PI_FAILED_END) {
if (send_message) {
ClrBit(v->flags, VRF_TRAIN_STUCK);
/* Show message to player. */
if (_settings_client.gui.lost_vehicle_warn && v->owner == _local_company) {
SetDParam(0, v->index);
AddVehicleAdviceNewsItem(STR_ADVICE_PLATFORM_TYPE + train_placement.info - PI_ERROR_BEGIN, v->index);
}
}
if (already_stuck) {
SetBit(v->flags, VRF_TRAIN_STUCK);
} else {
MarkTrainAsStuck(v);
}
return true;
} else {
VehicleServiceInExtendedDepot(v);
train_placement.PlaceTrain(v, DC_EXEC);
if (!IsExtendedDepot(v->tile)) return true;
}
} else {
VehicleServiceInExtendedDepot(v);
}
for (Train *u = v; u != nullptr; u = u->Next()) u->track &= ~TRACK_BIT_DEPOT;
v->cur_speed = 0;
v->UpdateAcceleration();
ProcessOrders(v);
if (CheckReverseTrain(v)) ReverseTrainDirection(v);
UpdateExtendedDepotReservation(v, false);
InvalidateWindowData(WC_VEHICLE_DEPOT, depot_id);
/* Check whether it is safe to exit the depot. */
if (UpdateSignalsOnSegment(v->tile, VehicleExitDir(v->direction, v->track), v->owner) == SIGSEG_PBS || _settings_game.pf.reserve_paths) {
if (!TryPathReserve(v, true, true)) return true;
}
return false;
} else {
for (const Train *u = v; u != nullptr; u = u->Next()) {
if (u->track != TRACK_BIT_DEPOT || u->tile != v->tile) return false;
if (!u->IsInDepot() || u->tile != v->tile) return false;
}
/* if the train got no power, then keep it in the depot */
if (v->gcache.cached_power == 0) {
v->vehstatus |= VS_STOPPED;
SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
SetWindowDirty(WC_VEHICLE_DEPOT, depot_id);
return true;
}
}
/* Check if we should wait here for unbunching. */
if (v->IsWaitingForUnbunching()) return true;
@ -2302,9 +2528,11 @@ static bool CheckTrainStayInDepot(Train *v)
}
/* We are leaving a depot, but have to go to the exact same one; re-enter. */
if (v->current_order.IsType(OT_GOTO_DEPOT) && v->tile == v->dest_tile) {
if (v->current_order.IsType(OT_GOTO_DEPOT) &&
IsRailDepotTile(v->tile) &&
v->current_order.GetDestination() == GetDepotIndex(v->tile)) {
/* Service when depot has no reservation. */
if (!HasDepotReservation(v->tile)) VehicleEnterDepot(v);
if (!HasDepotReservation(v->tile)) HandleTrainEnterDepot(v);
return true;
}
@ -2334,7 +2562,7 @@ static bool CheckTrainStayInDepot(Train *v)
v->UpdatePosition();
UpdateSignalsOnSegment(v->tile, INVALID_DIAGDIR, v->owner);
v->UpdateAcceleration();
InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
InvalidateWindowData(WC_VEHICLE_DEPOT, depot_id);
return false;
}
@ -2369,12 +2597,12 @@ static void ClearPathReservation(const Train *v, TileIndex tile, Trackdir track_
}
}
}
} else if (IsRailStationTile(tile)) {
} else if (IsRailStationTile(tile) || IsExtendedRailDepotTile(tile)) {
TileIndex new_tile = TileAddByDiagDir(tile, dir);
/* If the new tile is not a further tile of the same station, we
* clear the reservation for the whole platform. */
if (!IsCompatibleTrainStationTile(new_tile, tile)) {
SetRailStationPlatformReservation(tile, ReverseDiagDir(dir), false);
if (!IsCompatiblePlatformTile(new_tile, tile)) {
SetPlatformReservation(tile, ReverseDiagDir(dir), false);
}
} else {
/* Any other tile */
@ -2392,11 +2620,12 @@ void FreeTrainTrackReservation(const Train *v)
TileIndex tile = v->tile;
Trackdir td = v->GetVehicleTrackdir();
bool free_tile = !(IsRailStationTile(v->tile) || IsTileType(v->tile, MP_TUNNELBRIDGE));
bool free_tile = !(IsRailStationTile(v->tile) || IsExtendedRailDepotTile(v->tile) || IsTileType(v->tile, MP_TUNNELBRIDGE));
StationID station_id = IsRailStationTile(v->tile) ? GetStationIndex(v->tile) : INVALID_STATION;
DepotID depot_id = IsExtendedRailDepotTile(v->tile) ? GetDepotIndex(v->tile) : INVALID_DEPOT;
/* Can't be holding a reservation if we enter a depot. */
if (IsRailDepotTile(tile) && TrackdirToExitdir(td) != GetRailDepotDirection(tile)) return;
if (IsStandardRailDepotTile(tile) && TrackdirToExitdir(td) != GetRailDepotDirection(tile)) return;
if (v->track == TRACK_BIT_DEPOT) {
/* Front engine is in a depot. We enter if some part is not in the depot. */
for (const Train *u = v; u != nullptr; u = u->Next()) {
@ -2436,7 +2665,7 @@ void FreeTrainTrackReservation(const Train *v)
}
/* Don't free first station/bridge/tunnel if we are on it. */
if (free_tile || (!(ft.m_is_station && GetStationIndex(ft.m_new_tile) == station_id) && !ft.m_is_tunnel && !ft.m_is_bridge)) ClearPathReservation(v, tile, td);
if (free_tile || (!(ft.m_is_station && GetStationIndex(ft.m_new_tile) == station_id) && !(ft.m_is_extended_depot && GetDepotIndex(ft.m_new_tile) == depot_id) && !ft.m_is_tunnel && !ft.m_is_bridge)) ClearPathReservation(v, tile, td);
free_tile = true;
}
@ -2495,7 +2724,7 @@ static PBSTileInfo ExtendTrainReservation(const Train *v, TrackBits *new_tracks,
}
/* Station, depot or waypoint are a possible target. */
bool target_seen = ft.m_is_station || (IsTileType(ft.m_new_tile, MP_RAILWAY) && !IsPlainRail(ft.m_new_tile));
bool target_seen = ft.m_is_station || ft.m_is_extended_depot || (IsTileType(ft.m_new_tile, MP_RAILWAY) && !IsPlainRail(ft.m_new_tile));
if (target_seen || KillFirstBit(ft.m_new_td_bits) != TRACKDIR_BIT_NONE) {
/* Choice found or possible target encountered.
* On finding a possible target, we need to stop and let the pathfinder handle the
@ -2852,6 +3081,7 @@ bool TryPathReserve(Train *v, bool mark_as_stuck, bool first_tile_okay)
if (mark_as_stuck) MarkTrainAsStuck(v);
return false;
} else {
assert(IsStandardRailDepotTile(v->tile));
/* Depot not reserved, but the next tile might be. */
TileIndex next_tile = TileAddByDiagDir(v->tile, GetRailDepotDirection(v->tile));
if (HasReservedTracks(next_tile, DiagdirReachesTracks(GetRailDepotDirection(v->tile)))) return false;
@ -2907,19 +3137,6 @@ bool TryPathReserve(Train *v, bool mark_as_stuck, bool first_tile_okay)
}
static bool CheckReverseTrain(const Train *v)
{
if (_settings_game.difficulty.line_reverse_mode != 0 ||
v->track == TRACK_BIT_DEPOT || v->track == TRACK_BIT_WORMHOLE ||
!(v->direction & 1)) {
return false;
}
assert(v->track != TRACK_BIT_NONE);
return YapfTrainCheckReverse(v);
}
/**
* Get the location of the next station to visit.
* @param station Next station to visit.
@ -3065,6 +3282,7 @@ static bool TrainMovedChangeSignals(TileIndex tile, DiagDirection dir)
void Train::ReserveTrackUnderConsist() const
{
for (const Train *u = this; u != nullptr; u = u->Next()) {
if (u->vehstatus & VS_HIDDEN) continue;
switch (u->track) {
case TRACK_BIT_WORMHOLE:
TryReserveRailTrack(u->tile, DiagDirToDiagTrack(GetTunnelBridgeDirection(u->tile)));
@ -3093,6 +3311,24 @@ uint Train::Crash(bool flooded)
/* Remove the reserved path in front of the train if it is not stuck.
* Also clear all reserved tracks the train is currently on. */
if (!HasBit(this->flags, VRF_TRAIN_STUCK)) FreeTrainTrackReservation(this);
if (IsExtendedRailDepotTile(this->tile)) {
if (this->track & ~TRACK_BIT_DEPOT) {
for (Train *v = this; v != nullptr; v = v->Next()) {
v->track &= ~TRACK_BIT_DEPOT;
}
UpdateExtendedDepotReservation(this, false);
InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(this->tile));
}
/* Remove reserved tracks of platform ahead. */
TileIndexDiff diff = TileOffsByDiagDir(TrackdirToExitdir(this->GetVehicleTrackdir()));
for (TileIndex pt_tile = this->tile + diff; IsCompatiblePlatformTile(pt_tile, this->tile) && HasDepotReservation(pt_tile); pt_tile += diff) {
if (EnsureNoVisibleVehicleOnGround(pt_tile).Failed()) break;
SetDepotReservation(pt_tile, false);
MarkTileDirtyByTile(pt_tile);
}
}
for (const Train *v = this; v != nullptr; v = v->Next()) {
ClearPathReservation(v, v->tile, v->GetVehicleTrackdir());
if (IsTileType(v->tile, MP_TUNNELBRIDGE)) {
@ -3100,6 +3336,23 @@ uint Train::Crash(bool flooded)
* if the train has just entered the wormhole. */
SetTunnelBridgeReservation(GetOtherTunnelBridgeEnd(v->tile), false);
}
if (v->Next() == nullptr && (IsRailStationTile(v->tile) || IsExtendedRailDepotTile(v->tile))) {
/* Remove reserved tracks of platform tiles behind the train. */
TileIndexDiff diff = TileOffsByDiagDir(TrackdirToExitdir(ReverseTrackdir(v->GetVehicleTrackdir())));
for (TileIndex pt_tile = v->tile + diff; IsCompatiblePlatformTile(pt_tile, v->tile); pt_tile += diff) {
if (IsExtendedRailDepotTile(pt_tile)) {
if (!HasDepotReservation(pt_tile)) break;
if (EnsureNoVisibleVehicleOnGround(pt_tile).Failed()) break;
SetDepotReservation(pt_tile, false);
} else {
if (!HasStationReservation(pt_tile)) break;
if (EnsureNoVisibleVehicleOnGround(pt_tile).Failed()) break;
SetRailStationReservation(pt_tile, false);
}
MarkTileDirtyByTile(pt_tile);
}
}
}
/* we may need to update crossing we were approaching,
@ -3112,6 +3365,7 @@ uint Train::Crash(bool flooded)
}
pass += this->GroundVehicleBase::Crash(flooded);
this->ReserveTrackUnderConsist();
this->crash_anim_pos = flooded ? 4000 : 1; // max 4440, disappear pretty fast when flooded
return pass;
@ -3134,10 +3388,6 @@ static uint TrainCrashed(Train *v)
Game::NewEvent(new ScriptEventVehicleCrashed(v->index, v->tile, ScriptEventVehicleCrashed::CRASH_TRAIN));
}
/* Try to re-reserve track under already crashed train too.
* Crash() clears the reservation! */
v->ReserveTrackUnderConsist();
return num;
}
@ -3190,6 +3440,9 @@ static Vehicle *FindTrainCollideEnum(Vehicle *v, void *data)
tcc->num += TrainCrashed(tcc->v);
tcc->num += TrainCrashed(coll);
/* The crashing of the coll train frees reservation of train v: Reserve again for train v. */
tcc->v->ReserveTrackUnderConsist();
return nullptr; // continue searching
}
@ -3285,6 +3538,12 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
if (HasBit(r, VETS_ENTERED_STATION)) {
/* The new position is the end of the platform */
TrainEnterStation(v, r >> VETS_STATION_ID_OFFSET);
} else if (HasBit(r, VETS_ENTERED_DEPOT_PLATFORM)) {
if (HandleTrainEnterDepot(first)) {
v->UpdatePosition();
v->UpdateViewport(true, true);
return false;
}
}
}
} else {
@ -3628,7 +3887,7 @@ static void DeleteLastWagon(Train *v)
/* Update the depot window if the first vehicle is in depot -
* if v == first, then it is updated in PreDestructor() */
if (first->track == TRACK_BIT_DEPOT) {
SetWindowDirty(WC_VEHICLE_DEPOT, first->tile);
SetWindowDirty(WC_VEHICLE_DEPOT, GetDepotIndex(first->tile));
}
v->last_station_visited = first->last_station_visited; // for PreDestructor
}
@ -3670,7 +3929,7 @@ static void DeleteLastWagon(Train *v)
}
/* Update signals */
if (IsTileType(tile, MP_TUNNELBRIDGE) || IsRailDepotTile(tile)) {
if (IsTileType(tile, MP_TUNNELBRIDGE) || IsStandardRailDepotTile(tile)) {
UpdateSignalsOnSegment(tile, INVALID_DIAGDIR, owner);
} else {
SetSignalsOnBothDir(tile, track, owner);
@ -3821,9 +4080,14 @@ static bool TrainCanLeaveTile(const Train *v)
/* entering a depot? */
if (IsRailDepotTile(tile)) {
if (IsExtendedRailDepot(tile)) {
Direction dir = DiagDirToDir(GetRailDepotDirection(tile));
if (dir == v->direction || ReverseDir(dir) == v->direction) return false;
} else {
DiagDirection dir = ReverseDiagDir(GetRailDepotDirection(tile));
if (DiagDirToDir(dir) == v->direction) return false;
}
}
return true;
}
@ -3936,6 +4200,8 @@ static bool TrainLocoHandler(Train *v, bool mode)
/* exit if train is stopped */
if ((v->vehstatus & VS_STOPPED) && v->cur_speed == 0) return true;
if (v->ContinueServicing()) return true;
bool valid_order = !v->current_order.IsType(OT_NOTHING) && v->current_order.GetType() != OT_CONDITIONAL;
if (ProcessOrders(v) && CheckReverseTrain(v)) {
v->wait_counter = 0;
@ -4198,9 +4464,13 @@ Trackdir Train::GetVehicleTrackdir() const
{
if (this->vehstatus & VS_CRASHED) return INVALID_TRACKDIR;
if (this->track == TRACK_BIT_DEPOT) {
if (this->IsInDepot()) {
/* We'll assume the train is facing outwards */
return DiagDirToDiagTrackdir(GetRailDepotDirection(this->tile)); // Train in depot
if (this->track == TRACK_BIT_DEPOT)
return DiagDirToDiagTrackdir(DirToDiagDir(this->direction)); // Train in depot
Track track = FindFirstTrack(this->track & ~TRACK_BIT_DEPOT);
assert(IsValidTrack(track));
return TrackDirectionToTrackdir(track, this->direction);
}
if (this->track == TRACK_BIT_WORMHOLE) {

View File

@ -15,6 +15,7 @@
#include "vehicle_func.h"
#include "zoom_func.h"
#include "train_cmd.h"
#include "depot_map.h"
#include "table/strings.h"
@ -29,11 +30,12 @@
void CcBuildWagon(Commands, const CommandCost &result, VehicleID new_veh_id, uint, uint16_t, CargoArray, TileIndex tile, EngineID, bool, CargoID, ClientID)
{
if (result.Failed()) return;
DepotID depot_id = GetDepotIndex(tile);
/* find a locomotive in the depot. */
const Vehicle *found = nullptr;
for (const Train *t : Train::Iterate()) {
if (t->IsFrontEngine() && t->tile == tile && t->IsStoppedInDepot()) {
if (t->IsFrontEngine() && t->IsStoppedInDepot() && GetDepotIndex(t->tile) == depot_id) {
if (found != nullptr) return; // must be exactly one.
found = t;
}

View File

@ -0,0 +1,314 @@
/*
* 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 train_placement.cpp Handling of trains in depot platforms. */
#include "stdafx.h"
#include "error.h"
#include "news_func.h"
#include "company_func.h"
#include "strings_func.h"
#include "platform_func.h"
#include "depot_base.h"
#include "depot_map.h"
#include "train_placement.h"
#include "train.h"
#include "table/strings.h"
#include "safeguards.h"
/**
* Check if a train can be placed in a given tile.
* @param train The train.
* @param check_tile The tile where we want to check whether it is possible to place the train.
* @param executing False if testing and true if the call is being executed.
* @return whether it found a platform to place the train.
*/
bool TrainPlacement::CheckPlacement(const Train *train, TileIndex check_tile, bool executing)
{
assert(train != nullptr);
assert(IsRailDepotTile(check_tile));
RailType rt = GetRailType(check_tile);
PlacementInfo error_info = PI_FAILED_FREE_WAGGON;
bool is_extended_depot = IsExtendedRailDepot(check_tile);
bool succeeded = !train->IsFreeWagon();
if (succeeded) {
error_info = PI_FAILED_PLATFORM_TYPE;
for (const Train *t = train; t != nullptr && succeeded; t = t->Next()) {
RailType rail_type = Engine::Get(t->engine_type)->u.rail.railtype;
if (!IsCompatibleRail(rail_type, rt)) succeeded = false;
}
}
if (succeeded && is_extended_depot) {
error_info = PI_FAILED_LENGTH;
if (train->gcache.cached_total_length > GetPlatformLength(check_tile) * TILE_SIZE) succeeded = false;
}
if (succeeded) {
error_info = PI_FAILED_POWER;
bool has_power = false;
for (const Train *t = train; t != nullptr && !has_power; t = t->Next()) {
if (HasPowerOnRail(train->railtype, rt)) has_power = true;
}
if (!has_power) succeeded = false;
}
if (succeeded && is_extended_depot) {
error_info = PI_FAILED_RESERVED;
/* Check whether any tile of the platform is reserved. Don't assume all platform
* is reserved as a whole: sections of the platform may be reserved by crashed trains. */
for (TileIndex tile : GetPlatformTileArea(check_tile)) {
if (HasDepotReservation(tile)) {
succeeded = false;
break;
}
}
}
if (succeeded && executing) {
/* Do not check for signals if really not executing and action. */
error_info = PI_FAILED_SIGNALS;
SigSegState seg_state = UpdateSignalsOnSegment(check_tile, INVALID_DIAGDIR, train->owner);
if (train->force_proceed == TFP_NONE && seg_state == SIGSEG_FULL) succeeded = false;
}
if (succeeded) error_info = PI_SUCCESS;
if (error_info > this->info) {
this->best_tile = check_tile;
this->info = error_info;
/* A direction for the train must be choosen: the one that allows the longest train in platform. */
DiagDirection dir = GetRailDepotDirection(check_tile);
if (is_extended_depot && GetPlatformLength(check_tile, dir) > GetPlatformLength(check_tile, ReverseDiagDir(dir))) {
dir = ReverseDiagDir(dir);
}
this->best_dir = DiagDirToDir(dir);
}
return succeeded;
}
/**
* Before placing a train in the rails of a depot, a valid platform must
* be found. This function finds a tile for placing the train (and also gets the direction and track).
* If there is no valid tile, it will be returned as best_tile == INVALID_TILE or info == PI_FAILED_PLATFORM_TYPE.
* @param t The train we want to place in rails.
* @param executing False if testing and true if the call is being executed.
* @pre The train must be inside the rail depot as if it where in a standard depot.
* (i.e. the track is TRACK_BIT_DEPOT, vehicles are hidden...).
*/
void TrainPlacement::LookForPlaceInDepot(const Train *train, bool executing)
{
assert(train != nullptr);
assert(IsRailDepotTile(train->tile));
/* Initialitzation. */
bool is_extended_depot = IsExtendedRailDepot(train->tile);
this->best_tile = (this->placed || !is_extended_depot) ? train->tile : GetPlatformExtremeTile(train->tile, DirToDiagDir(train->direction));
assert(IsStandardRailDepot(this->best_tile) || IsAnyStartPlatformTile(this->best_tile));
this->best_dir = train->direction;
this->info = PI_BEGIN;
/* First candidate is the original position of the train. */
if (CheckPlacement(train, this->best_tile, executing)) return;
/* Check all platforms. */
Depot *depot = Depot::GetByTile(train->tile);
for (auto &depot_tile : depot->depot_tiles) {
if (CheckPlacement(train, depot_tile, executing)) return;
}
}
/**
* Check if a train can leave now or when other trains
* move away. It returns whether there is a platform long
* enough and with the appropriate rail type.
* @param train The train.
* @param executing False if testing and true if the call is being executed.
* @return true iff there is a compatible platform long enough.
*/
bool TrainPlacement::CanFindAppropriatePlatform(const Train *train, bool executing)
{
this->LookForPlaceInDepot(train, executing);
return this->info >= PI_WONT_LEAVE;
}
/**
* Lift a train in a depot: keep the positions of the elements of the chain if needed,
* and keep also the original tile, direction and track.
* @param train The train we want to lift.
* @pre The train must be inside a rail depot.
* (i.e. the track is 'valid track | TRACK_BIT_DEPOT' or just 'TRACK_BIT_DEPOT').
*/
void TrainPlacement::LiftTrain(Train *train, DoCommandFlag flags)
{
assert(train == nullptr || train->IsInDepot());
assert(train == nullptr || IsRailDepotTile(train->tile));
assert(this->placed == false);
/* Lift the train only if we have a train in an extended depot. */
if (train == nullptr || !IsExtendedRailDepot(train->tile)) return;
/* Do not lift in recursive commands of autoreplace. */
if (flags & DC_AUTOREPLACE) return;
/* If train is not placed... return, because train is already lifted. */
if ((train->track & ~TRACK_BIT_DEPOT) == 0) return;
/* Train is placed in rails: lift it. */
this->placed = true;
if (flags & DC_EXEC) FreeTrainTrackReservation(train);
for (Train *t = train; t != nullptr; t = t->Next()) {
// Lift.
t->track = TRACK_BIT_DEPOT;
t->tile = train->tile;
t->x_pos = train->x_pos;
t->y_pos = train->y_pos;
t->UpdatePosition();
t->UpdateViewport(true, true);
}
if ((flags & DC_EXEC) == 0) return;
SetPlatformReservation(train->tile, false);
UpdateExtendedDepotReservation(train, false);
UpdateSignalsOnSegment(train->tile, INVALID_DIAGDIR, train->owner);
}
/**
* When a train is lifted inside a depot, before starting its way again,
* must be placed in rails if in an extended rail depot; this function does all necessary things to do so.
* In general, it's the opposite of #LiftTrain
* @param train The train we want to place in rails.
* @param flags Associated command flags
* @pre The train must be inside the extended rail depot as if in a standard depot.
* (i.e. the track is TRACK_BIT_DEPOT, vehicles are hidden...).
*/
void TrainPlacement::PlaceTrain(Train *train, DoCommandFlag flags)
{
if (train == nullptr) return;
if (train != train->First()) return;
if (!IsRailDepotTile(train->tile)) return;
if (flags & DC_AUTOREPLACE) return;
bool executing = (flags & DC_EXEC) != 0;
/* Look for an appropriate platform. */
this->LookForPlaceInDepot(train, executing);
assert(!IsExtendedRailDepot(this->best_tile) || IsAnyStartPlatformTile(this->best_tile));
if (this->info < PI_FAILED_END || !executing) {
if (!executing) {
/* Restore the train. */
this->best_tile = train->tile;
this->best_dir = train->direction;
this->info = PI_SUCCESS;
}
if (!this->placed || (this->info < PI_FAILED_END && executing)) {
for (Train *t = train; t != nullptr; t = t->Next()) {
t->tile = this->best_tile;
t->vehstatus |= VS_HIDDEN;
t->track = TRACK_BIT_DEPOT;
}
if (!executing) return;
train->PowerChanged();
}
if (this->info < PI_FAILED_END && executing) {
/* Train cannot leave until changing the depot. Stop the train and send a message. */
if (info < PI_WONT_LEAVE) {
train->vehstatus |= VS_STOPPED;
/* If vehicle is not stopped and user is the local company, send a message if needed. */
if ((train->vehstatus & VS_STOPPED) == 0 && train->owner == _local_company && train->IsFrontEngine()) {
SetDParam(0, train->index);
AddVehicleAdviceNewsItem(STR_ADVICE_PLATFORM_TYPE + info - PI_ERROR_BEGIN, train->index);
}
}
return;
}
}
assert(this->best_tile != INVALID_TILE);
assert(this->best_dir != INVALID_DIR);
assert(IsRailDepotTile(this->best_tile));
if (executing) {
train->tile = this->best_tile;
train->track = TrackToTrackBits(GetRailDepotTrack(this->best_tile));
train->direction = this->best_dir;
train->PowerChanged();
}
if (IsStandardRailDepot(this->best_tile)) {
int x = TileX(this->best_tile) * TILE_SIZE + _vehicle_initial_x_fract[DirToDiagDir(this->best_dir)];
int y = TileY(this->best_tile) * TILE_SIZE + _vehicle_initial_y_fract[DirToDiagDir(this->best_dir)];
for (Train *t = train; t != nullptr; t = t->Next()) {
t->tile = this->best_tile;
t->direction = this->best_dir;
t->vehstatus |= VS_HIDDEN;
t->track = TRACK_BIT_DEPOT;
t->x_pos = x;
t->y_pos = y;
t->z_pos = GetSlopePixelZ(x, y);
t->UpdatePosition();
t->UpdateViewport(true, true);
}
return;
}
DiagDirection placing_dir = ReverseDiagDir(DirToDiagDir(this->best_dir));
static const uint8_t _plat_initial_x_fract[4] = {15, 8, 0, 8};
static const uint8_t _plat_initial_y_fract[4] = { 8, 0, 8, 15};
int x = TileX(this->best_tile) * TILE_SIZE | _plat_initial_x_fract[placing_dir];
int y = TileY(this->best_tile) * TILE_SIZE | _plat_initial_y_fract[placing_dir];
/* Add the offset for the first vehicle. */
x += TileIndexDiffCByDiagDir(placing_dir).x * (train->gcache.cached_veh_length + 1) / 2;
y += TileIndexDiffCByDiagDir(placing_dir).y * (train->gcache.cached_veh_length + 1) / 2;
/* Proceed placing the train in the given tile.
* At this point, the first vehicle contains the direction, tile and track.
* We must update positions of all the chain. */
for (Train *t = train; t != nullptr; t = t->Next()) {
t->vehstatus &= ~VS_HIDDEN;
t->direction = this->best_dir;
t->track = DiagDirToDiagTrackBits(placing_dir) | TRACK_BIT_DEPOT;
t->x_pos = x;
t->y_pos = y;
t->z_pos = GetSlopePixelZ(t->x_pos, t->y_pos);
t->tile = TileVirtXY(t->x_pos,t->y_pos);
assert(t->z_pos == train->z_pos);
assert(IsExtendedRailDepotTile(t->tile));
t->UpdatePosition();
t->UpdateViewport(true, true);
int advance = t->CalcNextVehicleOffset();
x += TileIndexDiffCByDiagDir(placing_dir).x * advance;
y += TileIndexDiffCByDiagDir(placing_dir).y * advance;
}
SetPlatformReservation(train->tile, true);
UpdateExtendedDepotReservation(train, true);
UpdateSignalsOnSegment(train->tile, INVALID_DIAGDIR, train->owner);
}

View File

@ -0,0 +1,58 @@
/*
* 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 train_placement.h Handling of trains in depot platforms. */
#ifndef TRAIN_PLA_H
#define TRAIN_PLA_H
#include "core/enum_type.hpp"
#include "train.h"
/* Flags of failure and success when placing a train. */
enum PlacementInfo {
PI_BEGIN = 0,
PI_FAILED_FREE_WAGGON = PI_BEGIN, // Free waggon: not to be placed.
PI_ERROR_BEGIN,
PI_FAILED_PLATFORM_TYPE = PI_ERROR_BEGIN, // No compatible platforms with train type.
PI_FAILED_LENGTH, // There are compatible platforms but not long enough.
PI_FAILED_POWER, // No engine gets power in the platform.
PI_WONT_LEAVE,
PI_FAILED_RESERVED = PI_WONT_LEAVE, // There are compatible platforms but reserved right now.
PI_FAILED_SIGNALS, // There are compatible platforms not reserved, but signals don't allow placing it now.
PI_FAILED_END,
PI_SUCCESS = PI_FAILED_END, // There is an appropriate platform.
PI_END,
};
/* Store position of a train and lift it when necessary. */
struct TrainPlacement {
bool placed; // True if train is placed in rails.
TileIndex best_tile; // Best tile for the train.
Direction best_dir; // Best direction for the train.
PlacementInfo info; // Info of possible problems in best platform.
TrainPlacement() : placed(false),
best_tile(INVALID_TILE),
best_dir(INVALID_DIR),
info(PI_FAILED_PLATFORM_TYPE) {}
bool CheckPlacement(const Train *train, TileIndex tile, bool executing);
void LookForPlaceInDepot(const Train *train, bool executing);
bool CanFindAppropriatePlatform(const Train *train, bool executing);
void LiftTrain(Train *train, DoCommandFlag flags);
void PlaceTrain(Train *train, DoCommandFlag flags);
};
static inline bool CheckIfTrainNeedsPlacement(const Train *train)
{
return IsExtendedRailDepot(train->tile) && (train->track & ~TRACK_BIT_DEPOT) == 0;
}
#endif /* TRAIN_PLA_H */

View File

@ -181,6 +181,117 @@ void VehicleServiceInDepot(Vehicle *v)
} while (v != nullptr && v->HasEngineType());
}
/**
* List of vehicles that should check for autoreplace this tick.
* Mapping of vehicle -> leave depot immediately after autoreplace.
*/
using AutoreplaceMap = std::map<VehicleID, bool>;
static AutoreplaceMap _vehicles_to_autoreplace;
void VehicleServiceInExtendedDepot(Vehicle *v)
{
/* Always work with the front of the vehicle */
assert(v == v->First());
assert(IsExtendedDepotTile(v->tile));
switch (v->type) {
case VEH_TRAIN: {
SetWindowClassesDirty(WC_TRAINS_LIST);
Train *t = Train::From(v);
t->ConsistChanged(CCF_ARRANGE);
t->UpdateViewport(true, true);
break;
}
case VEH_SHIP: {
SetWindowClassesDirty(WC_SHIPS_LIST);
Ship *ship = Ship::From(v);
ship->UpdateCache();
ship->UpdateViewport(true, true);
break;
}
case VEH_ROAD:
SetWindowClassesDirty(WC_ROADVEH_LIST);
break;
case VEH_AIRCRAFT:
SetWindowClassesDirty(WC_AIRCRAFT_LIST);
break;
default: NOT_REACHED();
}
DepotID depot_id = GetDepotIndex(v->tile);
SetWindowDirty(WC_VEHICLE_DEPOT, depot_id);
SetWindowDirty(WC_VEHICLE_VIEW, v->index);
InvalidateWindowData(WC_VEHICLE_DEPOT, depot_id);
VehicleServiceInDepot(v);
/* After a vehicle trigger, the graphics and properties of the vehicle could change. */
TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
v->MarkDirty();
if (v->current_order.IsType(OT_GOTO_DEPOT)) {
SetWindowDirty(WC_VEHICLE_VIEW, v->index);
const Order *real_order = v->GetOrder(v->cur_real_order_index);
Order t = v->current_order;
v->current_order.MakeDummy();
/* Test whether we are heading for this depot. If not, do nothing.
* Note: The target depot for nearest-/manual-depot-orders is only updated on junctions, but we want to accept every depot. */
if ((t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
real_order != nullptr && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
t.GetDestination() != GetDepotIndex(v->tile)) {
/* We are heading for another depot, keep driving. */
return;
}
if (t.IsRefit()) {
Backup<CompanyID> cur_company(_current_company, v->owner);
CommandCost cost = std::get<0>(Command<CMD_REFIT_VEHICLE>::Do(DC_EXEC, v->index, t.GetRefitCargo(), 0xFF, false, false, 0));
cur_company.Restore();
if (cost.Failed()) {
_vehicles_to_autoreplace[v->index] = false;
if (v->owner == _local_company) {
/* Notify the user that we stopped the vehicle */
SetDParam(0, v->index);
AddVehicleAdviceNewsItem(STR_NEWS_ORDER_REFIT_FAILED, v->index);
}
} else if (cost.GetCost() != 0) {
v->profit_this_year -= cost.GetCost() << 8;
if (v->owner == _local_company) {
ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
}
}
}
if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
/* Part of orders */
v->DeleteUnreachedImplicitOrders();
UpdateVehicleTimetable(v, true);
v->IncrementImplicitOrderIndex();
}
if (t.GetDepotActionType() & ODATFB_HALT) {
/* Vehicles are always stopped on entering depots. Do not restart this one. */
_vehicles_to_autoreplace[v->index] = false;
/* Invalidate last_loading_station. As the link from the station
* before the stop to the station after the stop can't be predicted
* we shouldn't construct it when the vehicle visits the next stop. */
v->last_loading_station = INVALID_STATION;
if (v->owner == _local_company) {
SetDParam(0, v->index);
AddVehicleAdviceNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, v->index);
}
AI::NewEvent(v->owner, new ScriptEventVehicleWaitingInDepot(v->index));
}
}
}
/**
* Check if the vehicle needs to go to a depot in near future (if a opportunity presents itself) for service or replacement.
*
@ -297,7 +408,7 @@ uint Vehicle::Crash(bool)
InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
SetWindowDirty(WC_VEHICLE_DEPOT, this->tile);
if (IsDepotTile(this->tile)) SetWindowDirty(WC_VEHICLE_DEPOT, GetDepotIndex(this->tile));
delete this->cargo_payment;
assert(this->cargo_payment == nullptr); // cleared by ~CargoPayment
@ -556,6 +667,24 @@ CommandCost EnsureNoVehicleOnGround(TileIndex tile)
return CommandCost();
}
/**
* Ensure there is no visible vehicle at the ground at the given position.
* @param tile Position to examine.
* @return Succeeded command (ground is free) or failed command (a visible vehicle is found).
*/
CommandCost EnsureNoVisibleVehicleOnGround(TileIndex tile)
{
int z = GetTileMaxPixelZ(tile);
/* Value v is not safe in MP games, however, it is used to generate a local
* error message only (which may be different for different machines).
* Such a message does not affect MP synchronisation.
*/
Vehicle *v = VehicleFromPos(tile, &z, &EnsureNoVehicleProcZ, true);
if (v != nullptr && (v->vehstatus & VS_HIDDEN) == 0) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
return CommandCost();
}
/** Procedure called for every vehicle found in tunnel/bridge in the hash map */
static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
{
@ -687,13 +816,6 @@ void ResetVehicleColourMap()
for (Vehicle *v : Vehicle::Iterate()) { v->colourmap = PAL_NONE; }
}
/**
* List of vehicles that should check for autoreplace this tick.
* Mapping of vehicle -> leave depot immediately after autoreplace.
*/
using AutoreplaceMap = std::map<VehicleID, bool>;
static AutoreplaceMap _vehicles_to_autoreplace;
void InitializeVehicles()
{
_vehicles_to_autoreplace.clear();
@ -790,6 +912,22 @@ void Vehicle::ShiftDates(TimerGameEconomy::Date interval)
*/
void Vehicle::HandlePathfindingResult(bool path_found)
{
if (this->dest_tile != INVALID_TILE && IsDepotTypeTile(this->dest_tile, (TransportType)this->type) && IsDepotFullWithStoppedVehicles(this->dest_tile)) {
/* Vehicle cannot find a free depot. */
/* Were we already lost? */
if (HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
/* It is first time the problem occurred, set the "lost" flag. */
SetBit(this->vehicle_flags, VF_PATHFINDER_LOST);
/* Notify user about the event. */
AI::NewEvent(this->owner, new ScriptEventVehicleLost(this->index));
if (_settings_client.gui.lost_vehicle_warn && this->owner == _local_company) {
SetDParam(0, this->index);
AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_CAN_T_FIND_FREE_DEPOT, this->index);
}
return;
}
if (path_found) {
/* Route found, is the vehicle marked with "lost" flag? */
if (!HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
@ -868,8 +1006,8 @@ void Vehicle::PreDestructor()
if (v->disaster_vehicle != INVALID_VEHICLE) ReleaseDisasterVehicle(v->disaster_vehicle);
}
if (this->Previous() == nullptr) {
InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
if (this->Previous() == nullptr && IsDepotTile(this->tile)) {
InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(this->tile));
}
if (this->IsPrimaryVehicle()) {
@ -1078,7 +1216,12 @@ void CallVehicleTicks()
/* Start vehicle if we stopped them in VehicleEnteredDepotThisTick()
* We need to stop them between VehicleEnteredDepotThisTick() and here or we risk that
* they are already leaving the depot again before being replaced. */
if (it.second) v->vehstatus &= ~VS_STOPPED;
if (it.second) {
v->vehstatus &= ~VS_STOPPED;
} else if (IsExtendedDepotTile(v->tile)){
if (v->type == VEH_TRAIN) FreeTrainTrackReservation(Train::From(v));
UpdateExtendedDepotReservation(v, true);
}
/* Store the position of the effect as the vehicle pointer will become invalid later */
int x = v->x_pos;
@ -1554,12 +1697,11 @@ void VehicleEnterDepot(Vehicle *v)
/* Always work with the front of the vehicle */
assert(v == v->First());
DepotID depot_id = GetDepotIndex(v->tile);
switch (v->type) {
case VEH_TRAIN: {
Train *t = Train::From(v);
SetWindowClassesDirty(WC_TRAINS_LIST);
/* Clear path reservation */
SetDepotReservation(t->tile, false);
if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile);
UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner);
@ -1580,7 +1722,7 @@ void VehicleEnterDepot(Vehicle *v)
ship->state = TRACK_BIT_DEPOT;
ship->UpdateCache();
ship->UpdateViewport(true, true);
SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
SetWindowDirty(WC_VEHICLE_DEPOT, depot_id);
break;
}
@ -1595,9 +1737,9 @@ void VehicleEnterDepot(Vehicle *v)
if (v->type != VEH_TRAIN) {
/* Trains update the vehicle list when the first unit enters the depot and calls VehicleEnterDepot() when the last unit enters.
* We only increase the number of vehicles when the first one enters, so we will not need to search for more vehicles in the depot */
InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
InvalidateWindowData(WC_VEHICLE_DEPOT, depot_id);
}
SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
SetWindowDirty(WC_VEHICLE_DEPOT, depot_id);
v->vehstatus |= VS_HIDDEN;
v->cur_speed = 0;
@ -1619,7 +1761,7 @@ void VehicleEnterDepot(Vehicle *v)
* Note: The target depot for nearest-/manual-depot-orders is only updated on junctions, but we want to accept every depot. */
if ((v->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
real_order != nullptr && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
(v->type == VEH_AIRCRAFT ? v->current_order.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
v->current_order.GetDestination() != GetDepotIndex(v->tile)) {
/* We are heading for another depot, keep driving. */
return;
}

View File

@ -52,6 +52,7 @@ enum VehicleFlags {
VF_PATHFINDER_LOST, ///< Vehicle's pathfinder is lost.
VF_SERVINT_IS_CUSTOM, ///< Service interval is custom.
VF_SERVINT_IS_PERCENT, ///< Service interval is percent.
VF_IS_SERVICING, ///< Vehicle is servicing.
};
/** Bit numbers used to indicate which of the #NewGRFCache values are valid. */
@ -329,6 +330,7 @@ public:
uint8_t subspeed; ///< fractional speed
uint8_t acceleration; ///< used by train & aircraft
uint32_t motion_counter; ///< counter to occasionally play a vehicle sound.
uint16_t wait_counter; ///< waiting ticks (servicing, waiting in front of a signal or forced proceeding)
uint8_t progress; ///< The percentage (if divided by 256) this vehicle already crossed the tile unit.
uint8_t waiting_triggers; ///< Triggers to be yet matched before rerandomizing the random bits.
@ -781,6 +783,12 @@ public:
bool HandleBreakdown();
bool IsServicing() const { return HasBit(this->vehicle_flags, VF_IS_SERVICING); }
void StartService();
bool ContinueServicing();
void StopServicing();
bool NeedsAutorenewing(const Company *c, bool use_renew_setting = true) const;
bool NeedsServicing() const;

Some files were not shown because too many files have changed in this diff Show More