1 // template_vehicle_func.cpp
4 #include "window_gui.h"
6 #include "window_func.h"
7 #include "command_func.h"
8 #include "vehicle_gui.h"
10 #include "strings_func.h"
11 #include "vehicle_func.h"
12 #include "core/geometry_type.hpp"
14 #include "zoom_func.h"
16 #include "table/sprites.h"
17 #include "table/strings.h"
19 #include "cargoaction.h"
21 #include "company_func.h"
23 #include "spritecache.h"
24 #include "articulated_vehicles.h"
25 #include "autoreplace_func.h"
27 #include "depot_base.h"
29 #include "tbtr_template_vehicle.h"
30 #include "tbtr_template_vehicle_func.h"
35 Vehicle
*vhead
, *vtmp
;
38 // debugging printing functions for convenience, usually called from gdb
42 FOR_ALL_TEMPLATES(tv
) {
43 if (tv
->Prev()) continue;
45 printf("__________\n");
53 if (t
->Previous()) continue;
55 printf("__________\n");
59 void tbtr_debug_ptv(TemplateVehicle
* tv
)
63 printf("eid:%3d st:%2d tv:%p next:%p cargo: %d cargo_sub: %d\n", tv
->engine_type
, tv
->subtype
, tv
, tv
->Next(), tv
->cargo_type
, tv
->cargo_subtype
);
66 printf("eid:%3d st:%2d tv:%p next:%p cargo: %d cargo_sub: %d\n", tv
->engine_type
, tv
->subtype
, tv
, tv
->Next(), tv
->cargo_type
, tv
->cargo_subtype
);
69 void tbtr_debug_pvt(const Train
*printme
)
71 for (const Train
*tmp
= printme
; tmp
; tmp
= tmp
->Next()) {
72 if (tmp
->index
<= 0) {
73 printf("train has weird index: %d %d %p\n", tmp
->index
, tmp
->engine_type
, tmp
);
76 printf("eid:%3d index:%2d subtype:%2d vehstat: %d cargo_t: %d cargo_sub: %d ref:%p\n", tmp
->engine_type
, tmp
->index
, tmp
->subtype
, tmp
->vehstatus
, tmp
->cargo_type
, tmp
->cargo_subtype
, tmp
);
81 void BuildTemplateGuiList(GUITemplateList
*list
, Scrollbar
*vscroll
, Owner oid
, RailType railtype
)
84 const TemplateVehicle
*tv
;
86 FOR_ALL_TEMPLATES(tv
) {
87 if (tv
->owner
== oid
&& (tv
->IsPrimaryVehicle() || tv
->IsFreeWagonChain()) && TemplateVehicleContainsEngineOfRailtype(tv
, railtype
))
93 if (vscroll
) vscroll
->SetCount(list
->Length());
96 Money
CalculateOverallTemplateCost(const TemplateVehicle
*tv
)
100 for (; tv
; tv
= tv
->GetNextUnit()) {
101 val
+= (Engine::Get(tv
->engine_type
))->GetCost();
106 void DrawTemplate(const TemplateVehicle
*tv
, int left
, int right
, int y
)
110 bool rtl
= _current_text_dir
== TD_RTL
;
111 Direction dir
= rtl
? DIR_E
: DIR_W
;
113 DrawPixelInfo tmp_dpi
, *old_dpi
;
114 /* Position of highlight box */
117 int max_width
= right
- left
+ 1;
118 int height
= ScaleGUITrad(14);
120 if (!FillDrawPixelInfo(&tmp_dpi
, left
, y
, max_width
, height
)) return;
125 int px
= rtl
? max_width
: 0;
126 bool sel_articulated
= false;
127 for (; tv
!= nullptr && (rtl
? px
> 0 : px
< max_width
); tv
= tv
->Next()) {
128 int width
= tv
->image_width
;
130 if (rtl
? px
+ width
> 0 : px
- width
< max_width
) {
131 PaletteID pal
= GetEnginePalette(tv
->engine_type
, _current_company
);
132 tv
->sprite_seq
.Draw(px
+ (rtl
? -tv
->image_offset
.x
: tv
->image_offset
.x
), height
/ 2 + tv
->image_offset
.y
, pal
, false);
135 px
+= rtl
? -width
: width
;
141 // copy important stuff from the virtual vehicle to the template
142 inline void SetupTemplateVehicleFromVirtual(TemplateVehicle
*tmp
, TemplateVehicle
*prev
, Train
*virt
)
147 tmp
->SetFirst(prev
->First());
149 tmp
->railtype
= virt
->railtype
;
150 tmp
->owner
= virt
->owner
;
151 tmp
->value
= virt
->value
;
153 // set the subtype but also clear the virtual flag while doing it
154 tmp
->subtype
= virt
->subtype
& ~(1 << GVSF_VIRTUAL
);
155 // set the cargo type and capacity
156 tmp
->cargo_type
= virt
->cargo_type
;
157 tmp
->cargo_subtype
= virt
->cargo_subtype
;
158 tmp
->cargo_cap
= virt
->cargo_cap
;
160 const GroundVehicleCache
*gcache
= virt
->GetGroundVehicleCache();
161 tmp
->max_speed
= virt
->GetDisplayMaxSpeed();
162 tmp
->power
= gcache
->cached_power
;
165 for (const Train
*u
= virt
; u
!= nullptr; u
= u
->Next()) {
166 tmp
->weight
+= u
->GetLoadedWeight();
169 tmp
->max_te
= gcache
->cached_max_te
;
171 tmp
->spritenum
= virt
->spritenum
;
172 virt
->GetImage(DIR_W
, EIT_IN_DEPOT
, &tmp
->sprite_seq
);
173 tmp
->image_width
= virt
->GetDisplayImageWidth(&tmp
->image_offset
);
176 // create a full TemplateVehicle based train according to a virtual train
177 TemplateVehicle
* TemplateVehicleFromVirtualTrain(Train
*virt
)
182 Train
*init_virt
= virt
;
184 int len
= CountVehiclesInChain(virt
);
185 if ( !TemplateVehicle::CanAllocateItem(len
) )
188 TemplateVehicle
*tmp
, *prev
=0;
189 for ( ; virt
; virt
=virt
->Next() ) {
190 tmp
= new TemplateVehicle(virt
->engine_type
);
191 SetupTemplateVehicleFromVirtual(tmp
, prev
, virt
);
195 tmp
->First()->SetRealLength(CeilDiv(init_virt
->gcache
.cached_total_length
* 10, TILE_SIZE
));
199 // forward declaration, defined in train_cmd.cpp
200 CommandCost
CmdSellRailWagon(DoCommandFlag
, Vehicle
*, uint16
, uint32
);
202 Train
* DeleteVirtualTrain(Train
*chain
, Train
*to_del
) {
203 if ( chain
!= to_del
) {
204 CmdSellRailWagon(DC_EXEC
, to_del
, 0, 0);
208 chain
= chain
->GetNextUnit();
209 CmdSellRailWagon(DC_EXEC
, to_del
, 0, 0);
214 // retrieve template vehicle from template replacement that belongs to the given group
215 TemplateVehicle
* GetTemplateVehicleByGroupID(GroupID gid
) {
216 if (gid
>= NEW_GROUP
) return nullptr;
218 TemplateReplacement
*tr
;
220 FOR_ALL_TEMPLATE_REPLACEMENTS(tr
) {
221 if (tr
->Group() == gid
) {
222 return TemplateVehicle::GetIfValid(tr
->Template()); // there can be only one
230 * Check a template consist whether it contains any engine of the given railtype
232 bool TemplateVehicleContainsEngineOfRailtype(const TemplateVehicle
*tv
, RailType type
)
234 if (type
== INVALID_RAILTYPE
) return true;
236 /* For standard rail engines, allow only those */
237 if ( type
== RAILTYPE_BEGIN
|| type
== RAILTYPE_RAIL
) {
239 if ( tv
->railtype
!= type
)
241 tv
= tv
->GetNextUnit();
245 /* For electrified rail engines, standard wagons or engines are allowed to be included */
247 if ( tv
->railtype
== type
)
249 tv
= tv
->GetNextUnit();
255 bool ChainContainsVehicle(Train
*chain
, Train
*mem
) {
256 for (; chain
; chain
=chain
->Next())
263 Train
* ChainContainsEngine(EngineID eid
, Train
*chain
) {
264 for (; chain
; chain
=chain
->GetNextUnit())
265 if (chain
->engine_type
== eid
)
271 Train
* DepotContainsEngine(TileIndex tile
, EngineID eid
, Train
*not_in
=0) {
274 // conditions: v is stopped in the given depot, has the right engine and if 'not_in' is given v must not be contained within 'not_in'
275 // if 'not_in' is nullptr, no check is needed
277 // If the veh belongs to a chain, wagons will not return true on IsStoppedInDepot(), only primary vehicles will
278 // in case of t not a primary veh, we demand it to be a free wagon to consider it for replacement
279 && ((t
->IsPrimaryVehicle() && t
->IsStoppedInDepot()) || t
->IsFreeWagon())
280 && t
->engine_type
==eid
281 && (not_in
==0 || ChainContainsVehicle(not_in
, t
)==0))
287 void NeutralizeStatus(Train
*t
) {
288 DoCommand(t
->tile
, DEFAULT_GROUP
, t
->index
, DC_EXEC
, CMD_ADD_VEHICLE_GROUP
);
289 DoCommand(0, t
->index
| CO_UNSHARE
<< 30, 0, DC_EXEC
, CMD_CLONE_ORDER
);
290 DoCommand(0, t
->index
, FreeUnitIDGenerator(VEH_TRAIN
, t
->owner
).NextID(), DC_EXEC
, CMD_SET_VEHICLE_UNIT_NUMBER
);
291 DoCommand(0, t
->index
, 0, DC_EXEC
, CMD_RENAME_VEHICLE
, nullptr);
294 bool TrainMatchesTemplate(const Train
*t
, TemplateVehicle
*tv
) {
296 if ( t
->engine_type
!= tv
->engine_type
)
298 t
= t
->GetNextUnit();
299 tv
= tv
->GetNextUnit();
301 if ( (t
&& !tv
) || (!t
&& tv
) )
307 bool TrainMatchesTemplateRefit(const Train
*t
, TemplateVehicle
*tv
)
309 if ( !tv
->refit_as_template
)
313 if ( t
->cargo_type
!= tv
->cargo_type
|| t
->cargo_subtype
!= tv
->cargo_subtype
)
315 t
= t
->GetNextUnit();
316 tv
= tv
->GetNextUnit();
320 void BreakUpRemainders(Train
*t
) {
323 if ( HasBit(t
->subtype
, GVSF_ENGINE
) ) {
326 DoCommand(move
->tile
, move
->index
, INVALID_VEHICLE
, DC_EXEC
, CMD_MOVE_RAIL_VEHICLE
);
327 NeutralizeStatus( move
);
334 // make sure the real train wagon has the right cargo
335 void CopyWagonStatus(TemplateVehicle
*from
, Train
*to
) {
336 to
->cargo_type
= from
->cargo_type
;
337 to
->cargo_subtype
= from
->cargo_subtype
;
340 int NumTrainsNeedTemplateReplacement(GroupID g_id
, TemplateVehicle
*tv
)
343 if ( !tv
) return count
;
347 if ( t
->IsPrimaryVehicle() && t
->group_id
== g_id
&& (!TrainMatchesTemplate(t
, tv
) || !TrainMatchesTemplateRefit(t
, tv
)) )
352 // refit each vehicle in t as is in tv, assume t and tv contain the same types of vehicles
353 void CmdRefitTrainFromTemplate(Train
*t
, TemplateVehicle
*tv
, DoCommandFlag flags
)
357 uint32 cb
= GetCmdRefitVeh(t
);
359 DoCommand(t
->tile
, t
->index
, tv
->cargo_type
| tv
->cargo_subtype
<< 8 | 1 << 16 | (1 << 5), flags
, cb
);
362 t
= t
->GetNextUnit();
363 tv
= tv
->GetNextUnit();
367 /** using cmdtemplatereplacevehicle as test-function (i.e. with flag DC_NONE) is not a good idea as that function relies on
368 * actually moving vehicles around to work properly.
369 * We do this worst-cast test instead.
371 CommandCost
TestBuyAllTemplateVehiclesInChain(TemplateVehicle
*tv
, TileIndex tile
)
373 CommandCost
cost(EXPENSES_NEW_VEHICLES
);
375 for ( ; tv
; tv
=tv
->GetNextUnit() )
376 cost
.AddCost( DoCommand(tile
, tv
->engine_type
, 0, DC_NONE
, CMD_BUILD_VEHICLE
) );
382 /** Transfer as much cargo from a given (single train) vehicle onto a chain of vehicles.
383 * I.e., iterate over the chain from head to tail and use all available cargo capacity (w.r.t. cargo type of course)
384 * to store the cargo from the given single vehicle.
385 * @param old_veh: ptr to the single vehicle, which's cargo shall be moved
386 * @param new_head: ptr to the head of the chain, which shall obtain old_veh's cargo
387 * @return: amount of moved cargo TODO
389 void TransferCargoForTrain(Train
*old_veh
, Train
*new_head
)
391 assert(new_head
->IsPrimaryVehicle());
393 CargoID _cargo_type
= old_veh
->cargo_type
;
394 byte _cargo_subtype
= old_veh
->cargo_subtype
;
396 // how much cargo has to be moved (if possible)
397 uint remainingAmount
= old_veh
->cargo
.TotalCount();
398 // each vehicle in the new chain shall be given as much of the old cargo as possible, until none is left
399 for (Train
*tmp
=new_head
; tmp
!=nullptr && remainingAmount
>0; tmp
=tmp
->GetNextUnit())
401 if (tmp
->cargo_type
== _cargo_type
&& tmp
->cargo_subtype
== _cargo_subtype
)
403 // calculate the free space for new cargo on the current vehicle
404 uint curCap
= tmp
->cargo_cap
- tmp
->cargo
.TotalCount();
405 uint moveAmount
= min(remainingAmount
, curCap
);
406 // move (parts of) the old vehicle's cargo onto the current vehicle of the new chain
409 old_veh
->cargo
.Shift(moveAmount
, &tmp
->cargo
);
410 remainingAmount
-= moveAmount
;
415 // TODO: needs to be implemented, too
416 // // from autoreplace_cmd.cpp : 121
417 /* Any left-overs will be thrown away, but not their feeder share. */
418 //if (src->cargo_cap < src->cargo.TotalCount()) src->cargo.Truncate(src->cargo.TotalCount() - src->cargo_cap);
420 /* Update train weight etc., the old vehicle will be sold anyway */
421 new_head
->ConsistChanged(CCF_LOADUNLOAD
);