1 /**
2 * convoi_t Class for vehicle associations
3 * Hansj�rg Malthaner
4 */
5
6 #include <stdlib.h>
7
8 #include "simdebug.h"
9 #include "simunits.h"
10 #include "simworld.h"
11 #include "simware.h"
12 #include "player/finance.h" // convert_money
13 #include "player/simplay.h"
14 #include "simconvoi.h"
15 #include "simhalt.h"
16 #include "simdepot.h"
17 #include "gui/simwin.h"
18 #include "simmenu.h"
19 #include "simcolor.h"
20 #include "simmesg.h"
21 #include "simintr.h"
22 #include "simlinemgmt.h"
23 #include "simline.h"
24 #include "freight_list_sorter.h"
25
26 #include "gui/minimap.h"
27 #include "gui/convoi_info_t.h"
28 #include "gui/schedule_gui.h"
29 #include "gui/depot_frame.h"
30 #include "gui/messagebox.h"
31 #include "gui/convoi_detail_t.h"
32 #include "boden/grund.h"
33 #include "boden/wege/schiene.h" // for railblocks
34
35 #include "descriptor/vehicle_desc.h"
36 #include "descriptor/roadsign_desc.h"
37
38 #include "dataobj/schedule.h"
39 #include "dataobj/route.h"
40 #include "dataobj/loadsave.h"
41 #include "dataobj/translator.h"
42 #include "dataobj/environment.h"
43
44 #include "display/viewport.h"
45
46 #include "obj/crossing.h"
47 #include "obj/roadsign.h"
48 #include "obj/wayobj.h"
49
50 #include "vehicle/simvehicle.h"
51 #include "vehicle/overtaker.h"
52
53 #include "utils/simrandom.h"
54 #include "utils/simstring.h"
55 #include "utils/cbuffer_t.h"
56
57
58 /*
59 * Waiting time for loading (ms)
60 * @author Hj- Malthaner
61 */
62 #define WTT_LOADING 2000
63
64
65 karte_ptr_t convoi_t::welt;
66
67 /*
68 * Debugging helper - translate state value to human readable name
69 * @author Hj- Malthaner
70 */
71 static const char * state_names[convoi_t::MAX_STATES] =
72 {
73 "INITIAL",
74 "EDIT_SCHEDULE",
75 "ROUTING_1",
76 "",
77 "",
78 "NO_ROUTE",
79 "DRIVING",
80 "LOADING",
81 "WAITING_FOR_CLEARANCE",
82 "WAITING_FOR_CLEARANCE_ONE_MONTH",
83 "CAN_START",
84 "CAN_START_ONE_MONTH",
85 "SELF_DESTRUCT", // self destruct
86 "WAITING_FOR_CLEARANCE_TWO_MONTHS",
87 "CAN_START_TWO_MONTHS",
88 "LEAVING_DEPOT",
89 "ENTERING_DEPOT"
90 };
91
92
93 /**
94 * Calculates speed of slowest vehicle in the given array
95 * @author Hj. Matthaner
96 */
calc_min_top_speed(const array_tpl<vehicle_t * > & fahr,uint8 anz_vehikel)97 static int calc_min_top_speed(const array_tpl<vehicle_t*>& fahr, uint8 anz_vehikel)
98 {
99 int min_top_speed = SPEED_UNLIMITED;
100 for(uint8 i=0; i<anz_vehikel; i++) {
101 min_top_speed = min(min_top_speed, kmh_to_speed( fahr[i]->get_desc()->get_topspeed() ) );
102 }
103 return min_top_speed;
104 }
105
106
init(player_t * player)107 void convoi_t::init(player_t *player)
108 {
109 owner = player;
110
111 is_electric = false;
112 sum_gesamtweight = sum_weight = 0;
113 sum_running_costs = sum_gear_and_power = previous_delta_v = 0;
114 sum_power = 0;
115 min_top_speed = SPEED_UNLIMITED;
116 speedbonus_kmh = SPEED_UNLIMITED; // speed_to_kmh() not needed
117
118 schedule = NULL;
119 schedule_target = koord3d::invalid;
120 line = linehandle_t();
121
122 anz_vehikel = 0;
123 steps_driven = -1;
124 withdraw = false;
125 has_obsolete = false;
126 no_load = false;
127 wait_lock = 0;
128 arrived_time = 0;
129
130 jahresgewinn = 0;
131 total_distance_traveled = 0;
132
133 distance_since_last_stop = 0;
134 sum_speed_limit = 0;
135 maxspeed_average_count = 0;
136 next_reservation_index = 0;
137
138 alte_richtung = ribi_t::none;
139 next_wolke = 0;
140
141 state = INITIAL;
142
143 *name_and_id = 0;
144 name_offset = 0;
145
146 freight_info_resort = true;
147 freight_info_order = 0;
148 loading_level = 0;
149 loading_limit = 0;
150
151 speed_limit = SPEED_UNLIMITED;
152 max_record_speed = 0;
153 brake_speed_soll = SPEED_UNLIMITED;
154 akt_speed_soll = 0; // target speed
155 akt_speed = 0; // current speed
156 sp_soll = 0;
157
158 next_stop_index = 65535;
159
160 line_update_pending = linehandle_t();
161
162 home_depot = koord3d::invalid;
163
164 recalc_data_front = true;
165 recalc_data = true;
166 recalc_speed_limit = true;
167 }
168
169
convoi_t(loadsave_t * file)170 convoi_t::convoi_t(loadsave_t* file) : fahr(default_vehicle_length, NULL)
171 {
172 self = convoihandle_t();
173 init(0);
174 rdwr(file);
175 }
176
177
convoi_t(player_t * player)178 convoi_t::convoi_t(player_t* player) : fahr(default_vehicle_length, NULL)
179 {
180 self = convoihandle_t(this);
181 player->book_convoi_number(1);
182 init(player);
183 set_name( "Unnamed" );
184 welt->add_convoi( self );
185 init_financial_history();
186 }
187
188
~convoi_t()189 convoi_t::~convoi_t()
190 {
191 owner->book_convoi_number( -1);
192
193 assert(self.is_bound());
194 assert(anz_vehikel==0);
195
196 // close windows
197 destroy_win( magic_convoi_info+self.get_id() );
198
199 DBG_MESSAGE("convoi_t::~convoi_t()", "destroying %d, %p", self.get_id(), this);
200 // stop following
201 if(welt->get_viewport()->get_follow_convoi()==self) {
202 welt->get_viewport()->set_follow_convoi( convoihandle_t() );
203 }
204
205 welt->sync.remove( this );
206 welt->rem_convoi( self );
207
208 // Knightly : if lineless convoy -> unregister from stops
209 if( !line.is_bound() ) {
210 unregister_stops();
211 }
212
213 // force asynchronous recalculation
214 if(schedule) {
215 if(!schedule->is_editing_finished()) {
216 destroy_win((ptrdiff_t)schedule);
217 }
218 if (!schedule->empty() && !line.is_bound()) {
219 welt->set_schedule_counter();
220 }
221 delete schedule;
222 }
223
224 // @author hsiegeln - deregister from line (again) ...
225 unset_line();
226
227 self.detach();
228 }
229
230
231 // waypoint: no stop, resp. for airplanes in air (i.e. no air strip below)
is_waypoint(koord3d ziel) const232 bool convoi_t::is_waypoint( koord3d ziel ) const
233 {
234 if( fahr[0]->get_waytype() == air_wt ) {
235 // separate logic for airplanes, since the can have waypoints over stops etc.
236 grund_t *gr = welt->lookup_kartenboden(ziel.get_2d());
237 if( gr == NULL || gr->get_weg(air_wt) == NULL ) {
238 // during flight always a waypoint
239 return true;
240 }
241 else if( gr->get_depot() ) {
242 // but a depot is not a waypoint
243 return false;
244 }
245 // so we are on a taxiway/runway here ...
246 }
247 return !haltestelle_t::get_halt(ziel,get_owner()).is_bound();
248 }
249
250
251 /**
252 * unreserves the whole remaining route
253 */
unreserve_route()254 void convoi_t::unreserve_route()
255 {
256 // need a route, vehicles, and vehicles must belong to this convoi
257 // (otherwise crash during loading when fahr[0]->convoi is not initialized yet
258 if( !route.empty() && anz_vehikel>0 && fahr[0]->get_convoi() == this ) {
259 rail_vehicle_t* lok = dynamic_cast<rail_vehicle_t*>(fahr[0]);
260 if (lok) {
261 // free all reserved blocks
262 uint16 dummy;
263 lok->block_reserver(get_route(), back()->get_route_index(), dummy, dummy, 100000, false, true);
264 }
265 }
266 }
267
268
269 /**
270 * reserves route until next_reservation_index
271 */
reserve_route()272 void convoi_t::reserve_route()
273 {
274 if( !route.empty() && anz_vehikel>0 && (is_waiting() || state==DRIVING || state==LEAVING_DEPOT) ) {
275 for( int idx = back()->get_route_index(); idx < next_reservation_index /*&& idx < route.get_count()*/; idx++ ) {
276 if( grund_t *gr = welt->lookup( route.at(idx) ) ) {
277 if( schiene_t *sch = (schiene_t *)gr->get_weg( front()->get_waytype() ) ) {
278 sch->reserve( self, ribi_type( route.at(max(1u,idx)-1u), route.at(min(route.get_count()-1u,idx+1u)) ) );
279 }
280 }
281 }
282 }
283 }
284
285 /**
286 * Sets route_index of all vehicles to startindex.
287 * Puts all vehicles on tile at this position in the route.
288 * Convoy stills needs to be pushed that the convoy is right on track.
289 * @returns length of convoy minus last vehicle
290 */
move_to(uint16 const start_index)291 uint32 convoi_t::move_to(uint16 const start_index)
292 {
293 steps_driven = -1;
294 koord3d k = route.at(start_index);
295 grund_t* gr = welt->lookup(k);
296
297 uint32 train_length = 0;
298 for (unsigned i = 0; i != anz_vehikel; ++i) {
299 vehicle_t& v = *fahr[i];
300
301 if( grund_t const* gr = welt->lookup(v.get_pos()) ) {
302 v.mark_image_dirty(v.get_image(), 0);
303 v.leave_tile();
304 // maybe unreserve this
305 if( schiene_t* const rails = obj_cast<schiene_t>(gr->get_weg(v.get_waytype())) ) {
306 rails->unreserve(&v);
307 }
308 }
309 // propagate new index to vehicle, will set all movement related variables, in particular pos
310 v.initialise_journey(start_index, true);
311 // now put vehicle on the tile
312 if (gr) {
313 v.enter_tile(gr);
314 }
315
316 if (i != anz_vehikel - 1U) {
317 train_length += v.get_desc()->get_length();
318 }
319 }
320 return train_length;
321 }
322
323
finish_rd()324 void convoi_t::finish_rd()
325 {
326 if(schedule==NULL) {
327 if( state!=INITIAL ) {
328 grund_t *gr = welt->lookup(home_depot);
329 if(gr && gr->get_depot()) {
330 dbg->warning( "convoi_t::finish_rd()","No schedule during loading convoi %i: State will be initial!", self.get_id() );
331 for( uint8 i=0; i<anz_vehikel; i++ ) {
332 fahr[i]->set_pos(home_depot);
333 }
334 state = INITIAL;
335 }
336 else {
337 dbg->error( "convoi_t::finish_rd()","No schedule during loading convoi %i: Convoi will be destroyed!", self.get_id() );
338 for( uint8 i=0; i<anz_vehikel; i++ ) {
339 fahr[i]->set_pos(koord3d::invalid);
340 }
341 destroy();
342 return;
343 }
344 }
345 // anyway reassign convoi pointer ...
346 for( uint8 i=0; i<anz_vehikel; i++ ) {
347 vehicle_t* v = fahr[i];
348 v->set_convoi(this);
349 if( state!=INITIAL && welt->lookup(v->get_pos()) ) {
350 // mark vehicle as used
351 v->set_driven();
352 }
353 }
354 return;
355 }
356 else {
357 // restore next schedule target for non-stop waypoint handling
358 const koord3d ziel = schedule->get_current_entry().pos;
359 if( anz_vehikel>0 && is_waypoint(ziel) ) {
360 schedule_target = ziel;
361 }
362 }
363
364 bool realign_position = false;
365 if( anz_vehikel>0 ) {
366 DBG_MESSAGE("convoi_t::finish_rd()","state=%s, next_stop_index=%d", state_names[state], next_stop_index );
367 // only realign convois not leaving depot to avoid jumps through signals
368 if( steps_driven!=-1 ) {
369 for( uint8 i=0; i<anz_vehikel; i++ ) {
370 vehicle_t* v = fahr[i];
371 v->set_leading( i==0 );
372 v->set_last( i+1==anz_vehikel );
373 v->calc_height();
374 // this sets the convoi and will renew the block reservation, if needed!
375 v->set_convoi(this);
376 }
377 }
378 else {
379 // test also for realignment
380 sint16 step_pos = 0;
381 koord3d drive_pos;
382 uint8 const diagonal_vehicle_steps_per_tile = (uint8)(130560U / welt->get_settings().get_pak_diagonal_multiplier());
383 for( uint8 i=0; i<anz_vehikel; i++ ) {
384 vehicle_t* v = fahr[i];
385 v->set_leading( i==0 );
386 v->set_last( i+1==anz_vehikel );
387 v->calc_height();
388 // this sets the convoi and will renew the block reservation, if needed!
389 v->set_convoi(this);
390
391 // wrong alignment here => must relocate
392 if(v->need_realignment()) {
393 // diagonal => convoi must restart
394 realign_position |= ribi_t::is_bend(v->get_direction()) && (state==DRIVING || is_waiting());
395 }
396 // if version is 99.17 or lower, some convois are broken, i.e. had too large gaps between vehicles
397 if( !realign_position && state!=INITIAL && state!=LEAVING_DEPOT ) {
398 if( i==0 ) {
399 step_pos = v->get_steps();
400 }
401 else {
402 if( drive_pos!=v->get_pos() ) {
403 // with long vehicles on diagonals, vehicles need not to be on consecutive tiles
404 // do some guessing here
405 uint32 dist = koord_distance(drive_pos, v->get_pos());
406 if (dist>1) {
407 step_pos += (dist-1) * diagonal_vehicle_steps_per_tile;
408 }
409 step_pos += ribi_t::is_bend(v->get_direction()) ? diagonal_vehicle_steps_per_tile : VEHICLE_STEPS_PER_TILE;
410 }
411 dbg->message("convoi_t::finish_rd()", "v: pos(%s) steps(%d) len=%d ribi=%d prev (%s) step(%d)", v->get_pos().get_str(), v->get_steps(), v->get_desc()->get_length()*16, v->get_direction(), drive_pos.get_2d().get_str(), step_pos);
412 if( abs( v->get_steps() - step_pos )>15 ) {
413 // not where it should be => realign
414 realign_position = true;
415 dbg->warning( "convoi_t::finish_rd()", "convoi (%s) is broken => realign", get_name() );
416 }
417 }
418 step_pos -= v->get_desc()->get_length_in_steps();
419 drive_pos = v->get_pos();
420 }
421 }
422 }
423 DBG_MESSAGE("convoi_t::finish_rd()","next_stop_index=%d", next_stop_index );
424
425 linehandle_t new_line = line;
426 if( !new_line.is_bound() ) {
427 // if there is a line with id=0 in the savegame try to assign cnv to this line
428 new_line = get_owner()->simlinemgmt.get_line_with_id_zero();
429 }
430 if( new_line.is_bound() ) {
431 if ( !schedule->matches( welt, new_line->get_schedule() ) ) {
432 // 101 version produced broken line ids => we have to find our line the hard way ...
433 vector_tpl<linehandle_t> lines;
434 get_owner()->simlinemgmt.get_lines(schedule->get_type(), &lines);
435 new_line = linehandle_t();
436 FOR(vector_tpl<linehandle_t>, const l, lines) {
437 if( schedule->matches( welt, l->get_schedule() ) ) {
438 // if a line is assigned, set line!
439 new_line = l;
440 break;
441 }
442 }
443 }
444 // now the line should match our schedule or else ...
445 if(new_line.is_bound()) {
446 line = new_line;
447 line->add_convoy(self);
448 DBG_DEBUG("convoi_t::finish_rd()","%s registers for %d", name_and_id, line.get_id());
449 }
450 else {
451 line = linehandle_t();
452 }
453 }
454 }
455 else {
456 // no vehicles in this convoi?!?
457 dbg->error( "convoi_t::finish_rd()","No vehicles in Convoi %i: will be destroyed!", self.get_id() );
458 destroy();
459 return;
460 }
461 // put convoi again right on track?
462 if(realign_position && anz_vehikel>1) {
463 // display just a warning
464 dbg->warning("convoi_t::finish_rd()","cnv %i is currently too long.",self.get_id());
465
466 if (route.empty()) {
467 // realigning needs a route
468 state = NO_ROUTE;
469 owner->report_vehicle_problem( self, koord3d::invalid );
470 dbg->error( "convoi_t::finish_rd()", "No valid route, but needs realignment at (%s)!", fahr[0]->get_pos().get_str() );
471 }
472 else {
473 // since start may have been changed
474 uint16 start_index = max(1,fahr[anz_vehikel-1]->get_route_index())-1;
475 if (start_index > route.get_count()) {
476 dbg->error( "convoi_t::finish_rd()", "Routeindex of last vehicle of (%s) too large!", get_name() );
477 start_index = 0;
478 }
479
480 uint32 train_length = move_to(start_index) + 1;
481 const koord3d last_start = fahr[0]->get_pos();
482
483 // now advance all convoi until it is completely on the track
484 fahr[0]->set_leading(false); // switches off signal checks ...
485 for(unsigned i=0; i<anz_vehikel; i++) {
486 vehicle_t* v = fahr[i];
487
488 v->get_smoke(false);
489 fahr[i]->do_drive( (VEHICLE_STEPS_PER_CARUNIT*train_length)<<YARDS_PER_VEHICLE_STEP_SHIFT );
490 train_length -= v->get_desc()->get_length();
491 v->get_smoke(true);
492
493 // eventually reserve this again
494 grund_t *gr=welt->lookup(v->get_pos());
495 // airplanes may have no ground ...
496 if (schiene_t* const sch0 = obj_cast<schiene_t>(gr->get_weg(fahr[i]->get_waytype()))) {
497 sch0->reserve(self,ribi_t::none);
498 }
499 }
500 fahr[0]->set_leading(true);
501 if( state != INITIAL && state != EDIT_SCHEDULE && fahr[0]->get_pos() != last_start ) {
502 state = WAITING_FOR_CLEARANCE;
503 }
504 }
505 }
506 if( state==LOADING ) {
507 // the fully the shorter => register again as older convoi
508 wait_lock = 2000-loading_level*20;
509 }
510 // when saving with open window, this can happen
511 if( state==EDIT_SCHEDULE ) {
512 if (env_t::networkmode) {
513 wait_lock = 30000; // 60s to drive on, if the client in question had left
514 }
515 schedule->finish_editing();
516 }
517 // remove wrong freight
518 check_freight();
519 // some convois had wrong old direction in them
520 if( state<DRIVING || state==LOADING ) {
521 alte_richtung = fahr[0]->get_direction();
522 }
523 // Knightly : if lineless convoy -> register itself with stops
524 if( !line.is_bound() ) {
525 register_stops();
526 }
527
528 calc_speedbonus_kmh();
529 }
530
531
532 // since now convoi states go via tool_t
call_convoi_tool(const char function,const char * extra) const533 void convoi_t::call_convoi_tool( const char function, const char *extra ) const
534 {
535 tool_t *tmp_tool = create_tool( TOOL_CHANGE_CONVOI | SIMPLE_TOOL );
536 cbuffer_t param;
537 param.printf("%c,%u", function, self.get_id());
538 if( extra && *extra ) {
539 param.printf(",%s", extra);
540 }
541 tmp_tool->set_default_param(param);
542 welt->set_tool( tmp_tool, get_owner() );
543 // since init always returns false, it is safe to delete immediately
544 delete tmp_tool;
545 }
546
547
rotate90(const sint16 y_size)548 void convoi_t::rotate90( const sint16 y_size )
549 {
550 record_pos.rotate90( y_size );
551 home_depot.rotate90( y_size );
552 route.rotate90( y_size );
553 if( schedule_target!=koord3d::invalid ) {
554 schedule_target.rotate90( y_size );
555 }
556 if(schedule) {
557 schedule->rotate90( y_size );
558 }
559 for( int i=0; i<anz_vehikel; i++ ) {
560 fahr[i]->rotate90_freight_destinations( y_size );
561 }
562 // eventually correct freight destinations (and remove all stale freight)
563 check_freight();
564 }
565
566
567 /**
568 * Return the convoi position.
569 * @return Convoi position
570 * @author Hj. Malthaner
571 */
get_pos() const572 koord3d convoi_t::get_pos() const
573 {
574 if(anz_vehikel > 0 && fahr[0]) {
575 return state==INITIAL ? home_depot : fahr[0]->get_pos();
576 }
577 else {
578 return koord3d::invalid;
579 }
580 }
581
582
583 /**
584 * Sets the name. Creates a copy of name.
585 * @author Hj. Malthaner
586 */
set_name(const char * name,bool with_new_id)587 void convoi_t::set_name(const char *name, bool with_new_id)
588 {
589 if( with_new_id ) {
590 char buf[128];
591 name_offset = sprintf(buf,"(%i) ",self.get_id() );
592 tstrncpy(buf + name_offset, translator::translate(name, welt->get_settings().get_name_language_id()), lengthof(buf) - name_offset);
593 tstrncpy(name_and_id, buf, lengthof(name_and_id));
594 }
595 else {
596 char buf[128];
597 // check if there is a id in the name string
598 name_offset = sprintf(buf,"(%i) ",self.get_id() );
599 if( strlen(name) < name_offset || strncmp(buf,name,name_offset)!=0) {
600 name_offset = 0;
601 }
602 tstrncpy(buf+name_offset, name+name_offset, sizeof(buf)-name_offset);
603 tstrncpy(name_and_id, buf, lengthof(name_and_id));
604 }
605 // now tell the windows that we were renamed
606 convoi_info_t *info = dynamic_cast<convoi_info_t*>(win_get_magic( magic_convoi_info+self.get_id()));
607 if (info) {
608 info->update_data();
609 }
610 if( in_depot() ) {
611 const grund_t *const ground = welt->lookup( get_home_depot() );
612 if( ground ) {
613 const depot_t *const depot = ground->get_depot();
614 if( depot ) {
615 depot_frame_t *const frame = dynamic_cast<depot_frame_t *>( win_get_magic( (ptrdiff_t)depot ) );
616 if( frame ) {
617 frame->update_data();
618 }
619 }
620 }
621 }
622 }
623
624
625 // length of convoi (16 is one tile)
get_length() const626 uint32 convoi_t::get_length() const
627 {
628 uint32 len = 0;
629 for( uint8 i=0; i<anz_vehikel; i++ ) {
630 len += fahr[i]->get_desc()->get_length();
631 }
632 return len;
633 }
634
635
636 /**
637 * convoi add their running cost for traveling one tile
638 * @author Hj. Malthaner
639 */
add_running_cost(const weg_t * weg)640 void convoi_t::add_running_cost( const weg_t *weg )
641 {
642 jahresgewinn += sum_running_costs;
643
644 if( weg && weg->get_owner()!=get_owner() && weg->get_owner()!=NULL ) {
645 // running on non-public way costs toll (since running costs are positive => invert)
646 sint32 toll = -(sum_running_costs*welt->get_settings().get_way_toll_runningcost_percentage())/100l;
647 if( welt->get_settings().get_way_toll_waycost_percentage() ) {
648 if( weg->is_electrified() && needs_electrification() ) {
649 // toll for using electricity
650 grund_t *gr = welt->lookup(weg->get_pos());
651 for( int i=1; i<gr->get_top(); i++ ) {
652 obj_t *d=gr->obj_bei(i);
653 if( wayobj_t const* const wo = obj_cast<wayobj_t>(d) ) {
654 if( wo->get_waytype()==weg->get_waytype() ) {
655 toll += (wo->get_desc()->get_maintenance()*welt->get_settings().get_way_toll_waycost_percentage())/100l;
656 break;
657 }
658 }
659 }
660 }
661 // now add normal way toll be maintenance
662 toll += (weg->get_desc()->get_maintenance()*welt->get_settings().get_way_toll_waycost_percentage())/100l;
663 }
664 weg->get_owner()->book_toll_received( toll, get_schedule()->get_waytype() );
665 get_owner()->book_toll_paid( -toll, get_schedule()->get_waytype() );
666 book( -toll, CONVOI_WAYTOLL);
667 book( -toll, CONVOI_PROFIT);
668 }
669 get_owner()->book_running_costs( sum_running_costs, get_schedule()->get_waytype());
670
671 book( sum_running_costs, CONVOI_OPERATIONS );
672 book( sum_running_costs, CONVOI_PROFIT );
673
674 total_distance_traveled ++;
675 distance_since_last_stop++;
676
677 sum_speed_limit += speed_to_kmh( min( min_top_speed, speed_limit ));
678 book( 1, CONVOI_DISTANCE );
679 }
680
681
682 /**
683 * Returns residual power given power, weight, and current speed.
684 * @param speed (in internal speed unit)
685 * @param total_power sum of power times gear (see calculation of sum_gear_and_power)
686 * @param friction_weight weight including friction of the convoy
687 * @param total_weight weight of the convoy
688 * @returns residual power
689 */
res_power(sint64 speed,sint32 total_power,sint64 friction_weight,sint64 total_weight)690 static inline sint32 res_power(sint64 speed, sint32 total_power, sint64 friction_weight, sint64 total_weight)
691 {
692 sint32 res = total_power - (sint32)( ( (sint64)speed * ( (friction_weight * (sint64)speed ) / 3125ll + 1ll) ) / 2048ll + (total_weight * 64ll) / 1000ll);
693 return res;
694 }
695
696 /* Calculates (and sets) new akt_speed
697 * needed for driving, entering and leaving a depot)
698 */
calc_acceleration(uint32 delta_t)699 void convoi_t::calc_acceleration(uint32 delta_t)
700 {
701 if( !recalc_data && !recalc_speed_limit && !recalc_data_front && (
702 (sum_friction_weight == sum_gesamtweight && akt_speed_soll <= akt_speed && akt_speed_soll+24 >= akt_speed) ||
703 (sum_friction_weight > sum_gesamtweight && akt_speed_soll == akt_speed) )
704 ) {
705 // at max speed => go with max speed and finish calculation here
706 // at slopes/curves, only do this if there is absolutely now change
707 akt_speed = akt_speed_soll;
708 return;
709 }
710
711 // Dwachs: only compute this if a vehicle in the convoi hopped
712 if( recalc_data || recalc_speed_limit ) {
713 // calculate total friction and lowest speed limit
714 const vehicle_t* v = front();
715 speed_limit = min( min_top_speed, v->get_speed_limit() );
716 if (recalc_data) {
717 sum_gesamtweight = v->get_total_weight();
718 sum_friction_weight = v->get_frictionfactor() * sum_gesamtweight;
719 }
720
721 for( unsigned i=1; i<anz_vehikel; i++ ) {
722 const vehicle_t* v = fahr[i];
723 speed_limit = min( speed_limit, v->get_speed_limit() );
724
725 if (recalc_data) {
726 int total_vehicle_weight = v->get_total_weight();
727 sum_friction_weight += v->get_frictionfactor() * total_vehicle_weight;
728 sum_gesamtweight += total_vehicle_weight;
729 }
730 }
731 recalc_data = recalc_speed_limit = false;
732 akt_speed_soll = min( speed_limit, brake_speed_soll );
733 }
734
735 if( recalc_data_front ) {
736 // brake at the end of stations/in front of signals and crossings
737 const uint32 tiles_left = 1 + get_next_stop_index() - front()->get_route_index();
738 brake_speed_soll = SPEED_UNLIMITED;
739 if( tiles_left < 4 ) {
740 static sint32 brake_speed_countdown[4] = {
741 kmh_to_speed(25),
742 kmh_to_speed(50),
743 kmh_to_speed(100),
744 kmh_to_speed(200)
745 };
746 brake_speed_soll = brake_speed_countdown[tiles_left];
747 }
748 akt_speed_soll = min( speed_limit, brake_speed_soll );
749 recalc_data_front = false;
750 }
751
752 // Prissi: more pleasant and a little more "physical" model *
753
754 // try to simulate quadratic friction
755 if(sum_gesamtweight != 0) {
756 /*
757 * The parameter consist of two parts (optimized for good looking):
758 * - every vehicle in a convoi has a the friction of its weight
759 * - the dynamic friction is calculated that way, that v^2*weight*frictionfactor = 200 kW
760 * => heavier loaded and faster traveling => less power for acceleration is available!
761 * since delta_t can have any value, we have to scale the step size by this value.
762 * however, there is a quadratic friction term => if delta_t is too large the calculation may get weird results
763 * @author prissi
764 */
765
766 /* but for integer, we have to use the order below and calculate actually 64*deccel, like the sum_gear_and_power
767 * since akt_speed=10/128 km/h and we want 64*200kW=(100km/h)^2*100t, we must multiply by (128*2)/100
768 * But since the acceleration was too fast, we just decelerate 4x more => >>6 instead >>8 */
769 //sint32 deccel = ( ( (akt_speed*sum_friction_weight)>>6 )*(akt_speed>>2) ) / 25 + (sum_gesamtweight*64); // this order is needed to prevent overflows!
770 //sint32 deccel = (sint32)( ( (sint64)akt_speed * (sint64)sum_friction_weight * (sint64)akt_speed ) / (25ll*256ll) + sum_gesamtweight * 64ll) / 1000ll; // intermediate still overflows so sint64
771 //sint32 deccel = (sint32)( ( (sint64)akt_speed * ( (sum_friction_weight * (sint64)akt_speed ) / 3125ll + 1ll) ) / 2048ll + (sum_gesamtweight * 64ll) / 1000ll);
772
773 // prissi: integer sucks with planes => using floats ...
774 // turfit: result can overflow sint32 and double so onto sint64. planes ok.
775 //sint32 delta_v = (sint32)( ( (double)( (akt_speed>akt_speed_soll?0l:sum_gear_and_power) - deccel)*(double)delta_t)/(double)sum_gesamtweight);
776
777 sint64 residual_power = res_power(akt_speed, akt_speed>akt_speed_soll? 0l : sum_gear_and_power, sum_friction_weight, sum_gesamtweight);
778
779 // we normalize delta_t to 1/64th and check for speed limit */
780 //sint32 delta_v = ( ( (akt_speed>akt_speed_soll?0l:sum_gear_and_power) - deccel) * delta_t)/sum_gesamtweight;
781 sint64 delta_v = ( residual_power * (sint64)delta_t * 1000ll) / (sint64)sum_gesamtweight;
782
783 // we need more accurate arithmetic, so we store the previous value
784 delta_v += previous_delta_v;
785 previous_delta_v = (sint32)(delta_v & 0x00000FFFll);
786 // and finally calculate new speed
787 akt_speed = max(akt_speed_soll>>4, akt_speed+(sint32)(delta_v>>12l) );
788 }
789 else {
790 // very old vehicle ...
791 akt_speed += 16;
792 }
793
794 // obey speed maximum with additional const brake ...
795 if(akt_speed > akt_speed_soll) {
796 if (akt_speed > akt_speed_soll + 24) {
797 akt_speed -= 24;
798 if(akt_speed > akt_speed_soll+kmh_to_speed(20)) {
799 akt_speed = akt_speed_soll+kmh_to_speed(20);
800 }
801 }
802 else {
803 akt_speed = akt_speed_soll;
804 }
805 }
806
807 // new record?
808 if(akt_speed > max_record_speed) {
809 max_record_speed = akt_speed;
810 record_pos = fahr[0]->get_pos().get_2d();
811 }
812 }
813
814
815 /**
816 * Calculates maximal possible speed.
817 * Uses iterative technique to take care of integer arithmetic.
818 */
calc_max_speed(uint64 total_power,uint64 total_weight,sint32 speed_limit)819 sint32 convoi_t::calc_max_speed(uint64 total_power, uint64 total_weight, sint32 speed_limit)
820 {
821 // precision is 0.5 km/h
822 const sint32 tol = kmh_to_speed(1)/2;
823 // bisection to find max speed
824 sint32 pl,pr,pm;
825 sint64 sl,sr,sm;
826
827 // test speed_limit
828 sr = speed_limit;
829 pr = res_power(sr, (sint32)total_power, total_weight, total_weight);
830 if (pr >= 0) {
831 return (sint32)sr; // convoy can travel at speed given by speed_limit
832 }
833 sl = 1;
834 pl = res_power(sl, (sint32)total_power, total_weight, total_weight);
835 if (pl <= 0) {
836 return 0; // no power to move at all
837 }
838
839 // bisection algorithm to find speed for which residual power is zero
840 while (sr - sl > tol) {
841 sm = (sl + sr)/2;
842 if (sm == sl) break;
843
844 pm = res_power(sm, (sint32)total_power, total_weight, total_weight);
845
846 if (((sint64)pl)*pm <= 0) {
847 pr = pm;
848 sr = sm;
849 }
850 else {
851 pl = pm;
852 sl = sm;
853 }
854 }
855 return (sint32)sl;
856 }
857
858
get_vehicle_at_length(uint16 length)859 int convoi_t::get_vehicle_at_length(uint16 length)
860 {
861 int current_length = 0;
862 for( int i=0; i<anz_vehikel; i++ ) {
863 current_length += fahr[i]->get_desc()->get_length();
864 if(length<current_length) {
865 return i;
866 }
867 }
868 return anz_vehikel;
869 }
870
871
872 // moves all vehicles of a convoi
sync_step(uint32 delta_t)873 sync_result convoi_t::sync_step(uint32 delta_t)
874 {
875 // still have to wait before next action?
876 wait_lock -= delta_t;
877 if(wait_lock > 0) {
878 return SYNC_OK;
879 }
880 wait_lock = 0;
881
882 switch(state) {
883 case INITIAL:
884 // in depot, should not be in sync list, remove
885 return SYNC_REMOVE;
886
887 case EDIT_SCHEDULE:
888 case ROUTING_1:
889 case DUMMY4:
890 case DUMMY5:
891 case NO_ROUTE:
892 case CAN_START:
893 case CAN_START_ONE_MONTH:
894 case CAN_START_TWO_MONTHS:
895 // Hajo: this is an async task, see step()
896 break;
897
898 case ENTERING_DEPOT:
899 break;
900
901 case LEAVING_DEPOT:
902 {
903 // ok, so we will accelerate
904 akt_speed_soll = max( akt_speed_soll, kmh_to_speed(30) );
905 calc_acceleration(delta_t);
906 sp_soll += (akt_speed*delta_t);
907
908 // now actually move the units
909 while(sp_soll>>12) {
910 // Attempt to move one step.
911 uint32 sp_hat = fahr[0]->do_drive(1<<YARDS_PER_VEHICLE_STEP_SHIFT);
912 int v_nr = get_vehicle_at_length((++steps_driven)>>4);
913 // stop when depot reached
914 if (state==INITIAL) {
915 return SYNC_REMOVE;
916 }
917 if (state==ROUTING_1) {
918 break;
919 }
920 // until all are moving or something went wrong (sp_hat==0)
921 if(sp_hat==0 || v_nr==anz_vehikel) {
922 steps_driven = -1;
923 state = DRIVING;
924 return SYNC_OK;
925 }
926 // now only the right numbers
927 for(int i=1; i<=v_nr; i++) {
928 fahr[i]->do_drive(sp_hat);
929 }
930 sp_soll -= sp_hat;
931 }
932 // smoke for the engines
933 next_wolke += delta_t;
934 if(next_wolke>500) {
935 next_wolke = 0;
936 for(int i=0; i<anz_vehikel; i++ ) {
937 fahr[i]->make_smoke();
938 }
939 }
940 }
941 break; // LEAVING_DEPOT
942
943 case DRIVING:
944 {
945 calc_acceleration(delta_t);
946
947 // now actually move the units
948 sp_soll += (akt_speed*delta_t);
949 uint32 sp_hat = fahr[0]->do_drive(sp_soll);
950 // stop when depot reached ...
951 if(state==INITIAL) {
952 return SYNC_REMOVE;
953 }
954 // now move the rest (so all vehikel are moving synchronously)
955 for(unsigned i=1; i<anz_vehikel; i++) {
956 fahr[i]->do_drive(sp_hat);
957 }
958 // maybe we have been stopped be something => avoid wide jumps
959 sp_soll = (sp_soll-sp_hat) & 0x0FFF;
960
961 // smoke for the engines
962 next_wolke += delta_t;
963 if(next_wolke>500) {
964 next_wolke = 0;
965 for(int i=0; i<anz_vehikel; i++ ) {
966 fahr[i]->make_smoke();
967 }
968 }
969 }
970 break; // DRIVING
971
972 case LOADING:
973 // Hajo: loading is an async task, see laden()
974 break;
975
976 case WAITING_FOR_CLEARANCE:
977 case WAITING_FOR_CLEARANCE_ONE_MONTH:
978 case WAITING_FOR_CLEARANCE_TWO_MONTHS:
979 // Hajo: waiting is asynchronous => fixed waiting order and route search
980 break;
981
982 case SELF_DESTRUCT:
983 // see step, since destruction during a screen update may give strange effects
984 break;
985
986 default:
987 dbg->fatal("convoi_t::sync_step()", "Wrong state %d!\n", state);
988 break;
989 }
990
991 return SYNC_OK;
992 }
993
994
995 /**
996 * Berechne route von Start- zu Zielkoordinate
997 * @author Hanjs�rg Malthaner
998 */
drive_to()999 bool convoi_t::drive_to()
1000 {
1001 if( anz_vehikel>0 ) {
1002
1003 // unreserve all tiles that are covered by the train but do not contain one of the wagons,
1004 // otherwise repositioning of the train drive_to may lead to stray reserved tiles
1005 if (dynamic_cast<rail_vehicle_t*>(fahr[0])!=NULL && anz_vehikel > 1) {
1006 // route-index points to next position in route
1007 // it is completely off when convoi leaves depot
1008 uint16 index0 = min(fahr[0]->get_route_index()-1, route.get_count());
1009 for(uint8 i=1; i<anz_vehikel; i++) {
1010 uint16 index1 = fahr[i]->get_route_index();
1011 for(uint16 j = index1; j<index0; j++) {
1012 // unreserve track on tiles between wagons
1013 grund_t *gr = welt->lookup(route.at(j));
1014 if (schiene_t *track = (schiene_t *)gr->get_weg( front()->get_waytype() ) ) {
1015 track->unreserve(self);
1016 }
1017 }
1018 index0 = min(index1-1, route.get_count());
1019 }
1020 }
1021
1022 koord3d start = fahr[0]->get_pos();
1023 koord3d ziel = schedule->get_current_entry().pos;
1024
1025 // avoid stopping mid-halt
1026 if( start==ziel ) {
1027 halthandle_t halt = haltestelle_t::get_halt(ziel,get_owner());
1028 if( halt.is_bound() && route.is_contained(start) ) {
1029 for( uint32 i=route.index_of(start); i<route.get_count(); i++ ) {
1030 grund_t *gr = welt->lookup(route.at(i));
1031 if( gr && gr->get_halt()==halt ) {
1032 ziel = gr->get_pos();
1033 }
1034 else {
1035 break;
1036 }
1037 }
1038 }
1039 }
1040
1041 if( !fahr[0]->calc_route( start, ziel, speed_to_kmh(min_top_speed), &route ) ) {
1042 if( state != NO_ROUTE ) {
1043 state = NO_ROUTE;
1044 get_owner()->report_vehicle_problem( self, ziel );
1045 }
1046 // wait 25s before next attempt
1047 wait_lock = 25000;
1048 }
1049 else {
1050 bool route_ok = true;
1051 const uint8 current_stop = schedule->get_current_stop();
1052 if( fahr[0]->get_waytype() != water_wt ) {
1053 air_vehicle_t *const plane = dynamic_cast<air_vehicle_t *>(fahr[0]);
1054 uint32 takeoff = 0, search = 0, landing = 0;
1055 air_vehicle_t::flight_state plane_state = air_vehicle_t::taxiing;
1056 if( plane ) {
1057 // due to the complex state system of aircrafts, we have to save index and state
1058 plane->get_event_index( plane_state, takeoff, search, landing );
1059 }
1060
1061 // set next schedule target position if next is a waypoint
1062 if( is_waypoint(ziel) ) {
1063 schedule_target = ziel;
1064 }
1065
1066 // continue route search until the destination is a station
1067 while( is_waypoint(ziel) ) {
1068 start = ziel;
1069 schedule->advance();
1070 ziel = schedule->get_current_entry().pos;
1071
1072 if( schedule->get_current_stop() == current_stop ) {
1073 // looped around without finding a halt => entire schedule is waypoints.
1074 break;
1075 }
1076
1077 route_t next_segment;
1078 if( !fahr[0]->calc_route( start, ziel, speed_to_kmh(min_top_speed), &next_segment ) ) {
1079 // do we still have a valid route to proceed => then go until there
1080 if( route.get_count()>1 ) {
1081 break;
1082 }
1083 // we are stuck on our first routing attempt => give up
1084 if( state != NO_ROUTE ) {
1085 state = NO_ROUTE;
1086 get_owner()->report_vehicle_problem( self, ziel );
1087 }
1088 // wait 25s before next attempt
1089 wait_lock = 25000;
1090 route_ok = false;
1091 break;
1092 }
1093 else {
1094 bool looped = false;
1095 if( fahr[0]->get_waytype() != air_wt ) {
1096 // check if the route circles back on itself (only check the first tile, should be enough)
1097 looped = route.is_contained(next_segment.at(1));
1098 #if 0
1099 // this will forbid an eight figure, which might be clever to avoid a problem of reserving one own track
1100 for( unsigned i = 1; i<next_segment.get_count(); i++ ) {
1101 if( route.is_contained(next_segment.at(i)) ) {
1102 looped = true;
1103 break;
1104 }
1105 }
1106 #endif
1107 }
1108
1109 if( looped ) {
1110 // proceed upto the waypoint before the loop. Will pause there for a new route search.
1111 break;
1112 }
1113 else {
1114 uint32 count_offset = route.get_count()-1;
1115 route.append( &next_segment);
1116 if( plane ) {
1117 // maybe we need to restore index
1118 air_vehicle_t::flight_state dummy1;
1119 uint32 new_takeoff, new_search, new_landing;
1120 plane->get_event_index( dummy1, new_takeoff, new_search, new_landing );
1121 if( takeoff == 0x7FFFFFFF && new_takeoff != 0x7FFFFFFF ) {
1122 takeoff = new_takeoff + count_offset;
1123 }
1124 if( landing == 0x7FFFFFFF && new_landing != 0x7FFFFFFF ) {
1125 landing = new_landing + count_offset;
1126 }
1127 if( search == 0x7FFFFFFF && new_search != 0x7FFFFFFF ) {
1128 search = new_search + count_offset;
1129 }
1130 }
1131 }
1132 }
1133 }
1134
1135 if( plane ) {
1136 // due to the complex state system of aircrafts, we have to restore index and state
1137 plane->set_event_index( plane_state, takeoff, search, landing );
1138 }
1139 }
1140
1141 schedule->set_current_stop(current_stop);
1142 if( route_ok ) {
1143 vorfahren();
1144 return true;
1145 }
1146 }
1147 }
1148 return false;
1149 }
1150
1151
1152 /**
1153 * Ein Fahrzeug hat ein Problem erkannt und erzwingt die
1154 * Berechnung einer neuen Route
1155 * @author Hanjs�rg Malthaner
1156 */
suche_neue_route()1157 void convoi_t::suche_neue_route()
1158 {
1159 state = ROUTING_1;
1160 wait_lock = 0;
1161 }
1162
1163
1164 /**
1165 * Asynchrne step methode des Convois
1166 * @author Hj. Malthaner
1167 */
step()1168 void convoi_t::step()
1169 {
1170 if( wait_lock > 0 ) {
1171 return;
1172 }
1173
1174 // moved check to here, as this will apply the same update
1175 // logic/constraints convois have for manual schedule manipulation
1176 if (line_update_pending.is_bound()) {
1177 check_pending_updates();
1178 }
1179
1180 switch(state) {
1181
1182 case LOADING:
1183 laden();
1184 break;
1185
1186 case DUMMY4:
1187 case DUMMY5:
1188 break;
1189
1190 case EDIT_SCHEDULE:
1191 // schedule window closed?
1192 if(schedule!=NULL && schedule->is_editing_finished()) {
1193
1194 set_schedule(schedule);
1195 schedule_target = koord3d::invalid;
1196
1197 if( schedule->empty() ) {
1198 // no entry => no route ...
1199 state = NO_ROUTE;
1200 owner->report_vehicle_problem( self, koord3d::invalid );
1201 }
1202 else {
1203 // Schedule changed at station
1204 // this station? then complete loading task else drive on
1205 halthandle_t h = haltestelle_t::get_halt( get_pos(), get_owner() );
1206 if( h.is_bound() && h==haltestelle_t::get_halt( schedule->get_current_entry().pos, get_owner() ) ) {
1207 if (route.get_count() > 0) {
1208 koord3d const& pos = route.back();
1209 if (h == haltestelle_t::get_halt(pos, get_owner())) {
1210 state = get_pos() == pos ? LOADING : DRIVING;
1211 break;
1212 }
1213 }
1214 else {
1215 if( drive_to() ) {
1216 state = DRIVING;
1217 break;
1218 }
1219 }
1220 }
1221
1222 if( schedule->get_current_entry().pos==get_pos() ) {
1223 // position in depot: waiting
1224 grund_t *gr = welt->lookup(schedule->get_current_entry().pos);
1225 if( gr && gr->get_depot() ) {
1226 betrete_depot( gr->get_depot() );
1227 }
1228 else {
1229 state = ROUTING_1;
1230 }
1231 }
1232 else {
1233 // go to next
1234 state = ROUTING_1;
1235 }
1236 }
1237 }
1238 break;
1239
1240 case ROUTING_1:
1241 {
1242 vehicle_t* v = fahr[0];
1243
1244 if( schedule->empty() ) {
1245 state = NO_ROUTE;
1246 owner->report_vehicle_problem( self, koord3d::invalid );
1247 }
1248 else {
1249 // check first, if we are already there:
1250 assert( schedule->get_current_stop()<schedule->get_count() );
1251 if( v->get_pos()==schedule->get_current_entry().pos ) {
1252 schedule->advance();
1253 }
1254 // Hajo: now calculate a new route
1255 drive_to();
1256 // finally, was there a record last time?
1257 if(max_record_speed>welt->get_record_speed(fahr[0]->get_waytype())) {
1258 welt->notify_record(self, max_record_speed, record_pos);
1259 }
1260 }
1261 }
1262 break;
1263
1264 case NO_ROUTE:
1265 // stuck vehicles
1266 if (schedule->empty()) {
1267 // no entries => no route ...
1268 }
1269 else {
1270 // Hajo: now calculate a new route
1271 drive_to();
1272 }
1273 break;
1274
1275 case CAN_START:
1276 case CAN_START_ONE_MONTH:
1277 case CAN_START_TWO_MONTHS:
1278 {
1279 vehicle_t* v = fahr[0];
1280
1281 sint32 restart_speed = -1;
1282 if( v->can_enter_tile( restart_speed, 0 ) ) {
1283 // can reserve new block => drive on
1284 state = (steps_driven>=0) ? LEAVING_DEPOT : DRIVING;
1285 if(haltestelle_t::get_halt(v->get_pos(),owner).is_bound()) {
1286 v->play_sound();
1287 }
1288 }
1289 else if( steps_driven==0 ) {
1290 // on rail depot tile, do not reserve this
1291 if( grund_t *gr = welt->lookup(fahr[0]->get_pos()) ) {
1292 if (schiene_t* const sch0 = obj_cast<schiene_t>(gr->get_weg(fahr[0]->get_waytype()))) {
1293 sch0->unreserve(fahr[0]);
1294 }
1295 }
1296 }
1297 if(restart_speed>=0) {
1298 akt_speed = restart_speed;
1299 }
1300 if(state==CAN_START || state==CAN_START_ONE_MONTH) {
1301 set_tiles_overtaking( 0 );
1302 }
1303 }
1304 break;
1305
1306 case WAITING_FOR_CLEARANCE_ONE_MONTH:
1307 case WAITING_FOR_CLEARANCE_TWO_MONTHS:
1308 case WAITING_FOR_CLEARANCE:
1309 {
1310 sint32 restart_speed = -1;
1311 if( fahr[0]->can_enter_tile( restart_speed, 0 ) ) {
1312 state = (steps_driven>=0) ? LEAVING_DEPOT : DRIVING;
1313 }
1314 if(restart_speed>=0) {
1315 akt_speed = restart_speed;
1316 }
1317 if(state!=DRIVING) {
1318 set_tiles_overtaking( 0 );
1319 }
1320 }
1321 break;
1322
1323 // must be here; may otherwise confuse window management
1324 case SELF_DESTRUCT:
1325 welt->set_dirty();
1326 destroy();
1327 return; // must not continue method after deleting this object
1328
1329 default: /* keeps compiler silent*/
1330 break;
1331 }
1332
1333 // calculate new waiting time
1334 switch( state ) {
1335 // handled by routine
1336 case LOADING:
1337 break;
1338
1339 // immediate action needed
1340 case SELF_DESTRUCT:
1341 case LEAVING_DEPOT:
1342 case ENTERING_DEPOT:
1343 case DRIVING:
1344 case DUMMY4:
1345 case DUMMY5:
1346 wait_lock = 0;
1347 break;
1348
1349 // just waiting for action here
1350 case INITIAL:
1351 welt->sync.remove(this);
1352 /* FALLTHROUGH */
1353 case EDIT_SCHEDULE:
1354 case NO_ROUTE:
1355 wait_lock = max( wait_lock, 25000 );
1356 break;
1357
1358 // action soon needed
1359 case ROUTING_1:
1360 case CAN_START:
1361 case WAITING_FOR_CLEARANCE:
1362 wait_lock = max( wait_lock, 500 );
1363 break;
1364
1365 // waiting for free way, not too heavy, not to slow
1366 case CAN_START_ONE_MONTH:
1367 case WAITING_FOR_CLEARANCE_ONE_MONTH:
1368 case CAN_START_TWO_MONTHS:
1369 case WAITING_FOR_CLEARANCE_TWO_MONTHS:
1370 wait_lock = 2500;
1371 break;
1372 default: ;
1373 }
1374 }
1375
1376
new_year()1377 void convoi_t::new_year()
1378 {
1379 jahresgewinn = 0;
1380 }
1381
1382
new_month()1383 void convoi_t::new_month()
1384 {
1385 // should not happen: leftover convoi without vehicles ...
1386 if(anz_vehikel==0) {
1387 DBG_DEBUG("convoi_t::new_month()","no vehicles => self destruct!");
1388 self_destruct();
1389 return;
1390 }
1391 // update statistics of average speed
1392 if( maxspeed_average_count==0 ) {
1393 financial_history[0][CONVOI_MAXSPEED] = distance_since_last_stop>0 ? get_speedbonus_kmh() : 0;
1394 }
1395 maxspeed_average_count = 0;
1396 // everything normal: update histroy
1397 for (int j = 0; j<MAX_CONVOI_COST; j++) {
1398 for (int k = MAX_MONTHS-1; k>0; k--) {
1399 financial_history[k][j] = financial_history[k-1][j];
1400 }
1401 financial_history[0][j] = 0;
1402 }
1403 // remind every new month again
1404 if( state==NO_ROUTE ) {
1405 get_owner()->report_vehicle_problem( self, get_pos() );
1406 }
1407 // check for traffic jam
1408 if(state==WAITING_FOR_CLEARANCE) {
1409 state = WAITING_FOR_CLEARANCE_ONE_MONTH;
1410 // check, if now free ...
1411 // might also reset the state!
1412 sint32 restart_speed = -1;
1413 if( fahr[0]->can_enter_tile( restart_speed, 0 ) ) {
1414 state = DRIVING;
1415 }
1416 if(restart_speed>=0) {
1417 akt_speed = restart_speed;
1418 }
1419 }
1420 else if(state==WAITING_FOR_CLEARANCE_ONE_MONTH) {
1421 // make sure, not another vehicle with same line is loading in front
1422 bool notify = true;
1423 // check, if we are not waiting for load
1424 if( line.is_bound() && loading_level==0 ) {
1425 for( uint i=0; i < line->count_convoys(); i++ ) {
1426 convoihandle_t cnv = line->get_convoy(i);
1427 if( cnv.is_bound() && cnv->get_state()==LOADING && cnv->get_loading_level() < cnv->get_loading_limit() ) {
1428 // convoi on this line is waiting for load => assume we are waiting behind
1429 notify = false;
1430 break;
1431 }
1432 }
1433 }
1434 if( notify ) {
1435 get_owner()->report_vehicle_problem( self, koord3d::invalid );
1436 }
1437 state = WAITING_FOR_CLEARANCE_TWO_MONTHS;
1438 }
1439 // check for traffic jam
1440 if(state==CAN_START) {
1441 state = CAN_START_ONE_MONTH;
1442 }
1443 else if(state==CAN_START_ONE_MONTH || state==CAN_START_TWO_MONTHS ) {
1444 get_owner()->report_vehicle_problem( self, koord3d::invalid );
1445 state = CAN_START_TWO_MONTHS;
1446 }
1447 // check for obsolete vehicles in the convoi
1448 if(!has_obsolete && welt->use_timeline()) {
1449 // convoi has obsolete vehicles?
1450 const int month_now = welt->get_timeline_year_month();
1451 has_obsolete = false;
1452 for(unsigned j=0; j<get_vehicle_count(); j++ ) {
1453 if (fahr[j]->get_desc()->is_retired(month_now)) {
1454 has_obsolete = true;
1455 break;
1456 }
1457 }
1458 }
1459 }
1460
1461
betrete_depot(depot_t * dep)1462 void convoi_t::betrete_depot(depot_t *dep)
1463 {
1464 // first remove reservation, if train is still on track
1465 unreserve_route();
1466
1467 // Hajo: remove vehicles from world data structure
1468 for(unsigned i=0; i<anz_vehikel; i++) {
1469 vehicle_t* v = fahr[i];
1470
1471 grund_t* gr = welt->lookup(v->get_pos());
1472 if(gr) {
1473 // remove from blockstrecke
1474 v->set_last(true);
1475 v->leave_tile();
1476 v->set_flag( obj_t::not_on_map );
1477 }
1478 }
1479
1480 dep->convoi_arrived(self, get_schedule());
1481
1482 destroy_win( magic_convoi_info+self.get_id() );
1483
1484 maxspeed_average_count = 0;
1485 state = INITIAL;
1486 }
1487
1488
start()1489 void convoi_t::start()
1490 {
1491 if(state == INITIAL || state == ROUTING_1) {
1492
1493 // set home depot to location of depot convoi is leaving
1494 if(route.empty()) {
1495 home_depot = fahr[0]->get_pos();
1496 }
1497 else {
1498 home_depot = route.front();
1499 fahr[0]->set_pos( home_depot );
1500 }
1501 // put the convoi on the depot ground, to get automatic rotation
1502 // (vorfahren() will remove it anyway again.)
1503 grund_t *gr = welt->lookup( home_depot );
1504 assert(gr);
1505 gr->obj_add( fahr[0] );
1506
1507 // put into sync list
1508 welt->sync.add(this);
1509
1510 alte_richtung = ribi_t::none;
1511 no_load = false;
1512
1513 state = ROUTING_1;
1514
1515 // recalc weight and image
1516 // also for any vehicle entered a depot, set_letztes is true! => reset it correctly
1517 sint64 restwert_delta = 0;
1518 for(unsigned i=0; i<anz_vehikel; i++) {
1519 fahr[i]->set_leading( false );
1520 fahr[i]->set_last( false );
1521 restwert_delta -= fahr[i]->calc_sale_value();
1522 fahr[i]->set_driven();
1523 restwert_delta += fahr[i]->calc_sale_value();
1524 fahr[i]->clear_flag( obj_t::not_on_map );
1525 }
1526 fahr[0]->set_leading( true );
1527 fahr[anz_vehikel-1]->set_last( true );
1528 // do not show the vehicle - it will be wrong positioned -vorfahren() will correct this
1529 fahr[0]->set_image(IMG_EMPTY);
1530
1531 // update finances for used vehicle reduction when first driven
1532 owner->update_assets( restwert_delta, get_schedule()->get_waytype());
1533
1534 // calc state for convoi
1535 calc_loading();
1536 calc_speedbonus_kmh();
1537 maxspeed_average_count = 0;
1538
1539 if(line.is_bound()) {
1540 // might have changed the vehicles in this car ...
1541 line->recalc_catg_index();
1542 }
1543 else {
1544 welt->set_schedule_counter();
1545 }
1546 wait_lock = 0;
1547
1548 DBG_MESSAGE("convoi_t::start()","Convoi %s wechselt von INITIAL nach ROUTING_1", name_and_id);
1549 }
1550 else {
1551 dbg->warning("convoi_t::start()","called with state=%s\n",state_names[state]);
1552 }
1553 }
1554
1555
1556 /* called, when at a destination
1557 * can be waypoint, depot or a stop
1558 * called from the first vehicle_t of a convoi */
ziel_erreicht()1559 void convoi_t::ziel_erreicht()
1560 {
1561 const vehicle_t* v = fahr[0];
1562 alte_richtung = v->get_direction();
1563
1564 // check, what is at destination!
1565 const grund_t *gr = welt->lookup(v->get_pos());
1566 depot_t *dp = gr->get_depot();
1567
1568 if(dp) {
1569 // ok, we are entering a depot
1570 cbuffer_t buf;
1571
1572 // we still book the money for the trip; however, the freight will be deleted (by the vehicle in the depot itself)
1573 calc_gewinn();
1574
1575 akt_speed = 0;
1576 buf.printf( translator::translate("%s has entered a depot."), get_name() );
1577 welt->get_message()->add_message(buf, v->get_pos().get_2d(),message_t::warnings, PLAYER_FLAG|get_owner()->get_player_nr(), IMG_EMPTY);
1578
1579 betrete_depot(dp);
1580 }
1581 else {
1582 // no depot reached, check for stop!
1583 halthandle_t halt = haltestelle_t::get_halt(schedule->get_current_entry().pos,owner);
1584 if( halt.is_bound() && gr->get_weg_ribi(v->get_waytype())!=0 ) {
1585 // seems to be a stop, so book the money for the trip
1586 akt_speed = 0;
1587 halt->book(1, HALT_CONVOIS_ARRIVED);
1588 state = LOADING;
1589 arrived_time = welt->get_ticks();
1590 }
1591 else {
1592 // Neither depot nor station: waypoint
1593 schedule->advance();
1594 state = ROUTING_1;
1595 }
1596 }
1597 wait_lock = 0;
1598 }
1599
1600
1601 /**
1602 * Wartet bis Fahrzeug 0 freie Fahrt meldet
1603 * @author Hj. Malthaner
1604 */
warten_bis_weg_frei(sint32 restart_speed)1605 void convoi_t::warten_bis_weg_frei(sint32 restart_speed)
1606 {
1607 if(!is_waiting()) {
1608 state = WAITING_FOR_CLEARANCE;
1609 wait_lock = 0;
1610 }
1611 if(restart_speed>=0) {
1612 // langsam anfahren
1613 akt_speed = restart_speed;
1614 }
1615 }
1616
1617
add_vehikel(vehicle_t * v,bool infront)1618 bool convoi_t::add_vehikel(vehicle_t *v, bool infront)
1619 {
1620 DBG_MESSAGE("convoi_t::add_vehikel()","at pos %i of %i total vehikels.",anz_vehikel,fahr.get_count());
1621 // extend array if requested
1622 if(anz_vehikel == fahr.get_count()) {
1623 fahr.resize(anz_vehikel+1,NULL);
1624 DBG_MESSAGE("convoi_t::add_vehikel()","extend array_tpl to %i totals.",fahr.get_count());
1625 }
1626 // now append
1627 if (anz_vehikel < fahr.get_count()) {
1628 v->set_convoi(this);
1629
1630 if(infront) {
1631 for(unsigned i = anz_vehikel; i > 0; i--) {
1632 fahr[i] = fahr[i - 1];
1633 }
1634 fahr[0] = v;
1635 } else {
1636 fahr[anz_vehikel] = v;
1637 }
1638 anz_vehikel ++;
1639
1640 const vehicle_desc_t *info = v->get_desc();
1641 if(info->get_power()) {
1642 is_electric |= info->get_engine_type()==vehicle_desc_t::electric;
1643 }
1644 sum_power += info->get_power();
1645 sum_gear_and_power += info->get_power()*info->get_gear();
1646 sum_weight += info->get_weight();
1647 sum_running_costs -= info->get_running_cost();
1648 min_top_speed = min( min_top_speed, kmh_to_speed( v->get_desc()->get_topspeed() ) );
1649 sum_gesamtweight = sum_weight;
1650 calc_loading();
1651 freight_info_resort = true;
1652 // Add good_catg_index:
1653 if(v->get_cargo_max() != 0) {
1654 const goods_desc_t *ware=v->get_cargo_type();
1655 if(ware!=goods_manager_t::none ) {
1656 goods_catg_index.append_unique( ware->get_catg_index() );
1657 }
1658 }
1659 // check for obsolete
1660 if(!has_obsolete && welt->use_timeline()) {
1661 has_obsolete = info->is_retired( welt->get_timeline_year_month() );
1662 }
1663 player_t::add_maintenance( get_owner(), info->get_maintenance(), info->get_waytype() );
1664 }
1665 else {
1666 return false;
1667 }
1668
1669 // der convoi hat jetzt ein neues ende
1670 set_erstes_letztes();
1671
1672 DBG_MESSAGE("convoi_t::add_vehikel()","now %i of %i total vehikels.",anz_vehikel,fahr.get_count());
1673 return true;
1674 }
1675
1676
remove_vehikel_bei(uint16 i)1677 vehicle_t *convoi_t::remove_vehikel_bei(uint16 i)
1678 {
1679 vehicle_t *v = NULL;
1680 if(i<anz_vehikel) {
1681 v = fahr[i];
1682 if(v != NULL) {
1683 for(unsigned j=i; j<anz_vehikel-1u; j++) {
1684 fahr[j] = fahr[j + 1];
1685 }
1686
1687 v->set_convoi(NULL);
1688
1689 --anz_vehikel;
1690 fahr[anz_vehikel] = NULL;
1691
1692 const vehicle_desc_t *info = v->get_desc();
1693 sum_power -= info->get_power();
1694 sum_gear_and_power -= info->get_power()*info->get_gear();
1695 sum_weight -= info->get_weight();
1696 sum_running_costs += info->get_running_cost();
1697 player_t::add_maintenance( get_owner(), -info->get_maintenance(), info->get_waytype() );
1698 }
1699 sum_gesamtweight = sum_weight;
1700 calc_loading();
1701 freight_info_resort = true;
1702
1703 // der convoi hat jetzt ein neues ende
1704 if(anz_vehikel > 0) {
1705 set_erstes_letztes();
1706 }
1707
1708 // Hajo: calculate new minimum top speed
1709 min_top_speed = calc_min_top_speed(fahr, anz_vehikel);
1710
1711 // check for obsolete
1712 if(has_obsolete) {
1713 has_obsolete = false;
1714 const int month_now = welt->get_timeline_year_month();
1715 for(unsigned i=0; i<anz_vehikel; i++) {
1716 has_obsolete |= fahr[i]->get_desc()->is_retired(month_now);
1717 }
1718 }
1719
1720 recalc_catg_index();
1721
1722 // still requires electrifications?
1723 if(is_electric) {
1724 is_electric = false;
1725 for(unsigned i=0; i<anz_vehikel; i++) {
1726 if(fahr[i]->get_desc()->get_power()) {
1727 is_electric |= fahr[i]->get_desc()->get_engine_type()==vehicle_desc_t::electric;
1728 }
1729 }
1730 }
1731 }
1732 return v;
1733 }
1734
1735
1736 // recalc what good this convoy is moving
recalc_catg_index()1737 void convoi_t::recalc_catg_index()
1738 {
1739 goods_catg_index.clear();
1740
1741 for( uint8 i = 0; i < get_vehicle_count(); i++ ) {
1742 // Only consider vehicles that really transport something
1743 // this helps against routing errors through passenger
1744 // trains pulling only freight wagons
1745 if(get_vehikel(i)->get_cargo_max() == 0) {
1746 continue;
1747 }
1748 const goods_desc_t *ware=get_vehikel(i)->get_cargo_type();
1749 if(ware!=goods_manager_t::none ) {
1750 goods_catg_index.append_unique( ware->get_catg_index() );
1751 }
1752 }
1753 /* since during composition of convois all kinds of composition could happen,
1754 * we do not enforce schedule recalculation here; it will be done anyway all times when leaving the INTI state ...
1755 */
1756 }
1757
1758
set_erstes_letztes()1759 void convoi_t::set_erstes_letztes()
1760 {
1761 // anz_vehikel muss korrekt init sein
1762 if(anz_vehikel>0) {
1763 fahr[0]->set_leading(true);
1764 for(unsigned i=1; i<anz_vehikel; i++) {
1765 fahr[i]->set_leading(false);
1766 fahr[i - 1]->set_last(false);
1767 }
1768 fahr[anz_vehikel - 1]->set_last(true);
1769 }
1770 else {
1771 dbg->warning("convoi_t::set_erstes_letzes()", "called with anz_vehikel==0!");
1772 }
1773 }
1774
1775
1776 // remove wrong freight when schedule changes etc.
check_freight()1777 void convoi_t::check_freight()
1778 {
1779 for(unsigned i=0; i<anz_vehikel; i++) {
1780 fahr[i]->remove_stale_cargo();
1781 }
1782 calc_loading();
1783 freight_info_resort = true;
1784 }
1785
1786
set_schedule(schedule_t * f)1787 bool convoi_t::set_schedule(schedule_t * f)
1788 {
1789 if( state==SELF_DESTRUCT ) {
1790 return false;
1791 }
1792
1793 DBG_DEBUG("convoi_t::set_schedule()", "new=%p, old=%p", f, schedule);
1794 assert(f != NULL);
1795
1796 // happens to be identical?
1797 if(schedule!=f) {
1798 // now check, we we have been bond to a line we are about to lose:
1799 bool changed = false;
1800 if( line.is_bound() ) {
1801 if( !f->matches( welt, line->get_schedule() ) ) {
1802 // change from line to individual schedule
1803 // -> unset line now and register stops from new schedule later
1804 changed = true;
1805 unset_line();
1806 }
1807 }
1808 else {
1809 if( !f->matches( welt, schedule ) ) {
1810 // Knightly : merely change schedule and do not involve line
1811 // -> unregister stops from old schedule now and register stops from new schedule later
1812 changed = true;
1813 unregister_stops();
1814 }
1815 }
1816 // destroy a possibly open schedule window
1817 if( schedule && !schedule->is_editing_finished() ) {
1818 destroy_win((ptrdiff_t)schedule);
1819 delete schedule;
1820 }
1821 schedule = f;
1822 if( changed ) {
1823 // Knightly : if line is unset or schedule is changed
1824 // -> register stops from new schedule
1825 register_stops();
1826 welt->set_schedule_counter(); // must trigger refresh
1827 }
1828 }
1829
1830 // remove wrong freight
1831 check_freight();
1832
1833 // ok, now we have a schedule
1834 if(state != INITIAL) {
1835 state = EDIT_SCHEDULE;
1836 }
1837 // to avoid jumping trains
1838 alte_richtung = fahr[0]->get_direction();
1839 wait_lock = 0;
1840 return true;
1841 }
1842
1843
create_schedule()1844 schedule_t *convoi_t::create_schedule()
1845 {
1846 if(schedule == NULL) {
1847 const vehicle_t* v = fahr[0];
1848
1849 if (v != NULL) {
1850 schedule = v->generate_new_schedule();
1851 schedule->finish_editing();
1852 }
1853 }
1854
1855 return schedule;
1856 }
1857
1858
1859 /* checks, if we go in the same direction;
1860 * true: convoy prepared
1861 * false: must recalculate position
1862 * on all error we better use the normal starting procedure ...
1863 */
can_go_alte_richtung()1864 bool convoi_t::can_go_alte_richtung()
1865 {
1866 // invalid route? nothing to test, must start new
1867 if(route.empty()) {
1868 return false;
1869 }
1870
1871 // going backwards? then recalculate all
1872 ribi_t::ribi neue_richtung_rwr = ribi_t::backward(fahr[0]->calc_direction(route.front(), route.at(min(2, route.get_count() - 1))));
1873 // DBG_MESSAGE("convoi_t::go_alte_richtung()","neu=%i,rwr_neu=%i,alt=%i",neue_richtung_rwr,ribi_t::backward(neue_richtung_rwr),alte_richtung);
1874 if(neue_richtung_rwr&alte_richtung) {
1875 akt_speed = 8;
1876 return false;
1877 }
1878
1879 // now get the actual length and the tile length
1880 uint16 convoi_length = 15;
1881 uint16 tile_length = 24;
1882 unsigned i; // for visual C++
1883 const vehicle_t* pred = NULL;
1884 for(i=0; i<anz_vehikel; i++) {
1885 const vehicle_t* v = fahr[i];
1886 grund_t *gr = welt->lookup(v->get_pos());
1887
1888 // not last vehicle?
1889 // the length of last vehicle does not matter when it comes to positioning of vehicles
1890 if ( i+1 < anz_vehikel) {
1891 convoi_length += v->get_desc()->get_length();
1892 }
1893
1894 if(gr==NULL || (pred!=NULL && (abs(v->get_pos().x-pred->get_pos().x)>=2 || abs(v->get_pos().y-pred->get_pos().y)>=2)) ) {
1895 // ending here is an error!
1896 // this is an already broken train => restart
1897 dbg->warning("convoi_t::go_alte_richtung()","broken convoy (id %i) found => fixing!",self.get_id());
1898 akt_speed = 8;
1899 return false;
1900 }
1901
1902 // now check, if ribi is straight and train is not
1903 ribi_t::ribi weg_ribi = gr->get_weg_ribi_unmasked(v->get_waytype());
1904 if(ribi_t::is_straight(weg_ribi) && (weg_ribi|v->get_direction())!=weg_ribi) {
1905 dbg->warning("convoi_t::go_alte_richtung()","convoy with wrong vehicle directions (id %i) found => fixing!",self.get_id());
1906 akt_speed = 8;
1907 return false;
1908 }
1909
1910 if( pred && pred->get_pos()!=v->get_pos() ) {
1911 tile_length += (ribi_t::is_straight(welt->lookup(pred->get_pos())->get_weg_ribi_unmasked(pred->get_waytype())) ? 16 : 8192/vehicle_t::get_diagonal_multiplier())*koord_distance(pred->get_pos(),v->get_pos());
1912 }
1913
1914 pred = v;
1915 }
1916 // check if convoi is way too short (even for diagonal tracks)
1917 tile_length += (ribi_t::is_straight(welt->lookup(fahr[anz_vehikel-1]->get_pos())->get_weg_ribi_unmasked(fahr[anz_vehikel-1]->get_waytype())) ? 16 : 8192/vehicle_t::get_diagonal_multiplier());
1918 if( convoi_length>tile_length ) {
1919 dbg->warning("convoi_t::go_alte_richtung()","convoy too short (id %i) => fixing!",self.get_id());
1920 akt_speed = 8;
1921 return false;
1922 }
1923
1924 uint16 length = min((convoi_length/16u)+4u,route.get_count()); // maximum length in tiles to check
1925
1926 // we just check, whether we go back (i.e. route tiles other than zero have convoi vehicles on them)
1927 for( int index=1; index<length; index++ ) {
1928 grund_t *gr=welt->lookup(route.at(index));
1929 // now check, if we are already here ...
1930 for(unsigned i=0; i<anz_vehikel; i++) {
1931 if (gr->obj_ist_da(fahr[i])) {
1932 // we are turning around => start slowly and rebuilt train
1933 akt_speed = 8;
1934 return false;
1935 }
1936 }
1937 }
1938
1939 //DBG_MESSAGE("convoi_t::go_alte_richtung()","alte=%d, neu_rwr=%d",alte_richtung,neue_richtung_rwr);
1940
1941 // we continue our journey; however later cars need also a correct route entry
1942 // eventually we need to add their positions to the convois route
1943 koord3d pos = fahr[0]->get_pos();
1944 assert(pos == route.front());
1945 if(welt->lookup(pos)->get_depot()) {
1946 return false;
1947 }
1948 else {
1949 for(i=0; i<anz_vehikel; i++) {
1950 vehicle_t* v = fahr[i];
1951 // eventually add current position to the route
1952 if (route.front() != v->get_pos() && route.at(1) != v->get_pos()) {
1953 route.insert(v->get_pos());
1954 }
1955 }
1956 }
1957
1958 // since we need the route for every vehicle of this convoi,
1959 // we must set the current route index (instead assuming 1)
1960 length = min((convoi_length/8u),route.get_count()-1); // maximum length in tiles to check
1961 bool ok=false;
1962 for(i=0; i<anz_vehikel; i++) {
1963 vehicle_t* v = fahr[i];
1964
1965 // this is such awkward, since it takes into account different vehicle length
1966 const koord3d vehicle_start_pos = v->get_pos();
1967 for( int idx=0; idx<=length; idx++ ) {
1968 if(route.at(idx)==vehicle_start_pos) {
1969 // set route index, no recalculations necessary
1970 v->initialise_journey(idx, false );
1971 ok = true;
1972
1973 // check direction
1974 uint8 richtung = v->get_direction();
1975 uint8 neu_richtung = v->calc_direction( route.at(max(idx-1,0)), v->get_pos_next());
1976 // we need to move to this place ...
1977 if(neu_richtung!=richtung && (i!=0 || anz_vehikel==1 || ribi_t::is_bend(neu_richtung)) ) {
1978 // 90 deg bend!
1979 return false;
1980 }
1981
1982 break;
1983 }
1984 }
1985 // too short?!? (rather broken then!)
1986 if(!ok) {
1987 return false;
1988 }
1989 }
1990
1991 return true;
1992 }
1993
1994
1995 // put the convoi on its way
vorfahren()1996 void convoi_t::vorfahren()
1997 {
1998 // Hajo: init speed settings
1999 sp_soll = 0;
2000 set_tiles_overtaking( 0 );
2001 recalc_data_front = true;
2002 recalc_data = true;
2003
2004 koord3d k0 = route.front();
2005 grund_t *gr = welt->lookup(k0);
2006 bool at_dest = false;
2007 if(gr && gr->get_depot()) {
2008 // start in depot
2009 for(unsigned i=0; i<anz_vehikel; i++) {
2010 vehicle_t* v = fahr[i];
2011
2012 // remove from old position
2013 grund_t* gr = welt->lookup(v->get_pos());
2014 if(gr) {
2015 gr->obj_remove(v);
2016 if(gr->ist_uebergang()) {
2017 crossing_t *cr = gr->find<crossing_t>(2);
2018 cr->release_crossing(v);
2019 }
2020 // eventually unreserve this
2021 if( schiene_t* const sch0 = obj_cast<schiene_t>(gr->get_weg(fahr[i]->get_waytype())) ) {
2022 sch0->unreserve(v);
2023 }
2024 }
2025 v->initialise_journey(0, true);
2026 // set at new position
2027 gr = welt->lookup(v->get_pos());
2028 assert(gr);
2029 v->enter_tile(gr);
2030 }
2031
2032 // just advances the first vehicle
2033 vehicle_t* v0 = fahr[0];
2034 v0->set_leading(false); // switches off signal checks ...
2035 v0->get_smoke(false);
2036 steps_driven = 0;
2037 // drive half a tile:
2038 for(int i=0; i<anz_vehikel; i++) {
2039 fahr[i]->do_drive( (VEHICLE_STEPS_PER_TILE/2)<<YARDS_PER_VEHICLE_STEP_SHIFT );
2040 }
2041 v0->get_smoke(true);
2042 v0->set_leading(true); // switches on signal checks to reserve the next route
2043
2044 // until all other are on the track
2045 state = CAN_START;
2046 }
2047 else {
2048 // still leaving depot (steps_driven!=0) or going in other direction or misalignment?
2049 if( steps_driven>0 || !can_go_alte_richtung() ) {
2050
2051 // start route from the beginning at index 0, place everything on start
2052 uint32 train_length = move_to(0);
2053
2054 // move one train length to the start position ...
2055 // in north/west direction, we leave the vehicle away to start as much back as possible
2056 ribi_t::ribi neue_richtung = fahr[0]->get_direction();
2057 if(neue_richtung==ribi_t::south || neue_richtung==ribi_t::east) {
2058 // drive the convoi to the same position, but do not hop into next tile!
2059 if( train_length%16==0 ) {
2060 // any space we need => just add
2061 train_length += fahr[anz_vehikel-1]->get_desc()->get_length();
2062 }
2063 else {
2064 // limit train to front of tile
2065 train_length += min( (train_length%CARUNITS_PER_TILE)-1, fahr[anz_vehikel-1]->get_desc()->get_length() );
2066 }
2067 }
2068 else {
2069 train_length += 1;
2070 }
2071 train_length = max(1,train_length);
2072
2073 // now advance all convoi until it is completely on the track
2074 fahr[0]->set_leading(false); // switches off signal checks ...
2075 uint32 dist = VEHICLE_STEPS_PER_CARUNIT*train_length<<YARDS_PER_VEHICLE_STEP_SHIFT;
2076 for(unsigned i=0; i<anz_vehikel; i++) {
2077 vehicle_t* v = fahr[i];
2078
2079 v->get_smoke(false);
2080 uint32 const driven = fahr[i]->do_drive( dist );
2081 if (i==0 && driven < dist) {
2082 // we are already at our destination
2083 at_dest = true;
2084 }
2085 // this gives the length in carunits, 1/CARUNITS_PER_TILE of a full tile => all cars closely coupled!
2086 v->get_smoke(true);
2087
2088 uint32 const vlen = ((VEHICLE_STEPS_PER_CARUNIT*v->get_desc()->get_length())<<YARDS_PER_VEHICLE_STEP_SHIFT);
2089 if (vlen > dist) {
2090 break;
2091 }
2092 dist = driven - vlen;
2093 }
2094 fahr[0]->set_leading(true);
2095 }
2096 if (!at_dest) {
2097 state = CAN_START;
2098
2099 // to advance more smoothly
2100 sint32 restart_speed = -1;
2101 if( fahr[0]->can_enter_tile( restart_speed, 0 ) ) {
2102 // can reserve new block => drive on
2103 if(haltestelle_t::get_halt(k0,owner).is_bound()) {
2104 fahr[0]->play_sound();
2105 }
2106 state = DRIVING;
2107 }
2108 }
2109 else {
2110 ziel_erreicht();
2111 }
2112 }
2113
2114 // finally reserve route (if needed)
2115 if( fahr[0]->get_waytype()!=air_wt && !at_dest ) {
2116 // do not pre-reserve for airplanes
2117 for(unsigned i=0; i<anz_vehikel; i++) {
2118 // eventually reserve this
2119 vehicle_t const& v = *fahr[i];
2120 if (schiene_t* const sch0 = obj_cast<schiene_t>(welt->lookup(v.get_pos())->get_weg(v.get_waytype()))) {
2121 sch0->reserve(self,ribi_t::none);
2122 }
2123 else {
2124 break;
2125 }
2126 }
2127 }
2128
2129 wait_lock = 0;
2130 INT_CHECK("simconvoi 711");
2131 }
2132
2133
rdwr_convoihandle_t(loadsave_t * file,convoihandle_t & cnv)2134 void convoi_t::rdwr_convoihandle_t(loadsave_t *file, convoihandle_t &cnv)
2135 {
2136 if( file->is_version_atleast(112, 3) ) {
2137 uint16 id = (file->is_saving() && cnv.is_bound()) ? cnv.get_id() : 0;
2138 file->rdwr_short( id );
2139 if (file->is_loading()) {
2140 cnv.set_id( id );
2141 }
2142 }
2143 }
2144
2145
rdwr(loadsave_t * file)2146 void convoi_t::rdwr(loadsave_t *file)
2147 {
2148 xml_tag_t t( file, "convoi_t" );
2149
2150 sint32 dummy;
2151 sint32 owner_n = welt->sp2num(owner);
2152
2153 if(file->is_saving()) {
2154 if( file->is_version_less(101, 0) ) {
2155 file->wr_obj_id("Convoi");
2156 // the matching read is in karte_t::laden(loadsave*)...
2157 }
2158 }
2159
2160 // do the update, otherwise we might lose the line after save & reload
2161 if(file->is_saving() && line_update_pending.is_bound()) {
2162 check_pending_updates();
2163 }
2164
2165 simline_t::rdwr_linehandle_t(file, line);
2166
2167 // we want persistent convoihandles so we can keep dialogues open in network games
2168 if( file->is_loading() ) {
2169 if( file->is_version_less(112, 3) ) {
2170 self = convoihandle_t( this );
2171 }
2172 else {
2173 uint16 id;
2174 file->rdwr_short( id );
2175 self = convoihandle_t( this, id );
2176 }
2177 }
2178 else if( file->is_version_atleast(112, 3) ) {
2179 uint16 id = self.get_id();
2180 file->rdwr_short( id );
2181 }
2182
2183 dummy = anz_vehikel;
2184 file->rdwr_long(dummy);
2185 anz_vehikel = (uint8)dummy;
2186
2187 if(file->is_version_less(99, 14)) {
2188 // was anz_ready
2189 file->rdwr_long(dummy);
2190 }
2191
2192 file->rdwr_long(wait_lock);
2193 // some versions may produce broken safegames apparently
2194 if(wait_lock > 60000) {
2195 dbg->warning("convoi_t::sync_prepre()","Convoi %d: wait lock out of bounds: wait_lock = %d, setting to 60000",self.get_id(), wait_lock);
2196 wait_lock = 60000;
2197 }
2198
2199 bool dummy_bool=false;
2200 file->rdwr_bool(dummy_bool);
2201 file->rdwr_long(owner_n);
2202 file->rdwr_long(akt_speed);
2203 file->rdwr_long(akt_speed_soll);
2204 file->rdwr_long(sp_soll);
2205 file->rdwr_enum(state);
2206 file->rdwr_enum(alte_richtung);
2207
2208 // read the yearly income (which has since then become a 64 bit value)
2209 // will be recalculated later directly from the history
2210 if(file->is_version_less(89, 4)) {
2211 file->rdwr_long(dummy);
2212 }
2213
2214 route.rdwr(file);
2215
2216 if(file->is_loading()) {
2217 // extend array if requested (only needed for trains)
2218 if(anz_vehikel > fahr.get_count()) {
2219 fahr.resize(anz_vehikel, NULL);
2220 }
2221 owner = welt->get_player( owner_n );
2222
2223 // Hajo: sanity check for values ... plus correction
2224 if(sp_soll < 0) {
2225 sp_soll = 0;
2226 }
2227 }
2228
2229 file->rdwr_str(name_and_id + name_offset, lengthof(name_and_id) - name_offset);
2230 if(file->is_loading()) {
2231 set_name(name_and_id+name_offset); // will add id automatically
2232 }
2233
2234 koord3d dummy_pos;
2235 if(file->is_saving()) {
2236 for(unsigned i=0; i<anz_vehikel; i++) {
2237 file->wr_obj_id( fahr[i]->get_typ() );
2238 fahr[i]->rdwr_from_convoi(file);
2239 }
2240 }
2241 else {
2242 bool override_monorail = false;
2243 is_electric = false;
2244 for( uint8 i=0; i<anz_vehikel; i++ ) {
2245 obj_t::typ typ = (obj_t::typ)file->rd_obj_id();
2246 vehicle_t *v = 0;
2247
2248 const bool first = (i==0);
2249 const bool last = (i==anz_vehikel-1u);
2250 if(override_monorail) {
2251 // ignore type for ancient monorails
2252 v = new monorail_vehicle_t(file, first, last);
2253 }
2254 else {
2255 switch(typ) {
2256 case obj_t::old_automobil:
2257 case obj_t::road_vehicle: v = new road_vehicle_t(file, first, last); break;
2258 case obj_t::old_waggon:
2259 case obj_t::rail_vehicle: v = new rail_vehicle_t(file, first, last); break;
2260 case obj_t::old_schiff:
2261 case obj_t::water_vehicle: v = new water_vehicle_t(file, first, last); break;
2262 case obj_t::old_aircraft:
2263 case obj_t::air_vehicle: v = new air_vehicle_t(file, first, last); break;
2264 case obj_t::old_monorailwaggon:
2265 case obj_t::monorail_vehicle: v = new monorail_vehicle_t(file, first, last); break;
2266 case obj_t::maglev_vehicle: v = new maglev_vehicle_t(file, first, last); break;
2267 case obj_t::narrowgauge_vehicle: v = new narrowgauge_vehicle_t(file, first, last); break;
2268 default:
2269 dbg->fatal("convoi_t::convoi_t()","Can't load vehicle type %d", typ);
2270 }
2271 }
2272
2273 // no matching vehicle found?
2274 if(v->get_desc()==NULL) {
2275 // will create orphan object, but better than crashing at deletion ...
2276 dbg->error("convoi_t::convoi_t()","Can't load vehicle and no replacement found!");
2277 i --;
2278 anz_vehikel --;
2279 continue;
2280 }
2281
2282 // in very old games, monorail was a railway
2283 // so we need to convert this
2284 // freight will be lost, but game will be loadable
2285 if(i==0 && v->get_desc()->get_waytype()==monorail_wt && v->get_typ()==obj_t::rail_vehicle) {
2286 override_monorail = true;
2287 vehicle_t *v_neu = new monorail_vehicle_t( v->get_pos(), v->get_desc(), v->get_owner(), NULL );
2288 v->discard_cargo();
2289 delete v;
2290 v = v_neu;
2291 }
2292
2293 if(file->is_version_less(99, 4)) {
2294 dummy_pos.rdwr(file);
2295 }
2296
2297 const vehicle_desc_t *info = v->get_desc();
2298 assert(info);
2299
2300 // Hajo: if we load a game from a file which was saved from a
2301 // game with a different vehicle.tab, there might be no vehicle
2302 // info
2303 if(info) {
2304 sum_power += info->get_power();
2305 sum_gear_and_power += info->get_power()*info->get_gear();
2306 sum_weight += info->get_weight();
2307 sum_running_costs -= info->get_running_cost();
2308 is_electric |= info->get_engine_type()==vehicle_desc_t::electric;
2309 has_obsolete |= welt->use_timeline() && info->is_retired( welt->get_timeline_year_month() );
2310 player_t::add_maintenance( get_owner(), info->get_maintenance(), info->get_waytype() );
2311 }
2312
2313 // some versions save vehicles after leaving depot with koord3d::invalid
2314 if(v->get_pos()==koord3d::invalid) {
2315 state = INITIAL;
2316 }
2317
2318 if(state!=INITIAL) {
2319 grund_t *gr;
2320 gr = welt->lookup(v->get_pos());
2321 if(!gr) {
2322 gr = welt->lookup_kartenboden(v->get_pos().get_2d());
2323 if(gr) {
2324 dbg->error("convoi_t::rdwr()", "invalid position %s for vehicle %s in state %d (setting to %i,%i,%i)", v->get_pos().get_str(), v->get_name(), state, gr->get_pos().x, gr->get_pos().y, gr->get_pos().z );
2325 v->set_pos( gr->get_pos() );
2326 }
2327 else {
2328 dbg->fatal("convoi_t::rdwr()", "invalid position %s for vehicle %s in state %d", v->get_pos().get_str(), v->get_name(), state);
2329 }
2330 state = INITIAL;
2331 }
2332 // add to blockstrecke
2333 if(v->get_waytype()==track_wt || v->get_waytype()==monorail_wt || v->get_waytype()==maglev_wt || v->get_waytype()==narrowgauge_wt) {
2334 schiene_t* sch = (schiene_t*)gr->get_weg(v->get_waytype());
2335 if(sch) {
2336 sch->reserve(self,ribi_t::none);
2337 }
2338 // add to crossing
2339 if(gr->ist_uebergang()) {
2340 gr->find<crossing_t>()->add_to_crossing(v);
2341 }
2342 }
2343 if( gr->get_top()>253 ) {
2344 dbg->warning( "convoi_t::rdwr()", "cannot put vehicle on ground at (%s)", gr->get_pos().get_str() );
2345 }
2346 gr->obj_add(v);
2347 v->clear_flag(obj_t::not_on_map);
2348 }
2349 else {
2350 v->set_flag(obj_t::not_on_map);
2351 }
2352
2353 // add to convoi
2354 fahr[i] = v;
2355 }
2356 sum_gesamtweight = sum_weight;
2357 }
2358
2359 bool has_schedule = (schedule != NULL);
2360 file->rdwr_bool(has_schedule);
2361 if(has_schedule) {
2362 //DBG_MESSAGE("convoi_t::rdwr()","convoi has a schedule, state %s!",state_names[state]);
2363 const vehicle_t* v = fahr[0];
2364 if(file->is_loading() && v) {
2365 schedule = v->generate_new_schedule();
2366 }
2367 // Hajo: hack to load corrupted games -> there is a schedule
2368 // but no vehicle so we can't determine the exact type of
2369 // schedule needed. This hack is safe because convois
2370 // without vehicles get deleted right after loading.
2371 // Since generic schedules are not allowed, we use a train_schedule_t
2372 if(schedule == 0) {
2373 schedule = new train_schedule_t();
2374 }
2375
2376 // Hajo: now read the schedule, we have one for sure here
2377 schedule->rdwr( file );
2378 }
2379
2380 if(file->is_loading()) {
2381 next_wolke = 0;
2382 calc_loading();
2383 }
2384
2385 // Hajo: calculate new minimum top speed
2386 min_top_speed = calc_min_top_speed(fahr, anz_vehikel);
2387
2388 // Hajo: since sp_ist became obsolete, sp_soll is used modulo 65536
2389 sp_soll &= 65535;
2390
2391 if(file->is_version_less(88, 4)) {
2392 // load statistics
2393 int j;
2394 for (j = 0; j<3; j++) {
2395 for (size_t k = MAX_MONTHS; k-- != 0;) {
2396 file->rdwr_longlong(financial_history[k][j]);
2397 }
2398 }
2399 for (j = 2; j<5; j++) {
2400 for (size_t k = MAX_MONTHS; k-- != 0;) {
2401 file->rdwr_longlong(financial_history[k][j]);
2402 }
2403 }
2404 for (size_t k = MAX_MONTHS; k-- != 0;) {
2405 financial_history[k][CONVOI_DISTANCE] = 0;
2406 financial_history[k][CONVOI_MAXSPEED] = 0;
2407 financial_history[k][CONVOI_WAYTOLL] = 0;
2408 }
2409 }
2410 else if( file->is_version_less(102, 3) ){
2411 // load statistics
2412 for (int j = 0; j<5; j++) {
2413 for (size_t k = MAX_MONTHS; k-- != 0;) {
2414 file->rdwr_longlong(financial_history[k][j]);
2415 }
2416 }
2417 for (size_t k = MAX_MONTHS; k-- != 0;) {
2418 financial_history[k][CONVOI_DISTANCE] = 0;
2419 financial_history[k][CONVOI_MAXSPEED] = 0;
2420 financial_history[k][CONVOI_WAYTOLL] = 0;
2421 }
2422 }
2423 else if( file->is_version_less(111, 1) ){
2424 // load statistics
2425 for (int j = 0; j<6; j++) {
2426 for (size_t k = MAX_MONTHS; k-- != 0;) {
2427 file->rdwr_longlong(financial_history[k][j]);
2428 }
2429 }
2430 for (size_t k = MAX_MONTHS; k-- != 0;) {
2431 financial_history[k][CONVOI_MAXSPEED] = 0;
2432 financial_history[k][CONVOI_WAYTOLL] = 0;
2433 }
2434 }
2435 else if( file->is_version_less(112, 8) ){
2436 // load statistics
2437 for (int j = 0; j<7; j++) {
2438 for (size_t k = MAX_MONTHS; k-- != 0;) {
2439 file->rdwr_longlong(financial_history[k][j]);
2440 }
2441 }
2442 for (size_t k = MAX_MONTHS; k-- != 0;) {
2443 financial_history[k][CONVOI_WAYTOLL] = 0;
2444 }
2445 }
2446 else
2447 {
2448 // load statistics
2449 for (int j = 0; j<MAX_CONVOI_COST; j++) {
2450 for (size_t k = MAX_MONTHS; k-- != 0;) {
2451 file->rdwr_longlong(financial_history[k][j]);
2452 }
2453 }
2454 }
2455
2456 // the convoi odometer
2457 if( file->is_version_atleast(102, 3) ){
2458 file->rdwr_longlong( total_distance_traveled);
2459 }
2460
2461 // since it was saved as an signed int
2462 // we recalc it anyhow
2463 if(file->is_loading()) {
2464 jahresgewinn = 0;
2465 for(int i=welt->get_last_month()%12; i>=0; i-- ) {
2466 jahresgewinn += financial_history[i][CONVOI_PROFIT];
2467 }
2468 }
2469
2470 // save/restore pending line updates
2471 if(file->is_version_atleast(84, 9) && file->is_version_less(99, 13)) {
2472 file->rdwr_long(dummy); // ignore
2473 }
2474 if(file->is_loading()) {
2475 line_update_pending = linehandle_t();
2476 }
2477
2478 if(file->is_version_atleast(84, 10)) {
2479 home_depot.rdwr(file);
2480 }
2481
2482 // Old versions recorded last_stop_pos in convoi, not in vehicle
2483 koord3d last_stop_pos_convoi = koord3d(0,0,0);
2484 if (anz_vehikel !=0) {
2485 last_stop_pos_convoi = fahr[0]->last_stop_pos;
2486 }
2487 if(file->is_version_atleast(87, 1)) {
2488 last_stop_pos_convoi.rdwr(file);
2489 }
2490 else {
2491 last_stop_pos_convoi =
2492 !route.empty() ? route.front() :
2493 anz_vehikel != 0 ? fahr[0]->get_pos() :
2494 koord3d(0, 0, 0);
2495 }
2496
2497 // for leaving the depot routine
2498 if(file->is_version_less(99, 14)) {
2499 steps_driven = -1;
2500 }
2501 else {
2502 file->rdwr_short(steps_driven);
2503 }
2504
2505 // waiting time left ...
2506 if(file->is_version_atleast(99, 17)) {
2507 if(file->is_saving()) {
2508 if( has_schedule && schedule->get_current_entry().waiting_time_shift > 0 ) {
2509 uint32 diff_ticks = arrived_time + (welt->ticks_per_world_month >> (16 - schedule->get_current_entry().waiting_time_shift)) - welt->get_ticks();
2510 file->rdwr_long(diff_ticks);
2511 }
2512 else {
2513 uint32 diff_ticks = 0xFFFFFFFFu; // write old WAIT_INFINITE value for backwards compatibility
2514 file->rdwr_long(diff_ticks);
2515 }
2516 }
2517 else {
2518 uint32 diff_ticks = 0;
2519 file->rdwr_long(diff_ticks);
2520 arrived_time = has_schedule ? welt->get_ticks() - (welt->ticks_per_world_month >> (16 - schedule->get_current_entry().waiting_time_shift)) + diff_ticks : 0;
2521 }
2522 }
2523
2524 // since 99015, the last stop will be maintained by the vehikels themselves
2525 if(file->is_version_less(99, 15)) {
2526 for(unsigned i=0; i<anz_vehikel; i++) {
2527 fahr[i]->last_stop_pos = last_stop_pos_convoi;
2528 }
2529 }
2530
2531 // overtaking status
2532 if(file->is_version_less(100, 1)) {
2533 set_tiles_overtaking( 0 );
2534 }
2535 else {
2536 file->rdwr_byte(tiles_overtaking);
2537 set_tiles_overtaking( tiles_overtaking );
2538 }
2539 // no_load, withdraw
2540 if(file->is_version_less(102, 1)) {
2541 no_load = false;
2542 withdraw = false;
2543 }
2544 else {
2545 file->rdwr_bool(no_load);
2546 file->rdwr_bool(withdraw);
2547 }
2548
2549 if(file->is_version_atleast(111, 1)) {
2550 file->rdwr_long( distance_since_last_stop );
2551 file->rdwr_long( sum_speed_limit );
2552 }
2553
2554 if( file->is_version_atleast(111, 2) ) {
2555 file->rdwr_long( maxspeed_average_count );
2556 }
2557
2558 if( file->is_version_atleast(111, 3) ) {
2559 file->rdwr_short( next_stop_index );
2560 file->rdwr_short( next_reservation_index );
2561 }
2562
2563 if( file->is_loading() ) {
2564 reserve_route();
2565 recalc_catg_index();
2566 }
2567 }
2568
2569
open_info_window()2570 void convoi_t::open_info_window()
2571 {
2572 if( in_depot() ) {
2573 // Knightly : if ownership matches, we can try to open the depot dialog
2574 if( get_owner()==welt->get_active_player() ) {
2575 grund_t *const ground = welt->lookup( get_home_depot() );
2576 if( ground ) {
2577 depot_t *const depot = ground->get_depot();
2578 if( depot ) {
2579 depot->show_info();
2580 // try to activate this particular convoy in the depot
2581 depot_frame_t *const frame = dynamic_cast<depot_frame_t *>( win_get_magic( (ptrdiff_t)depot ) );
2582 if( frame ) {
2583 frame->activate_convoi(self);
2584 }
2585 }
2586 }
2587 }
2588 }
2589 else {
2590 if( env_t::verbose_debug ) {
2591 dump();
2592 }
2593 create_win( new convoi_info_t(self), w_info, magic_convoi_info+self.get_id() );
2594 }
2595 }
2596
2597
info(cbuffer_t & buf) const2598 void convoi_t::info(cbuffer_t & buf) const
2599 {
2600 const vehicle_t* v = fahr[0];
2601 if (v != NULL) {
2602 char tmp[128];
2603
2604 buf.printf("\n %d/%dkm/h (%1.2f$/km)\n", speed_to_kmh(min_top_speed), v->get_desc()->get_topspeed(), get_running_cost() / 100.0);
2605 buf.printf(" %s: %ikW\n", translator::translate("Leistung"), sum_power);
2606 buf.printf(" %s: %i (%i) t\n", translator::translate("Gewicht"), sum_weight, sum_gesamtweight - sum_weight);
2607 buf.printf(" %s: ", translator::translate("Gewinn"));
2608 money_to_string(tmp, (double)jahresgewinn);
2609 buf.append(tmp);
2610 buf.append("\n");
2611 }
2612 }
2613
2614
2615 // sort order of convoi
set_sortby(uint8 sort_order)2616 void convoi_t::set_sortby(uint8 sort_order)
2617 {
2618 freight_info_order = sort_order;
2619 freight_info_resort = true;
2620 }
2621
2622
2623 // caches the last info; resorts only when needed
get_freight_info(cbuffer_t & buf)2624 void convoi_t::get_freight_info(cbuffer_t & buf)
2625 {
2626 if(freight_info_resort) {
2627 freight_info_resort = false;
2628 // rebuilt the list with goods ...
2629 vector_tpl<ware_t> total_fracht;
2630
2631 size_t const n = goods_manager_t::get_count();
2632 ALLOCA(uint32, max_loaded_waren, n);
2633 MEMZERON(max_loaded_waren, n);
2634
2635 for( uint32 i = 0; i != anz_vehikel; ++i ) {
2636 const vehicle_t* v = fahr[i];
2637
2638 // first add to capacity indicator
2639 const goods_desc_t* ware_desc = v->get_desc()->get_freight_type();
2640 const uint16 menge = v->get_desc()->get_capacity();
2641 if(menge>0 && ware_desc!=goods_manager_t::none) {
2642 max_loaded_waren[ware_desc->get_index()] += menge;
2643 }
2644
2645 // then add the actual load
2646 FOR(slist_tpl<ware_t>, ware, v->get_cargo()) {
2647 FOR(vector_tpl<ware_t>, & tmp, total_fracht) {
2648 // could this be joined with existing freight?
2649
2650 // for pax: join according next stop
2651 // for all others we *must* use target coordinates
2652 if( ware.same_destination(tmp) ) {
2653 tmp.menge += ware.menge;
2654 ware.menge = 0;
2655 break;
2656 }
2657 }
2658
2659 // if != 0 we could not join it to existing => load it
2660 if(ware.menge != 0) {
2661 total_fracht.append(ware);
2662 }
2663 }
2664
2665 INT_CHECK("simconvoi 2643");
2666 }
2667 buf.clear();
2668
2669 // apend info on total capacity
2670 slist_tpl <ware_t>capacity;
2671 for (size_t i = 0; i != n; ++i) {
2672 if(max_loaded_waren[i]>0 && i!=goods_manager_t::INDEX_NONE) {
2673 ware_t ware(goods_manager_t::get_info(i));
2674 ware.menge = max_loaded_waren[i];
2675 // append to category?
2676 slist_tpl<ware_t>::iterator j = capacity.begin();
2677 slist_tpl<ware_t>::iterator end = capacity.end();
2678 while (j != end && j->get_desc()->get_catg_index() < ware.get_desc()->get_catg_index()) ++j;
2679 if (j != end && j->get_desc()->get_catg_index() == ware.get_desc()->get_catg_index()) {
2680 j->menge += max_loaded_waren[i];
2681 } else {
2682 // not yet there
2683 capacity.insert(j, ware);
2684 }
2685 }
2686 }
2687
2688 // show new info
2689 freight_list_sorter_t::sort_freight(total_fracht, buf, (freight_list_sorter_t::sort_mode_t)freight_info_order, &capacity, "loaded");
2690 }
2691 }
2692
2693
open_schedule_window(bool show)2694 void convoi_t::open_schedule_window( bool show )
2695 {
2696 DBG_MESSAGE("convoi_t::open_schedule_window()","Id = %ld, State = %d, Lock = %d",self.get_id(), state, wait_lock);
2697
2698 // manipulation of schedule not allowed while:
2699 // - just starting
2700 // - a line update is pending
2701 if( (state==EDIT_SCHEDULE || line_update_pending.is_bound()) && get_owner()==welt->get_active_player() ) {
2702 if (show) {
2703 create_win( new news_img("Not allowed!\nThe convoi's schedule can\nnot be changed currently.\nTry again later!"), w_time_delete, magic_none );
2704 }
2705 return;
2706 }
2707
2708 if(state==DRIVING) {
2709 // book the current value of goods
2710 calc_gewinn();
2711 }
2712
2713 akt_speed = 0; // stop the train ...
2714 if(state!=INITIAL) {
2715 state = EDIT_SCHEDULE;
2716 }
2717 wait_lock = 25000;
2718 alte_richtung = fahr[0]->get_direction();
2719
2720 if( show ) {
2721 // Open schedule dialog
2722 create_win( new schedule_gui_t(schedule,get_owner(),self), w_info, (ptrdiff_t)schedule );
2723 // TODO: what happens if no client opens the window??
2724 }
2725 schedule->start_editing();
2726 }
2727
2728
2729 /**
2730 * Check validity of convoi with respect to vehicle constraints
2731 */
pruefe_alle()2732 bool convoi_t::pruefe_alle()
2733 {
2734 bool ok = anz_vehikel == 0 || fahr[0]->get_desc()->can_follow(NULL);
2735 unsigned i;
2736
2737 const vehicle_t* pred = fahr[0];
2738 for(i = 1; ok && i < anz_vehikel; i++) {
2739 const vehicle_t* v = fahr[i];
2740 ok = pred->get_desc()->can_lead(v->get_desc()) &&
2741 v->get_desc()->can_follow(pred->get_desc());
2742 pred = v;
2743 }
2744 if(ok) {
2745 ok = pred->get_desc()->can_lead(NULL);
2746 }
2747
2748 return ok;
2749 }
2750
2751
2752 /**
2753 * Kontrolliert Be- und Entladen
2754 * @author Hj. Malthaner
2755 *
2756 * V.Meyer: minimum_loading is now stored in the object (not returned)
2757 */
laden()2758 void convoi_t::laden()
2759 {
2760 if( state == EDIT_SCHEDULE ) {
2761 return;
2762 }
2763
2764 // just wait a little longer if this is a non-bound halt
2765 wait_lock = (WTT_LOADING*2)+(self.get_id())%1024;
2766
2767 halthandle_t halt = haltestelle_t::get_halt(schedule->get_current_entry().pos,owner);
2768 // eigene haltestelle ?
2769 if( halt.is_bound() ) {
2770 const player_t* halt_owner = halt->get_owner();
2771 if( halt_owner == get_owner() || halt_owner == welt->get_public_player() ) {
2772 // loading/unloading ...
2773 halt->request_loading( self );
2774 }
2775 }
2776 }
2777
2778
2779 /**
2780 * calculate income for last hop
2781 * @author Hj. Malthaner
2782 */
calc_gewinn()2783 void convoi_t::calc_gewinn()
2784 {
2785 sint64 gewinn = 0;
2786
2787 for(unsigned i=0; i<anz_vehikel; i++) {
2788 vehicle_t* v = fahr[i];
2789 sint64 tmp;
2790 gewinn += tmp = v->calc_revenue(v->last_stop_pos, v->get_pos() );
2791 // get_schedule is needed as v->get_waytype() returns track_wt for trams (instead of tram_wt
2792 owner->book_revenue(tmp, fahr[0]->get_pos().get_2d(), get_schedule()->get_waytype(), v->get_cargo_type()->get_index() );
2793 v->last_stop_pos = v->get_pos();
2794 }
2795
2796 // update statistics of average speed
2797 if( distance_since_last_stop ) {
2798 financial_history[0][CONVOI_MAXSPEED] *= maxspeed_average_count;
2799 financial_history[0][CONVOI_MAXSPEED] += get_speedbonus_kmh();
2800 maxspeed_average_count ++;
2801 financial_history[0][CONVOI_MAXSPEED] /= maxspeed_average_count;
2802 }
2803 distance_since_last_stop = 0;
2804 sum_speed_limit = 0;
2805
2806 if(gewinn) {
2807 jahresgewinn += gewinn;
2808
2809 book(gewinn, CONVOI_PROFIT);
2810 book(gewinn, CONVOI_REVENUE);
2811 }
2812 }
2813
2814
2815 /**
2816 * convoi an haltestelle anhalten
2817 * @author Hj. Malthaner
2818 *
2819 * V.Meyer: minimum_loading is now stored in the object (not returned)
2820 */
hat_gehalten(halthandle_t halt)2821 void convoi_t::hat_gehalten(halthandle_t halt)
2822 {
2823 grund_t *gr=welt->lookup(fahr[0]->get_pos());
2824
2825 // now find out station length
2826 uint16 vehicles_loading = 0;
2827 if( gr->is_water() ) {
2828 // harbour has any size
2829 vehicles_loading = anz_vehikel;
2830 }
2831 else {
2832 // calculate real station length
2833 // and numbers of vehicles that can be (un)loaded
2834 koord zv = koord( ribi_t::backward(fahr[0]->get_direction()) );
2835 koord3d pos = fahr[0]->get_pos();
2836 // start on bridge?
2837 pos.z += gr->get_weg_yoff() / TILE_HEIGHT_STEP;
2838 // difference between actual station length and vehicle lenghts
2839 sint16 station_length = -fahr[vehicles_loading]->get_desc()->get_length();
2840 do {
2841 // advance one station tile
2842 station_length += CARUNITS_PER_TILE;
2843
2844 while(station_length >= 0) {
2845 vehicles_loading++;
2846 if (vehicles_loading < anz_vehikel) {
2847 station_length -= fahr[vehicles_loading]->get_desc()->get_length();
2848 }
2849 else {
2850 // all vehicles fit into station
2851 goto station_tile_search_ready;
2852 }
2853 }
2854
2855 // search for next station tile
2856 pos += zv;
2857 gr = welt->lookup(pos);
2858 if (gr == NULL) {
2859 gr = welt->lookup(pos-koord3d(0,0,1));
2860 if (gr == NULL) {
2861 gr = welt->lookup(pos-koord3d(0,0,2));
2862 }
2863 if (gr && (pos.z != gr->get_hoehe() + gr->get_weg_yoff()/TILE_HEIGHT_STEP) ) {
2864 // not end/start of bridge
2865 break;
2866 }
2867 }
2868
2869 } while( gr && gr->get_halt() == halt );
2870 // finished
2871 station_tile_search_ready: ;
2872 }
2873
2874 // prepare a list of all destination halts in the schedule
2875 vector_tpl<halthandle_t> destination_halts(schedule->get_count());
2876 if (!no_load) {
2877 const uint8 count = schedule->get_count();
2878 for( uint8 i=1; i<count; i++ ) {
2879 const uint8 wrap_i = (i + schedule->get_current_stop()) % count;
2880
2881 const halthandle_t plan_halt = haltestelle_t::get_halt(schedule->entries[wrap_i].pos, owner);
2882 if(plan_halt == halt) {
2883 // we will come later here again ...
2884 break;
2885 }
2886 else if( !plan_halt.is_bound() ) {
2887 if( grund_t *gr = welt->lookup( schedule->entries[wrap_i].pos ) ) {
2888 if( gr->get_depot() ) {
2889 // do not load for stops after a depot
2890 break;
2891 }
2892 }
2893 continue;
2894 }
2895 destination_halts.append(plan_halt);
2896 }
2897 }
2898
2899 // only load vehicles in station
2900 // don't load when vehicle is being withdrawn
2901 bool changed_loading_level = false;
2902 uint32 time = WTT_LOADING; // min time for loading/unloading
2903 sint64 gewinn = 0;
2904
2905 // cargo type of previous vehicle that could not be filled
2906 const goods_desc_t* cargo_type_prev = NULL;
2907
2908 for(unsigned i=0; i<vehicles_loading; i++) {
2909 vehicle_t* v = fahr[i];
2910
2911 // we need not to call this on the same position
2912 if( v->last_stop_pos != v->get_pos() ) {
2913 sint64 tmp;
2914 // calc_revenue
2915 gewinn += tmp = v->calc_revenue(v->last_stop_pos, v->get_pos() );
2916 owner->book_revenue(tmp, fahr[0]->get_pos().get_2d(), get_schedule()->get_waytype(), v->get_cargo_type()->get_index());
2917 v->last_stop_pos = v->get_pos();
2918 }
2919
2920 const grund_t *gr = welt->lookup( schedule->entries[(schedule->get_current_stop()+1)%schedule->get_count()].pos );
2921 const bool next_depot = gr && gr->get_depot();
2922 uint16 amount = v->unload_cargo(halt, next_depot );
2923
2924 if( !no_load && !next_depot && v->get_total_cargo() < v->get_cargo_max() ) {
2925 // load if: unloaded something (might go back) or previous non-filled car requested different cargo type
2926 if (amount>0 || cargo_type_prev==NULL || !cargo_type_prev->is_interchangeable(v->get_cargo_type())) {
2927 // load
2928 amount += v->load_cargo(halt, destination_halts);
2929 }
2930 if (v->get_total_cargo() < v->get_cargo_max()) {
2931 // not full
2932 cargo_type_prev = v->get_cargo_type();
2933 }
2934 }
2935
2936 if( amount ) {
2937 time = max( time, (amount*v->get_desc()->get_loading_time()) / max(v->get_cargo_max(), 1) );
2938 v->mark_image_dirty(v->get_image(), 0);
2939 v->calc_image();
2940 changed_loading_level = true;
2941 }
2942 }
2943 freight_info_resort |= changed_loading_level;
2944 if( changed_loading_level ) {
2945 halt->recalc_status();
2946 }
2947
2948 // any unloading/loading went on?
2949 if( changed_loading_level ) {
2950 calc_loading();
2951 }
2952 loading_limit = schedule->get_current_entry().minimum_loading;
2953
2954 // update statistics of average speed
2955 if( distance_since_last_stop ) {
2956 financial_history[0][CONVOI_MAXSPEED] *= maxspeed_average_count;
2957 financial_history[0][CONVOI_MAXSPEED] += get_speedbonus_kmh();
2958 maxspeed_average_count ++;
2959 financial_history[0][CONVOI_MAXSPEED] /= maxspeed_average_count;
2960 }
2961 distance_since_last_stop = 0;
2962 sum_speed_limit = 0;
2963
2964 if(gewinn) {
2965 jahresgewinn += gewinn;
2966
2967 book(gewinn, CONVOI_PROFIT);
2968 book(gewinn, CONVOI_REVENUE);
2969 }
2970
2971 // loading is finished => maybe drive on
2972 if( loading_level >= loading_limit || no_load
2973 || (schedule->get_current_entry().waiting_time_shift > 0 && welt->get_ticks() - arrived_time > (welt->ticks_per_world_month >> (16 - schedule->get_current_entry().waiting_time_shift)) ) ) {
2974
2975 if( withdraw && (loading_level == 0 || goods_catg_index.empty()) ) {
2976 // destroy when empty
2977 self_destruct();
2978 return;
2979 }
2980
2981 calc_speedbonus_kmh();
2982
2983 // add available capacity after loading(!) to statistics
2984 for (unsigned i = 0; i<anz_vehikel; i++) {
2985 book(get_vehikel(i)->get_cargo_max()-get_vehikel(i)->get_total_cargo(), CONVOI_CAPACITY);
2986 }
2987
2988 // Advance schedule
2989 schedule->advance();
2990 state = ROUTING_1;
2991 loading_limit = 0;
2992 }
2993
2994 INT_CHECK( "convoi_t::hat_gehalten" );
2995
2996 // at least wait the minimum time for loading
2997 wait_lock = time;
2998 }
2999
3000
calc_restwert() const3001 sint64 convoi_t::calc_restwert() const
3002 {
3003 sint64 result = 0;
3004
3005 for(uint i=0; i<anz_vehikel; i++) {
3006 result += fahr[i]->calc_sale_value();
3007 }
3008 return result;
3009 }
3010
3011
3012 /**
3013 * Calculate loading_level and loading_limit. This depends on current state (loading or not).
3014 * @author Volker Meyer
3015 * @date 20.06.2003
3016 */
calc_loading()3017 void convoi_t::calc_loading()
3018 {
3019 int fracht_max = 0;
3020 int fracht_menge = 0;
3021 for(unsigned i=0; i<anz_vehikel; i++) {
3022 const vehicle_t* v = fahr[i];
3023 fracht_max += v->get_cargo_max();
3024 fracht_menge += v->get_total_cargo();
3025 }
3026 loading_level = fracht_max > 0 ? (fracht_menge*100)/fracht_max : 100;
3027 loading_limit = 0; // will be set correctly from hat_gehalten() routine
3028
3029 // since weight has changed
3030 recalc_data=true;
3031 }
3032
3033
calc_speedbonus_kmh()3034 void convoi_t::calc_speedbonus_kmh()
3035 {
3036 // init with default
3037 const sint32 cnv_min_top_kmh = speed_to_kmh( min_top_speed );
3038 speedbonus_kmh = cnv_min_top_kmh;
3039 // flying aircraft have 0 friction --> speed not limited by power, so just use top_speed
3040 if( front()!=NULL && front()->get_waytype() != air_wt ) {
3041 sint32 total_max_weight = 0;
3042 sint32 total_weight = 0;
3043 for( unsigned i=0; i<anz_vehikel; i++ ) {
3044 const vehicle_desc_t* const desc = fahr[i]->get_desc();
3045 total_max_weight += desc->get_weight();
3046 total_weight += fahr[i]->get_total_weight(); // convoi_t::sum_gesamweight may not be updated yet when this method is called...
3047 if( desc->get_freight_type() == goods_manager_t::none ) {
3048 ; // nothing
3049 }
3050 else if( desc->get_freight_type()->get_catg() == 0 ) {
3051 // use full weight for passengers, mail, and special goods
3052 total_max_weight += desc->get_freight_type()->get_weight_per_unit() * desc->get_capacity();
3053 }
3054 else {
3055 // use actual weight for regular goods
3056 total_max_weight += fahr[i]->get_cargo_weight();
3057 }
3058 }
3059 // very old vehicles have zero weight ...
3060 if( total_weight>0 ) {
3061
3062 speedbonus_kmh = speed_to_kmh( calc_max_speed(sum_gear_and_power, total_max_weight, min_top_speed) );
3063
3064 // convoi overtakers use current actual weight for achievable speed
3065 if( front()->get_overtaker() ) {
3066 max_power_speed = calc_max_speed(sum_gear_and_power, total_weight, min_top_speed);
3067 }
3068 }
3069 }
3070 }
3071
3072
3073 // return the current bonus speed
get_speedbonus_kmh() const3074 sint32 convoi_t::get_speedbonus_kmh() const
3075 {
3076 if( distance_since_last_stop > 0 && front()!=NULL && front()->get_waytype() != air_wt ) {
3077 return min( speedbonus_kmh, (sint32)(sum_speed_limit / distance_since_last_stop) );
3078 }
3079 return speedbonus_kmh;
3080 }
3081
3082
3083 // return the current bonus speed
get_average_kmh() const3084 uint32 convoi_t::get_average_kmh() const
3085 {
3086 if( distance_since_last_stop > 0 ) {
3087 return sum_speed_limit / distance_since_last_stop;
3088 }
3089 return speedbonus_kmh;
3090 }
3091
3092
3093 /**
3094 * Schedule convois for self destruction. Will be executed
3095 * upon next sync step
3096 * @author Hj. Malthaner
3097 */
self_destruct()3098 void convoi_t::self_destruct()
3099 {
3100 line_update_pending = linehandle_t(); // does not bother to add it to a new line anyway ...
3101 // convois in depot are not contained in the map array!
3102 if(state==INITIAL) {
3103 destroy();
3104 }
3105 else {
3106 state = SELF_DESTRUCT;
3107 wait_lock = 0;
3108 }
3109 }
3110
3111
3112 /**
3113 * Helper method to remove convois from the map that cannot
3114 * removed normally (i.e. by sending to a depot) anymore.
3115 * This is a workaround for bugs in the game.
3116 * @author Hj. Malthaner
3117 * @date 12-Jul-03
3118 */
destroy()3119 void convoi_t::destroy()
3120 {
3121 // can be only done here, with a valid convoihandle ...
3122 if(fahr[0]) {
3123 fahr[0]->set_convoi(NULL);
3124 }
3125
3126 if( state == INITIAL ) {
3127 // in depot => not on map
3128 for( uint8 i = anz_vehikel; i-- != 0; ) {
3129 fahr[i]->set_flag( obj_t::not_on_map );
3130 }
3131 }
3132 state = SELF_DESTRUCT;
3133
3134 if(schedule!=NULL && !schedule->is_editing_finished()) {
3135 destroy_win((ptrdiff_t)schedule);
3136 }
3137
3138 if( line.is_bound() ) {
3139 // needs to be done here to remove correctly ware catg from lines
3140 unset_line();
3141 delete schedule;
3142 schedule = NULL;
3143 }
3144
3145 // pay the current value
3146 owner->book_new_vehicle( calc_restwert(), get_pos().get_2d(), fahr[0] ? fahr[0]->get_desc()->get_waytype() : ignore_wt );
3147
3148 for( uint8 i = anz_vehikel; i-- != 0; ) {
3149 if( !fahr[i]->get_flag( obj_t::not_on_map ) ) {
3150 // remove from rails/roads/crossings
3151 grund_t *gr = welt->lookup(fahr[i]->get_pos());
3152 fahr[i]->set_last( true );
3153 fahr[i]->leave_tile();
3154 if( gr && gr->ist_uebergang() ) {
3155 gr->find<crossing_t>()->release_crossing(fahr[i]);
3156 }
3157 fahr[i]->set_flag( obj_t::not_on_map );
3158
3159 }
3160 player_t::add_maintenance( owner, -fahr[i]->get_desc()->get_maintenance(), fahr[i]->get_desc()->get_waytype() );
3161
3162 fahr[i]->discard_cargo();
3163 fahr[i]->cleanup(owner);
3164 delete fahr[i];
3165 }
3166 anz_vehikel = 0;
3167
3168 delete this;
3169 }
3170
3171
3172 /**
3173 * Debug info nach stderr
3174 * @author Hj. Malthaner
3175 * @date 04-Sep-03
3176 */
dump() const3177 void convoi_t::dump() const
3178 {
3179 dbg->debug("convoi::dump()",
3180 "\nanz_vehikel = %d\n"
3181 "wait_lock = %d\n"
3182 "owner_n = %d\n"
3183 "akt_speed = %d\n"
3184 "akt_speed_soll = %d\n"
3185 "sp_soll = %d\n"
3186 "state = %d\n"
3187 "statename = %s\n"
3188 "alte_richtung = %d\n"
3189 "jahresgewinn = %ld\n" // %lld crashes mingw now, cast gewinn to long ...
3190 "name = '%s'\n"
3191 "line_id = '%d'\n"
3192 "schedule = '%p'",
3193 (int)anz_vehikel,
3194 (int)wait_lock,
3195 (int)welt->sp2num(owner),
3196 (int)akt_speed,
3197 (int)akt_speed_soll,
3198 (int)sp_soll,
3199 (int)state,
3200 (const char *)(state_names[state]),
3201 (int)alte_richtung,
3202 (long)(jahresgewinn/100),
3203 (const char *)name_and_id,
3204 line.is_bound() ? line.get_id() : 0,
3205 (const void *)schedule );
3206 }
3207
3208
book(sint64 amount,int cost_type)3209 void convoi_t::book(sint64 amount, int cost_type)
3210 {
3211 assert( cost_type<MAX_CONVOI_COST);
3212
3213 financial_history[0][cost_type] += amount;
3214 if (line.is_bound()) {
3215 line->book( amount, simline_t::convoi_to_line_catgory(cost_type) );
3216 }
3217 }
3218
3219
init_financial_history()3220 void convoi_t::init_financial_history()
3221 {
3222 for (int j = 0; j<MAX_CONVOI_COST; j++) {
3223 for (size_t k = MAX_MONTHS; k-- != 0;) {
3224 financial_history[k][j] = 0;
3225 }
3226 }
3227 }
3228
3229
get_fix_cost() const3230 sint32 convoi_t::get_fix_cost() const
3231 {
3232 sint32 running_cost = 0;
3233 for( unsigned i = 0; i < get_vehicle_count(); i++ ) {
3234 running_cost += fahr[i]->get_desc()->get_maintenance();
3235 }
3236 return running_cost;
3237 }
3238
3239
get_running_cost() const3240 sint32 convoi_t::get_running_cost() const
3241 {
3242 sint32 running_cost = 0;
3243 for( unsigned i = 0; i < get_vehicle_count(); i++ ) {
3244 running_cost += fahr[i]->get_operating_cost();
3245 }
3246 return running_cost;
3247 }
3248
3249
get_purchase_cost() const3250 sint64 convoi_t::get_purchase_cost() const
3251 {
3252 sint64 purchase_cost = 0;
3253 for( unsigned i = 0; i < get_vehicle_count(); i++ ) {
3254 purchase_cost += fahr[i]->get_desc()->get_price();
3255 }
3256 return purchase_cost;
3257 }
3258
3259
3260 /**
3261 * set line
3262 * since convoys must operate on a copy of the route's schedule, we apply a fresh copy
3263 * @author hsiegeln
3264 */
set_line(linehandle_t org_line)3265 void convoi_t::set_line(linehandle_t org_line)
3266 {
3267 // to remove a convoi from a line, call unset_line(); passing a NULL is not allowed!
3268 if(!org_line.is_bound()) {
3269 return;
3270 }
3271 if( line.is_bound() ) {
3272 unset_line();
3273 }
3274 else {
3275 // Knightly : originally a lineless convoy -> unregister itself from stops as it now belongs to a line
3276 unregister_stops();
3277 // must trigger refresh if old schedule was not empty
3278 if (schedule && !schedule->empty()) {
3279 welt->set_schedule_counter();
3280 }
3281 }
3282 line_update_pending = org_line;
3283 check_pending_updates();
3284 }
3285
3286
3287 /**
3288 * unset line
3289 * removes convoy from route without destroying its schedule
3290 * => no need to recalculate connections!
3291 * @author hsiegeln
3292 */
unset_line()3293 void convoi_t::unset_line()
3294 {
3295 if( line.is_bound() ) {
3296 DBG_DEBUG("convoi_t::unset_line()", "removing old destinations from line=%d, schedule=%p",line.get_id(),schedule);
3297 line->remove_convoy(self);
3298 line = linehandle_t();
3299 line_update_pending = linehandle_t();
3300 }
3301 }
3302
3303
3304 // matches two halts; if the pos is not identical, maybe the halt still is the same
matches_halt(const koord3d pos1,const koord3d pos2)3305 bool convoi_t::matches_halt( const koord3d pos1, const koord3d pos2 )
3306 {
3307 halthandle_t halt1 = haltestelle_t::get_halt(pos1, owner );
3308 return pos1==pos2 || (halt1.is_bound() && halt1==haltestelle_t::get_halt( pos2, owner ));
3309 }
3310
3311
3312 // updates a line schedule and tries to find the best next station to go
check_pending_updates()3313 void convoi_t::check_pending_updates()
3314 {
3315 if( line_update_pending.is_bound() ) {
3316 // create dummy schedule
3317 if( schedule==NULL ) {
3318 schedule = create_schedule();
3319 }
3320 schedule_t* new_schedule = line_update_pending->get_schedule();
3321 int current_stop = schedule->get_current_stop(); // save current position of schedule
3322 bool is_same = false;
3323 bool is_depot = false;
3324 koord3d current = koord3d::invalid, depot = koord3d::invalid;
3325
3326 if (schedule->empty() || new_schedule->empty()) {
3327 // was no entry or is no entry => goto 1st stop
3328 current_stop = 0;
3329 }
3330 else {
3331 // something to check for ...
3332 current = schedule->get_current_entry().pos;
3333
3334 if( current_stop<new_schedule->get_count() && current==new_schedule->entries[current_stop].pos ) {
3335 // next pos is the same => keep the convoi state
3336 is_same = true;
3337 }
3338 else {
3339 // check depot first (must also keept this state)
3340 is_depot = (welt->lookup(current) && welt->lookup(current)->get_depot() != NULL);
3341
3342 if(is_depot) {
3343 // depot => current_stop+1 (depot will be restore later before this)
3344 depot = current;
3345 schedule->remove();
3346 current = schedule->get_current_entry().pos;
3347 }
3348
3349 /* there could be only one entry that matches best:
3350 * we try first same sequence as in old schedule;
3351 * if not found, we try for same nextnext station
3352 * (To detect also places, where only the platform
3353 * changed, we also compare the halthandle)
3354 */
3355 const koord3d next = schedule->entries[(current_stop+1)%schedule->get_count()].pos;
3356 const koord3d nextnext = schedule->entries[(current_stop+2)%schedule->get_count()].pos;
3357 const koord3d nextnextnext = schedule->entries[(current_stop+3)%schedule->get_count()].pos;
3358 int how_good_matching = 0;
3359 const uint8 new_count = new_schedule->get_count();
3360
3361 for( uint8 i=0; i<new_count; i++ ) {
3362 int quality =
3363 matches_halt(current,new_schedule->entries[i].pos)*3 +
3364 matches_halt(next,new_schedule->entries[(i+1)%new_count].pos)*4 +
3365 matches_halt(nextnext,new_schedule->entries[(i+2)%new_count].pos)*2 +
3366 matches_halt(nextnextnext,new_schedule->entries[(i+3)%new_count].pos);
3367 if( quality>how_good_matching ) {
3368 // better match than previous: but depending of distance, the next number will be different
3369 if( matches_halt(current,new_schedule->entries[i].pos) ) {
3370 current_stop = i;
3371 }
3372 else if( matches_halt(next,new_schedule->entries[(i+1)%new_count].pos) ) {
3373 current_stop = i+1;
3374 }
3375 else if( matches_halt(nextnext,new_schedule->entries[(i+2)%new_count].pos) ) {
3376 current_stop = i+2;
3377 }
3378 else if( matches_halt(nextnextnext,new_schedule->entries[(i+3)%new_count].pos) ) {
3379 current_stop = i+3;
3380 }
3381 current_stop %= new_count;
3382 how_good_matching = quality;
3383 }
3384 }
3385
3386 if(how_good_matching==0) {
3387 // nothing matches => take the one from the line
3388 current_stop = new_schedule->get_current_stop();
3389 }
3390 // if we go to same, then we do not need route recalculation ...
3391 is_same = matches_halt(current,new_schedule->entries[current_stop].pos);
3392 }
3393 }
3394
3395 // we may need to update the line and connection tables
3396 if( !line.is_bound() ) {
3397 line_update_pending->add_convoy(self);
3398 }
3399 line = line_update_pending;
3400 line_update_pending = linehandle_t();
3401
3402 // destroy old schedule and all related windows
3403 if(!schedule->is_editing_finished()) {
3404 schedule->copy_from( new_schedule );
3405 schedule->set_current_stop(current_stop); // set new schedule current position to best match
3406 schedule->start_editing();
3407 }
3408 else {
3409 schedule->copy_from( new_schedule );
3410 schedule->set_current_stop(current_stop); // set new schedule current position to one before best match
3411 }
3412
3413 if(is_depot) {
3414 // next was depot. restore it
3415 schedule->insert(welt->lookup(depot));
3416 schedule->set_current_stop( (schedule->get_current_stop()+schedule->get_count()-1)%schedule->get_count() );
3417 }
3418
3419 if (state != INITIAL) {
3420 // remove wrong freight
3421 check_freight();
3422
3423 if(is_same || is_depot) {
3424 /* same destination
3425 * We are already there => keep current state
3426 */
3427 }
3428 else {
3429 // need re-routing
3430 state = EDIT_SCHEDULE;
3431 }
3432 // make this change immediately
3433 if( state!=LOADING ) {
3434 wait_lock = 0;
3435 }
3436 }
3437 }
3438 }
3439
3440
3441 /**
3442 * Register the convoy with the stops in the schedule
3443 * @author Knightly
3444 */
register_stops()3445 void convoi_t::register_stops()
3446 {
3447 if( schedule ) {
3448 FOR(minivec_tpl<schedule_entry_t>, const& i, schedule->entries) {
3449 halthandle_t const halt = haltestelle_t::get_halt(i.pos, get_owner());
3450 if( halt.is_bound() ) {
3451 halt->add_convoy(self);
3452 }
3453 }
3454 }
3455 }
3456
3457
3458 /**
3459 * Unregister the convoy from the stops in the schedule
3460 * @author Knightly
3461 */
unregister_stops()3462 void convoi_t::unregister_stops()
3463 {
3464 if( schedule ) {
3465 FOR(minivec_tpl<schedule_entry_t>, const& i, schedule->entries) {
3466 halthandle_t const halt = haltestelle_t::get_halt(i.pos, get_owner());
3467 if( halt.is_bound() ) {
3468 halt->remove_convoy(self);
3469 }
3470 }
3471 }
3472 }
3473
3474
3475 // set next stop before breaking will occur (or route search etc.)
3476 // currently only used for tracks
set_next_stop_index(uint16 n)3477 void convoi_t::set_next_stop_index(uint16 n)
3478 {
3479 // stop at station or signals, not at waypoints
3480 if( n==INVALID_INDEX ) {
3481 // find out if stop or waypoint, waypoint: do not brake at waypoints
3482 grund_t const* const gr = welt->lookup(route.back());
3483 if( gr && gr->is_halt() ) {
3484 n = route.get_count()-1-1; // extra -1 to brake 1 tile earlier when entering station
3485 }
3486 }
3487 next_stop_index = n+1;
3488 }
3489
3490
3491 /* including this route_index, the route was reserved the last time
3492 * currently only used for tracks
3493 */
set_next_reservation_index(uint16 n)3494 void convoi_t::set_next_reservation_index(uint16 n)
3495 {
3496 // stop at station or signals, not at waypoints
3497 if( n==INVALID_INDEX ) {
3498 n = route.get_count()-1;
3499 }
3500 next_reservation_index = n;
3501 }
3502
3503
3504 /*
3505 * the current state saved as color
3506 * Meanings are BLACK (ok), WHITE (no convois), YELLOW (no vehicle moved), RED (last month income minus), BLUE (at least one convoi vehicle is obsolete)
3507 */
get_status_color() const3508 PIXVAL convoi_t::get_status_color() const
3509 {
3510 if(state==INITIAL) {
3511 // in depot/under assembly
3512 return SYSCOL_TEXT_HIGHLIGHT;
3513 }
3514 else if (state == WAITING_FOR_CLEARANCE_ONE_MONTH || state == CAN_START_ONE_MONTH || get_state() == NO_ROUTE) {
3515 // stuck or no route
3516 return color_idx_to_rgb(COL_ORANGE);
3517 }
3518 else if(financial_history[0][CONVOI_PROFIT]+financial_history[1][CONVOI_PROFIT]<0) {
3519 // ok, not performing best
3520 return MONEY_MINUS;
3521 }
3522 else if((financial_history[0][CONVOI_OPERATIONS]|financial_history[1][CONVOI_OPERATIONS])==0) {
3523 // nothing moved
3524 return SYSCOL_TEXT_UNUSED;
3525 }
3526 else if(has_obsolete) {
3527 return color_idx_to_rgb(COL_BLUE);
3528 }
3529 // normal state
3530 return SYSCOL_TEXT;
3531 }
3532
3533
3534 // returns tiles needed for this convoi
get_tile_length() const3535 uint16 convoi_t::get_tile_length() const
3536 {
3537 uint16 carunits=0;
3538 for(uint8 i=0; i<anz_vehikel-1; i++) {
3539 carunits += fahr[i]->get_desc()->get_length();
3540 }
3541 // the last vehicle counts differently in stations and for reserving track
3542 // (1) add 8 = 127/256 tile to account for the driving in stations in north/west direction
3543 // see at the end of vehicle_t::hop()
3544 // (2) for length of convoi for loading in stations the length of the last vehicle matters
3545 // see convoi_t::hat_gehalten
3546 carunits += max(CARUNITS_PER_TILE/2, fahr[anz_vehikel-1]->get_desc()->get_length());
3547
3548 uint16 tiles = (carunits + CARUNITS_PER_TILE - 1) / CARUNITS_PER_TILE;
3549 return tiles;
3550 }
3551
3552
3553 // if withdraw and empty, then self destruct
set_withdraw(bool new_withdraw)3554 void convoi_t::set_withdraw(bool new_withdraw)
3555 {
3556 withdraw = new_withdraw;
3557 if( withdraw && (loading_level==0 || goods_catg_index.empty())) {
3558 // test if convoi in depot and not driving
3559 grund_t *gr = welt->lookup( get_pos());
3560 if( gr && gr->get_depot() && state == INITIAL ) {
3561 #if 1
3562 // do not touch line bound convois in depots
3563 withdraw = false;
3564 no_load = false;
3565 #else
3566 // disassemble also line bound convois in depots
3567 gr->get_depot()->disassemble_convoi(self, true);
3568 #endif
3569 }
3570 else {
3571 self_destruct();
3572 }
3573 }
3574 }
3575
3576
3577 /**
3578 * conditions for a city car to overtake another overtaker.
3579 * The city car is not overtaking/being overtaken.
3580 * @author isidoro
3581 */
can_overtake(overtaker_t * other_overtaker,sint32 other_speed,sint16 steps_other)3582 bool convoi_t::can_overtake(overtaker_t *other_overtaker, sint32 other_speed, sint16 steps_other)
3583 {
3584 if(fahr[0]->get_waytype()!=road_wt) {
3585 return false;
3586 }
3587
3588 if (!other_overtaker->can_be_overtaken()) {
3589 return false;
3590 }
3591
3592 if( other_speed == 0 ) {
3593 /* overtaking a loading convoi
3594 * => we can do a lazy check, since halts are always straight
3595 */
3596 grund_t *gr = welt->lookup(get_pos());
3597 if( gr==NULL ) {
3598 // should never happen, since there is a vehicle in front of us ...
3599 return false;
3600 }
3601 weg_t *str = gr->get_weg(road_wt);
3602 if( str==0 ) {
3603 // also this is not possible, since a car loads in front of is!?!
3604 return false;
3605 }
3606
3607 uint16 idx = fahr[0]->get_route_index();
3608 const sint32 tiles = other_speed == 0 ? 2 : (steps_other-1)/(CARUNITS_PER_TILE*VEHICLE_STEPS_PER_CARUNIT) + get_tile_length() + 1;
3609 if( tiles > 0 && idx+(uint32)tiles >= route.get_count() ) {
3610 // needs more space than there
3611 return false;
3612 }
3613
3614 for( sint32 i=0; i<tiles; i++ ) {
3615 grund_t *gr = welt->lookup( route.at( idx+i ) );
3616 if( gr==NULL ) {
3617 return false;
3618 }
3619 weg_t *str = gr->get_weg(road_wt);
3620 if( str==0 ) {
3621 return false;
3622 }
3623 // not overtaking on railroad crossings or normal crossings ...
3624 if( str->is_crossing() ) {
3625 return false;
3626 }
3627 if( ribi_t::is_threeway(str->get_ribi()) ) {
3628 return false;
3629 }
3630 // Check for other vehicles on the next tile
3631 const uint8 top = gr->get_top();
3632 for( uint8 j=1; j<top; j++ ) {
3633 if( vehicle_base_t* const v = obj_cast<vehicle_base_t>(gr->obj_bei(j)) ) {
3634 // check for other traffic on the road
3635 const overtaker_t *ov = v->get_overtaker();
3636 if(ov) {
3637 if(this!=ov && other_overtaker!=ov) {
3638 return false;
3639 }
3640 }
3641 else if( v->get_waytype()==road_wt && v->get_typ()!=obj_t::pedestrian ) {
3642 return false;
3643 }
3644 }
3645 }
3646 }
3647 set_tiles_overtaking( tiles );
3648 return true;
3649 }
3650
3651 int diff_speed = akt_speed - other_speed;
3652 if( diff_speed < kmh_to_speed(5) ) {
3653 return false;
3654 }
3655
3656 // Number of tiles overtaking will take
3657 int n_tiles = 0;
3658
3659 // Distance it takes overtaking (unit: vehicle_steps) = my_speed * time_overtaking
3660 // time_overtaking = tiles_to_overtake/diff_speed
3661 // tiles_to_overtake = convoi_length + current pos within tile + (pos_other_convoi within tile + length of other convoi) - one tile
3662 int distance = akt_speed*(fahr[0]->get_steps()+get_length_in_steps()+steps_other-VEHICLE_STEPS_PER_TILE)/diff_speed;
3663 int time_overtaking = 0;
3664
3665 // Conditions for overtaking:
3666 // Flat tiles, with no stops, no crossings, no signs, no change of road speed limit
3667 // First phase: no traffic except me and my overtaken car in the dangerous zone
3668 unsigned int route_index = fahr[0]->get_route_index()+1;
3669 koord3d pos = fahr[0]->get_pos();
3670 koord3d pos_prev = route_index > 2 ? route.at(route_index-2) : pos;
3671 koord3d pos_next;
3672
3673 while( distance > 0 ) {
3674
3675 if( route_index >= route.get_count() ) {
3676 return false;
3677 }
3678
3679 pos_next = route.at(route_index++);
3680 grund_t *gr = welt->lookup(pos);
3681 // no ground, or slope => about
3682 if( gr==NULL || gr->get_weg_hang()!=slope_t::flat ) {
3683 return false;
3684 }
3685
3686 weg_t *str = gr->get_weg(road_wt);
3687 if( str==NULL ) {
3688 return false;
3689 }
3690 // the only roadsign we must account for are choose points and traffic lights
3691 if( str->has_sign() ) {
3692 const roadsign_t *rs = gr->find<roadsign_t>(1);
3693 if(rs) {
3694 const roadsign_desc_t *rb = rs->get_desc();
3695 if(rb->is_choose_sign() || rb->is_traffic_light() ) {
3696 // because we need to stop here ...
3697 return false;
3698 }
3699 }
3700 }
3701 // not overtaking on railroad crossings or on normal crossings ...
3702 if( str->is_crossing() || ribi_t::is_threeway(str->get_ribi()) ) {
3703 return false;
3704 }
3705 // street gets too slow (TODO: should be able to be correctly accounted for)
3706 if( akt_speed > kmh_to_speed(str->get_max_speed()) ) {
3707 return false;
3708 }
3709
3710 int d = ribi_t::is_straight(str->get_ribi()) ? VEHICLE_STEPS_PER_TILE : vehicle_base_t::get_diagonal_vehicle_steps_per_tile();
3711 distance -= d;
3712 time_overtaking += d;
3713
3714 // Check for other vehicles
3715 const uint8 top = gr->get_top();
3716 for( uint8 j=1; j<top; j++ ) {
3717 if (vehicle_base_t* const v = obj_cast<vehicle_base_t>(gr->obj_bei(j))) {
3718 // check for other traffic on the road
3719 const overtaker_t *ov = v->get_overtaker();
3720 if(ov) {
3721 if(this!=ov && other_overtaker!=ov) {
3722 return false;
3723 }
3724 }
3725 else if( v->get_waytype()==road_wt && v->get_typ()!=obj_t::pedestrian ) {
3726 // sheeps etc.
3727 return false;
3728 }
3729 }
3730 }
3731 n_tiles++;
3732 pos_prev = pos;
3733 pos = pos_next;
3734 }
3735
3736 // Second phase: only facing traffic is forbidden
3737 // Since street speed can change, we do the calculation with time.
3738 // Each empty tile will subtract tile_dimension/max_street_speed.
3739 // If time is exhausted, we are guaranteed that no facing traffic will
3740 // invade the dangerous zone.
3741 // Conditions for the street are milder: e.g. if no street, no facing traffic
3742 time_overtaking = (time_overtaking << 16)/akt_speed;
3743 while( time_overtaking > 0 ) {
3744
3745 if( route_index >= route.get_count() ) {
3746 return false;
3747 }
3748
3749 pos_next = route.at(route_index++);
3750 grund_t *gr= welt->lookup(pos);
3751 if( gr==NULL ) {
3752 // will cause a route search, but is ok
3753 break;
3754 }
3755
3756 weg_t *str = gr->get_weg(road_wt);
3757 if( str==NULL ) {
3758 break;
3759 }
3760 // cannot check for oncoming traffic over crossings
3761 if( ribi_t::is_threeway(str->get_ribi()) ) {
3762 return false;
3763 }
3764
3765 if( ribi_t::is_straight(str->get_ribi()) ) {
3766 time_overtaking -= (VEHICLE_STEPS_PER_TILE<<16) / kmh_to_speed(str->get_max_speed());
3767 }
3768 else {
3769 time_overtaking -= (vehicle_base_t::get_diagonal_vehicle_steps_per_tile()<<16) / kmh_to_speed(str->get_max_speed());
3770 }
3771
3772 // Check for other vehicles in facing direction
3773 ribi_t::ribi their_direction = ribi_t::backward( fahr[0]->calc_direction(pos_prev, pos_next) );
3774 const uint8 top = gr->get_top();
3775 for( uint8 j=1; j<top; j++ ) {
3776 vehicle_base_t* const v = obj_cast<vehicle_base_t>(gr->obj_bei(j));
3777 if (v && v->get_direction() == their_direction && v->get_overtaker()) {
3778 return false;
3779 }
3780 }
3781 pos_prev = pos;
3782 pos = pos_next;
3783 }
3784
3785 set_tiles_overtaking( 1+n_tiles );
3786 other_overtaker->set_tiles_overtaking( -1-(n_tiles*(akt_speed-diff_speed))/akt_speed );
3787 return true;
3788 }
3789
3790
get_stat_converted(int month,int cost_type) const3791 sint64 convoi_t::get_stat_converted(int month, int cost_type) const
3792 {
3793 sint64 value = financial_history[month][cost_type];
3794 switch(cost_type) {
3795 case CONVOI_REVENUE:
3796 case CONVOI_OPERATIONS:
3797 case CONVOI_PROFIT:
3798 case CONVOI_WAYTOLL:
3799 value = convert_money(value);
3800 break;
3801 default: ;
3802 }
3803 return value;
3804 }
3805
3806
send_to_depot(bool local)3807 const char* convoi_t::send_to_depot(bool local)
3808 {
3809 // iterate over all depots and try to find shortest route
3810 route_t *shortest_route = new route_t();
3811 route_t *route = new route_t();
3812 koord3d home = koord3d::invalid;
3813 vehicle_t *v = front();
3814 FOR(slist_tpl<depot_t*>, const depot, depot_t::get_depot_list()) {
3815 if (depot->get_waytype() != v->get_desc()->get_waytype() || depot->get_owner() != get_owner()) {
3816 continue;
3817 }
3818 koord3d pos = depot->get_pos();
3819
3820 if(!shortest_route->empty() && koord_distance(pos, get_pos()) >= shortest_route->get_count()-1) {
3821 // the current route is already shorter, no need to search further
3822 continue;
3823 }
3824 if (v->calc_route(get_pos(), pos, 50, route)) { // do not care about speed
3825 if( route->get_count() < shortest_route->get_count() || shortest_route->empty() ) {
3826 // just swap the pointers
3827 sim::swap(shortest_route, route);
3828 home = pos;
3829 }
3830 }
3831 }
3832 delete route;
3833 DBG_MESSAGE("shortest route has ", "%i hops", shortest_route->get_count()-1);
3834
3835 if (local) {
3836 if (convoi_info_t *info = dynamic_cast<convoi_info_t*>(win_get_magic( magic_convoi_info+self.get_id()))) {
3837 info->route_search_finished();
3838 }
3839 }
3840 // if route to a depot has been found, update the convoi's schedule
3841 const char *txt;
3842 if( !shortest_route->empty() ) {
3843 schedule_t *schedule = get_schedule()->copy();
3844 schedule->insert(welt->lookup(home));
3845 schedule->set_current_stop( (schedule->get_current_stop()+schedule->get_count()-1)%schedule->get_count() );
3846 set_schedule(schedule);
3847 txt = "Convoi has been sent\nto the nearest depot\nof appropriate type.\n";
3848 }
3849 else {
3850 txt = "Home depot not found!\nYou need to send the\nconvoi to the depot\nmanually.";
3851 }
3852 delete shortest_route;
3853
3854 return txt;
3855 }
3856