Fix crash when setting separation mode for vehicles with no orders list.
[openttd-joker.git] / src / tbtr_template_vehicle_func.cpp
blob0afd72c5aff0bfa4902890ec746f400614bea004
1 // template_vehicle_func.cpp
3 #include "stdafx.h"
4 #include "window_gui.h"
5 #include "gfx_func.h"
6 #include "window_func.h"
7 #include "command_func.h"
8 #include "vehicle_gui.h"
9 #include "train.h"
10 #include "strings_func.h"
11 #include "vehicle_func.h"
12 #include "core/geometry_type.hpp"
13 #include "debug.h"
14 #include "zoom_func.h"
16 #include "table/sprites.h"
17 #include "table/strings.h"
19 #include "cargoaction.h"
20 #include "train.h"
21 #include "company_func.h"
22 #include "newgrf.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"
32 #include <map>
33 #include <stdio.h>
35 Vehicle *vhead, *vtmp;
37 #ifdef _DEBUG
38 // debugging printing functions for convenience, usually called from gdb
39 void tbtr_debug_pat()
41 TemplateVehicle *tv;
42 FOR_ALL_TEMPLATES(tv) {
43 if (tv->Prev()) continue;
44 tbtr_debug_ptv(tv);
45 printf("__________\n");
49 void tbtr_debug_pav()
51 Train *t;
52 FOR_ALL_TRAINS(t) {
53 if (t->Previous()) continue;
54 tbtr_debug_pvt(t);
55 printf("__________\n");
59 void tbtr_debug_ptv(TemplateVehicle* tv)
61 if (!tv) return;
62 while (tv->Next() ) {
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);
64 tv = tv->Next();
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);
74 return;
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);
79 #endif
81 void BuildTemplateGuiList(GUITemplateList *list, Scrollbar *vscroll, Owner oid, RailType railtype)
83 list->Clear();
84 const TemplateVehicle *tv;
86 FOR_ALL_TEMPLATES(tv) {
87 if (tv->owner == oid && (tv->IsPrimaryVehicle() || tv->IsFreeWagonChain()) && TemplateVehicleContainsEngineOfRailtype(tv, railtype))
88 *list->Append() = tv;
92 list->RebuildDone();
93 if (vscroll) vscroll->SetCount(list->Length());
96 Money CalculateOverallTemplateCost(const TemplateVehicle *tv)
98 Money val = 0;
100 for (; tv; tv = tv->GetNextUnit()) {
101 val += (Engine::Get(tv->engine_type))->GetCost();
103 return val;
106 void DrawTemplate(const TemplateVehicle *tv, int left, int right, int y)
108 if (!tv) return;
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 */
115 int highlight_l = 0;
116 int highlight_r = 0;
117 int max_width = right - left + 1;
118 int height = ScaleGUITrad(14);
120 if (!FillDrawPixelInfo(&tmp_dpi, left, y, max_width, height)) return;
122 old_dpi = _cur_dpi;
123 _cur_dpi = &tmp_dpi;
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;
138 _cur_dpi = old_dpi;
141 // copy important stuff from the virtual vehicle to the template
142 inline void SetupTemplateVehicleFromVirtual(TemplateVehicle *tmp, TemplateVehicle *prev, Train *virt)
144 if (prev) {
145 prev->SetNext(tmp);
146 tmp->SetPrev(prev);
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;
163 tmp->weight = 0;
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)
179 if ( !virt )
180 return 0;
182 Train *init_virt = virt;
184 int len = CountVehiclesInChain(virt);
185 if ( !TemplateVehicle::CanAllocateItem(len) )
186 return 0;
188 TemplateVehicle *tmp, *prev=0;
189 for ( ; virt; virt=virt->Next() ) {
190 tmp = new TemplateVehicle(virt->engine_type);
191 SetupTemplateVehicleFromVirtual(tmp, prev, virt);
192 prev = tmp;
195 tmp->First()->SetRealLength(CeilDiv(init_virt->gcache.cached_total_length * 10, TILE_SIZE));
196 return tmp->First();
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);
205 return chain;
207 else {
208 chain = chain->GetNextUnit();
209 CmdSellRailWagon(DC_EXEC, to_del, 0, 0);
210 return chain;
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
226 return 0;
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 ) {
238 while ( tv ) {
239 if ( tv->railtype != type )
240 return false;
241 tv = tv->GetNextUnit();
243 return true;
245 /* For electrified rail engines, standard wagons or engines are allowed to be included */
246 while ( tv ) {
247 if ( tv->railtype == type )
248 return true;
249 tv = tv->GetNextUnit();
251 return false;
254 //helper
255 bool ChainContainsVehicle(Train *chain, Train *mem) {
256 for (; chain; chain=chain->Next())
257 if ( chain == mem )
258 return true;
259 return false;
262 // has O(n)
263 Train* ChainContainsEngine(EngineID eid, Train *chain) {
264 for (; chain; chain=chain->GetNextUnit())
265 if (chain->engine_type == eid)
266 return chain;
267 return 0;
270 // has O(n^2)
271 Train* DepotContainsEngine(TileIndex tile, EngineID eid, Train *not_in=0) {
272 Train *t;
273 FOR_ALL_TRAINS(t) {
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
276 if ( t->tile==tile
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))
282 return t;
284 return 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) {
295 while ( t && tv ) {
296 if ( t->engine_type != tv->engine_type )
297 return false;
298 t = t->GetNextUnit();
299 tv = tv->GetNextUnit();
301 if ( (t && !tv) || (!t && tv) )
302 return false;
303 return true;
307 bool TrainMatchesTemplateRefit(const Train *t, TemplateVehicle *tv)
309 if ( !tv->refit_as_template )
310 return true;
312 while ( t && tv ) {
313 if ( t->cargo_type != tv->cargo_type || t->cargo_subtype != tv->cargo_subtype )
314 return false;
315 t = t->GetNextUnit();
316 tv = tv->GetNextUnit();
318 return true;
320 void BreakUpRemainders(Train *t) {
321 while ( t ) {
322 Train *move;
323 if ( HasBit(t->subtype, GVSF_ENGINE) ) {
324 move = t;
325 t = t->Next();
326 DoCommand(move->tile, move->index, INVALID_VEHICLE, DC_EXEC, CMD_MOVE_RAIL_VEHICLE);
327 NeutralizeStatus( move );
329 else
330 t = t->Next();
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)
342 int count = 0;
343 if ( !tv ) return count;
345 const Train *t;
346 FOR_ALL_TRAINS(t) {
347 if ( t->IsPrimaryVehicle() && t->group_id == g_id && (!TrainMatchesTemplate(t, tv) || !TrainMatchesTemplateRefit(t, tv)) )
348 count++;
350 return count;
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)
355 while ( t && tv ) {
356 // refit t as tv
357 uint32 cb = GetCmdRefitVeh(t);
359 DoCommand(t->tile, t->index, tv->cargo_type | tv->cargo_subtype << 8 | 1 << 16 | (1 << 5), flags, cb);
361 // next
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) );
378 return cost;
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
407 if (moveAmount > 0)
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);