1 /*
2 * Copyright (c) 1997 - 2001 Hj. Malthaner
3 *
4 * This file is part of the Simutrans project under the artistic license.
5 * (see license.txt)
6 *
7 * All moving stuff (vehicle_base_t) and all player vehicle (derived from vehicle_t)
8 *
9 * 01.11.99 Moved from simobj.cc
10 *
11 * Hansjoerg Malthaner, Nov. 1999
12 */
13
14 #include <limits.h>
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <math.h>
18
19 #include "../boden/grund.h"
20 #include "../boden/wege/runway.h"
21 #include "../boden/wege/kanal.h"
22 #include "../boden/wege/schiene.h"
23 #include "../boden/wege/monorail.h"
24 #include "../boden/wege/strasse.h"
25
26 #include "../bauer/goods_manager.h"
27
28 #include "../simworld.h"
29 #include "../simdebug.h"
30 #include "../simdepot.h"
31 #include "../simconvoi.h"
32 #include "../simunits.h"
33
34 #include "../player/simplay.h"
35 #include "../simfab.h"
36 #include "../simware.h"
37 #include "../simhalt.h"
38 #include "../simsound.h"
39
40 #include "../display/simimg.h"
41 #include "../simmesg.h"
42 #include "../simcolor.h"
43 #include "../display/simgraph.h"
44
45 #include "../simline.h"
46
47 #include "../simintr.h"
48
49 #include "../obj/wolke.h"
50 #include "../obj/signal.h"
51 #include "../obj/roadsign.h"
52 #include "../obj/crossing.h"
53 #include "../obj/zeiger.h"
54
55 #include "../gui/minimap.h"
56
57 #include "../descriptor/citycar_desc.h"
58 #include "../descriptor/goods_desc.h"
59 #include "../descriptor/skin_desc.h"
60 #include "../descriptor/roadsign_desc.h"
61
62 #include "../dataobj/schedule.h"
63 #include "../dataobj/translator.h"
64 #include "../dataobj/loadsave.h"
65 #include "../dataobj/environment.h"
66
67 #include "../utils/simstring.h"
68 #include "../utils/cbuffer_t.h"
69
70 #include "../bauer/vehikelbauer.h"
71
72 #include "simvehicle.h"
73 #include "simroadtraffic.h"
74
75
76 /* get dx and dy from dir (just to remind you)
77 * any vehicle (including city cars and pedestrians)
78 * will go this distance per sync step.
79 * (however, the real dirs are only calculated during display, these are the old ones)
80 */
81 sint8 vehicle_base_t::dxdy[ 8*2 ] = {
82 -2, 1, // s
83 -2, -1, // w
84 -4, 0, // sw
85 0, 2, //se
86 2, -1, // n
87 2, 1, // e
88 4, 0, // ne
89 0, -2 // nw
90 };
91
92
93 // Constants
94 uint8 vehicle_base_t::old_diagonal_vehicle_steps_per_tile = 128;
95 uint8 vehicle_base_t::diagonal_vehicle_steps_per_tile = 181;
96 uint16 vehicle_base_t::diagonal_multiplier = 724;
97
98
99 // set only once, before loading!
set_diagonal_multiplier(uint32 multiplier,uint32 old_diagonal_multiplier)100 void vehicle_base_t::set_diagonal_multiplier( uint32 multiplier, uint32 old_diagonal_multiplier )
101 {
102 diagonal_multiplier = (uint16)multiplier;
103 diagonal_vehicle_steps_per_tile = (uint8)(130560u/diagonal_multiplier) + 1;
104 old_diagonal_vehicle_steps_per_tile = (uint8)(130560u/old_diagonal_multiplier) + 1;
105 }
106
107
108 // if true, convoi, must restart!
need_realignment() const109 bool vehicle_base_t::need_realignment() const
110 {
111 return old_diagonal_vehicle_steps_per_tile!=diagonal_vehicle_steps_per_tile && ribi_t::is_bend(direction);
112 }
113
114 // [0]=xoff [1]=yoff
115 sint8 vehicle_base_t::driveleft_base_offsets[8][2] =
116 {
117 { 12, 6 },
118 { -12, 6 },
119 { 0, 6 },
120 { 12, 0 },
121 { -12, -6 },
122 { 12, -6 },
123 { 0, -6 },
124 { -12, 0 }
125 // { 12, -12, 0, 12, -12, 12, 0 -12 },
126 // { 6, 6, 6, 0, -6, -6, -6, 0 }
127 };
128
129 // [0]=xoff [1]=yoff
130 sint8 vehicle_base_t::overtaking_base_offsets[8][2];
131
132 // recalc offsets for overtaking
set_overtaking_offsets(bool driving_on_the_left)133 void vehicle_base_t::set_overtaking_offsets( bool driving_on_the_left )
134 {
135 sint8 sign = driving_on_the_left ? -1 : 1;
136 // a tile has the internal size of
137 const sint8 XOFF=12;
138 const sint8 YOFF=6;
139
140 overtaking_base_offsets[0][0] = sign * XOFF;
141 overtaking_base_offsets[1][0] = -sign * XOFF;
142 overtaking_base_offsets[2][0] = 0;
143 overtaking_base_offsets[3][0] = sign * XOFF;
144 overtaking_base_offsets[4][0] = -sign * XOFF;
145 overtaking_base_offsets[5][0] = sign * XOFF;
146 overtaking_base_offsets[6][0] = 0;
147 overtaking_base_offsets[7][0] = sign * (-XOFF-YOFF);
148
149 overtaking_base_offsets[0][1] = sign * YOFF;
150 overtaking_base_offsets[1][1] = sign * YOFF;
151 overtaking_base_offsets[2][1] = sign * YOFF;
152 overtaking_base_offsets[3][1] = 0;
153 overtaking_base_offsets[4][1] = -sign * YOFF;
154 overtaking_base_offsets[5][1] = -sign * YOFF;
155 overtaking_base_offsets[6][1] = -sign * YOFF;
156 overtaking_base_offsets[7][1] = 0;
157 }
158
159
160 /**
161 * Checks if this vehicle must change the square upon next move
162 * THIS IS ONLY THERE FOR LOADING OLD SAVES!
163 * @author Hj. Malthaner
164 */
is_about_to_hop(const sint8 neu_xoff,const sint8 neu_yoff) const165 bool vehicle_base_t::is_about_to_hop( const sint8 neu_xoff, const sint8 neu_yoff ) const
166 {
167 const sint8 y_off_2 = 2*neu_yoff;
168 const sint8 c_plus = y_off_2 + neu_xoff;
169 const sint8 c_minus = y_off_2 - neu_xoff;
170
171 return ! (c_plus < OBJECT_OFFSET_STEPS*2 && c_minus < OBJECT_OFFSET_STEPS*2 && c_plus > -OBJECT_OFFSET_STEPS*2 && c_minus > -OBJECT_OFFSET_STEPS*2);
172 }
173
174
vehicle_base_t()175 vehicle_base_t::vehicle_base_t():
176 obj_t()
177 {
178 image = IMG_EMPTY;
179 set_flag( obj_t::is_vehicle );
180 steps = 0;
181 steps_next = VEHICLE_STEPS_PER_TILE - 1;
182 use_calc_height = true;
183 dx = 0;
184 dy = 0;
185 zoff_start = zoff_end = 0;
186 disp_lane = 2;
187 }
188
189
vehicle_base_t(koord3d pos)190 vehicle_base_t::vehicle_base_t(koord3d pos):
191 obj_t(pos)
192 {
193 image = IMG_EMPTY;
194 set_flag( obj_t::is_vehicle );
195 pos_next = pos;
196 steps = 0;
197 steps_next = VEHICLE_STEPS_PER_TILE - 1;
198 use_calc_height = true;
199 dx = 0;
200 dy = 0;
201 zoff_start = zoff_end = 0;
202 disp_lane = 2;
203 }
204
205
rotate90()206 void vehicle_base_t::rotate90()
207 {
208 koord3d pos_cur = get_pos();
209 pos_cur.rotate90( welt->get_size().y-1 );
210 set_pos( pos_cur );
211 // directions are counterclockwise to ribis!
212 direction = ribi_t::rotate90( direction );
213 pos_next.rotate90( welt->get_size().y-1 );
214 // new offsets: very tricky ...
215 sint8 new_dx = -dy*2;
216 dy = dx/2;
217 dx = new_dx;
218 // new pos + step offsets (only possible, since we know the height!
219 sint8 neu_yoff = get_xoff()/2;
220 set_xoff( -get_yoff()*2 );
221 set_yoff( neu_yoff );
222 // adjust disp_lane individually
223 }
224
225
leave_tile()226 void vehicle_base_t::leave_tile()
227 {
228 // first: release crossing
229 grund_t *gr = welt->lookup(get_pos());
230 if( gr && gr->ist_uebergang() ) {
231 crossing_t *cr = gr->find<crossing_t>(2);
232 grund_t *gr2 = welt->lookup(pos_next);
233 if( gr2==NULL || gr2==gr || !gr2->ist_uebergang() || cr->get_logic()!=gr2->find<crossing_t>(2)->get_logic() ) {
234 cr->release_crossing(this);
235 }
236 }
237
238 // then remove from ground (or search whole map, if failed)
239 if(!get_flag(not_on_map) && (gr==NULL || !gr->obj_remove(this)) ) {
240
241 // was not removed (not found?)
242 dbg->error("vehicle_base_t::leave_tile()","'typ %i' %p could not be removed from %d %d", get_typ(), this, get_pos().x, get_pos().y);
243 DBG_MESSAGE("vehicle_base_t::leave_tile()","checking all plan squares");
244
245 // check, whether it is on another height ...
246 const planquadrat_t *pl = welt->access( get_pos().get_2d() );
247 if( pl ) {
248 gr = pl->get_boden_von_obj(this);
249 if( gr ) {
250 gr->obj_remove(this);
251 dbg->warning("vehicle_base_t::leave_tile()","removed vehicle typ %i (%p) from %d %d",get_typ(), this, get_pos().x, get_pos().y);
252 }
253 return;
254 }
255
256 koord k;
257 bool ok = false;
258
259 for(k.y=0; k.y<welt->get_size().y; k.y++) {
260 for(k.x=0; k.x<welt->get_size().x; k.x++) {
261 grund_t *gr = welt->access( k )->get_boden_von_obj(this);
262 if(gr && gr->obj_remove(this)) {
263 dbg->warning("vehicle_base_t::leave_tile()","removed vehicle typ %i (%p) from %d %d",get_name(), this, k.x, k.y);
264 ok = true;
265 }
266 }
267 }
268
269 if(!ok) {
270 dbg->error("vehicle_base_t::leave_tile()","'%s' %p was not found on any map square!",get_name(), this);
271 }
272 }
273 }
274
275
enter_tile(grund_t * gr)276 void vehicle_base_t::enter_tile(grund_t* gr)
277 {
278 if(!gr) {
279 dbg->error("vehicle_base_t::enter_tile()","'%s' new position (%i,%i,%i)!",get_name(), get_pos().x, get_pos().y, get_pos().z );
280 gr = welt->lookup_kartenboden(get_pos().get_2d());
281 set_pos( gr->get_pos() );
282 }
283 gr->obj_add(this);
284 }
285
286
287 /* THE routine for moving vehicles
288 * it will drive on as log as it can
289 * @return the distance actually traveled
290 */
do_drive(uint32 distance)291 uint32 vehicle_base_t::do_drive(uint32 distance)
292 {
293
294 uint32 steps_to_do = distance >> YARDS_PER_VEHICLE_STEP_SHIFT;
295
296 if( steps_to_do == 0 ) {
297 // ok, we will not move in this steps
298 return 0;
299 }
300 // ok, so moving ...
301 if( !get_flag(obj_t::dirty) ) {
302 mark_image_dirty( image, 0 );
303 set_flag( obj_t::dirty );
304 }
305
306 grund_t *gr = NULL; // if hopped, then this is new position
307
308 uint32 steps_target = steps_to_do + steps;
309
310 uint32 distance_travelled; // Return value
311
312 if( steps_target > steps_next ) {
313 // We are going far enough to hop.
314
315 // We'll be adding steps_next+1 for each hop, as if we
316 // started at the beginning of this tile, so for an accurate
317 // count of steps done we must subtract the location we started with.
318 sint32 steps_done = -steps;
319
320 // Hop as many times as possible.
321 while( steps_target > steps_next && (gr = hop_check()) ) {
322 // now do the update for hopping
323 steps_target -= steps_next+1;
324 steps_done += steps_next+1;
325 koord pos_prev(get_pos().get_2d());
326 hop(gr);
327 use_calc_height = true;
328
329 // set offsets
330 set_xoff( (dx<0) ? OBJECT_OFFSET_STEPS : -OBJECT_OFFSET_STEPS );
331 set_yoff( (dy<0) ? OBJECT_OFFSET_STEPS/2 : -OBJECT_OFFSET_STEPS/2 );
332 if(dx*dy==0) {
333 if(dx==0) {
334 if(dy>0) {
335 set_xoff( pos_prev.x!=get_pos().x ? -OBJECT_OFFSET_STEPS : OBJECT_OFFSET_STEPS );
336 }
337 else {
338 set_xoff( pos_prev.x!=get_pos().x ? OBJECT_OFFSET_STEPS : -OBJECT_OFFSET_STEPS );
339 }
340 }
341 else {
342 if(dx>0) {
343 set_yoff( pos_prev.y!=get_pos().y ? OBJECT_OFFSET_STEPS/2 : -OBJECT_OFFSET_STEPS/2 );
344 }
345 else {
346 set_yoff( pos_prev.y!=get_pos().y ? -OBJECT_OFFSET_STEPS/2 : OBJECT_OFFSET_STEPS/2 );
347 }
348 }
349 }
350 }
351
352 if( steps_next == 0 ) {
353 // only needed for aircrafts, which can turn on the same tile
354 // the indicate the turn with this here
355 steps_next = VEHICLE_STEPS_PER_TILE - 1;
356 steps_target = VEHICLE_STEPS_PER_TILE - 1;
357 steps_done -= VEHICLE_STEPS_PER_TILE - 1;
358 }
359
360 // Update internal status, how far we got within the tile.
361 if( steps_target <= steps_next ) {
362 steps = steps_target;
363 }
364 else {
365 // could not go as far as we wanted (hop_check failed) => stop at end of tile
366 steps = steps_next;
367 }
368
369 steps_done += steps;
370 distance_travelled = steps_done << YARDS_PER_VEHICLE_STEP_SHIFT;
371
372 }
373 else {
374 // Just travel to target, it's on same tile
375 steps = steps_target;
376 distance_travelled = distance & YARDS_VEHICLE_STEP_MASK; // round down to nearest step
377 }
378
379 if(use_calc_height) {
380 calc_height(gr);
381 }
382 // remaining steps
383 return distance_travelled;
384 }
385
386
387 // to make smaller steps than the tile granularity, we have to use this trick
get_screen_offset(int & xoff,int & yoff,const sint16 raster_width) const388 void vehicle_base_t::get_screen_offset( int &xoff, int &yoff, const sint16 raster_width ) const
389 {
390 // vehicles needs finer steps to appear smoother
391 sint32 display_steps = (uint32)steps*(uint16)raster_width;
392 if(dx && dy) {
393 display_steps &= 0xFFFFFC00;
394 }
395 else {
396 display_steps = (display_steps*diagonal_multiplier)>>10;
397 }
398 xoff += (display_steps*dx) >> 10;
399 yoff += ((display_steps*dy) >> 10) + (get_hoff(raster_width))/(4*16);
400 }
401
402
403 // calcs new direction and applies it to the vehicles
calc_set_direction(const koord3d & start,const koord3d & ende)404 ribi_t::ribi vehicle_base_t::calc_set_direction(const koord3d& start, const koord3d& ende)
405 {
406 ribi_t::ribi direction = ribi_t::none;
407
408 const sint8 di = ende.x - start.x;
409 const sint8 dj = ende.y - start.y;
410
411 if(dj < 0 && di == 0) {
412 direction = ribi_t::north;
413 dx = 2;
414 dy = -1;
415 steps_next = VEHICLE_STEPS_PER_TILE - 1;
416 } else if(dj > 0 && di == 0) {
417 direction = ribi_t::south;
418 dx = -2;
419 dy = 1;
420 steps_next = VEHICLE_STEPS_PER_TILE - 1;
421 } else if(di < 0 && dj == 0) {
422 direction = ribi_t::west;
423 dx = -2;
424 dy = -1;
425 steps_next = VEHICLE_STEPS_PER_TILE - 1;
426 } else if(di >0 && dj == 0) {
427 direction = ribi_t::east;
428 dx = 2;
429 dy = 1;
430 steps_next = VEHICLE_STEPS_PER_TILE - 1;
431 } else if(di > 0 && dj > 0) {
432 direction = ribi_t::southeast;
433 dx = 0;
434 dy = 2;
435 steps_next = diagonal_vehicle_steps_per_tile - 1;
436 } else if(di < 0 && dj < 0) {
437 direction = ribi_t::northwest;
438 dx = 0;
439 dy = -2;
440 steps_next = diagonal_vehicle_steps_per_tile - 1;
441 } else if(di > 0 && dj < 0) {
442 direction = ribi_t::northeast;
443 dx = 4;
444 dy = 0;
445 steps_next = diagonal_vehicle_steps_per_tile - 1;
446 } else {
447 direction = ribi_t::southwest;
448 dx = -4;
449 dy = 0;
450 steps_next = diagonal_vehicle_steps_per_tile - 1;
451 }
452 // we could artificially make diagonals shorter: but this would break existing game behaviour
453 return direction;
454 }
455
456
457 // this routine calculates the new height
458 // beware of bridges, tunnels, slopes, ...
calc_height(grund_t * gr)459 void vehicle_base_t::calc_height(grund_t *gr)
460 {
461 use_calc_height = false; // assume, we are only needed after next hop
462 zoff_start = zoff_end = 0; // assume flat way
463
464 if(gr==NULL) {
465 gr = welt->lookup(get_pos());
466 }
467 if(gr==NULL) {
468 // slope changed below a moving thing?!?
469 return;
470 }
471 else if( gr->ist_tunnel() && gr->ist_karten_boden() && !is_flying() ) {
472 use_calc_height = true; // to avoid errors if underground mode is switched
473 if( grund_t::underground_mode == grund_t::ugm_none ||
474 (grund_t::underground_mode == grund_t::ugm_level && gr->get_hoehe() < grund_t::underground_level)
475 ) {
476 // need hiding? One of the few uses of XOR: not half driven XOR exiting => not hide!
477 ribi_t::ribi hang_ribi = ribi_type( gr->get_grund_hang() );
478 if((steps<(steps_next/2)) ^ ((hang_ribi&direction)!=0) ) {
479 set_image(IMG_EMPTY);
480 }
481 else {
482 calc_image();
483 }
484 }
485 }
486 else {
487 // force a valid image above ground, with special handling of tunnel entraces
488 if( get_image()==IMG_EMPTY ) {
489 if( !gr->ist_tunnel() && gr->ist_karten_boden() ) {
490 calc_image();
491 }
492 }
493
494 // will not work great with ways, but is very short!
495 slope_t::type hang = gr->get_weg_hang();
496 if( hang ) {
497 const uint slope_height = (hang & 7) ? 1 : 2;
498 ribi_t::ribi hang_ribi = ribi_type(hang);
499 if( ribi_t::doubles(hang_ribi) == ribi_t::doubles(direction)) {
500 zoff_start = hang_ribi & direction ? 2*slope_height : 0; // 0 .. 4
501 zoff_end = hang_ribi & ribi_t::backward(direction) ? 2*slope_height : 0; // 0 .. 4
502 }
503 else {
504 // only for shadows and movingobjs ...
505 zoff_start = hang_ribi & direction ? slope_height+1 : 0; // 0 .. 3
506 zoff_end = hang_ribi & ribi_t::backward(direction) ? slope_height+1 : 0; // 0 .. 3
507 }
508 }
509 else {
510 zoff_start = (gr->get_weg_yoff() * 2) / TILE_HEIGHT_STEP;
511 zoff_end = zoff_start;
512 }
513 }
514 }
515
516
get_hoff(const sint16 raster_width) const517 sint16 vehicle_base_t::get_hoff(const sint16 raster_width) const
518 {
519 sint16 h_start = -(sint8)TILE_HEIGHT_STEP * (sint8)zoff_start;
520 sint16 h_end = -(sint8)TILE_HEIGHT_STEP * (sint8)zoff_end;
521 return ((h_start*steps + h_end*(256-steps))*raster_width) >> 9;
522 }
523
524
525 /* true, if one could pass through this field
526 * also used for citycars, thus defined here
527 */
no_cars_blocking(const grund_t * gr,const convoi_t * cnv,const uint8 current_direction,const uint8 next_direction,const uint8 next_90direction)528 vehicle_base_t *vehicle_base_t::no_cars_blocking( const grund_t *gr, const convoi_t *cnv, const uint8 current_direction, const uint8 next_direction, const uint8 next_90direction )
529 {
530 // Search vehicle
531 for( uint8 pos=1; pos<(uint8)gr->get_top(); pos++ ) {
532 if( vehicle_base_t* const v = obj_cast<vehicle_base_t>(gr->obj_bei(pos)) ) {
533 if( v->get_typ()==obj_t::pedestrian ) {
534 continue;
535 }
536
537 // check for car
538 uint8 other_direction=255;
539 bool other_moving = false;
540 if( road_vehicle_t const* const at = obj_cast<road_vehicle_t>(v) ) {
541 // ignore ourself
542 if( cnv == at->get_convoi() ) {
543 continue;
544 }
545 other_direction = at->get_direction();
546 other_moving = at->get_convoi()->get_akt_speed() > kmh_to_speed(1);
547 }
548 // check for city car
549 else if( v->get_waytype() == road_wt ) {
550 other_direction = v->get_direction();
551 if( private_car_t const* const sa = obj_cast<private_car_t>(v) ){
552 other_moving = sa->get_current_speed() > 1;
553 }
554 }
555
556 // ok, there is another car ...
557 if( other_direction != 255 ) {
558 if( next_direction == other_direction && !ribi_t::is_threeway(gr->get_weg_ribi(road_wt)) ) {
559 // cars going in the same direction and no crossing => that mean blocking ...
560 return v;
561 }
562
563 const ribi_t::ribi other_90direction = (gr->get_pos().get_2d() == v->get_pos_next().get_2d()) ? other_direction : calc_direction(gr->get_pos(), v->get_pos_next());
564 if( other_90direction == next_90direction ) {
565 // Want to exit in same as other ~50% of the time
566 return v;
567 }
568
569 const bool drives_on_left = welt->get_settings().is_drive_left();
570 const bool across = next_direction == (drives_on_left ? ribi_t::rotate45l(next_90direction) : ribi_t::rotate45(next_90direction)); // turning across the opposite directions lane
571 const bool other_across = other_direction == (drives_on_left ? ribi_t::rotate45l(other_90direction) : ribi_t::rotate45(other_90direction)); // other is turning across the opposite directions lane
572 if( other_direction == next_direction && !(other_across || across) ) {
573 // entering same straight waypoint as other ~18%
574 return v;
575 }
576
577 const bool straight = next_direction == next_90direction; // driving straight
578 const ribi_t::ribi current_90direction = straight ? ribi_t::backward(next_90direction) : (~(next_direction|ribi_t::backward(next_90direction)))&0x0F;
579 const bool other_straight = other_direction == other_90direction; // other is driving straight
580 const bool other_exit_same_side = current_90direction == other_90direction; // other is exiting same side as we're entering
581 const bool other_exit_opposite_side = ribi_t::backward(current_90direction) == other_90direction; // other is exiting side across from where we're entering
582 if( across && ((ribi_t::is_perpendicular(current_90direction,other_direction) && other_moving) || (other_across && other_exit_opposite_side) || ((other_across || other_straight) && other_exit_same_side && other_moving) ) ) {
583 // other turning across in front of us from orth entry dir'n ~4%
584 return v;
585 }
586
587 const bool headon = ribi_t::backward(current_direction) == other_direction; // we're meeting the other headon
588 const bool other_exit_across = (drives_on_left ? ribi_t::rotate90l(next_90direction) : ribi_t::rotate90(next_90direction)) == other_90direction; // other is exiting by turning across the opposite directions lane
589 if( straight && (ribi_t::is_perpendicular(current_90direction,other_direction) || (other_across && other_moving && (other_exit_across || (other_exit_same_side && !headon))) ) ) {
590 // other turning across in front of us, but allow if other is stopped - duplicating historic behaviour ~2%
591 return v;
592 }
593 else if( other_direction == current_direction && current_90direction == ribi_t::none ) {
594 // entering same diagonal waypoint as other ~1%
595 return v;
596 }
597
598 // else other car is not blocking ~25%
599 }
600 }
601 }
602
603 // way is free
604 return NULL;
605 }
606
607
rotate90()608 void vehicle_t::rotate90()
609 {
610 vehicle_base_t::rotate90();
611 previous_direction = ribi_t::rotate90( previous_direction );
612 last_stop_pos.rotate90( welt->get_size().y-1 );
613 }
614
615
rotate90_freight_destinations(const sint16 y_size)616 void vehicle_t::rotate90_freight_destinations(const sint16 y_size)
617 {
618 // now rotate the freight
619 FOR(slist_tpl<ware_t>, & tmp, fracht) {
620 tmp.rotate90(y_size );
621 }
622 }
623
624
set_convoi(convoi_t * c)625 void vehicle_t::set_convoi(convoi_t *c)
626 {
627 /* cnv can have three values:
628 * NULL: not previously assigned
629 * 1 (only during loading): convoi wants to reserve the whole route
630 * other: previous convoi (in this case, currently always c==cnv)
631 *
632 * if c is NULL, then the vehicle is removed from the convoi
633 * (the rail_vehicle_t::set_convoi etc. routines must then remove a
634 * possibly pending reservation of stops/tracks)
635 */
636 assert( c==NULL || cnv==NULL || cnv==(convoi_t *)1 || c==cnv);
637 cnv = c;
638 if(cnv) {
639 // we need to re-establish the finish flag after loading
640 if(leading) {
641 route_t const& r = *cnv->get_route();
642 check_for_finish = r.empty() || route_index >= r.get_count() || get_pos() == r.at(route_index);
643 }
644 if( pos_next != koord3d::invalid ) {
645 route_t const& r = *cnv->get_route();
646 if (!r.empty() && route_index < r.get_count() - 1) {
647 grund_t const* const gr = welt->lookup(pos_next);
648 if (!gr || !gr->get_weg(get_waytype())) {
649 if (!(water_wt == get_waytype() && gr->is_water())) { // ships on the open sea are valid
650 pos_next = r.at(route_index + 1U);
651 }
652 }
653 }
654 }
655 // just correct freight destinations
656 FOR(slist_tpl<ware_t>, & c, fracht) {
657 c.finish_rd(welt);
658 }
659 }
660 }
661
662
663 /**
664 * Unload freight to halt
665 * @return sum of unloaded goods
666 * @author Hj. Malthaner
667 */
unload_cargo(halthandle_t halt,bool unload_all)668 uint16 vehicle_t::unload_cargo(halthandle_t halt, bool unload_all )
669 {
670 uint16 sum_menge = 0, sum_delivered = 0, index = 0;
671 if( !halt.is_bound() ) {
672 return 0;
673 }
674
675 if( halt->is_enabled( get_cargo_type() ) ) {
676 if( !fracht.empty() ) {
677
678 for( slist_tpl<ware_t>::iterator i = fracht.begin(), end = fracht.end(); i != end; ) {
679 const ware_t& tmp = *i;
680
681 halthandle_t end_halt = tmp.get_ziel();
682 halthandle_t via_halt = tmp.get_zwischenziel();
683
684 // check if destination or transfer is still valid
685 if( !end_halt.is_bound() || !via_halt.is_bound() ) {
686 // target halt no longer there => delete and remove from fab in transit
687 fabrik_t::update_transit( &tmp, false );
688 DBG_MESSAGE("vehicle_t::unload_freight()", "destination of %d %s is no longer reachable",tmp.menge,translator::translate(tmp.get_name()));
689 total_freight -= tmp.menge;
690 sum_weight -= tmp.menge * tmp.get_desc()->get_weight_per_unit();
691 i = fracht.erase( i );
692 }
693 else if( end_halt == halt || via_halt == halt || unload_all ) {
694
695 // printf("Liefere %d %s nach %s via %s an %s\n",
696 // tmp->menge,
697 // tmp->name(),
698 // end_halt->get_name(),
699 // via_halt->get_name(),
700 // halt->get_name());
701
702 // here, only ordinary goods should be processed
703 int menge = halt->liefere_an(tmp);
704 sum_menge += menge;
705 total_freight -= menge;
706 sum_weight -= tmp.menge * tmp.get_desc()->get_weight_per_unit();
707
708 index = tmp.get_index();
709
710 if(end_halt==halt) {
711 sum_delivered += menge;
712 }
713
714 i = fracht.erase( i );
715 }
716 else {
717 ++i;
718 }
719 }
720 }
721 }
722
723 if( sum_menge ) {
724 // book transported goods
725 get_owner()->book_transported( sum_menge, get_desc()->get_waytype(), index );
726
727 if( sum_delivered ) {
728 // book delivered goods to destination
729 get_owner()->book_delivered( sum_delivered, get_desc()->get_waytype(), index );
730 }
731
732 // add delivered goods to statistics
733 cnv->book( sum_menge, convoi_t::CONVOI_TRANSPORTED_GOODS );
734
735 // add delivered goods to halt's statistics
736 halt->book( sum_menge, HALT_ARRIVED );
737 }
738 return sum_menge;
739 }
740
741
742 /**
743 * Load freight from halt
744 * @return amount loaded
745 * @author Hj. Malthaner
746 */
load_cargo(halthandle_t halt,const vector_tpl<halthandle_t> & destination_halts)747 uint16 vehicle_t::load_cargo(halthandle_t halt, const vector_tpl<halthandle_t>& destination_halts)
748 {
749 if( !halt.is_bound() || !halt->gibt_ab(desc->get_freight_type()) ) {
750 return 0;
751 }
752
753 const uint16 total_freight_start = total_freight;
754 const uint16 capacity_left = desc->get_capacity() - total_freight;
755 if (capacity_left > 0) {
756
757 slist_tpl<ware_t> freight_add;
758 halt->fetch_goods( freight_add, desc->get_freight_type(), capacity_left, destination_halts);
759
760 if( freight_add.empty() ) {
761 // now empty, but usually, we can get it here ...
762 return 0;
763 }
764
765 for( slist_tpl<ware_t>::iterator iter_z = freight_add.begin(); iter_z != freight_add.end(); ) {
766 ware_t &ware = *iter_z;
767
768 total_freight += ware.menge;
769 sum_weight += ware.menge * ware.get_desc()->get_weight_per_unit();
770
771 // could this be joined with existing freight?
772 FOR( slist_tpl<ware_t>, & tmp, fracht ) {
773 // for pax: join according next stop
774 // for all others we *must* use target coordinates
775 if( ware.same_destination(tmp) ) {
776 tmp.menge += ware.menge;
777 ware.menge = 0;
778 break;
779 }
780 }
781
782 // if != 0 we could not join it to existing => load it
783 if( ware.menge != 0 ) {
784 ++iter_z;
785 // we add list directly
786 }
787 else {
788 iter_z = freight_add.erase(iter_z);
789 }
790 }
791
792 if( !freight_add.empty() ) {
793 fracht.append_list(freight_add);
794 }
795 }
796 return total_freight - total_freight_start;
797 }
798
799
800 /**
801 * Remove freight that no longer can reach it's destination
802 * i.e. because of a changed schedule
803 * @author Hj. Malthaner
804 */
remove_stale_cargo()805 void vehicle_t::remove_stale_cargo()
806 {
807 DBG_DEBUG("vehicle_t::remove_stale_cargo()", "called");
808
809 // and now check every piece of ware on board,
810 // if its target is somewhere on
811 // the new schedule, if not -> remove
812 slist_tpl<ware_t> kill_queue;
813 total_freight = 0;
814
815 if (!fracht.empty()) {
816 FOR(slist_tpl<ware_t>, & tmp, fracht) {
817 bool found = false;
818
819 if( tmp.get_zwischenziel().is_bound() ) {
820 // the original halt exists, but does we still go there?
821 FOR(minivec_tpl<schedule_entry_t>, const& i, cnv->get_schedule()->entries) {
822 if( haltestelle_t::get_halt( i.pos, cnv->get_owner()) == tmp.get_zwischenziel() ) {
823 found = true;
824 break;
825 }
826 }
827 }
828 if( !found ) {
829 // the target halt may have been joined or there is a closer one now, thus our original target is no longer valid
830 const int offset = cnv->get_schedule()->get_current_stop();
831 const int max_count = cnv->get_schedule()->entries.get_count();
832 for( int i=0; i<max_count; i++ ) {
833 // try to unload on next stop
834 halthandle_t halt = haltestelle_t::get_halt( cnv->get_schedule()->entries[ (i+offset)%max_count ].pos, cnv->get_owner() );
835 if( halt.is_bound() ) {
836 if( halt->is_enabled(tmp.get_index()) ) {
837 // ok, lets change here, since goods are accepted here
838 tmp.access_zwischenziel() = halt;
839 if (!tmp.get_ziel().is_bound()) {
840 // set target, to prevent that unload_freight drops cargo
841 tmp.set_ziel( halt );
842 }
843 found = true;
844 break;
845 }
846 }
847 }
848 }
849
850 if( !found ) {
851 kill_queue.insert(tmp);
852 }
853 else {
854 // since we need to point at factory (0,0), we recheck this too
855 koord k = tmp.get_zielpos();
856 fabrik_t *fab = fabrik_t::get_fab( k );
857 tmp.set_zielpos( fab ? fab->get_pos().get_2d() : k );
858
859 total_freight += tmp.menge;
860 }
861 }
862
863 FOR(slist_tpl<ware_t>, const& c, kill_queue) {
864 fabrik_t::update_transit( &c, false );
865 fracht.remove(c);
866 }
867 }
868 sum_weight = get_cargo_weight() + desc->get_weight();
869 }
870
871
play_sound() const872 void vehicle_t::play_sound() const
873 {
874 if( desc->get_sound() >= 0 && !welt->is_fast_forward() ) {
875 welt->play_sound_area_clipped(get_pos().get_2d(), desc->get_sound());
876 }
877 }
878
879
880 /**
881 * Prepare vehicle for new ride.
882 * Sets route_index, pos_next, steps_next.
883 * If @p recalc is true this sets position and recalculates/resets movement parameters.
884 * @author Hj. Malthaner
885 */
initialise_journey(uint16 start_route_index,bool recalc)886 void vehicle_t::initialise_journey(uint16 start_route_index, bool recalc)
887 {
888 route_index = start_route_index+1;
889 check_for_finish = false;
890 use_calc_height = true;
891
892 if(welt->is_within_limits(get_pos().get_2d())) {
893 mark_image_dirty( get_image(), 0 );
894 }
895
896 route_t const& r = *cnv->get_route();
897 if(!recalc) {
898 // always set pos_next
899 pos_next = r.at(route_index);
900 assert(get_pos() == r.at(start_route_index));
901 }
902 else {
903 // set pos_next
904 if (route_index < r.get_count()) {
905 pos_next = r.at(route_index);
906 }
907 else {
908 // already at end of route
909 check_for_finish = true;
910 }
911 set_pos(r.at(start_route_index));
912
913 // recalc directions
914 previous_direction = direction;
915 direction = calc_set_direction( get_pos(), pos_next );
916
917 zoff_start = zoff_end = 0;
918 steps = 0;
919
920 set_xoff( (dx<0) ? OBJECT_OFFSET_STEPS : -OBJECT_OFFSET_STEPS );
921 set_yoff( (dy<0) ? OBJECT_OFFSET_STEPS/2 : -OBJECT_OFFSET_STEPS/2 );
922
923 calc_image();
924 }
925 if ( ribi_t::is_single(direction) ) {
926 steps_next = VEHICLE_STEPS_PER_TILE - 1;
927 }
928 else {
929 steps_next = diagonal_vehicle_steps_per_tile - 1;
930 }
931 }
932
933
vehicle_t(koord3d pos,const vehicle_desc_t * desc,player_t * player)934 vehicle_t::vehicle_t(koord3d pos, const vehicle_desc_t* desc, player_t* player) :
935 vehicle_base_t(pos)
936 {
937 this->desc = desc;
938
939 set_owner( player );
940 purchase_time = welt->get_current_month();
941 cnv = NULL;
942 speed_limit = SPEED_UNLIMITED;
943
944 route_index = 1;
945
946 smoke = true;
947 direction = ribi_t::none;
948
949 current_friction = 4;
950 total_freight = 0;
951 sum_weight = desc->get_weight();
952
953 leading = last = false;
954 check_for_finish = false;
955 use_calc_height = true;
956 has_driven = false;
957
958 previous_direction = direction = ribi_t::none;
959 target_halt = halthandle_t();
960 }
961
962
vehicle_t()963 vehicle_t::vehicle_t() :
964 vehicle_base_t()
965 {
966 smoke = true;
967
968 desc = NULL;
969 cnv = NULL;
970
971 route_index = 1;
972 current_friction = 4;
973 sum_weight = 10;
974 total_freight = 0;
975
976 leading = last = false;
977 check_for_finish = false;
978 use_calc_height = true;
979
980 previous_direction = direction = ribi_t::none;
981 }
982
983
calc_route(koord3d start,koord3d ziel,sint32 max_speed,route_t * route)984 bool vehicle_t::calc_route(koord3d start, koord3d ziel, sint32 max_speed, route_t* route)
985 {
986 return route->calc_route(welt, start, ziel, this, max_speed, 0 );
987 }
988
989
hop_check()990 grund_t* vehicle_t::hop_check()
991 {
992 // the leading vehicle will do all the checks
993 if(leading) {
994 if(check_for_finish) {
995 // so we are there yet?
996 cnv->ziel_erreicht();
997 if(cnv->get_state()==convoi_t::INITIAL) {
998 // to avoid crashes with airplanes
999 use_calc_height = false;
1000 }
1001 return NULL;
1002 }
1003
1004 // now check, if we can go here
1005 grund_t *bd = welt->lookup(pos_next);
1006 if(bd==NULL || !check_next_tile(bd) || cnv->get_route()->empty()) {
1007 // way (weg) not existent (likely destroyed) or no route ...
1008 cnv->suche_neue_route();
1009 return NULL;
1010 }
1011
1012 // check for one-way sign etc.
1013 const waytype_t wt = get_waytype();
1014 if( air_wt != wt && route_index < cnv->get_route()->get_count()-1 ) {
1015 uint8 dir = get_ribi(bd);
1016 koord3d nextnext_pos = cnv->get_route()->at(route_index+1);
1017 if ( nextnext_pos == get_pos() ) {
1018 dbg->error("vehicle_t::hop_check", "route contains point (%s) twice for %s", nextnext_pos.get_str(), cnv->get_name());
1019 }
1020 uint8 new_dir = ribi_type(nextnext_pos - pos_next);
1021 if((dir&new_dir)==0) {
1022 // new one way sign here?
1023 cnv->suche_neue_route();
1024 return NULL;
1025 }
1026 // check for recently built bridges/tunnels or reverse branches (really slows down the game, so we do this only on slopes)
1027 if( bd->get_weg_hang() ) {
1028 grund_t *from;
1029 if( !bd->get_neighbour( from, get_waytype(), ribi_type( get_pos(), pos_next ) ) ) {
1030 // way likely destroyed or altered => reroute
1031 cnv->suche_neue_route();
1032 return NULL;
1033 }
1034 }
1035 }
1036
1037 sint32 restart_speed = -1;
1038 // ist_weg_frei() berechnet auch die Geschwindigkeit
1039 // mit der spaeter weitergefahren wird
1040 if( !can_enter_tile( bd, restart_speed, 0 ) ) {
1041 // stop convoi, when the way is not free
1042 cnv->warten_bis_weg_frei(restart_speed);
1043
1044 // don't continue
1045 return NULL;
1046 }
1047 // we cache it here, hop() will use it to save calls to karte_t::lookup
1048 return bd;
1049 }
1050 else {
1051 // this is needed since in convoi_t::vorfahren the flag 'leading' is set to null
1052 if(check_for_finish) {
1053 return NULL;
1054 }
1055 }
1056 return welt->lookup(pos_next);
1057 }
1058
1059
can_enter_tile(sint32 & restart_speed,uint8 second_check_count)1060 bool vehicle_t::can_enter_tile(sint32 &restart_speed, uint8 second_check_count)
1061 {
1062 grund_t *gr = welt->lookup( pos_next );
1063 if( gr ) {
1064 return can_enter_tile( gr, restart_speed, second_check_count );
1065 }
1066 else {
1067 if( !second_check_count ) {
1068 cnv->suche_neue_route();
1069 }
1070 return false;
1071 }
1072 }
1073
1074
leave_tile()1075 void vehicle_t::leave_tile()
1076 {
1077 vehicle_base_t::leave_tile();
1078 #ifndef DEBUG_ROUTES
1079 if(last && minimap_t::is_visible) {
1080 minimap_t::get_instance()->calc_map_pixel(get_pos().get_2d());
1081 }
1082 #endif
1083 }
1084
1085
1086 /* this routine add a vehicle to a tile and will insert it in the correct sort order to prevent overlaps
1087 * @author prissi
1088 */
enter_tile(grund_t * gr)1089 void vehicle_t::enter_tile(grund_t* gr)
1090 {
1091 vehicle_base_t::enter_tile(gr);
1092
1093 if(leading && minimap_t::is_visible ) {
1094 minimap_t::get_instance()->calc_map_pixel( get_pos().get_2d() );
1095 }
1096 }
1097
1098
hop(grund_t * gr)1099 void vehicle_t::hop(grund_t* gr)
1100 {
1101 leave_tile();
1102
1103 koord3d pos_prev = get_pos();
1104 set_pos( pos_next ); // next field
1105 if(route_index<cnv->get_route()->get_count()-1) {
1106 route_index ++;
1107 pos_next = cnv->get_route()->at(route_index);
1108 }
1109 else {
1110 route_index ++;
1111 check_for_finish = true;
1112 }
1113 previous_direction = direction;
1114
1115 // check if arrived at waypoint, and update schedule to next destination
1116 // route search through the waypoint is already complete
1117 if( get_pos()==cnv->get_schedule_target() ) {
1118 if( route_index >= cnv->get_route()->get_count() ) {
1119 // we end up here after loading a game or when a waypoint is reached which crosses next itself
1120 cnv->set_schedule_target( koord3d::invalid );
1121 }
1122 else {
1123 cnv->get_schedule()->advance();
1124 const koord3d ziel = cnv->get_schedule()->get_current_entry().pos;
1125 cnv->set_schedule_target( cnv->is_waypoint(ziel) ? ziel : koord3d::invalid );
1126 }
1127 }
1128
1129 // this is a required hack for aircrafts! Aircrafts can turn on a single square, and this confuses the previous calculation!
1130 // author: hsiegeln
1131 if(!check_for_finish && pos_prev==pos_next) {
1132 direction = calc_set_direction( get_pos(), pos_next);
1133 steps_next = 0;
1134 }
1135 else {
1136 if( pos_next!=get_pos() ) {
1137 direction = calc_set_direction( pos_prev, pos_next );
1138 }
1139 else if( ( check_for_finish && welt->lookup(pos_next) && ribi_t::is_straight(welt->lookup(pos_next)->get_weg_ribi_unmasked(get_waytype())) ) || welt->lookup(pos_next)->is_halt()) {
1140 // allow diagonal stops at waypoints on diagonal tracks but avoid them on halts and at straight tracks...
1141 direction = calc_set_direction( pos_prev, pos_next );
1142 }
1143 }
1144
1145 // change image if direction changes
1146 if (previous_direction != direction) {
1147 calc_image();
1148 }
1149 sint32 old_speed_limit = speed_limit;
1150
1151 enter_tile(gr);
1152 const weg_t *weg = gr->get_weg(get_waytype());
1153 if( weg ) {
1154 speed_limit = kmh_to_speed( weg->get_max_speed() );
1155 if( weg->is_crossing() ) {
1156 gr->find<crossing_t>(2)->add_to_crossing(this);
1157 }
1158 }
1159 else {
1160 speed_limit = SPEED_UNLIMITED;
1161 }
1162
1163 if( leading ) {
1164 if( check_for_finish && (direction==ribi_t::north || direction==ribi_t::west) ) {
1165 steps_next = (steps_next/2)+1;
1166 }
1167 cnv->add_running_cost( weg );
1168 cnv->must_recalc_data_front();
1169 }
1170
1171 // update friction and friction weight of convoy
1172 sint16 old_friction = current_friction;
1173 calc_friction(gr);
1174
1175 if (old_friction != current_friction) {
1176 cnv->update_friction_weight( (current_friction-old_friction) * (sint64)sum_weight);
1177 }
1178
1179 // if speed limit changed, then cnv must recalc
1180 if (speed_limit != old_speed_limit) {
1181 if (speed_limit < old_speed_limit) {
1182 if (speed_limit < cnv->get_speed_limit()) {
1183 // update
1184 cnv->set_speed_limit(speed_limit);
1185 }
1186 }
1187 else {
1188 if (old_speed_limit == cnv->get_speed_limit()) {
1189 // convoy's speed limit may be larger now
1190 cnv->must_recalc_speed_limit();
1191 }
1192 }
1193 }
1194 }
1195
1196
1197 /* calculates the current friction coefficient based on the current track
1198 * flat, slope, curve ...
1199 * @author prissi, HJ, Dwachs
1200 */
calc_friction(const grund_t * gr)1201 void vehicle_t::calc_friction(const grund_t *gr)
1202 {
1203
1204 // assume straight flat track
1205 current_friction = 1;
1206
1207 // curve: higher friction
1208 if(previous_direction != direction) {
1209 current_friction = 8;
1210 }
1211
1212 // or a hill?
1213 const slope_t::type hang = gr->get_weg_hang();
1214 if( hang != slope_t::flat ) {
1215 const uint slope_height = (hang & 7) ? 1 : 2;
1216 if( ribi_type(hang) == direction ) {
1217 // hill up, since height offsets are negative: heavy decelerate
1218 current_friction += 15 * slope_height * slope_height;
1219 }
1220 else {
1221 // hill down: accelerate
1222 current_friction += -7 * slope_height * slope_height;
1223 }
1224 }
1225 }
1226
1227
make_smoke() const1228 void vehicle_t::make_smoke() const
1229 {
1230 // does it smoke at all?
1231 if( smoke && desc->get_smoke() ) {
1232 // Hajo: only produce smoke when heavily accelerating or steam engine
1233 if( cnv->get_akt_speed() < (sint32)((cnv->get_speed_limit() * 7u) >> 3) || desc->get_engine_type() == vehicle_desc_t::steam ) {
1234 grund_t* const gr = welt->lookup( get_pos() );
1235 if( gr ) {
1236 wolke_t* const abgas = new wolke_t( get_pos(), get_xoff() + ((dx * (sint16)((uint16)steps * OBJECT_OFFSET_STEPS)) >> 8), get_yoff() + ((dy * (sint16)((uint16)steps * OBJECT_OFFSET_STEPS)) >> 8) + get_hoff(), desc->get_smoke() );
1237 if( !gr->obj_add( abgas ) ) {
1238 abgas->set_flag( obj_t::not_on_map );
1239 delete abgas;
1240 }
1241 else {
1242 welt->sync_way_eyecandy.add( abgas );
1243 }
1244 }
1245 }
1246 }
1247 }
1248
1249
1250 /**
1251 * Payment is done per hop. It iterates all goods and calculates
1252 * the income for the last hop. This method must be called upon
1253 * every stop.
1254 * @return income total for last hop
1255 * @author Hj. Malthaner
1256 */
calc_revenue(const koord3d & start,const koord3d & end) const1257 sint64 vehicle_t::calc_revenue(const koord3d& start, const koord3d& end) const
1258 {
1259 // may happen when waiting in station
1260 if (start == end || fracht.empty()) {
1261 return 0;
1262 }
1263
1264 // cnv_kmh = lesser of min_top_speed, power limited top speed, and average way speed limits on trip, except aircraft which are not power limited and don't have speed limits
1265 sint32 cnv_kmh = cnv->get_speedbonus_kmh();
1266
1267 sint64 value = 0;
1268
1269 // cache speedbonus price
1270 const goods_desc_t* last_freight = NULL;
1271 sint64 freight_revenue = 0;
1272
1273 sint32 dist = 0;
1274 if( welt->get_settings().get_pay_for_total_distance_mode() == settings_t::TO_PREVIOUS ) {
1275 // pay distance traveled
1276 dist = koord_distance( start, end );
1277 }
1278
1279 FOR(slist_tpl<ware_t>, const& ware, fracht) {
1280 if( ware.menge==0 ) {
1281 continue;
1282 }
1283 // which distance will be paid?
1284 switch(welt->get_settings().get_pay_for_total_distance_mode()) {
1285 case settings_t::TO_TRANSFER: {
1286 // pay distance traveled to next transfer stop
1287
1288 // now only use the real gain in difference for the revenue (may as well be negative!)
1289 if (ware.get_zwischenziel().is_bound()) {
1290 const koord &zwpos = ware.get_zwischenziel()->get_basis_pos();
1291 // cast of koord_distance to sint32 is necessary otherwise the r-value would be interpreted as unsigned, leading to overflows
1292 dist = (sint32)koord_distance( zwpos, start ) - (sint32)koord_distance( end, zwpos );
1293 }
1294 else {
1295 dist = koord_distance( end, start );
1296 }
1297 break;
1298 }
1299 case settings_t::TO_DESTINATION: {
1300 // pay only the distance, we get closer to our destination
1301
1302 // now only use the real gain in difference for the revenue (may as well be negative!)
1303 const koord &zwpos = ware.get_zielpos();
1304 // cast of koord_distance to sint32 is necessary otherwise the r-value would be interpreted as unsigned, leading to overflows
1305 dist = (sint32)koord_distance( zwpos, start ) - (sint32)koord_distance( end, zwpos );
1306 break;
1307 }
1308 default: ; // no need to recompute
1309 }
1310
1311 // calculate freight revenue incl. speed-bonus
1312 if (ware.get_desc() != last_freight) {
1313 freight_revenue = ware_t::calc_revenue(ware.get_desc(), get_desc()->get_waytype(), cnv_kmh);
1314 last_freight = ware.get_desc();
1315 }
1316 const sint64 price = freight_revenue * (sint64)dist * (sint64)ware.menge;
1317
1318 // sum up new price
1319 value += price;
1320 }
1321
1322 // Hajo: Rounded value, in cents
1323 return (value+1500ll)/3000ll;
1324 }
1325
1326
get_cargo_mass() const1327 const char *vehicle_t::get_cargo_mass() const
1328 {
1329 return get_cargo_type()->get_mass();
1330 }
1331
1332
1333 /**
1334 * Calculate transported cargo total weight in KG
1335 * @author Hj. Malthaner
1336 */
get_cargo_weight() const1337 uint32 vehicle_t::get_cargo_weight() const
1338 {
1339 uint32 weight = 0;
1340
1341 FOR(slist_tpl<ware_t>, const& c, fracht) {
1342 weight += c.menge * c.get_desc()->get_weight_per_unit();
1343 }
1344 return weight;
1345 }
1346
1347
get_cargo_info(cbuffer_t & buf) const1348 void vehicle_t::get_cargo_info(cbuffer_t & buf) const
1349 {
1350 if (fracht.empty()) {
1351 buf.append(" ");
1352 buf.append(translator::translate("leer"));
1353 buf.append("\n");
1354 } else {
1355 FOR(slist_tpl<ware_t>, const& ware, fracht) {
1356 const char * name = "Error in Routing";
1357
1358 halthandle_t halt = ware.get_ziel();
1359 if(halt.is_bound()) {
1360 name = halt->get_name();
1361 }
1362
1363 buf.printf(" %u%s %s > %s\n", ware.menge, translator::translate(ware.get_mass()), translator::translate(ware.get_name()), name);
1364 }
1365 }
1366 }
1367
1368
1369 /**
1370 * Delete all vehicle load
1371 * @author Hj. Malthaner
1372 */
discard_cargo()1373 void vehicle_t::discard_cargo()
1374 {
1375 FOR( slist_tpl<ware_t>, w, fracht ) {
1376 fabrik_t::update_transit( &w, false );
1377 }
1378 fracht.clear();
1379 sum_weight = desc->get_weight();
1380 }
1381
1382
calc_image()1383 void vehicle_t::calc_image()
1384 {
1385 image_id old_image=get_image();
1386 if (fracht.empty()) {
1387 set_image(desc->get_image_id(ribi_t::get_dir(get_direction()),NULL));
1388 }
1389 else {
1390 set_image(desc->get_image_id(ribi_t::get_dir(get_direction()), fracht.front().get_desc()));
1391 }
1392 if(old_image!=get_image()) {
1393 set_flag(obj_t::dirty);
1394 }
1395 }
1396
1397
get_loaded_image() const1398 image_id vehicle_t::get_loaded_image() const
1399 {
1400 return desc->get_image_id(ribi_t::dir_south, fracht.empty() ? NULL : fracht.front().get_desc());
1401 }
1402
1403
1404 // true, if this vehicle did not moved for some time
is_stuck()1405 bool vehicle_t::is_stuck()
1406 {
1407 return cnv==NULL || cnv->is_waiting();
1408 }
1409
1410
rdwr(loadsave_t * file)1411 void vehicle_t::rdwr(loadsave_t *file)
1412 {
1413 // this is only called from objlist => we save nothing ...
1414 assert( file->is_saving() ); (void)file;
1415 }
1416
1417
rdwr_from_convoi(loadsave_t * file)1418 void vehicle_t::rdwr_from_convoi(loadsave_t *file)
1419 {
1420 xml_tag_t r( file, "vehikel_t" );
1421
1422 sint32 fracht_count = 0;
1423
1424 if(file->is_saving()) {
1425 fracht_count = fracht.get_count();
1426 // we try to have one freight count to guess the right freight
1427 // when no desc is given
1428 if(fracht_count==0 && desc->get_freight_type()!=goods_manager_t::none && desc->get_capacity()>0) {
1429 fracht_count = 1;
1430 }
1431 }
1432
1433 obj_t::rdwr(file);
1434
1435 // since obj_t does no longer save positions
1436 if( file->is_version_atleast(101, 0) ) {
1437 koord3d pos = get_pos();
1438 pos.rdwr(file);
1439 set_pos(pos);
1440 }
1441
1442
1443 sint8 hoff = file->is_saving() ? get_hoff() : 0;
1444
1445 if(file->is_version_less(86, 6)) {
1446 sint32 l;
1447 file->rdwr_long(purchase_time);
1448 file->rdwr_long(l);
1449 dx = (sint8)l;
1450 file->rdwr_long(l);
1451 dy = (sint8)l;
1452 file->rdwr_long(l);
1453 hoff = (sint8)(l*TILE_HEIGHT_STEP/16);
1454 file->rdwr_long(speed_limit);
1455 file->rdwr_enum(direction);
1456 file->rdwr_enum(previous_direction);
1457 file->rdwr_long(fracht_count);
1458 file->rdwr_long(l);
1459 route_index = (uint16)l;
1460 purchase_time = (purchase_time >> welt->ticks_per_world_month_shift) + welt->get_settings().get_starting_year();
1461 DBG_MESSAGE("vehicle_t::rdwr_from_convoi()","bought at %i/%i.",(purchase_time%12)+1,purchase_time/12);
1462 }
1463 else {
1464 // prissi: changed several data types to save runtime memory
1465 file->rdwr_long(purchase_time);
1466 if(file->is_version_less(99, 18)) {
1467 file->rdwr_byte(dx);
1468 file->rdwr_byte(dy);
1469 }
1470 else {
1471 file->rdwr_byte(steps);
1472 file->rdwr_byte(steps_next);
1473 if(steps_next==old_diagonal_vehicle_steps_per_tile - 1 && file->is_loading()) {
1474 // reset diagonal length (convoi will be reset anyway, if game diagonal is different)
1475 steps_next = diagonal_vehicle_steps_per_tile - 1;
1476 }
1477 }
1478 sint16 dummy16 = ((16*(sint16)hoff)/TILE_HEIGHT_STEP);
1479 file->rdwr_short(dummy16);
1480 hoff = (sint8)((TILE_HEIGHT_STEP*(sint16)dummy16)/16);
1481 file->rdwr_long(speed_limit);
1482 file->rdwr_enum(direction);
1483 file->rdwr_enum(previous_direction);
1484 file->rdwr_long(fracht_count);
1485 file->rdwr_short(route_index);
1486 // restore dxdy information
1487 dx = dxdy[ ribi_t::get_dir(direction)*2];
1488 dy = dxdy[ ribi_t::get_dir(direction)*2+1];
1489 }
1490
1491 // convert steps to position
1492 if(file->is_version_less(99, 18)) {
1493 sint8 ddx=get_xoff(), ddy=get_yoff()-hoff;
1494 sint8 i=1;
1495 dx = dxdy[ ribi_t::get_dir(direction)*2];
1496 dy = dxdy[ ribi_t::get_dir(direction)*2+1];
1497
1498 while( !is_about_to_hop(ddx+dx*i,ddy+dy*i ) && i<16 ) {
1499 i++;
1500 }
1501 i--;
1502 set_xoff( ddx-(16-i)*dx );
1503 set_yoff( ddy-(16-i)*dy );
1504 if(file->is_loading()) {
1505 if(dx && dy) {
1506 steps = min( VEHICLE_STEPS_PER_TILE - 1, VEHICLE_STEPS_PER_TILE - 1-(i*16) );
1507 steps_next = VEHICLE_STEPS_PER_TILE - 1;
1508 }
1509 else {
1510 // will be corrected anyway, if in a convoi
1511 steps = min( diagonal_vehicle_steps_per_tile - 1, diagonal_vehicle_steps_per_tile - 1-(uint8)(((uint16)i*(uint16)(diagonal_vehicle_steps_per_tile - 1))/8) );
1512 steps_next = diagonal_vehicle_steps_per_tile - 1;
1513 }
1514 }
1515 }
1516
1517 // information about the target halt
1518 if(file->is_version_atleast(88, 7)) {
1519 bool target_info;
1520 if(file->is_loading()) {
1521 file->rdwr_bool(target_info);
1522 cnv = (convoi_t *)target_info; // will be checked during convoi reassignment
1523 }
1524 else {
1525 target_info = target_halt.is_bound();
1526 file->rdwr_bool(target_info);
1527 }
1528 }
1529 else {
1530 if(file->is_loading()) {
1531 cnv = NULL; // no reservation too
1532 }
1533 }
1534 if(file->is_version_less(112, 9)) {
1535 koord3d pos_prev(koord3d::invalid);
1536 pos_prev.rdwr(file);
1537 }
1538
1539 if(file->is_version_less(99, 5)) {
1540 koord3d dummy;
1541 dummy.rdwr(file); // current pos (is already saved as ding => ignore)
1542 }
1543 pos_next.rdwr(file);
1544
1545 if(file->is_saving()) {
1546 const char *s = desc->get_name();
1547 file->rdwr_str(s);
1548 }
1549 else {
1550 char s[256];
1551 file->rdwr_str(s, lengthof(s));
1552 desc = vehicle_builder_t::get_info(s);
1553 if(desc==NULL) {
1554 desc = vehicle_builder_t::get_info(translator::compatibility_name(s));
1555 }
1556 if(desc==NULL) {
1557 welt->add_missing_paks( s, karte_t::MISSING_VEHICLE );
1558 dbg->warning("vehicle_t::rdwr_from_convoi()","no vehicle pak for '%s' search for something similar", s);
1559 }
1560 }
1561
1562 if(file->is_saving()) {
1563 if (fracht.empty() && fracht_count>0) {
1564 // create dummy freight for savegame compatibility
1565 ware_t ware( desc->get_freight_type() );
1566 ware.menge = 0;
1567 ware.set_ziel( halthandle_t() );
1568 ware.set_zwischenziel( halthandle_t() );
1569 ware.set_zielpos( get_pos().get_2d() );
1570 ware.rdwr(file);
1571 }
1572 else {
1573 FOR(slist_tpl<ware_t>, ware, fracht) {
1574 ware.rdwr(file);
1575 }
1576 }
1577 }
1578 else {
1579 for(int i=0; i<fracht_count; i++) {
1580 ware_t ware(file);
1581 if( (desc==NULL || ware.menge>0) && welt->is_within_limits(ware.get_zielpos()) && ware.get_desc() ) {
1582 // also add, of the desc is unknown to find matching replacement
1583 fracht.insert(ware);
1584 #ifdef CACHE_TRANSIT
1585 if( file->is_version_less(112, 1) )
1586 #endif
1587 // restore in-transit information
1588 fabrik_t::update_transit( &ware, true );
1589 }
1590 else if( ware.menge>0 ) {
1591 if( ware.get_desc() ) {
1592 dbg->error( "vehicle_t::rdwr_from_convoi()", "%i of %s to %s ignored!", ware.menge, ware.get_name(), ware.get_zielpos().get_str() );
1593 }
1594 else {
1595 dbg->error( "vehicle_t::rdwr_from_convoi()", "%i of unknown to %s ignored!", ware.menge, ware.get_zielpos().get_str() );
1596 }
1597 }
1598 }
1599 }
1600
1601 // skip first last info (the convoi will know this better than we!)
1602 if(file->is_version_less(88, 7)) {
1603 bool dummy = 0;
1604 file->rdwr_bool(dummy);
1605 file->rdwr_bool(dummy);
1606 }
1607
1608 // koordinate of the last stop
1609 if(file->is_version_atleast(99, 15)) {
1610 // This used to be 2d, now it's 3d.
1611 if(file->is_version_less(112, 8)) {
1612 if(file->is_saving()) {
1613 koord last_stop_pos_2d = last_stop_pos.get_2d();
1614 last_stop_pos_2d.rdwr(file);
1615 }
1616 else {
1617 // loading. Assume ground level stop (could be wrong, but how would we know?)
1618 koord last_stop_pos_2d = koord::invalid;
1619 last_stop_pos_2d.rdwr(file);
1620 const grund_t* gr = welt->lookup_kartenboden(last_stop_pos_2d);
1621 if (gr) {
1622 last_stop_pos = koord3d(last_stop_pos_2d, gr->get_hoehe());
1623 }
1624 else {
1625 // no ground?!?
1626 last_stop_pos = koord3d::invalid;
1627 }
1628 }
1629 }
1630 else {
1631 // current version, 3d
1632 last_stop_pos.rdwr(file);
1633 }
1634 }
1635
1636 if(file->is_loading()) {
1637 leading = last = false; // dummy, will be set by convoi afterwards
1638 if(desc) {
1639 calc_image();
1640
1641 // full weight after loading
1642 sum_weight = get_cargo_weight() + desc->get_weight();
1643 }
1644 // recalc total freight
1645 total_freight = 0;
1646 FOR(slist_tpl<ware_t>, const& c, fracht) {
1647 total_freight += c.menge;
1648 }
1649 }
1650
1651 if( file->is_version_atleast(110, 0) ) {
1652 bool hd = has_driven;
1653 file->rdwr_bool( hd );
1654 has_driven = hd;
1655 }
1656 else {
1657 if (file->is_loading()) {
1658 has_driven = false;
1659 }
1660 }
1661 }
1662
1663
calc_sale_value() const1664 uint32 vehicle_t::calc_sale_value() const
1665 {
1666 // if already used, there is a general price reduction
1667 double value = (double)desc->get_price();
1668 if( has_driven ) {
1669 value *= (1000 - welt->get_settings().get_used_vehicle_reduction()) / 1000.0;
1670 }
1671 // after 20 year, it has only half value
1672 return (uint32)( value * pow(0.997, (int)(welt->get_current_month() - get_purchase_time())));
1673 }
1674
1675
show_info()1676 void vehicle_t::show_info()
1677 {
1678 if( cnv != NULL ) {
1679 cnv->open_info_window();
1680 } else {
1681 dbg->warning("vehicle_t::show_info()","cnv is null, can't open convoi window!");
1682 }
1683 }
1684
1685
info(cbuffer_t & buf) const1686 void vehicle_t::info(cbuffer_t & buf) const
1687 {
1688 if(cnv) {
1689 cnv->info(buf);
1690 }
1691 }
1692
1693
is_deletable(const player_t *)1694 const char *vehicle_t::is_deletable(const player_t *)
1695 {
1696 return "Fahrzeuge koennen so nicht entfernt werden";
1697 }
1698
1699
~vehicle_t()1700 vehicle_t::~vehicle_t()
1701 {
1702 // remove vehicle's marker from the minimap
1703 minimap_t::get_instance()->calc_map_pixel(get_pos().get_2d());
1704 }
1705
1706
1707 #ifdef MULTI_THREAD
display_overlay(int xpos,int ypos) const1708 void vehicle_t::display_overlay(int xpos, int ypos) const
1709 {
1710 if( cnv && leading ) {
1711 #else
1712 void vehicle_t::display_after(int xpos, int ypos, bool is_global) const
1713 {
1714 if( is_global && cnv && leading ) {
1715 #endif
1716 PIXVAL color = 0; // not used, but stop compiler warning about uninitialized
1717 char tooltip_text[1024];
1718 tooltip_text[0] = 0;
1719 uint8 state = env_t::show_vehicle_states;
1720 if( state==1 ) {
1721 // only show when mouse over vehicle
1722 if( welt->get_zeiger()->get_pos()==get_pos() ) {
1723 state = 2;
1724 }
1725 else {
1726 state = 0;
1727 }
1728 }
1729
1730 // now find out what has happened
1731 switch(cnv->get_state()) {
1732 case convoi_t::WAITING_FOR_CLEARANCE_ONE_MONTH:
1733 case convoi_t::WAITING_FOR_CLEARANCE:
1734 case convoi_t::CAN_START:
1735 case convoi_t::CAN_START_ONE_MONTH:
1736 if( state>=2 ) {
1737 snprintf( tooltip_text, lengthof(tooltip_text), "%s (%s)", translator::translate("Waiting for clearance!"), cnv->get_schedule()->get_current_entry().pos.get_str() );
1738 color = color_idx_to_rgb(COL_YELLOW);
1739 }
1740 break;
1741
1742 case convoi_t::LOADING:
1743 if( state>=1 ) {
1744 sprintf( tooltip_text, translator::translate("Loading (%i->%i%%)!"), cnv->get_loading_level(), cnv->get_loading_limit() );
1745 color = color_idx_to_rgb(COL_YELLOW);
1746 }
1747 break;
1748
1749 case convoi_t::EDIT_SCHEDULE:
1750 // case convoi_t::ROUTING_1:
1751 if( state>=2 ) {
1752 tstrncpy( tooltip_text, translator::translate("Schedule changing!"), lengthof(tooltip_text) );
1753 color = color_idx_to_rgb(COL_YELLOW);
1754 }
1755 break;
1756
1757 case convoi_t::DRIVING:
1758 if( state>=1 ) {
1759 grund_t const* const gr = welt->lookup(cnv->get_route()->back());
1760 if( gr && gr->get_depot() ) {
1761 tstrncpy( tooltip_text, translator::translate("go home"), lengthof(tooltip_text) );
1762 color = color_idx_to_rgb(COL_GREEN);
1763 }
1764 else if( cnv->get_no_load() ) {
1765 tstrncpy( tooltip_text, translator::translate("no load"), lengthof(tooltip_text) );
1766 color = color_idx_to_rgb(COL_GREEN);
1767 }
1768 }
1769 break;
1770
1771 case convoi_t::LEAVING_DEPOT:
1772 if( state>=2 ) {
1773 tstrncpy( tooltip_text, translator::translate("Leaving depot!"), lengthof(tooltip_text) );
1774 color = color_idx_to_rgb(COL_GREEN);
1775 }
1776 break;
1777
1778 case convoi_t::WAITING_FOR_CLEARANCE_TWO_MONTHS:
1779 case convoi_t::CAN_START_TWO_MONTHS:
1780 snprintf( tooltip_text, lengthof(tooltip_text), "%s (%s)", translator::translate("clf_chk_stucked"), cnv->get_schedule()->get_current_entry().pos.get_str() );
1781 color = color_idx_to_rgb(COL_ORANGE);
1782 break;
1783
1784 case convoi_t::NO_ROUTE:
1785 tstrncpy( tooltip_text, translator::translate("clf_chk_noroute"), lengthof(tooltip_text) );
1786 color = color_idx_to_rgb(COL_RED);
1787 break;
1788 }
1789
1790 // something to show?
1791 if( tooltip_text[0] ) {
1792 const int width = proportional_string_width(tooltip_text)+7;
1793 const int raster_width = get_current_tile_raster_width();
1794 get_screen_offset( xpos, ypos, raster_width );
1795 xpos += tile_raster_scale_x(get_xoff(), raster_width);
1796 ypos += tile_raster_scale_y(get_yoff(), raster_width)+14;
1797 if(ypos>LINESPACE+32 && ypos+LINESPACE<display_get_clip_wh().yy) {
1798 display_ddd_proportional_clip( xpos, ypos, width, 0, color, color_idx_to_rgb(COL_BLACK), tooltip_text, true );
1799 }
1800 }
1801 }
1802 }
1803
1804
1805
1806 road_vehicle_t::road_vehicle_t(koord3d pos, const vehicle_desc_t* desc, player_t* player, convoi_t* cn) :
1807 vehicle_t(pos, desc, player)
1808 {
1809 cnv = cn;
1810 }
1811
1812
1813 road_vehicle_t::road_vehicle_t(loadsave_t *file, bool is_first, bool is_last) : vehicle_t()
1814 {
1815 rdwr_from_convoi(file);
1816
1817 if( file->is_loading() ) {
1818 static const vehicle_desc_t *last_desc = NULL;
1819
1820 if(is_first) {
1821 last_desc = NULL;
1822 }
1823 // try to find a matching vehicle
1824 if(desc==NULL) {
1825 const goods_desc_t* w = (!fracht.empty() ? fracht.front().get_desc() : goods_manager_t::passengers);
1826 dbg->warning("road_vehicle_t::road_vehicle_t()","try to find a fitting vehicle for %s.", w->get_name() );
1827 desc = vehicle_builder_t::get_best_matching(road_wt, 0, (fracht.empty() ? 0 : 50), is_first?50:0, speed_to_kmh(speed_limit), w, true, last_desc, is_last );
1828 if(desc) {
1829 DBG_MESSAGE("road_vehicle_t::road_vehicle_t()","replaced by %s",desc->get_name());
1830 // still wrong load ...
1831 calc_image();
1832 }
1833 if(!fracht.empty() && fracht.front().menge == 0) {
1834 // this was only there to find a matching vehicle
1835 fracht.remove_first();
1836 }
1837 }
1838 if( desc ) {
1839 last_desc = desc;
1840 }
1841 calc_disp_lane();
1842 }
1843 }
1844
1845
1846 void road_vehicle_t::rotate90()
1847 {
1848 vehicle_t::rotate90();
1849 calc_disp_lane();
1850 }
1851
1852
1853 void road_vehicle_t::calc_disp_lane()
1854 {
1855 // driving in the back or the front
1856 ribi_t::ribi test_dir = welt->get_settings().is_drive_left() ? ribi_t::northeast : ribi_t::southwest;
1857 disp_lane = get_direction() & test_dir ? 1 : 3;
1858 }
1859
1860 // need to reset halt reservation (if there was one)
1861 bool road_vehicle_t::calc_route(koord3d start, koord3d ziel, sint32 max_speed, route_t* route)
1862 {
1863 assert(cnv);
1864 // free target reservation
1865 if(leading && previous_direction!=ribi_t::none && cnv && target_halt.is_bound() ) {
1866 // now reserve our choice (beware: might be longer than one tile!)
1867 for( uint32 length=0; length<cnv->get_tile_length() && length+1<cnv->get_route()->get_count(); length++ ) {
1868 target_halt->unreserve_position( welt->lookup( cnv->get_route()->at( cnv->get_route()->get_count()-length-1) ), cnv->self );
1869 }
1870 }
1871 target_halt = halthandle_t(); // no block reserved
1872 route_t::route_result_t r = route->calc_route(welt, start, ziel, this, max_speed, cnv->get_tile_length() );
1873 if( r == route_t::valid_route_halt_too_short ) {
1874 cbuffer_t buf;
1875 buf.printf( translator::translate("Vehicle %s cannot choose because stop too short!"), cnv->get_name());
1876 welt->get_message()->add_message( (const char *)buf, ziel.get_2d(), message_t::traffic_jams, PLAYER_FLAG | cnv->get_owner()->get_player_nr(), cnv->front()->get_base_image() );
1877 }
1878 return r;
1879 }
1880
1881
1882 bool road_vehicle_t::check_next_tile(const grund_t *bd) const
1883 {
1884 strasse_t *str=(strasse_t *)bd->get_weg(road_wt);
1885 if(str==NULL || str->get_max_speed()==0) {
1886 return false;
1887 }
1888 bool electric = cnv!=NULL ? cnv->needs_electrification() : desc->get_engine_type()==vehicle_desc_t::electric;
1889 if(electric && !str->is_electrified()) {
1890 return false;
1891 }
1892 // check for signs
1893 if(str->has_sign()) {
1894 const roadsign_t* rs = bd->find<roadsign_t>();
1895 if(rs!=NULL) {
1896 if( rs->get_desc()->get_min_speed()>0 && rs->get_desc()->get_min_speed()>kmh_to_speed(get_desc()->get_topspeed()) ) {
1897 return false;
1898 }
1899 if( rs->get_desc()->is_private_way() && (rs->get_player_mask() & (1<<get_player_nr()) ) == 0 ) {
1900 // private road
1901 return false;
1902 }
1903 // do not search further for a free stop beyond here
1904 if(target_halt.is_bound() && cnv->is_waiting() && rs->get_desc()->get_flags()&roadsign_desc_t::END_OF_CHOOSE_AREA) {
1905 return false;
1906 }
1907 }
1908 }
1909 return true;
1910 }
1911
1912
1913 // how expensive to go here (for way search)
1914 // author prissi
1915 int road_vehicle_t::get_cost(const grund_t *gr, const weg_t *w, const sint32 max_speed, ribi_t::ribi from) const
1916 {
1917 // first favor faster ways
1918 if(!w) {
1919 return 0xFFFF;
1920 }
1921
1922 // max_speed?
1923 sint32 max_tile_speed = w->get_max_speed();
1924
1925 // add cost for going (with maximum speed, cost is 1)
1926 int costs = (max_speed<=max_tile_speed) ? 1 : 4-(3*max_tile_speed)/max_speed;
1927
1928 // assume all traffic is not good ... (otherwise even smoke counts ... )
1929 costs += (w->get_statistics(WAY_STAT_CONVOIS) > ( 2 << (welt->get_settings().get_bits_per_month()-16) ) );
1930
1931 // effect of slope
1932 if( gr->get_weg_hang()!=0 ) {
1933 // Knightly : check if the slope is upwards, relative to the previous tile
1934 // 15 hardcoded, see get_cost_upslope()
1935 costs += 15 * get_sloping_upwards( gr->get_weg_hang(), from );
1936 }
1937 return costs;
1938 }
1939
1940
1941 // this routine is called by find_route, to determined if we reached a destination
1942 bool road_vehicle_t::is_target(const grund_t *gr, const grund_t *prev_gr) const
1943 {
1944 // just check, if we reached a free stop position of this halt
1945 if(gr->is_halt() && gr->get_halt()==target_halt && target_halt->is_reservable(gr,cnv->self)) {
1946 // now we must check the predecessor => try to advance as much as possible
1947 if(prev_gr!=NULL) {
1948 const koord dir=gr->get_pos().get_2d()-prev_gr->get_pos().get_2d();
1949 ribi_t::ribi ribi = ribi_type(dir);
1950 if( gr->get_weg(get_waytype())->get_ribi_maske() & ribi ) {
1951 // one way sign wrong direction
1952 return false;
1953 }
1954 grund_t *to;
1955 if( !gr->get_neighbour(to,road_wt,ribi) || !(to->get_halt()==target_halt) || (gr->get_weg(get_waytype())->get_ribi_maske() & ribi_type(dir))!=0 || !target_halt->is_reservable(to,cnv->self) ) {
1956 // end of stop: Is it long enough?
1957 uint16 tiles = cnv->get_tile_length();
1958 while( tiles>1 ) {
1959 if( !gr->get_neighbour(to,get_waytype(),ribi_t::backward(ribi)) || !(to->get_halt()==target_halt) || !target_halt->is_reservable(to,cnv->self) ) {
1960 return false;
1961 }
1962 gr = to;
1963 tiles --;
1964 }
1965 return true;
1966 }
1967 // can advance more
1968 return false;
1969 }
1970 //DBG_MESSAGE("is_target()","success at %i,%i",gr->get_pos().x,gr->get_pos().y);
1971 // return true;
1972 }
1973 return false;
1974 }
1975
1976
1977 // to make smaller steps than the tile granularity, we have to use this trick
1978 void road_vehicle_t::get_screen_offset( int &xoff, int &yoff, const sint16 raster_width ) const
1979 {
1980 vehicle_base_t::get_screen_offset( xoff, yoff, raster_width );
1981
1982 if( welt->get_settings().is_drive_left() ) {
1983 const int drive_left_dir = ribi_t::get_dir(get_direction());
1984 xoff += tile_raster_scale_x( driveleft_base_offsets[drive_left_dir][0], raster_width );
1985 yoff += tile_raster_scale_y( driveleft_base_offsets[drive_left_dir][1], raster_width );
1986 }
1987
1988 // eventually shift position to take care of overtaking
1989 if(cnv) {
1990 if( cnv->is_overtaking() ) {
1991 xoff += tile_raster_scale_x(overtaking_base_offsets[ribi_t::get_dir(get_direction())][0], raster_width);
1992 yoff += tile_raster_scale_x(overtaking_base_offsets[ribi_t::get_dir(get_direction())][1], raster_width);
1993 }
1994 else if( cnv->is_overtaken() ) {
1995 xoff -= tile_raster_scale_x(overtaking_base_offsets[ribi_t::get_dir(get_direction())][0], raster_width)/5;
1996 yoff -= tile_raster_scale_x(overtaking_base_offsets[ribi_t::get_dir(get_direction())][1], raster_width)/5;
1997 }
1998 }
1999 }
2000
2001
2002 // chooses a route at a choose sign; returns true on success
2003 bool road_vehicle_t::choose_route(sint32 &restart_speed, ribi_t::ribi start_direction, uint16 index)
2004 {
2005 if( cnv->get_schedule_target()!=koord3d::invalid ) {
2006 // destination is a waypoint!
2007 return true;
2008 }
2009
2010 // are we heading to a target?
2011 route_t *rt = cnv->access_route();
2012 target_halt = haltestelle_t::get_halt( rt->back(), get_owner() );
2013 if( target_halt.is_bound() ) {
2014
2015 // since convois can long than one tile, check is more difficult
2016 bool can_go_there = true;
2017 bool original_route = (rt->back() == cnv->get_schedule()->get_current_entry().pos);
2018 for( uint32 length=0; can_go_there && length<cnv->get_tile_length() && length+1<rt->get_count(); length++ ) {
2019 if( grund_t *gr = welt->lookup( rt->at( rt->get_count()-length-1) ) ) {
2020 if (gr->get_halt().is_bound()) {
2021 can_go_there &= target_halt->is_reservable( gr, cnv->self );
2022 }
2023 else {
2024 // if this is the original stop, it is too short!
2025 can_go_there |= original_route;
2026 }
2027 }
2028 }
2029 if( can_go_there ) {
2030 // then reserve it ...
2031 for( uint32 length=0; length<cnv->get_tile_length() && length+1<rt->get_count(); length++ ) {
2032 target_halt->reserve_position( welt->lookup( rt->at( rt->get_count()-length-1) ), cnv->self );
2033 }
2034 }
2035 else {
2036 // cannot go there => need slot search
2037
2038 // if we fail, we will wait in a step, much more simulation friendly
2039 if(!cnv->is_waiting()) {
2040 restart_speed = -1;
2041 target_halt = halthandle_t();
2042 return false;
2043 }
2044
2045 // check if there is a free position
2046 // this is much faster than waysearch
2047 if( !target_halt->find_free_position(road_wt,cnv->self,obj_t::road_vehicle )) {
2048 restart_speed = 0;
2049 target_halt = halthandle_t();
2050 return false;
2051 }
2052
2053 // now it make sense to search a route
2054 route_t target_rt;
2055 koord3d next3d = rt->at(index);
2056 if( !target_rt.find_route( welt, next3d, this, speed_to_kmh(cnv->get_min_top_speed()), start_direction, welt->get_settings().get_max_choose_route_steps() ) ) {
2057 // nothing empty or not route with less than 33 tiles
2058 target_halt = halthandle_t();
2059 restart_speed = 0;
2060 return false;
2061 }
2062
2063 // now reserve our choice (beware: might be longer than one tile!)
2064 for( uint32 length=0; length<cnv->get_tile_length() && length+1<target_rt.get_count(); length++ ) {
2065 target_halt->reserve_position( welt->lookup( target_rt.at( target_rt.get_count()-length-1) ), cnv->self );
2066 }
2067 rt->remove_koord_from( index );
2068 rt->append( &target_rt );
2069 }
2070 }
2071 return true;
2072 }
2073
2074
2075 bool road_vehicle_t::can_enter_tile(const grund_t *gr, sint32 &restart_speed, uint8 second_check_count)
2076 {
2077 // check for traffic lights (only relevant for the first car in a convoi)
2078 if( leading ) {
2079 // no further check, when already entered a crossing (to allow leaving it)
2080 if( !second_check_count ) {
2081 const grund_t *gr_current = welt->lookup(get_pos());
2082 if( gr_current && gr_current->ist_uebergang() ) {
2083 return true;
2084 }
2085 }
2086
2087 assert(gr);
2088
2089 const strasse_t *str = (strasse_t *)gr->get_weg(road_wt);
2090 if( !str || gr->get_top() > 250 ) {
2091 // too many cars here or no street
2092 return false;
2093 }
2094
2095 // first: check roadsigns
2096 const roadsign_t *rs = NULL;
2097 if( str->has_sign() ) {
2098 rs = gr->find<roadsign_t>();
2099 route_t const& r = *cnv->get_route();
2100
2101 if( rs && (route_index + 1u < r.get_count()) ) {
2102 // since at the corner, our direction may be diagonal, we make it straight
2103 uint8 direction90 = ribi_type(get_pos(), pos_next);
2104
2105 if( rs->get_desc()->is_traffic_light() && (rs->get_dir()&direction90) == 0 ) {
2106 // wait here
2107 restart_speed = 16;
2108 return false;
2109 }
2110 // check, if we reached a choose point
2111 else {
2112 // route position after road sign
2113 const koord pos_next_next = r.at(route_index + 1u).get_2d();
2114 // since at the corner, our direction may be diagonal, we make it straight
2115 direction90 = ribi_type( pos_next, pos_next_next );
2116
2117 if( rs->is_free_route(direction90) && !target_halt.is_bound() ) {
2118 if( second_check_count ) {
2119 return false;
2120 }
2121 if( !choose_route( restart_speed, direction90, route_index ) ) {
2122 return false;
2123 }
2124 }
2125 }
2126 }
2127 }
2128
2129 vehicle_base_t *obj = NULL;
2130 uint32 test_index = route_index + 1u;
2131
2132 // way should be clear for overtaking: we checked previously
2133 if( !cnv->is_overtaking() ) {
2134 // calculate new direction
2135 route_t const& r = *cnv->get_route();
2136 koord3d next = route_index < r.get_count() - 1u ? r.at(route_index + 1u) : pos_next;
2137 ribi_t::ribi curr_direction = get_direction();
2138 ribi_t::ribi curr_90direction = calc_direction(get_pos(), pos_next);
2139 ribi_t::ribi next_direction = calc_direction(get_pos(), next);
2140 ribi_t::ribi next_90direction = calc_direction(pos_next, next);
2141 obj = no_cars_blocking( gr, cnv, curr_direction, next_direction, next_90direction );
2142
2143 // do not block intersections
2144 const bool drives_on_left = welt->get_settings().is_drive_left();
2145 bool int_block = ribi_t::is_threeway(str->get_ribi_unmasked()) && (((drives_on_left ? ribi_t::rotate90l(curr_90direction) : ribi_t::rotate90(curr_90direction)) & str->get_ribi_unmasked()) || curr_90direction != next_90direction || (rs && rs->get_desc()->is_traffic_light()));
2146
2147 // check exit from crossings and intersections, allow to proceed after 4 consecutive
2148 while( !obj && (str->is_crossing() || int_block) && test_index < r.get_count() && test_index < route_index + 4u ) {
2149 if( str->is_crossing() ) {
2150 crossing_t* cr = gr->find<crossing_t>(2);
2151 if( !cr->request_crossing(this) ) {
2152 restart_speed = 0;
2153 return false;
2154 }
2155 }
2156
2157 // test next position
2158 gr = welt->lookup(r.at(test_index));
2159 if( !gr ) {
2160 // way (weg) not existent (likely destroyed)
2161 if( !second_check_count ) {
2162 cnv->suche_neue_route();
2163 }
2164 return false;
2165 }
2166
2167 str = (strasse_t *)gr->get_weg(road_wt);
2168 if( !str || gr->get_top() > 250 ) {
2169 // too many cars here or no street
2170 if( !second_check_count && !str) {
2171 cnv->suche_neue_route();
2172 }
2173 return false;
2174 }
2175
2176 // check cars
2177 curr_direction = next_direction;
2178 curr_90direction = next_90direction;
2179 if( test_index + 1u < r.get_count() ) {
2180 next = r.at(test_index + 1u);
2181 next_direction = calc_direction(r.at(test_index - 1u), next);
2182 next_90direction = calc_direction(r.at(test_index), next);
2183 obj = no_cars_blocking( gr, cnv, curr_direction, next_direction, next_90direction );
2184 }
2185 else {
2186 next = r.at(test_index);
2187 next_90direction = calc_direction(r.at(test_index - 1u), next);
2188 if( curr_direction == next_90direction || !gr->is_halt() ) {
2189 // check cars but allow to enter intersection if we are turning even when a car is blocking the halt on the last tile of our route
2190 // preserves old bus terminal behaviour
2191 obj = no_cars_blocking( gr, cnv, curr_direction, next_90direction, ribi_t::none );
2192 }
2193 }
2194
2195 // check roadsigns
2196 if( str->has_sign() ) {
2197 rs = gr->find<roadsign_t>();
2198 if( rs ) {
2199 // since at the corner, our direction may be diagonal, we make it straight
2200 if( rs->get_desc()->is_traffic_light() && (rs->get_dir() & curr_90direction)==0 ) {
2201 // wait here
2202 restart_speed = 16;
2203 return false;
2204 }
2205 // check, if we reached a choose point
2206
2207 else if( rs->is_free_route(curr_90direction) && !target_halt.is_bound() ) {
2208 if( second_check_count ) {
2209 return false;
2210 }
2211 if( !choose_route( restart_speed, curr_90direction, test_index ) ) {
2212 return false;
2213 }
2214 }
2215 }
2216 }
2217 else {
2218 rs = NULL;
2219 }
2220
2221 // check for blocking intersection
2222 int_block = ribi_t::is_threeway(str->get_ribi_unmasked()) && (((drives_on_left ? ribi_t::rotate90l(curr_90direction) : ribi_t::rotate90(curr_90direction)) & str->get_ribi_unmasked()) || curr_90direction != next_90direction || (rs && rs->get_desc()->is_traffic_light()));
2223
2224 test_index++;
2225 }
2226
2227 if( obj && test_index > route_index + 1u && !str->is_crossing() && !int_block ) {
2228 // found a car blocking us after checking at least 1 intersection or crossing
2229 // and the car is in a place we could stop. So if it can move, assume it will, so we will too.
2230 // but check only upto 8 cars ahead to prevent infinite recursion on roundabouts.
2231 if( second_check_count >= 8 ) {
2232 return false;
2233 }
2234 if( road_vehicle_t const* const car = obj_cast<road_vehicle_t>(obj) ) {
2235 const convoi_t* const ocnv = car->get_convoi();
2236 sint32 dummy;
2237 if( ocnv->front()->get_route_index() < ocnv->get_route()->get_count() && ocnv->front()->can_enter_tile( dummy, second_check_count + 1 ) ) {
2238 return true;
2239 }
2240 }
2241 }
2242 }
2243
2244 // stuck message ...
2245 if( obj && !second_check_count ) {
2246 if( obj->is_stuck() ) {
2247 // end of traffic jam, but no stuck message, because previous vehicle is stuck too
2248 restart_speed = 0;
2249 cnv->set_tiles_overtaking(0);
2250 cnv->reset_waiting();
2251 }
2252 else {
2253 if( test_index == route_index + 1u ) {
2254 // no intersections or crossings, we might be able to overtake this one ...
2255 overtaker_t *over = obj->get_overtaker();
2256 if( over && !over->is_overtaken() ) {
2257 if( over->is_overtaking() ) {
2258 // otherwise we would stop every time being overtaken
2259 return true;
2260 }
2261 // not overtaking/being overtake: we need to make a more thought test!
2262 if( road_vehicle_t const* const car = obj_cast<road_vehicle_t>(obj) ) {
2263 convoi_t* const ocnv = car->get_convoi();
2264 if( cnv->can_overtake( ocnv, (ocnv->get_state()==convoi_t::LOADING ? 0 : over->get_max_power_speed()), ocnv->get_length_in_steps()+ocnv->get_vehikel(0)->get_steps()) ) {
2265 return true;
2266 }
2267 }
2268 else if( private_car_t* const caut = obj_cast<private_car_t>(obj) ) {
2269 if( cnv->can_overtake(caut, caut->get_desc()->get_topspeed(), VEHICLE_STEPS_PER_TILE) ) {
2270 return true;
2271 }
2272 }
2273 }
2274 }
2275 // we have to wait ...
2276 restart_speed = (cnv->get_akt_speed()*3)/4;
2277 cnv->set_tiles_overtaking(0);
2278 }
2279 }
2280
2281 return obj==NULL;
2282 }
2283
2284 return true;
2285 }
2286
2287
2288 overtaker_t* road_vehicle_t::get_overtaker()
2289 {
2290 return cnv;
2291 }
2292
2293
2294 void road_vehicle_t::enter_tile(grund_t* gr)
2295 {
2296 vehicle_t::enter_tile(gr);
2297 calc_disp_lane();
2298
2299 const int cargo = get_total_cargo();
2300 weg_t *str = gr->get_weg(road_wt);
2301 str->book(cargo, WAY_STAT_GOODS);
2302 if (leading) {
2303 str->book(1, WAY_STAT_CONVOIS);
2304 cnv->update_tiles_overtaking();
2305 }
2306 }
2307
2308
2309 schedule_t * road_vehicle_t::generate_new_schedule() const
2310 {
2311 return new truck_schedule_t();
2312 }
2313
2314
2315 void road_vehicle_t::set_convoi(convoi_t *c)
2316 {
2317 DBG_MESSAGE("road_vehicle_t::set_convoi()","%p",c);
2318 if(c!=NULL) {
2319 bool target=(bool)cnv; // only during loadtype: cnv==1 indicates, that the convoi did reserve a stop
2320 vehicle_t::set_convoi(c);
2321 if(target && leading && c->get_route()->empty()) {
2322 // reinitialize the target halt
2323 const route_t *rt = cnv->get_route();
2324 target_halt = haltestelle_t::get_halt( rt->back(), get_owner() );
2325 if( target_halt.is_bound() ) {
2326 for( uint32 i=0; i<c->get_tile_length() && i+1<rt->get_count(); i++ ) {
2327 target_halt->reserve_position( welt->lookup( rt->at(rt->get_count()-i-1) ), cnv->self );
2328 }
2329 }
2330 }
2331 }
2332 else {
2333 if( cnv && leading && target_halt.is_bound() ) {
2334 // now reserve our choice (beware: might be longer than one tile!)
2335 for( uint32 length=0; length<cnv->get_tile_length() && length+1<cnv->get_route()->get_count(); length++ ) {
2336 target_halt->unreserve_position( welt->lookup( cnv->get_route()->at( cnv->get_route()->get_count()-length-1) ), cnv->self );
2337 }
2338 target_halt = halthandle_t();
2339 }
2340 cnv = NULL;
2341 }
2342 }
2343
2344
2345 /* from now on rail vehicles (and other vehicles using blocks) */
2346 rail_vehicle_t::rail_vehicle_t(loadsave_t *file, bool is_first, bool is_last) : vehicle_t()
2347 {
2348 vehicle_t::rdwr_from_convoi(file);
2349
2350 if( file->is_loading() ) {
2351 static const vehicle_desc_t *last_desc = NULL;
2352
2353 if(is_first) {
2354 last_desc = NULL;
2355 }
2356 // try to find a matching vehicle
2357 if(desc==NULL) {
2358 int power = (is_first || fracht.empty() || fracht.front() == goods_manager_t::none) ? 500 : 0;
2359 const goods_desc_t* w = fracht.empty() ? goods_manager_t::none : fracht.front().get_desc();
2360 dbg->warning("rail_vehicle_t::rail_vehicle_t()","try to find a fitting vehicle for %s.", power>0 ? "engine": w->get_name() );
2361 if(last_desc!=NULL && last_desc->can_follow(last_desc) && last_desc->get_freight_type()==w && (!is_last || last_desc->get_trailer(0)==NULL)) {
2362 // same as previously ...
2363 desc = last_desc;
2364 }
2365 else {
2366 // we have to search
2367 desc = vehicle_builder_t::get_best_matching(get_waytype(), 0, w!=goods_manager_t::none?5000:0, power, speed_to_kmh(speed_limit), w, false, last_desc, is_last );
2368 }
2369 if(desc) {
2370 DBG_MESSAGE("rail_vehicle_t::rail_vehicle_t()","replaced by %s",desc->get_name());
2371 calc_image();
2372 }
2373 else {
2374 dbg->error("rail_vehicle_t::rail_vehicle_t()","no matching desc found for %s!",w->get_name());
2375 }
2376 if (!fracht.empty() && fracht.front().menge == 0) {
2377 // this was only there to find a matching vehicle
2378 fracht.remove_first();
2379 }
2380 }
2381 // update last desc
2382 if( desc ) {
2383 last_desc = desc;
2384 }
2385 }
2386 }
2387
2388
2389 rail_vehicle_t::rail_vehicle_t(koord3d pos, const vehicle_desc_t* desc, player_t* player, convoi_t* cn) :
2390 vehicle_t(pos, desc, player)
2391 {
2392 cnv = cn;
2393 }
2394
2395
2396 rail_vehicle_t::~rail_vehicle_t()
2397 {
2398 if (cnv && leading) {
2399 route_t const& r = *cnv->get_route();
2400 if (!r.empty() && route_index < r.get_count()) {
2401 // free all reserved blocks
2402 uint16 dummy;
2403 block_reserver(&r, cnv->back()->get_route_index(), dummy, dummy, target_halt.is_bound() ? 100000 : 1, false, false);
2404 }
2405 }
2406 grund_t *gr = welt->lookup(get_pos());
2407 if(gr) {
2408 schiene_t * sch = (schiene_t *)gr->get_weg(get_waytype());
2409 if(sch) {
2410 sch->unreserve(this);
2411 }
2412 }
2413 }
2414
2415
2416 void rail_vehicle_t::set_convoi(convoi_t *c)
2417 {
2418 if(c!=cnv) {
2419 DBG_MESSAGE("rail_vehicle_t::set_convoi()","new=%p old=%p",c,cnv);
2420 if(leading) {
2421 if(cnv!=NULL && cnv!=(convoi_t *)1) {
2422 // free route from old convoi
2423 route_t const& r = *cnv->get_route();
2424 if( !r.empty() && route_index + 1U < r.get_count() - 1 ) {
2425 uint16 dummy;
2426 block_reserver(&r, cnv->back()->get_route_index(), dummy, dummy, 100000, false, false);
2427 target_halt = halthandle_t();
2428 }
2429 }
2430 else if( c->get_next_reservation_index()==0 ) {
2431 assert(c!=NULL);
2432 // eventually search new route
2433 route_t const& r = *c->get_route();
2434 if( (r.get_count()<=route_index || r.empty() || get_pos()==r.back()) && c->get_state()!=convoi_t::INITIAL && c->get_state()!=convoi_t::LOADING && c->get_state()!=convoi_t::SELF_DESTRUCT ) {
2435 check_for_finish = true;
2436 dbg->warning("rail_vehicle_t::set_convoi()", "convoi %i had a too high route index! (%i of max %i)", c->self.get_id(), route_index, r.get_count() - 1);
2437 }
2438 // set default next stop index
2439 c->set_next_stop_index( max(route_index,1)-1 );
2440 // need to reserve new route?
2441 if( !check_for_finish && c->get_state()!=convoi_t::SELF_DESTRUCT && (c->get_state()==convoi_t::DRIVING || c->get_state()>=convoi_t::LEAVING_DEPOT) ) {
2442 sint32 num_index = cnv==(convoi_t *)1 ? 1001 : 0; // only during loadtype: cnv==1 indicates, that the convoi did reserve a stop
2443 uint16 next_signal, next_crossing;
2444 cnv = c;
2445 if( block_reserver(&r, max(route_index,1)-1, next_signal, next_crossing, num_index, true, false) ) {
2446 c->set_next_stop_index( next_signal>next_crossing ? next_crossing : next_signal );
2447 }
2448 }
2449 }
2450 }
2451 vehicle_t::set_convoi(c);
2452 }
2453 }
2454
2455
2456 // need to reset halt reservation (if there was one)
2457 bool rail_vehicle_t::calc_route(koord3d start, koord3d ziel, sint32 max_speed, route_t* route)
2458 {
2459 if(leading && route_index<cnv->get_route()->get_count()) {
2460 // free all reserved blocks
2461 uint16 dummy;
2462 block_reserver(cnv->get_route(), cnv->back()->get_route_index(), dummy, dummy, target_halt.is_bound() ? 100000 : 1, false, true);
2463 }
2464 cnv->set_next_reservation_index( 0 ); // nothing to reserve
2465 target_halt = halthandle_t(); // no block reserved
2466 // use length 8888 tiles to advance to the end of all stations
2467 return route->calc_route(welt, start, ziel, this, max_speed, 8888 /*cnv->get_tile_length()*/ );
2468 }
2469
2470
2471 bool rail_vehicle_t::check_next_tile(const grund_t *bd) const
2472 {
2473 schiene_t const* const sch = obj_cast<schiene_t>(bd->get_weg(get_waytype()));
2474 if( !sch ) {
2475 return false;
2476 }
2477
2478 // Hajo: diesel and steam engines can use electrified track as well.
2479 // also allow driving on foreign tracks ...
2480 const bool needs_no_electric = !(cnv!=NULL ? cnv->needs_electrification() : desc->get_engine_type()==vehicle_desc_t::electric);
2481 if( (!needs_no_electric && !sch->is_electrified()) || sch->get_max_speed() == 0 ) {
2482 return false;
2483 }
2484
2485 if (depot_t *depot = bd->get_depot()) {
2486 if (depot->get_waytype() != desc->get_waytype() || depot->get_owner() != get_owner()) {
2487 return false;
2488 }
2489 }
2490 // now check for special signs
2491 if(sch->has_sign()) {
2492 const roadsign_t* rs = bd->find<roadsign_t>();
2493 if( rs->get_desc()->get_wtyp()==get_waytype() ) {
2494 if( cnv != NULL && rs->get_desc()->get_min_speed() > 0 && rs->get_desc()->get_min_speed() > cnv->get_min_top_speed() ) {
2495 // below speed limit
2496 return false;
2497 }
2498 if( rs->get_desc()->is_private_way() && (rs->get_player_mask() & (1<<get_player_nr()) ) == 0 ) {
2499 // private road
2500 return false;
2501 }
2502 }
2503 }
2504
2505 if( target_halt.is_bound() && cnv->is_waiting() ) {
2506 // we are searching a stop here:
2507 // ok, we can go where we already are ...
2508 if(bd->get_pos()==get_pos()) {
2509 return true;
2510 }
2511 // we cannot pass an end of choose area
2512 if(sch->has_sign()) {
2513 const roadsign_t* rs = bd->find<roadsign_t>();
2514 if( rs->get_desc()->get_wtyp()==get_waytype() ) {
2515 if( rs->get_desc()->get_flags() & roadsign_desc_t::END_OF_CHOOSE_AREA ) {
2516 return false;
2517 }
2518 }
2519 }
2520 // but we can only use empty blocks ...
2521 // now check, if we could enter here
2522 return sch->can_reserve(cnv->self);
2523 }
2524
2525 return true;
2526 }
2527
2528
2529 // how expensive to go here (for way search)
2530 // author prissi
2531 int rail_vehicle_t::get_cost(const grund_t *gr, const weg_t *w, const sint32 max_speed, ribi_t::ribi from) const
2532 {
2533 // first favor faster ways
2534 if( w==NULL ) {
2535 // only occurs when deletion during way search
2536 return 999;
2537 }
2538
2539 // add cost for going (with maximum speed, cost is 1)
2540 const sint32 max_tile_speed = w->get_max_speed();
2541 int costs = (max_speed<=max_tile_speed) ? 1 : 4-(3*max_tile_speed)/max_speed;
2542
2543 // effect of slope
2544 if( gr->get_weg_hang()!=0 ) {
2545 // Knightly : check if the slope is upwards, relative to the previous tile
2546 // 25 hardcoded, see get_cost_upslope()
2547 costs += 25 * get_sloping_upwards( gr->get_weg_hang(), from );
2548 }
2549
2550 return costs;
2551 }
2552
2553
2554 // this routine is called by find_route, to determined if we reached a destination
2555 bool rail_vehicle_t::is_target(const grund_t *gr,const grund_t *prev_gr) const
2556 {
2557 const schiene_t * sch1 = (const schiene_t *) gr->get_weg(get_waytype());
2558 // first check blocks, if we can go there
2559 if( sch1->can_reserve(cnv->self) ) {
2560 // just check, if we reached a free stop position of this halt
2561 if( gr->is_halt() && gr->get_halt()==target_halt ) {
2562 // now we must check the predecessor ...
2563 if( prev_gr!=NULL ) {
2564 const koord dir=gr->get_pos().get_2d()-prev_gr->get_pos().get_2d();
2565 const ribi_t::ribi ribi = ribi_type(dir);
2566 if( gr->get_weg(get_waytype())->get_ribi_maske() & ribi ) {
2567 // signal/one way sign wrong direction
2568 return false;
2569 }
2570 grund_t *to;
2571 if( !gr->get_neighbour(to,get_waytype(),ribi) || !(to->get_halt()==target_halt) || (to->get_weg(get_waytype())->get_ribi_maske() & ribi_type(dir))!=0 ) {
2572 // end of stop: Is it long enough?
2573 // end of stop could be also signal!
2574 uint16 tiles = cnv->get_tile_length();
2575 while( tiles>1 ) {
2576 if( gr->get_weg(get_waytype())->get_ribi_maske() & ribi || !gr->get_neighbour(to,get_waytype(),ribi_t::backward(ribi)) || !(to->get_halt()==target_halt) ) {
2577 return false;
2578 }
2579 gr = to;
2580 tiles --;
2581 }
2582 return true;
2583 }
2584 }
2585 }
2586 }
2587 return false;
2588 }
2589
2590
2591 bool rail_vehicle_t::is_longblock_signal_clear(signal_t *sig, uint16 next_block, sint32 &restart_speed)
2592 {
2593 // longblock signal: first check, whether there is a signal coming up on the route => just like normal signal
2594 uint16 next_signal, next_crossing;
2595 if( !block_reserver( cnv->get_route(), next_block+1, next_signal, next_crossing, 0, true, false ) ) {
2596 // not even the "Normal" signal route part is free => no bother checking further on
2597 sig->set_state( roadsign_t::rot );
2598 restart_speed = 0;
2599 return false;
2600 }
2601
2602 if( next_signal != INVALID_INDEX ) {
2603 // success, and there is a signal before end of route => finished
2604 sig->set_state( roadsign_t::gruen );
2605 cnv->set_next_stop_index( min( next_crossing, next_signal ) );
2606 return true;
2607 }
2608
2609 // no signal before end_of_route => need to do route search in a step
2610 if( !cnv->is_waiting() ) {
2611 restart_speed = -1;
2612 return false;
2613 }
2614
2615 // now we can use the route search array
2616 // (route until end is already reserved at this point!)
2617 uint8 schedule_index = cnv->get_schedule()->get_current_stop()+1;
2618 route_t target_rt;
2619 koord3d cur_pos = cnv->get_route()->back();
2620 uint16 dummy, next_next_signal;
2621 if(schedule_index >= cnv->get_schedule()->get_count()) {
2622 schedule_index = 0;
2623 }
2624 while( schedule_index != cnv->get_schedule()->get_current_stop() ) {
2625 // now search
2626 // search for route
2627 bool success = target_rt.calc_route( welt, cur_pos, cnv->get_schedule()->entries[schedule_index].pos, this, speed_to_kmh(cnv->get_min_top_speed()), 8888 /*cnv->get_tile_length()*/ );
2628 if( target_rt.is_contained(get_pos()) ) {
2629 // do not reserve route going through my current stop&
2630 break;
2631 }
2632 if( success ) {
2633 success = block_reserver( &target_rt, 1, next_next_signal, dummy, 0, true, false );
2634 block_reserver( &target_rt, 1, dummy, dummy, 0, false, false );
2635 }
2636
2637 if( success ) {
2638 // ok, would be free
2639 if( next_next_signal<target_rt.get_count() ) {
2640 // and here is a signal => finished
2641 // (however, if it is this signal, we need to renew reservation ...
2642 if( target_rt.at(next_next_signal) == cnv->get_route()->at( next_block ) ) {
2643 block_reserver( cnv->get_route(), next_block+1, next_signal, next_crossing, 0, true, false );
2644 }
2645 sig->set_state( roadsign_t::gruen );
2646 cnv->set_next_stop_index( min( min( next_crossing, next_signal ), cnv->get_route()->get_count() ) );
2647 return true;
2648 }
2649 }
2650
2651 if( !success ) {
2652 block_reserver( cnv->get_route(), next_block+1, next_next_signal, dummy, 0, false, false );
2653 sig->set_state( roadsign_t::rot );
2654 restart_speed = 0;
2655 return false;
2656 }
2657 // prepare for next leg of schedule
2658 cur_pos = target_rt.back();
2659 schedule_index ++;
2660 if(schedule_index >= cnv->get_schedule()->get_count()) {
2661 schedule_index = 0;
2662 }
2663 }
2664 if( cnv->get_next_stop_index()-1 <= route_index ) {
2665 cnv->set_next_stop_index( cnv->get_route()->get_count()-1 );
2666 }
2667 return true;
2668 }
2669
2670
2671 bool rail_vehicle_t::is_choose_signal_clear(signal_t *sig, const uint16 start_block, sint32 &restart_speed)
2672 {
2673 bool choose_ok = false;
2674 target_halt = halthandle_t();
2675
2676 uint16 next_signal, next_crossing;
2677 grund_t const* const target = welt->lookup(cnv->get_route()->back());
2678
2679 if( cnv->get_schedule_target()!=koord3d::invalid ) {
2680 // destination is a waypoint!
2681 goto skip_choose;
2682 }
2683
2684 if( target==NULL ) {
2685 cnv->suche_neue_route();
2686 return false;
2687 }
2688
2689 // first check, if we are not heading to a waypoint
2690 if( !target->get_halt().is_bound() ) {
2691 goto skip_choose;
2692 }
2693
2694 // now we might choose something at least
2695 choose_ok = true;
2696
2697 // check, if there is another choose signal or end_of_choose on the route
2698 for( uint32 idx=start_block+1; choose_ok && idx<cnv->get_route()->get_count(); idx++ ) {
2699 grund_t *gr = welt->lookup(cnv->get_route()->at(idx));
2700 if( gr==0 ) {
2701 choose_ok = false;
2702 break;
2703 }
2704 if( gr->get_halt()==target->get_halt() ) {
2705 target_halt = gr->get_halt();
2706 break;
2707 }
2708 weg_t *way = gr->get_weg(get_waytype());
2709 if( way==0 ) {
2710 choose_ok = false;
2711 break;
2712 }
2713 if( way->has_sign() ) {
2714 roadsign_t *rs = gr->find<roadsign_t>(1);
2715 if( rs && rs->get_desc()->get_wtyp()==get_waytype() ) {
2716 if( rs->get_desc()->get_flags() & roadsign_desc_t::END_OF_CHOOSE_AREA ) {
2717 // end of choose on route => not choosing here
2718 choose_ok = false;
2719 }
2720 }
2721 }
2722 if( way->has_signal() ) {
2723 signal_t *sig = gr->find<signal_t>(1);
2724 if( sig && sig->get_desc()->is_choose_sign() ) {
2725 // second choose signal on route => not choosing here
2726 choose_ok = false;
2727 }
2728 }
2729 }
2730
2731 skip_choose:
2732 if( !choose_ok ) {
2733 // just act as normal signal
2734 if( block_reserver( cnv->get_route(), start_block+1, next_signal, next_crossing, 0, true, false ) ) {
2735 sig->set_state( roadsign_t::gruen );
2736 cnv->set_next_stop_index( min( next_crossing, next_signal ) );
2737 return true;
2738 }
2739 // not free => wait here if directly in front
2740 sig->set_state( roadsign_t::rot );
2741 restart_speed = 0;
2742 return false;
2743 }
2744
2745 target_halt = target->get_halt();
2746 if( !block_reserver( cnv->get_route(), start_block+1, next_signal, next_crossing, 100000, true, false ) ) {
2747 // no free route to target!
2748 // note: any old reservations should be invalid after the block reserver call.
2749 // => We can now start freshly all over
2750
2751 if(!cnv->is_waiting()) {
2752 restart_speed = -1;
2753 target_halt = halthandle_t();
2754 return false;
2755 }
2756 // now we are in a step and can use the route search array
2757
2758 // now it we are in a step and can use the route search
2759 route_t target_rt;
2760 const int richtung = ribi_type(get_pos(), pos_next); // to avoid confusion at diagonals
2761 if( !target_rt.find_route( welt, cnv->get_route()->at(start_block), this, speed_to_kmh(cnv->get_min_top_speed()), richtung, welt->get_settings().get_max_choose_route_steps() ) ) {
2762 // nothing empty or not route with less than get_max_choose_route_steps() tiles
2763 target_halt = halthandle_t();
2764 sig->set_state( roadsign_t::rot );
2765 restart_speed = 0;
2766 return false;
2767 }
2768 else {
2769 // try to alloc the whole route
2770 cnv->access_route()->remove_koord_from(start_block);
2771 cnv->access_route()->append( &target_rt );
2772 if( !block_reserver( cnv->get_route(), start_block+1, next_signal, next_crossing, 100000, true, false ) ) {
2773 dbg->error( "rail_vehicle_t::is_choose_signal_clear()", "could not reserved route after find_route!" );
2774 target_halt = halthandle_t();
2775 sig->set_state( roadsign_t::rot );
2776 restart_speed = 0;
2777 return false;
2778 }
2779 }
2780 // reserved route to target
2781 }
2782 sig->set_state( roadsign_t::gruen );
2783 cnv->set_next_stop_index( min( next_crossing, next_signal ) );
2784 return true;
2785 }
2786
2787
2788 bool rail_vehicle_t::is_pre_signal_clear(signal_t *sig, uint16 next_block, sint32 &restart_speed)
2789 {
2790 // parse to next signal; if needed recurse, since we allow cascading
2791 uint16 next_signal, next_crossing;
2792 if( block_reserver( cnv->get_route(), next_block+1, next_signal, next_crossing, 0, true, false ) ) {
2793 if( next_signal == INVALID_INDEX || cnv->get_route()->at(next_signal) == cnv->get_route()->back() || is_signal_clear( next_signal, restart_speed ) ) {
2794 // ok, end of route => we can go
2795 sig->set_state( roadsign_t::gruen );
2796 cnv->set_next_stop_index( min( next_signal, next_crossing ) );
2797 return true;
2798 }
2799 // when we reached here, the way is apparently not free => release reservation and set state to next free
2800 sig->set_state( roadsign_t::naechste_rot );
2801 block_reserver( cnv->get_route(), next_block+1, next_signal, next_crossing, 0, false, false );
2802 restart_speed = 0;
2803 return false;
2804 }
2805 // if we end up here, there was not even the next block free
2806 sig->set_state( roadsign_t::rot );
2807 restart_speed = 0;
2808 return false;
2809 }
2810
2811
2812
2813 bool rail_vehicle_t::is_priority_signal_clear(signal_t *sig, uint16 next_block, sint32 &restart_speed)
2814 {
2815 // parse to next signal; if needed recurse, since we allow cascading
2816 uint16 next_signal, next_crossing;
2817
2818 if( block_reserver( cnv->get_route(), next_block+1, next_signal, next_crossing, 0, true, false ) ) {
2819 if( next_signal == INVALID_INDEX || cnv->get_route()->at(next_signal) == cnv->get_route()->back() || is_signal_clear( next_signal, restart_speed ) ) {
2820 // ok, end of route => we can go
2821 sig->set_state( roadsign_t::gruen );
2822 cnv->set_next_stop_index( min( next_signal, next_crossing ) );
2823
2824 return true;
2825 }
2826
2827 // when we reached here, the way after the last signal is not free though the way before is => we can still go
2828 if( cnv->get_next_stop_index()<=next_signal+1 ) {
2829 // only show third aspect on last signal of cascade
2830 sig->set_state( roadsign_t::naechste_rot );
2831 }
2832 else {
2833 sig->set_state( roadsign_t::gruen );
2834 }
2835 cnv->set_next_stop_index( min( next_signal, next_crossing ) );
2836
2837 return false;
2838 }
2839
2840 // if we end up here, there was not even the next block free
2841 sig->set_state( roadsign_t::rot );
2842 restart_speed = 0;
2843
2844 return false;
2845 }
2846
2847
2848 bool rail_vehicle_t::is_signal_clear(uint16 next_block, sint32 &restart_speed)
2849 {
2850 // called, when there is a signal; will call other signal routines if needed
2851 grund_t *gr_next_block = welt->lookup(cnv->get_route()->at(next_block));
2852 signal_t *sig = gr_next_block->find<signal_t>();
2853 if( sig==NULL ) {
2854 dbg->error( "rail_vehicle_t::is_signal_clear()", "called at %s without a signal!", cnv->get_route()->at(next_block).get_str() );
2855 return true;
2856 }
2857
2858 // action depend on the next signal
2859 const roadsign_desc_t *sig_desc=sig->get_desc();
2860
2861 // simple signal: fail, if next block is not free
2862 if( sig_desc->is_simple_signal() ) {
2863
2864 uint16 next_signal, next_crossing;
2865 if( block_reserver( cnv->get_route(), next_block+1, next_signal, next_crossing, 0, true, false ) ) {
2866 sig->set_state( roadsign_t::gruen );
2867 cnv->set_next_stop_index( min( next_crossing, next_signal ) );
2868 return true;
2869 }
2870 // not free => wait here if directly in front
2871 sig->set_state( roadsign_t::rot );
2872 restart_speed = 0;
2873 return false;
2874 }
2875
2876 if( sig_desc->is_pre_signal() ) {
2877 return is_pre_signal_clear( sig, next_block, restart_speed );
2878 }
2879
2880 if ( sig_desc->is_priority_signal() ) {
2881 return is_priority_signal_clear( sig, next_block, restart_speed );
2882 }
2883
2884 if( sig_desc->is_longblock_signal() ) {
2885 return is_longblock_signal_clear( sig, next_block, restart_speed );
2886 }
2887
2888 if( sig_desc->is_choose_sign() ) {
2889 return is_choose_signal_clear( sig, next_block, restart_speed );
2890 }
2891
2892 dbg->error( "rail_vehicle_t::is_signal_clear()", "felt through at signal at %s", cnv->get_route()->at(next_block).get_str() );
2893 return false;
2894 }
2895
2896
2897 bool rail_vehicle_t::can_enter_tile(const grund_t *gr, sint32 &restart_speed, uint8)
2898 {
2899 assert(leading);
2900 uint16 next_signal, next_crossing;
2901 if( cnv->get_state()==convoi_t::CAN_START || cnv->get_state()==convoi_t::CAN_START_ONE_MONTH || cnv->get_state()==convoi_t::CAN_START_TWO_MONTHS ) {
2902 // reserve first block at the start until the next signal
2903 grund_t *gr_current = welt->lookup( get_pos() );
2904 weg_t *w = gr_current ? gr_current->get_weg(get_waytype()) : NULL;
2905 if( w==NULL || !(w->has_signal() || w->is_crossing()) ) {
2906 // free track => reserve up to next signal
2907 if( !block_reserver(cnv->get_route(), max(route_index,1)-1, next_signal, next_crossing, 0, true, false ) ) {
2908 restart_speed = 0;
2909 return false;
2910 }
2911 cnv->set_next_stop_index( next_crossing<next_signal ? next_crossing : next_signal );
2912 return true;
2913 }
2914 cnv->set_next_stop_index( max(route_index,1)-1 );
2915 if( steps<steps_next ) {
2916 // not yet at tile border => can drive to signal safely
2917 return true;
2918 }
2919 // we start with a signal/crossing => use stuff below ...
2920 }
2921
2922 assert(gr);
2923 if(gr->get_top()>250) {
2924 // too many objects here
2925 return false;
2926 }
2927
2928 schiene_t *w = (schiene_t *)gr->get_weg(get_waytype());
2929 if(w==NULL) {
2930 return false;
2931 }
2932
2933 /* this should happen only before signals ...
2934 * but if it is already reserved, we can save lots of other checks later
2935 */
2936 if( !w->can_reserve(cnv->self) ) {
2937 restart_speed = 0;
2938 return false;
2939 }
2940
2941 // is there any signal/crossing to be reserved?
2942 uint16 next_block = cnv->get_next_stop_index()-1;
2943 if( next_block >= cnv->get_route()->get_count() ) {
2944 // no obstacle in the way => drive on ...
2945 return true;
2946 }
2947
2948 // signal disappeared, train passes the tile of former signal
2949 if( next_block+1 < route_index ) {
2950 // we need to reserve the next block even if there is no signal present anymore
2951 bool ok = block_reserver( cnv->get_route(), route_index, next_signal, next_crossing, 0, true, false );
2952 if (ok) {
2953 cnv->set_next_stop_index( min( next_crossing, next_signal ) );
2954 }
2955 return ok;
2956 // if reservation was not possible the train will wait on the track until block is free
2957 }
2958
2959 if( next_block <= route_index+3 ) {
2960 koord3d block_pos=cnv->get_route()->at(next_block);
2961 grund_t *gr_next_block = welt->lookup(block_pos);
2962 const schiene_t *sch1 = gr_next_block ? (const schiene_t *)gr_next_block->get_weg(get_waytype()) : NULL;
2963 if(sch1==NULL) {
2964 // way (weg) not existent (likely destroyed)
2965 cnv->suche_neue_route();
2966 return false;
2967 }
2968
2969 // Is a crossing?
2970 // note: crossing and signal might exist on same tile
2971 // so first check crossing
2972 if( sch1->is_crossing() ) {
2973 if( crossing_t* cr = gr_next_block->find<crossing_t>(2) ) {
2974 // ok, here is a draw/turnbridge ...
2975 bool ok = cr->request_crossing(this);
2976 if(!ok) {
2977 // cannot cross => wait here
2978 restart_speed = 0;
2979 return cnv->get_next_stop_index()>route_index+1;
2980 }
2981 else if( !sch1->has_signal() ) {
2982 // can reserve: find next place to do something and drive on
2983 if( block_pos == cnv->get_route()->back() ) {
2984 // is also last tile => go on ...
2985 cnv->set_next_stop_index( INVALID_INDEX );
2986 return true;
2987 }
2988 else if( !block_reserver( cnv->get_route(), cnv->get_next_stop_index(), next_signal, next_crossing, 0, true, false ) ) {
2989 dbg->error( "rail_vehicle_t::can_enter_tile()", "block not free but was reserved!" );
2990 return false;
2991 }
2992 cnv->set_next_stop_index( next_crossing<next_signal ? next_crossing : next_signal );
2993 }
2994 }
2995 }
2996
2997 // next check for signal
2998 if( sch1->has_signal() ) {
2999 if( !is_signal_clear( next_block, restart_speed ) ) {
3000 // only return false, if we are directly in front of the signal
3001 return cnv->get_next_stop_index()>route_index;
3002 }
3003 }
3004 }
3005 return true;
3006 }
3007
3008
3009 /*
3010 * reserves or un-reserves all blocks and returns the handle to the next block (if there)
3011 * if count is larger than 1, (and defined) maximum MAX_CHOOSE_BLOCK_TILES tiles will be checked
3012 * (freeing or reserving a choose signal path)
3013 * if (!reserve && force_unreserve) then un-reserve everything till the end of the route
3014 * return the last checked block
3015 * @author prissi
3016 */
3017 bool rail_vehicle_t::block_reserver(const route_t *route, uint16 start_index, uint16 &next_signal_index, uint16 &next_crossing_index, int count, bool reserve, bool force_unreserve ) const
3018 {
3019 bool success=true;
3020 #ifdef MAX_CHOOSE_BLOCK_TILES
3021 int max_tiles=2*MAX_CHOOSE_BLOCK_TILES; // max tiles to check for choosesignals
3022 #endif
3023 slist_tpl<grund_t *> signs; // switch all signals on their way too ...
3024
3025 if(start_index>=route->get_count()) {
3026 cnv->set_next_reservation_index( max(route->get_count(),1)-1 );
3027 return 0;
3028 }
3029
3030 if(route->at(start_index)==get_pos() && reserve) {
3031 start_index++;
3032 }
3033
3034 if( !reserve ) {
3035 cnv->set_next_reservation_index( start_index );
3036 }
3037
3038 // find next block segment en route
3039 uint16 i=start_index;
3040 next_signal_index=INVALID_INDEX;
3041 next_crossing_index=INVALID_INDEX;
3042 bool unreserve_now = false;
3043 for ( ; success && count>=0 && i<route->get_count(); i++) {
3044
3045 koord3d pos = route->at(i);
3046 grund_t *gr = welt->lookup(pos);
3047 schiene_t * sch1 = gr ? (schiene_t *)gr->get_weg(get_waytype()) : NULL;
3048 if(sch1==NULL && reserve) {
3049 // reserve until the end of track
3050 break;
3051 }
3052 // we un-reserve also nonexistent tiles! (may happen during deletion)
3053
3054 #ifdef MAX_CHOOSE_BLOCK_TILES
3055 max_tiles--;
3056 if(max_tiles<0 && count>1) {
3057 break;
3058 }
3059 #endif
3060 if(reserve) {
3061 if( sch1->has_signal() && i<route->get_count()-1 ) {
3062 if(count) {
3063 signs.append(gr);
3064 }
3065 count --;
3066 next_signal_index = i;
3067 }
3068 if( !sch1->reserve( cnv->self, ribi_type( route->at(max(1u,i)-1u), route->at(min(route->get_count()-1u,i+1u)) ) ) ) {
3069 success = false;
3070 }
3071 if(next_crossing_index==INVALID_INDEX && sch1->is_crossing()) {
3072 next_crossing_index = i;
3073 }
3074 }
3075 else if(sch1) {
3076 if(!sch1->unreserve(cnv->self)) {
3077 if(unreserve_now) {
3078 // reached an reserved or free track => finished
3079 return false;
3080 }
3081 }
3082 else {
3083 // un-reserve from here (used during sale, since there might be reserved tiles not freed)
3084 unreserve_now = !force_unreserve;
3085 }
3086 if(sch1->has_signal()) {
3087 signal_t* signal = gr->find<signal_t>();
3088 if(signal) {
3089 signal->set_state(roadsign_t::rot);
3090 }
3091 }
3092 if(sch1->is_crossing()) {
3093 gr->find<crossing_t>()->release_crossing(this);
3094 }
3095 }
3096 }
3097
3098 if(!reserve) {
3099 return false;
3100 }
3101 // here we go only with reserve
3102
3103 //DBG_MESSAGE("block_reserver()","signals at %i, success=%d",next_signal_index,success);
3104
3105 // free, in case of un-reserve or no success in reservation
3106 if(!success) {
3107 // free reservation
3108 for ( int j=start_index; j<i; j++) {
3109 schiene_t * sch1 = (schiene_t *)welt->lookup( route->at(j))->get_weg(get_waytype());
3110 sch1->unreserve(cnv->self);
3111 }
3112 cnv->set_next_reservation_index( start_index );
3113 return false;
3114 }
3115
3116 // ok, switch everything green ...
3117 FOR(slist_tpl<grund_t*>, const g, signs) {
3118 if (signal_t* const signal = g->find<signal_t>()) {
3119 signal->set_state(roadsign_t::gruen);
3120 }
3121 }
3122 cnv->set_next_reservation_index( i );
3123
3124 return true;
3125 }
3126
3127
3128 /* beware: we must un-reserve rail blocks... */
3129 void rail_vehicle_t::leave_tile()
3130 {
3131 vehicle_t::leave_tile();
3132 // fix counters
3133 if(last) {
3134 grund_t *gr = welt->lookup( get_pos() );
3135 if(gr) {
3136 schiene_t *sch0 = (schiene_t *) gr->get_weg(get_waytype());
3137 if(sch0) {
3138 sch0->unreserve(this);
3139 // tell next signal?
3140 // and switch to red
3141 if(sch0->has_signal()) {
3142 signal_t* sig = gr->find<signal_t>();
3143 if(sig) {
3144 sig->set_state(roadsign_t::rot);
3145 }
3146 }
3147 }
3148 }
3149 }
3150 }
3151
3152
3153 void rail_vehicle_t::enter_tile(grund_t* gr)
3154 {
3155 vehicle_t::enter_tile(gr);
3156
3157 if( schiene_t *sch0 = (schiene_t *) gr->get_weg(get_waytype()) ) {
3158 // way statistics
3159 const int cargo = get_total_cargo();
3160 sch0->book(cargo, WAY_STAT_GOODS);
3161 if(leading) {
3162 sch0->book(1, WAY_STAT_CONVOIS);
3163 sch0->reserve( cnv->self, get_direction() );
3164 }
3165 }
3166 }
3167
3168
3169 schedule_t * rail_vehicle_t::generate_new_schedule() const
3170 {
3171 return desc->get_waytype()==tram_wt ? new tram_schedule_t() : new train_schedule_t();
3172 }
3173
3174
3175 schedule_t * monorail_vehicle_t::generate_new_schedule() const
3176 {
3177 return new monorail_schedule_t();
3178 }
3179
3180
3181 schedule_t * maglev_vehicle_t::generate_new_schedule() const
3182 {
3183 return new maglev_schedule_t();
3184 }
3185
3186
3187 schedule_t * narrowgauge_vehicle_t::generate_new_schedule() const
3188 {
3189 return new narrowgauge_schedule_t();
3190 }
3191
3192
3193 water_vehicle_t::water_vehicle_t(koord3d pos, const vehicle_desc_t* desc, player_t* player, convoi_t* cn) :
3194 vehicle_t(pos, desc, player)
3195 {
3196 cnv = cn;
3197 }
3198
3199
3200 water_vehicle_t::water_vehicle_t(loadsave_t *file, bool is_first, bool is_last) : vehicle_t()
3201 {
3202 vehicle_t::rdwr_from_convoi(file);
3203
3204 if( file->is_loading() ) {
3205 static const vehicle_desc_t *last_desc = NULL;
3206
3207 if(is_first) {
3208 last_desc = NULL;
3209 }
3210 // try to find a matching vehicle
3211 if(desc==NULL) {
3212 dbg->warning("water_vehicle_t::water_vehicle_t()", "try to find a fitting vehicle for %s.", !fracht.empty() ? fracht.front().get_name() : "passengers");
3213 desc = vehicle_builder_t::get_best_matching(water_wt, 0, fracht.empty() ? 0 : 30, 100, 40, !fracht.empty() ? fracht.front().get_desc() : goods_manager_t::passengers, true, last_desc, is_last );
3214 if(desc) {
3215 calc_image();
3216 }
3217 }
3218 // update last desc
3219 if( desc ) {
3220 last_desc = desc;
3221 }
3222 }
3223 }
3224
3225
3226 void water_vehicle_t::enter_tile(grund_t* gr)
3227 {
3228 vehicle_t::enter_tile(gr);
3229
3230 if( weg_t *ch = gr->get_weg(water_wt) ) {
3231 // we are in a channel, so book statistics
3232 ch->book(get_total_cargo(), WAY_STAT_GOODS);
3233 if (leading) {
3234 ch->book(1, WAY_STAT_CONVOIS);
3235 }
3236 }
3237 }
3238
3239
3240 bool water_vehicle_t::check_next_tile(const grund_t *bd) const
3241 {
3242 if( bd->is_water() ) {
3243 return true;
3244 }
3245 // channel can have more stuff to check
3246 const weg_t *w = bd->get_weg(water_wt);
3247 #if ENABLE_WATERWAY_SIGNS
3248 if( w && w->has_sign() ) {
3249 const roadsign_t* rs = bd->find<roadsign_t>();
3250 if( rs->get_desc()->get_wtyp()==get_waytype() ) {
3251 if( cnv !=NULL && rs->get_desc()->get_min_speed() > 0 && rs->get_desc()->get_min_speed() > cnv->get_min_top_speed() ) {
3252 // below speed limit
3253 return false;
3254 }
3255 if( rs->get_desc()->is_private_way() && (rs->get_player_mask() & (1<<get_player_nr()) ) == 0 ) {
3256 // private road
3257 return false;
3258 }
3259 }
3260 }
3261 #endif
3262 return (w && w->get_max_speed()>0);
3263 }
3264
3265
3266 /* Since slopes are handled different for ships
3267 * @author prissi
3268 */
3269 void water_vehicle_t::calc_friction(const grund_t *gr)
3270 {
3271 // or a hill?
3272 if(gr->get_weg_hang()) {
3273 // hill up or down => in lock => decelerate
3274 current_friction = 16;
3275 }
3276 else {
3277 // flat track
3278 current_friction = 1;
3279 }
3280
3281 if(previous_direction != direction) {
3282 // curve: higher friction
3283 current_friction *= 2;
3284 }
3285 }
3286
3287
3288 bool water_vehicle_t::can_enter_tile(const grund_t *gr, sint32 &restart_speed, uint8)
3289 {
3290 restart_speed = -1;
3291
3292 if(leading) {
3293
3294 assert(gr);
3295 if( gr->get_top()>251 ) {
3296 // too many ships already here ..
3297 return false;
3298 }
3299 weg_t *w = gr->get_weg(water_wt);
3300 if(w && w->is_crossing()) {
3301 // ok, here is a draw/turn-bridge ...
3302 crossing_t* cr = gr->find<crossing_t>();
3303 if(!cr->request_crossing(this)) {
3304 restart_speed = 0;
3305 return false;
3306 }
3307 }
3308 }
3309 return true;
3310 }
3311
3312
3313 schedule_t * water_vehicle_t::generate_new_schedule() const
3314 {
3315 return new ship_schedule_t();
3316 }
3317
3318
3319 /**** from here on planes ***/
3320
3321
3322 // for flying things, everywhere is good ...
3323 // another function only called during route searching
3324 ribi_t::ribi air_vehicle_t::get_ribi(const grund_t *gr) const
3325 {
3326 switch(state) {
3327 case taxiing:
3328 case looking_for_parking:
3329 return gr->get_weg_ribi(air_wt);
3330
3331 case taxiing_to_halt:
3332 {
3333 // we must invert all one way signs here, since we start from the target position here!
3334 weg_t *w = gr->get_weg(air_wt);
3335 if(w) {
3336 ribi_t::ribi r = w->get_ribi_unmasked();
3337 if( ribi_t::ribi mask = w->get_ribi_maske() ) {
3338 r &= mask;
3339 }
3340 return r;
3341 }
3342 return ribi_t::none;
3343 }
3344
3345 case landing:
3346 case departing:
3347 {
3348 ribi_t::ribi dir = gr->get_weg_ribi(air_wt);
3349 if(dir==0) {
3350 return ribi_t::all;
3351 }
3352 return dir;
3353 }
3354
3355 case flying:
3356 case circling:
3357 return ribi_t::all;
3358 }
3359 return ribi_t::none;
3360 }
3361
3362
3363 // how expensive to go here (for way search)
3364 // author prissi
3365 int air_vehicle_t::get_cost(const grund_t *, const weg_t *w, const sint32, ribi_t::ribi) const
3366 {
3367 // first favor faster ways
3368 int costs = 0;
3369
3370 if(state==flying) {
3371 if(w==NULL) {
3372 costs += 1;
3373 }
3374 else {
3375 if(w->get_desc()->get_styp()==type_flat) {
3376 costs += 25;
3377 }
3378 }
3379 }
3380 else {
3381 // only, if not flying ...
3382 const runway_t *rw =(const runway_t *)w;
3383 // if we are on a runway, then take into account how many convois are already going there
3384 if( rw->get_desc()->get_styp()==1 ) {
3385 costs += rw->get_reservation_count()*9; // encourage detours even during take off
3386 }
3387 if(w->get_desc()->get_styp()==type_flat) {
3388 costs += 3;
3389 }
3390 else {
3391 costs += 2;
3392 }
3393 }
3394
3395 return costs;
3396 }
3397
3398
3399 // whether the ground is drivable or not depends on the current state of the airplane
3400 bool air_vehicle_t::check_next_tile(const grund_t *bd) const
3401 {
3402 switch (state) {
3403 case taxiing:
3404 case taxiing_to_halt:
3405 case looking_for_parking:
3406 //DBG_MESSAGE("check_next_tile()","at %i,%i",bd->get_pos().x,bd->get_pos().y);
3407 return (bd->hat_weg(air_wt) && bd->get_weg(air_wt)->get_max_speed()>0);
3408
3409 case landing:
3410 case departing:
3411 case flying:
3412 case circling:
3413 {
3414 //DBG_MESSAGE("air_vehicle_t::check_next_tile()","(cnv %i) in idx %i",cnv->self.get_id(),route_index );
3415 // prissi: here a height check could avoid too high mountains
3416 return true;
3417 }
3418 }
3419 return false;
3420 }
3421
3422
3423 // this routine is called by find_route, to determined if we reached a destination
3424 bool air_vehicle_t::is_target(const grund_t *gr,const grund_t *) const
3425 {
3426 if(state!=looking_for_parking || !target_halt.is_bound()) {
3427 // search for the end of the runway
3428 const weg_t *w=gr->get_weg(air_wt);
3429 if(w && w->get_desc()->get_styp()==type_runway) {
3430 // ok here is a runway
3431 ribi_t::ribi ribi= w->get_ribi_unmasked();
3432 if(ribi_t::is_single(ribi) && (ribi&approach_dir)!=0) {
3433 // pointing in our direction
3434 // here we should check for length, but we assume everything is ok
3435 return true;
3436 }
3437 }
3438 }
3439 else {
3440 // otherwise we just check, if we reached a free stop position of this halt
3441 if(gr->get_halt()==target_halt && target_halt->is_reservable(gr,cnv->self)) {
3442 return true;
3443 }
3444 }
3445 return false;
3446 }
3447
3448
3449 /* finds a free stop, calculates a route and reserve the position
3450 * else return false
3451 */
3452 bool air_vehicle_t::find_route_to_stop_position()
3453 {
3454 if(target_halt.is_bound()) {
3455 //DBG_MESSAGE("aircraft_t::find_route_to_stop_position()","bound! (cnv %i)",cnv->self.get_id());
3456 return true; // already searched with success
3457 }
3458
3459 // check for skipping circle
3460 route_t *rt=cnv->access_route();
3461
3462 //DBG_MESSAGE("aircraft_t::find_route_to_stop_position()","can approach? (cnv %i)",cnv->self.get_id());
3463
3464 grund_t const* const last = welt->lookup(rt->back());
3465 target_halt = last ? last->get_halt() : halthandle_t();
3466 if(!target_halt.is_bound()) {
3467 return true; // no halt to search
3468 }
3469
3470 // then: check if the search point is still on a runway (otherwise just proceed)
3471 grund_t const* const target = welt->lookup(rt->at(search_for_stop));
3472 if(target==NULL || !target->hat_weg(air_wt)) {
3473 target_halt = halthandle_t();
3474 DBG_MESSAGE("aircraft_t::find_route_to_stop_position()","no runway found at (%s)",rt->at(search_for_stop).get_str());
3475 return true; // no runway any more ...
3476 }
3477
3478 // is our target occupied?
3479 // DBG_MESSAGE("aircraft_t::find_route_to_stop_position()","state %i",state);
3480 if(!target_halt->find_free_position(air_wt,cnv->self,obj_t::air_vehicle) ) {
3481 target_halt = halthandle_t();
3482 DBG_MESSAGE("aircraft_t::find_route_to_stop_position()","no free position found!");
3483 return false;
3484 }
3485 else {
3486 // calculate route to free position:
3487
3488 // if we fail, we will wait in a step, much more simulation friendly
3489 // and the route finder is not re-entrant!
3490 if(!cnv->is_waiting()) {
3491 target_halt = halthandle_t();
3492 return false;
3493 }
3494
3495 // now search a route
3496 //DBG_MESSAGE("aircraft_t::find_route_to_stop_position()","some free: find route from index %i",suchen);
3497 route_t target_rt;
3498 flight_state prev_state = state;
3499 state = looking_for_parking;
3500 if(!target_rt.find_route( welt, rt->at(search_for_stop), this, 500, ribi_t::all, welt->get_settings().get_max_choose_route_steps() )) {
3501 DBG_MESSAGE("aircraft_t::find_route_to_stop_position()","found no route to free one");
3502 // circle slowly another round ...
3503 target_halt = halthandle_t();
3504 state = prev_state;
3505 return false;
3506 }
3507 state = prev_state;
3508
3509 // now reserve our choice ...
3510 target_halt->reserve_position(welt->lookup(target_rt.back()), cnv->self);
3511 //DBG_MESSAGE("aircraft_t::find_route_to_stop_position()", "found free stop near %i,%i,%i", target_rt.back().x, target_rt.back().y, target_rt.back().z);
3512 rt->remove_koord_from(search_for_stop);
3513 rt->append( &target_rt );
3514 return true;
3515 }
3516 }
3517
3518
3519 // main routine: searches the new route in up to three steps
3520 // must also take care of stops under traveling and the like
3521 bool air_vehicle_t::calc_route(koord3d start, koord3d ziel, sint32 max_speed, route_t* route)
3522 {
3523 //DBG_MESSAGE("aircraft_t::calc_route()","search route from %i,%i,%i to %i,%i,%i",start.x,start.y,start.z,ziel.x,ziel.y,ziel.z);
3524
3525 if(leading && cnv) {
3526 // free target reservation
3527 if( target_halt.is_bound() ) {
3528 if (grund_t* const target = welt->lookup(cnv->get_route()->back())) {
3529 target_halt->unreserve_position(target,cnv->self);
3530 }
3531 }
3532 // free runway reservation
3533 block_reserver( route_index, route->get_count(), false );
3534 }
3535 target_halt = halthandle_t(); // no block reserved
3536
3537 const weg_t *w=welt->lookup(start)->get_weg(air_wt);
3538 bool start_in_the_air = (w==NULL);
3539 bool end_in_air=false;
3540
3541 search_for_stop = takeoff = touchdown = 0x7ffffffful;
3542 if(!start_in_the_air) {
3543
3544 // see, if we find a direct route: We are finished
3545 state = taxiing;
3546 if(route->calc_route( welt, start, ziel, this, max_speed, 0 )) {
3547 // ok, we can taxi to our location
3548 return true;
3549 }
3550 }
3551
3552 if(start_in_the_air || (w->get_desc()->get_styp()==type_runway && ribi_t::is_single(w->get_ribi())) ) {
3553 // we start here, if we are in the air or at the end of a runway
3554 search_start = start;
3555 start_in_the_air = true;
3556 route->clear();
3557 //DBG_MESSAGE("aircraft_t::calc_route()","start in air at %i,%i,%i",search_start.x,search_start.y,search_start.z);
3558 }
3559 else {
3560 // not found and we are not on the takeoff tile (where the route search will fail too) => we try to calculate a complete route, starting with the way to the runway
3561
3562 // second: find start runway end
3563 state = taxiing;
3564 #ifdef USE_DIFFERENT_WIND
3565 approach_dir = get_approach_ribi( ziel, start ); // reverse
3566 //DBG_MESSAGE("aircraft_t::calc_route()","search runway start near %i,%i,%i with corner in %x",start.x,start.y,start.z, approach_dir);
3567 #else
3568 approach_dir = ribi_t::northeast; // reverse
3569 DBG_MESSAGE("aircraft_t::calc_route()","search runway start near (%s)",start.get_str());
3570 #endif
3571 if(!route->find_route( welt, start, this, max_speed, ribi_t::all, 100 )) {
3572 DBG_MESSAGE("aircraft_t::calc_route()","failed");
3573 return false;
3574 }
3575 // save the route
3576 search_start = route->back();
3577 //DBG_MESSAGE("aircraft_t::calc_route()","start at ground (%s)",search_start.get_str());
3578 }
3579
3580 // second: find target runway end
3581
3582 state = taxiing_to_halt; // only used for search
3583
3584 #ifdef USE_DIFFERENT_WIND
3585 approach_dir = get_approach_ribi( start, ziel ); // reverse
3586 //DBG_MESSAGE("aircraft_t::calc_route()","search runway target near %i,%i,%i in corners %x",ziel.x,ziel.y,ziel.z,approach_dir);
3587 #else
3588 approach_dir = ribi_t::southwest; // reverse
3589 //DBG_MESSAGE("aircraft_t::calc_route()","search runway target near %i,%i,%i in corners %x",ziel.x,ziel.y,ziel.z);
3590 #endif
3591 route_t end_route;
3592
3593 if(!end_route.find_route( welt, ziel, this, max_speed, ribi_t::all, welt->get_settings().get_max_choose_route_steps() )) {
3594 // well, probably this is a waypoint
3595 if( grund_t *target = welt->lookup(ziel) ) {
3596 if( !target->get_weg(air_wt) ) {
3597 end_in_air = true;
3598 search_end = ziel;
3599 }
3600 else {
3601 // we have a taxiway/illegal runway here we cannot reach
3602 return false; // no route!
3603 }
3604 }
3605 else {
3606 // illegal coordinates?
3607 return false;
3608 }
3609 }
3610 else {
3611 // save target route
3612 search_end = end_route.back();
3613 }
3614 //DBG_MESSAGE("aircraft_t::calc_route()","end at ground (%s)",search_end.get_str());
3615
3616 // create target route
3617 if(!start_in_the_air) {
3618 takeoff = route->get_count()-1;
3619 koord start_dir(welt->lookup(search_start)->get_weg_ribi(air_wt));
3620 if(start_dir!=koord(0,0)) {
3621 // add the start
3622 ribi_t::ribi start_ribi = ribi_t::backward(ribi_type(start_dir));
3623 const grund_t *gr=NULL;
3624 // add the start
3625 int endi = 1;
3626 int over = 3;
3627 // now add all runway + 3 ...
3628 do {
3629 if(!welt->is_within_limits(search_start.get_2d()+(start_dir*endi)) ) {
3630 break;
3631 }
3632 gr = welt->lookup_kartenboden(search_start.get_2d()+(start_dir*endi));
3633 if(over<3 || (gr->get_weg_ribi(air_wt)&start_ribi)==0) {
3634 over --;
3635 }
3636 endi ++;
3637 route->append(gr->get_pos());
3638 } while( over>0 );
3639 // out of map
3640 if(gr==NULL) {
3641 dbg->error("aircraft_t::calc_route()","out of map!");
3642 return false;
3643 }
3644 // need some extra step to avoid 180 deg turns
3645 if( start_dir.x!=0 && sgn(start_dir.x)!=sgn(search_end.x-search_start.x) ) {
3646 route->append( welt->lookup_kartenboden(gr->get_pos().get_2d()+koord(0,(search_end.y>search_start.y) ? 1 : -1 ) )->get_pos() );
3647 route->append( welt->lookup_kartenboden(gr->get_pos().get_2d()+koord(0,(search_end.y>search_start.y) ? 2 : -2 ) )->get_pos() );
3648 }
3649 else if( start_dir.y!=0 && sgn(start_dir.y)!=sgn(search_end.y-search_start.y) ) {
3650 route->append( welt->lookup_kartenboden(gr->get_pos().get_2d()+koord((search_end.x>search_start.x) ? 1 : -1 ,0) )->get_pos() );
3651 route->append( welt->lookup_kartenboden(gr->get_pos().get_2d()+koord((search_end.x>search_start.x) ? 2 : -2 ,0) )->get_pos() );
3652 }
3653 }
3654 else {
3655 // init with startpos
3656 dbg->error("aircraft_t::calc_route()","Invalid route calculation: start is on a single direction field ...");
3657 }
3658 state = taxiing;
3659 flying_height = 0;
3660 target_height = (sint16)get_pos().z*TILE_HEIGHT_STEP;
3661 }
3662 else {
3663 // init with current pos (in air ... )
3664 route->clear();
3665 route->append( start );
3666 state = flying;
3667 if(flying_height==0) {
3668 flying_height = 3*TILE_HEIGHT_STEP;
3669 }
3670 takeoff = 0;
3671 target_height = ((sint16)get_pos().z+3)*TILE_HEIGHT_STEP;
3672 }
3673
3674 //DBG_MESSAGE("aircraft_t::calc_route()","take off ok");
3675
3676 koord3d landing_start=search_end;
3677 if(!end_in_air) {
3678 // now find way to start of landing pos
3679 ribi_t::ribi end_ribi = welt->lookup(search_end)->get_weg_ribi(air_wt);
3680 koord end_dir(end_ribi);
3681 end_ribi = ribi_t::backward(end_ribi);
3682 if(end_dir!=koord(0,0)) {
3683 // add the start
3684 const grund_t *gr;
3685 int endi = 1;
3686 int over = 3;
3687 // now add all runway + 3 ...
3688 do {
3689 if(!welt->is_within_limits(search_end.get_2d()+(end_dir*endi)) ) {
3690 break;
3691 }
3692 gr = welt->lookup_kartenboden(search_end.get_2d()+(end_dir*endi));
3693 if(over<3 || (gr->get_weg_ribi(air_wt)&end_ribi)==0) {
3694 over --;
3695 }
3696 endi ++;
3697 landing_start = gr->get_pos();
3698 } while( over>0 );
3699 }
3700 }
3701 else {
3702 search_for_stop = touchdown = 0x7FFFFFFFul;
3703 }
3704
3705 // just some straight routes ...
3706 if(!route->append_straight_route(welt,landing_start)) {
3707 // should never fail ...
3708 dbg->error( "aircraft_t::calc_route()", "No straight route found!" );
3709 return false;
3710 }
3711
3712 if(!end_in_air) {
3713
3714 // find starting direction
3715 int offset = 0;
3716 switch(welt->lookup(search_end)->get_weg_ribi(air_wt)) {
3717 case ribi_t::north: offset = 0; break;
3718 case ribi_t::west: offset = 4; break;
3719 case ribi_t::south: offset = 8; break;
3720 case ribi_t::east: offset = 12; break;
3721 }
3722
3723 // now make a curve
3724 koord circlepos=landing_start.get_2d();
3725 static const koord circle_koord[16]={ koord(0,1), koord(0,1), koord(1,0), koord(0,1), koord(1,0), koord(1,0), koord(0,-1), koord(1,0), koord(0,-1), koord(0,-1), koord(-1,0), koord(0,-1), koord(-1,0), koord(-1,0), koord(0,1), koord(-1,0) };
3726
3727 // circle to the left
3728 for( int i=0; i<16; i++ ) {
3729 circlepos += circle_koord[(offset+i+16)%16];
3730 if(welt->is_within_limits(circlepos)) {
3731 route->append( welt->lookup_kartenboden(circlepos)->get_pos() );
3732 }
3733 else {
3734 // could only happen during loading old savegames;
3735 // in new versions it should not possible to build a runway here
3736 route->clear();
3737 dbg->error("aircraft_t::calc_route()","airport too close to the edge! (Cannot go to %i,%i!)",circlepos.x,circlepos.y);
3738 return false;
3739 }
3740 }
3741
3742 touchdown = route->get_count()+2;
3743 route->append_straight_route(welt,search_end);
3744
3745 // now the route reach point (+1, since it will check before entering the tile ...)
3746 search_for_stop = route->get_count()-1;
3747
3748 // now we just append the rest
3749 for( int i=end_route.get_count()-2; i>=0; i-- ) {
3750 route->append(end_route.at(i));
3751 }
3752 }
3753
3754 //DBG_MESSAGE("aircraft_t::calc_route()","departing=%i touchdown=%i suchen=%i total=%i state=%i",takeoff, touchdown, suchen, route->get_count()-1, state );
3755 return true;
3756 }
3757
3758
3759 /* reserves runways (reserve true) or removes the reservation
3760 * finishes when reaching end tile or leaving the ground (end of runway)
3761 * @return true if the reservation is successful
3762 */
3763 bool air_vehicle_t::block_reserver( uint32 start, uint32 end, bool reserve ) const
3764 {
3765 bool start_now = false;
3766 bool success = true;
3767
3768 const route_t *route = cnv->get_route();
3769 if(route->empty()) {
3770 return false;
3771 }
3772
3773 for( uint32 i=start; success && i<end && i<route->get_count(); i++) {
3774
3775 grund_t *gr = welt->lookup(route->at(i));
3776 runway_t *sch1 = gr ? (runway_t *)gr->get_weg(air_wt) : NULL;
3777 if( !sch1 ) {
3778 if(reserve) {
3779 if(!start_now) {
3780 // touched down here
3781 start = i;
3782 }
3783 else {
3784 // most likely left the ground here ...
3785 end = i;
3786 break;
3787 }
3788 }
3789 }
3790 else {
3791 // we un-reserve also nonexistent tiles! (may happen during deletion)
3792 if(reserve) {
3793 start_now = true;
3794 sch1->add_convoi_reservation(cnv->self);
3795 if( !sch1->reserve(cnv->self,ribi_t::none) ) {
3796 // unsuccessful => must un-reserve all
3797 success = false;
3798 end = i;
3799 break;
3800 }
3801 // end of runway?
3802 if( i > start && (ribi_t::is_single( sch1->get_ribi_unmasked() ) || sch1->get_desc()->get_styp() != type_runway) ) {
3803 end = i;
3804 break;
3805 }
3806 }
3807 else {
3808 // we always unreserve everything
3809 sch1->unreserve(cnv->self);
3810 }
3811 }
3812 }
3813
3814 // un-reserve if not successful
3815 if( !success && reserve ) {
3816 for( uint32 i=start; i<end; i++ ) {
3817 grund_t *gr = welt->lookup(route->at(i));
3818 if (gr) {
3819 runway_t* sch1 = (runway_t *)gr->get_weg(air_wt);
3820 if (sch1) {
3821 sch1->unreserve(cnv->self);
3822 }
3823 }
3824 }
3825 return false;
3826 }
3827
3828 if( reserve && end<touchdown ) {
3829 // reserve runway for landing for load balancing
3830 for( uint32 i=touchdown; i<route->get_count(); i++ ) {
3831 if( grund_t *gr = welt->lookup(route->at(i)) ) {
3832 if( runway_t* sch1 = (runway_t *)gr->get_weg(air_wt) ) {
3833 if( sch1->get_desc()->get_styp()!=type_runway ) {
3834 break;
3835 }
3836 sch1->add_convoi_reservation( cnv->self );
3837 }
3838 }
3839 }
3840 }
3841
3842 return success;
3843 }
3844
3845
3846 // handles all the decisions on the ground an in the air
3847 bool air_vehicle_t::can_enter_tile(const grund_t *gr, sint32 &restart_speed, uint8)
3848 {
3849 restart_speed = -1;
3850
3851 assert(gr);
3852 if(gr->get_top()>250) {
3853 // too many objects here
3854 return false;
3855 }
3856
3857 if( route_index < takeoff && route_index > 1 && takeoff<cnv->get_route()->get_count()-1 ) {
3858 // check, if tile occupied by a plane on ground
3859 if( route_index > 1 ) {
3860 for( uint8 i = 1; i<gr->get_top(); i++ ) {
3861 obj_t *obj = gr->obj_bei(i);
3862 if( obj->get_typ()==obj_t::air_vehicle && ((air_vehicle_t *)obj)->is_on_ground() ) {
3863 restart_speed = 0;
3864 return false;
3865 }
3866 }
3867 }
3868 // need to reserve runway?
3869 runway_t *rw = (runway_t *)gr->get_weg(air_wt);
3870 if(rw==NULL) {
3871 cnv->suche_neue_route();
3872 return false;
3873 }
3874 // next tile a runway => then reserve
3875 if(rw->get_desc()->get_styp()==type_runway) {
3876 // try to reserve the runway
3877 if(!block_reserver(takeoff,takeoff+100,true)) {
3878 // runway already blocked ...
3879 restart_speed = 0;
3880 return false;
3881 }
3882 }
3883 return true;
3884 }
3885
3886 if( state == taxiing ) {
3887 // enforce on ground for taxiing
3888 flying_height = 0;
3889 // we may need to unreserve the runway after leaving it
3890 if( route_index >= touchdown ) {
3891 runway_t *rw = (runway_t *)gr->get_weg(air_wt);
3892 // next tile a not runway => then unreserve
3893 if( rw == NULL || rw->get_desc()->get_styp() != type_runway || gr->is_halt() ) {
3894 block_reserver( touchdown, search_for_stop+1, false );
3895 }
3896 }
3897 }
3898
3899 if( route_index == takeoff && state == taxiing ) {
3900 // try to reserve the runway if not already done
3901 if(route_index==2 && !block_reserver(takeoff,takeoff+100,true)) {
3902 // runway blocked, wait at start of runway
3903 restart_speed = 0;
3904 return false;
3905 }
3906 // stop shortly at the end of the runway
3907 state = departing;
3908 restart_speed = 0;
3909 return false;
3910 }
3911
3912 //DBG_MESSAGE("aircraft_t::ist_weg_frei()","index %i<>%i",route_index,touchdown);
3913
3914 // check for another circle ...
3915 if( route_index==(touchdown-3) ) {
3916 if( !block_reserver( touchdown, search_for_stop+1, true ) ) {
3917 route_index -= 16;
3918 return true;
3919 }
3920 state = landing;
3921 return true;
3922 }
3923
3924 if( route_index==touchdown-16-3 && state!=circling ) {
3925 // just check, if the end of runway is free; we will wait there
3926 if( block_reserver( touchdown, search_for_stop+1, true ) ) {
3927 route_index += 16;
3928 // can land => set landing height
3929 state = landing;
3930 }
3931 else {
3932 // circle slowly next round
3933 state = circling;
3934 cnv->must_recalc_data();
3935 if( leading ) {
3936 cnv->must_recalc_data_front();
3937 }
3938 }
3939 }
3940
3941 if(route_index==search_for_stop && state==landing && !target_halt.is_bound()) {
3942
3943 // if we fail, we will wait in a step, much more simulation friendly
3944 // and the route finder is not re-entrant!
3945 if(!cnv->is_waiting()) {
3946 return false;
3947 }
3948
3949 // nothing free here?
3950 if(find_route_to_stop_position()) {
3951 // stop reservation successful
3952 state = taxiing;
3953 return true;
3954 }
3955 restart_speed = 0;
3956 return false;
3957 }
3958
3959 if(state==looking_for_parking) {
3960 state = taxiing;
3961 }
3962
3963 if(state == taxiing && gr->is_halt() && gr->find<air_vehicle_t>()) {
3964 // the next step is a parking position. We do not enter, if occupied!
3965 restart_speed = 0;
3966 return false;
3967 }
3968
3969 return true;
3970 }
3971
3972
3973 // this must also change the internal modes for the calculation
3974 void air_vehicle_t::enter_tile(grund_t* gr)
3975 {
3976 vehicle_t::enter_tile(gr);
3977
3978 if( this->is_on_ground() ) {
3979 runway_t *w=(runway_t *)gr->get_weg(air_wt);
3980 if(w) {
3981 const int cargo = get_total_cargo();
3982 w->book(cargo, WAY_STAT_GOODS);
3983 if (leading) {
3984 w->book(1, WAY_STAT_CONVOIS);
3985 }
3986 }
3987 }
3988 }
3989
3990
3991 air_vehicle_t::air_vehicle_t(loadsave_t *file, bool is_first, bool is_last) : vehicle_t()
3992 {
3993 rdwr_from_convoi(file);
3994
3995 if( file->is_loading() ) {
3996 static const vehicle_desc_t *last_desc = NULL;
3997
3998 if(is_first) {
3999 last_desc = NULL;
4000 }
4001 // try to find a matching vehicle
4002 if(desc==NULL) {
4003 dbg->warning("aircraft_t::aircraft_t()", "try to find a fitting vehicle for %s.", !fracht.empty() ? fracht.front().get_name() : "passengers");
4004 desc = vehicle_builder_t::get_best_matching(air_wt, 0, 101, 1000, 800, !fracht.empty() ? fracht.front().get_desc() : goods_manager_t::passengers, true, last_desc, is_last );
4005 if(desc) {
4006 calc_image();
4007 }
4008 }
4009 // update last desc
4010 if( desc ) {
4011 last_desc = desc;
4012 }
4013 }
4014 }
4015
4016
4017 air_vehicle_t::air_vehicle_t(koord3d pos, const vehicle_desc_t* desc, player_t* player, convoi_t* cn) :
4018 vehicle_t(pos, desc, player)
4019 {
4020 cnv = cn;
4021 state = taxiing;
4022 flying_height = 0;
4023 target_height = pos.z;
4024 }
4025
4026
4027 air_vehicle_t::~air_vehicle_t()
4028 {
4029 // mark aircraft (after_image) dirty, since we have no "real" image
4030 const int raster_width = get_current_tile_raster_width();
4031 sint16 yoff = tile_raster_scale_y(-flying_height-get_hoff()-2, raster_width);
4032
4033 mark_image_dirty( image, yoff);
4034 mark_image_dirty( image, 0 );
4035 }
4036
4037
4038 void air_vehicle_t::set_convoi(convoi_t *c)
4039 {
4040 DBG_MESSAGE("aircraft_t::set_convoi()","%p",c);
4041 if(leading && (unsigned long)cnv > 1) {
4042 // free stop reservation
4043 route_t const& r = *cnv->get_route();
4044 if(target_halt.is_bound()) {
4045 target_halt->unreserve_position(welt->lookup(r.back()), cnv->self);
4046 target_halt = halthandle_t();
4047 }
4048 if (!r.empty()) {
4049 // free runway reservation
4050 if(route_index>=takeoff && route_index<touchdown-4 && state!=flying) {
4051 block_reserver( takeoff, takeoff+100, false );
4052 }
4053 else if(route_index>=touchdown-1 && state!=taxiing) {
4054 block_reserver( touchdown, search_for_stop+1, false );
4055 }
4056 }
4057 }
4058 // maybe need to restore state?
4059 if(c!=NULL) {
4060 bool target=(bool)cnv;
4061 vehicle_t::set_convoi(c);
4062 if(leading) {
4063 if(target) {
4064 // reinitialize the target halt
4065 grund_t* const target=welt->lookup(cnv->get_route()->back());
4066 target_halt = target->get_halt();
4067 if(target_halt.is_bound()) {
4068 target_halt->reserve_position(target,cnv->self);
4069 }
4070 }
4071 // restore reservation
4072 if( grund_t *gr = welt->lookup(get_pos()) ) {
4073 if( weg_t *weg = gr->get_weg(air_wt) ) {
4074 if( weg->get_desc()->get_styp()==type_runway ) {
4075 // but only if we are on a runway ...
4076 if( route_index>=takeoff && route_index<touchdown-21 && state!=flying ) {
4077 block_reserver( takeoff, takeoff+100, true );
4078 }
4079 else if( route_index>=touchdown-1 && state!=taxiing ) {
4080 block_reserver( touchdown, search_for_stop+1, true );
4081 }
4082 }
4083 }
4084 }
4085 }
4086 }
4087 else {
4088 vehicle_t::set_convoi(NULL);
4089 }
4090 }
4091
4092
4093 schedule_t *air_vehicle_t::generate_new_schedule() const
4094 {
4095 return new airplane_schedule_t();
4096 }
4097
4098
4099 void air_vehicle_t::rdwr_from_convoi(loadsave_t *file)
4100 {
4101 xml_tag_t t( file, "aircraft_t" );
4102
4103 // initialize as vehicle_t::rdwr_from_convoi calls get_image()
4104 if (file->is_loading()) {
4105 state = taxiing;
4106 flying_height = 0;
4107 }
4108 vehicle_t::rdwr_from_convoi(file);
4109
4110 file->rdwr_enum(state);
4111 file->rdwr_short(flying_height);
4112 flying_height &= ~(TILE_HEIGHT_STEP-1);
4113 file->rdwr_short(target_height);
4114 file->rdwr_long(search_for_stop);
4115 file->rdwr_long(touchdown);
4116 file->rdwr_long(takeoff);
4117 }
4118
4119
4120 #ifdef USE_DIFFERENT_WIND
4121 // well lots of code to make sure, we have at least two different directions for the runway search
4122 uint8 air_vehicle_t::get_approach_ribi( koord3d start, koord3d ziel )
4123 {
4124 uint8 dir = ribi_type(start, ziel); // reverse
4125 // make sure, there are at last two directions to choose, or you might en up with not route
4126 if(ribi_t::is_single(dir)) {
4127 dir |= (dir<<1);
4128 if(dir>16) {
4129 dir += 1;
4130 }
4131 }
4132 return dir&0x0F;
4133 }
4134 #endif
4135
4136
4137 void air_vehicle_t::hop(grund_t* gr)
4138 {
4139 sint32 new_speed_limit = SPEED_UNLIMITED;
4140 sint32 new_friction = 0;
4141
4142 // take care of in-flight height ...
4143 const sint16 h_cur = (sint16)get_pos().z*TILE_HEIGHT_STEP;
4144 const sint16 h_next = (sint16)pos_next.z*TILE_HEIGHT_STEP;
4145
4146 switch(state) {
4147 case departing: {
4148 flying_height = 0;
4149 target_height = h_cur;
4150 new_friction = max( 1, 28/(1+(route_index-takeoff)*2) ); // 9 5 4 3 2 2 1 1...
4151
4152 // take off, when a) end of runway or b) last tile of runway or c) fast enough
4153 weg_t *weg=welt->lookup(get_pos())->get_weg(air_wt);
4154 if( (weg==NULL || // end of runway (broken runway)
4155 weg->get_desc()->get_styp()!=type_runway || // end of runway (grass now ... )
4156 (route_index>takeoff+1 && ribi_t::is_single(weg->get_ribi_unmasked())) ) || // single ribi at end of runway
4157 cnv->get_akt_speed()>kmh_to_speed(desc->get_topspeed())/3 // fast enough
4158 ) {
4159 state = flying;
4160 new_friction = 1;
4161 block_reserver( takeoff, touchdown-1, false );
4162 flying_height = h_cur - h_next;
4163 target_height = h_cur+TILE_HEIGHT_STEP*3;
4164 }
4165 break;
4166 }
4167 case circling: {
4168 new_speed_limit = kmh_to_speed(desc->get_topspeed())/3;
4169 new_friction = 4;
4170 // do not change height any more while circling
4171 flying_height += h_cur;
4172 flying_height -= h_next;
4173 break;
4174 }
4175 case flying: {
4176 // since we are at a tile border, round up to the nearest value
4177 flying_height += h_cur;
4178 if( flying_height < target_height ) {
4179 flying_height = (flying_height+TILE_HEIGHT_STEP) & ~(TILE_HEIGHT_STEP-1);
4180 }
4181 else if( flying_height > target_height ) {
4182 flying_height = (flying_height-TILE_HEIGHT_STEP);
4183 }
4184 flying_height -= h_next;
4185 // did we have to change our flight height?
4186 if( target_height-h_next > TILE_HEIGHT_STEP*5 ) {
4187 // Move down
4188 target_height -= TILE_HEIGHT_STEP*2;
4189 }
4190 else if( target_height-h_next < TILE_HEIGHT_STEP*2 ) {
4191 // Move up
4192 target_height += TILE_HEIGHT_STEP*2;
4193 }
4194 break;
4195 }
4196 case landing: {
4197 new_speed_limit = kmh_to_speed(desc->get_topspeed())/3; // ==approach speed
4198 new_friction = 8;
4199 flying_height += h_cur;
4200 if( flying_height < target_height ) {
4201 flying_height = (flying_height+TILE_HEIGHT_STEP) & ~(TILE_HEIGHT_STEP-1);
4202 }
4203 else if( flying_height > target_height ) {
4204 flying_height = (flying_height-TILE_HEIGHT_STEP);
4205 }
4206
4207 if (route_index >= touchdown) {
4208 // come down, now!
4209 target_height = h_next;
4210
4211 // touchdown!
4212 if (flying_height==h_next) {
4213 const sint32 taxi_speed = kmh_to_speed( min( 60, desc->get_topspeed()/4 ) );
4214 if( cnv->get_akt_speed() <= taxi_speed ) {
4215 new_speed_limit = taxi_speed;
4216 new_friction = 16;
4217 }
4218 else {
4219 const sint32 runway_left = search_for_stop - route_index;
4220 new_speed_limit = min( new_speed_limit, runway_left*runway_left*taxi_speed ); // ...approach 540 240 60 60
4221 const sint32 runway_left_fr = max( 0, 6-runway_left );
4222 new_friction = max( new_friction, min( desc->get_topspeed()/12, 4 + 4*(runway_left_fr*runway_left_fr+1) )); // ...8 8 12 24 44 72 108 152
4223 }
4224 }
4225 }
4226 else {
4227 // runway is on this height
4228 const sint16 runway_height = cnv->get_route()->at(touchdown).z*TILE_HEIGHT_STEP;
4229
4230 // we are too low, ascent asap
4231 if (flying_height < runway_height + TILE_HEIGHT_STEP) {
4232 target_height = runway_height + TILE_HEIGHT_STEP;
4233 }
4234 // too high, descent
4235 else if (flying_height + h_next - h_cur > runway_height + (sint16)(touchdown-route_index-1)*TILE_HEIGHT_STEP) {
4236 target_height = runway_height + (touchdown-route_index-1)*TILE_HEIGHT_STEP;
4237 }
4238 }
4239 flying_height -= h_next;
4240 break;
4241 }
4242 default: {
4243 new_speed_limit = kmh_to_speed( min( 60, desc->get_topspeed()/4 ) );
4244 new_friction = 16;
4245 flying_height = 0;
4246 target_height = h_next;
4247 break;
4248 }
4249 }
4250
4251 // hop to next tile
4252 vehicle_t::hop(gr);
4253
4254 speed_limit = new_speed_limit;
4255 current_friction = new_friction;
4256
4257 // friction factors and speed limit may have changed
4258 // TODO use the same logic as in vehicle_t::hop
4259 cnv->must_recalc_data();
4260 }
4261
4262
4263 // this routine will display the aircraft (if in flight)
4264 #ifdef MULTI_THREAD
4265 void air_vehicle_t::display_after(int xpos_org, int ypos_org, const sint8 clip_num) const
4266 #else
4267 void air_vehicle_t::display_after(int xpos_org, int ypos_org, bool is_global) const
4268 #endif
4269 {
4270 if( image != IMG_EMPTY && !is_on_ground() ) {
4271 int xpos = xpos_org, ypos = ypos_org;
4272
4273 const int raster_width = get_current_tile_raster_width();
4274 const sint16 z = get_pos().z;
4275 if( z + flying_height/TILE_HEIGHT_STEP - 1 > grund_t::underground_level ) {
4276 return;
4277 }
4278 const sint16 target = target_height - ((sint16)z*TILE_HEIGHT_STEP);
4279 sint16 current_flughohe = flying_height;
4280 if( current_flughohe < target ) {
4281 current_flughohe += (steps*TILE_HEIGHT_STEP) >> 8;
4282 }
4283 else if( current_flughohe > target ) {
4284 current_flughohe -= (steps*TILE_HEIGHT_STEP) >> 8;
4285 }
4286
4287 sint8 hoff = get_hoff();
4288 ypos += tile_raster_scale_y(get_yoff()-current_flughohe-hoff-2, raster_width);
4289 xpos += tile_raster_scale_x(get_xoff(), raster_width);
4290 get_screen_offset( xpos, ypos, raster_width );
4291
4292 display_swap_clip_wh(CLIP_NUM_VAR);
4293 // will be dirty
4294 // the aircraft!!!
4295 display_color( image, xpos, ypos, get_player_nr(), true, true/*get_flag(obj_t::dirty)*/ CLIP_NUM_PAR);
4296 #ifndef MULTI_THREAD
4297 vehicle_t::display_after( xpos_org, ypos_org - tile_raster_scale_y( current_flughohe - hoff - 2, raster_width ), is_global );
4298 #endif
4299 display_swap_clip_wh(CLIP_NUM_VAR);
4300 }
4301 #ifdef MULTI_THREAD
4302 }
4303 void air_vehicle_t::display_overlay(int xpos_org, int ypos_org) const
4304 {
4305 if( image != IMG_EMPTY && !is_on_ground() ) {
4306 const int raster_width = get_current_tile_raster_width();
4307 const sint16 z = get_pos().z;
4308 if( z + flying_height/TILE_HEIGHT_STEP - 1 > grund_t::underground_level ) {
4309 return;
4310 }
4311 const sint16 target = target_height - ((sint16)z*TILE_HEIGHT_STEP);
4312 sint16 current_flughohe = flying_height;
4313 if( current_flughohe < target ) {
4314 current_flughohe += (steps*TILE_HEIGHT_STEP) >> 8;
4315 }
4316 else if( current_flughohe > target ) {
4317 current_flughohe -= (steps*TILE_HEIGHT_STEP) >> 8;
4318 }
4319
4320 vehicle_t::display_overlay( xpos_org, ypos_org - tile_raster_scale_y( current_flughohe - get_hoff() - 2, raster_width ) );
4321 }
4322 #endif
4323 else if( is_on_ground() ) {
4324 // show loading tooltips on ground
4325 #ifdef MULTI_THREAD
4326 vehicle_t::display_overlay( xpos_org, ypos_org );
4327 #else
4328 vehicle_t::display_after( xpos_org, ypos_org, is_global );
4329 #endif
4330 }
4331 }
4332
4333
4334 const char *air_vehicle_t::is_deletable(const player_t *player)
4335 {
4336 if (is_on_ground()) {
4337 return vehicle_t::is_deletable(player);
4338 }
4339 return NULL;
4340 }
4341