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
8 #include <stdlib.h>
9 #include <stdio.h>
10 #include <string.h>
11
12 #include "simconvoi.h"
13 #include "vehicle/simvehicle.h"
14 #include "gui/simwin.h"
15 #include "player/simplay.h"
16 #include "simworld.h"
17 #include "simdepot.h"
18 #include "simline.h"
19 #include "simlinemgmt.h"
20 #include "simmenu.h"
21
22 #include "gui/depot_frame.h"
23 #include "gui/messagebox.h"
24
25 #include "dataobj/schedule.h"
26 #include "dataobj/loadsave.h"
27 #include "dataobj/translator.h"
28
29 #include "bauer/hausbauer.h"
30 #include "obj/gebaeude.h"
31
32 #include "bauer/vehikelbauer.h"
33
34 #include "descriptor/building_desc.h"
35
36 #include "utils/cbuffer_t.h"
37
38
39 slist_tpl<depot_t *> depot_t::all_depots;
40
depot_t(loadsave_t * file)41 depot_t::depot_t(loadsave_t *file) : gebaeude_t()
42 {
43 rdwr(file);
44 if(file->is_version_less(88, 2)) {
45 set_yoff(0);
46 }
47 all_depots.append(this);
48 selected_filter = VEHICLE_FILTER_RELEVANT;
49 selected_sort_by = SORT_BY_DEFAULT;
50 last_selected_line = linehandle_t();
51 command_pending = false;
52 }
53
54
depot_t(koord3d pos,player_t * player,const building_tile_desc_t * t)55 depot_t::depot_t(koord3d pos, player_t *player, const building_tile_desc_t *t) :
56 gebaeude_t(pos, player, t)
57 {
58 all_depots.append(this);
59 selected_filter = VEHICLE_FILTER_RELEVANT;
60 selected_sort_by = SORT_BY_DEFAULT;
61 last_selected_line = linehandle_t();
62 command_pending = false;
63 }
64
65
~depot_t()66 depot_t::~depot_t()
67 {
68 destroy_win((ptrdiff_t)this);
69 all_depots.remove(this);
70 }
71
72
73 // finds the next/previous depot relative to the current position
find_depot(koord3d start,const obj_t::typ depot_type,const player_t * player,bool forward)74 depot_t *depot_t::find_depot( koord3d start, const obj_t::typ depot_type, const player_t *player, bool forward)
75 {
76 depot_t *found = NULL;
77 koord3d found_pos = forward ? koord3d(welt->get_size().x+1,welt->get_size().y+1,welt->get_groundwater()) : koord3d(-1,-1,-1);
78 sint32 found_hash = forward ? 0x7FFFFFF : -1;
79 sint32 start_hash = start.x + (8192 * start.y);
80 FOR(slist_tpl<depot_t*>, const d, all_depots) {
81 if(d->get_typ()==depot_type && d->get_owner()==player) {
82 // ok, the right type of depot
83 const koord3d pos = d->get_pos();
84 if(pos==start) {
85 // ignore the start point
86 continue;
87 }
88 sint32 hash = (pos.x + (8192 * pos.y));
89 if(forward) {
90 if(hash>start_hash || (hash==start_hash && pos.z>start.z)) {
91 // found a suitable one
92 if(hash<found_hash || (hash==found_hash && pos.z<found_pos.z)) {
93 // which is closer ...
94 found = d;
95 found_pos = pos;
96 found_hash = hash;
97 }
98 }
99 }
100 else {
101 // search to start of the map
102 if(hash<start_hash || (hash==start_hash && pos.z<start.z)) {
103 // found a suitable one
104 if(hash>found_hash || (hash==found_hash && pos.z>found_pos.z)) {
105 // which is closer ...
106 found = d;
107 found_pos = pos;
108 found_hash = hash;
109 }
110 }
111 }
112 }
113 }
114 return found;
115 }
116
117
118 // again needed for server
call_depot_tool(char tool,convoihandle_t cnv,const char * extra)119 void depot_t::call_depot_tool( char tool, convoihandle_t cnv, const char *extra)
120 {
121 // call depot tool
122 tool_t *tmp_tool = create_tool( TOOL_CHANGE_DEPOT | SIMPLE_TOOL );
123 cbuffer_t buf;
124 buf.printf( "%c,%s,%hu", tool, get_pos().get_str(), cnv.get_id() );
125 if( extra ) {
126 buf.append( "," );
127 buf.append( extra );
128 }
129 tmp_tool->set_default_param(buf);
130 welt->set_tool( tmp_tool, get_owner() );
131 // since init always returns false, it is safe to delete immediately
132 delete tmp_tool;
133 }
134
135
136 /* this is called on two occasions:
137 * first a convoy reaches the depot during its journey
138 * second during loading a convoi is stored in a depot => only store it again
139 */
convoi_arrived(convoihandle_t acnv,bool schedule_adjust)140 void depot_t::convoi_arrived(convoihandle_t acnv, bool schedule_adjust)
141 {
142 if(schedule_adjust) {
143 // here a regular convoi arrived
144
145 for(unsigned i=0; i<acnv->get_vehicle_count(); i++) {
146 vehicle_t *v = acnv->get_vehikel(i);
147 // Hajo: reset vehikel data
148 v->discard_cargo();
149 v->set_pos( koord3d::invalid );
150 v->set_leading( i==0 );
151 v->set_last( i+1==acnv->get_vehicle_count() );
152 }
153 // Volker: remove depot from schedule
154 schedule_t *schedule = acnv->get_schedule();
155 for( int i=0; i<schedule->get_count(); i++ ) {
156 // only if convoi found
157 if(schedule->entries[i].pos==get_pos()) {
158 schedule->set_current_stop( i );
159 schedule->remove();
160 acnv->set_schedule(schedule);
161 break;
162 }
163 }
164 }
165 // this part stores the convoi in the depot
166 convois.append(acnv);
167 depot_frame_t *depot_frame = dynamic_cast<depot_frame_t *>(win_get_magic( (ptrdiff_t)this ));
168 if(depot_frame) {
169 depot_frame->action_triggered(NULL,(long int)0);
170 }
171 acnv->set_home_depot( get_pos() );
172 DBG_MESSAGE("depot_t::convoi_arrived()", "convoi %d, %p entered depot", acnv.get_id(), acnv.get_rep());
173 }
174
175
show_info()176 void depot_t::show_info()
177 {
178 create_win( new depot_frame_t(this), w_info, (ptrdiff_t)this );
179 }
180
181
buy_vehicle(const vehicle_desc_t * info)182 vehicle_t* depot_t::buy_vehicle(const vehicle_desc_t* info)
183 {
184 DBG_DEBUG("depot_t::buy_vehicle()", info->get_name());
185 vehicle_t* veh = vehicle_builder_t::build(get_pos(), get_owner(), NULL, info );
186 DBG_DEBUG("depot_t::buy_vehicle()", "vehiclebauer %p", veh);
187
188 vehicles.append(veh);
189 DBG_DEBUG("depot_t::buy_vehicle()", "appended %i vehicle", vehicles.get_count());
190 return veh;
191 }
192
193
append_vehicle(convoihandle_t cnv,vehicle_t * veh,bool infront,bool local_execution)194 void depot_t::append_vehicle(convoihandle_t cnv, vehicle_t* veh, bool infront, bool local_execution)
195 {
196 /* create a new convoi, if necessary */
197 if (!cnv.is_bound()) {
198 cnv = add_convoi( local_execution );
199 }
200 veh->set_pos(get_pos());
201 cnv->add_vehikel(veh, infront);
202 vehicles.remove(veh);
203 }
204
205
remove_vehicle(convoihandle_t cnv,int ipos)206 void depot_t::remove_vehicle(convoihandle_t cnv, int ipos)
207 {
208 vehicle_t* veh = cnv->remove_vehikel_bei( ipos );
209 if( veh ) {
210 vehicles.append( veh );
211 }
212 }
213
214
remove_vehicles_to_end(convoihandle_t cnv,int ipos)215 void depot_t::remove_vehicles_to_end(convoihandle_t cnv, int ipos)
216 {
217 while( vehicle_t* veh = cnv->remove_vehikel_bei( ipos ) ) {
218 vehicles.append( veh );
219 }
220 }
221
222
sell_vehicle(vehicle_t * veh)223 void depot_t::sell_vehicle(vehicle_t* veh)
224 {
225 vehicles.remove(veh);
226 get_owner()->book_new_vehicle((sint64)veh->calc_sale_value(), get_pos().get_2d(), get_waytype() );
227 DBG_MESSAGE("depot_t::sell_vehicle()", "this=%p sells %p", this, veh);
228 delete veh;
229 }
230
231
232 // returns the index of the oldest/newest vehicle in a list
find_oldest_newest(const vehicle_desc_t * desc,bool old)233 vehicle_t* depot_t::find_oldest_newest(const vehicle_desc_t* desc, bool old)
234 {
235 vehicle_t* found_veh = NULL;
236 FOR( slist_tpl<vehicle_t*>, const veh, vehicles ) {
237 if( veh->get_desc() == desc ) {
238 // joy of XOR, finally a line where I could use it!
239 if( found_veh == NULL ||
240 old ^ (found_veh->get_purchase_time() > veh->get_purchase_time()) ) {
241 found_veh = veh;
242 }
243 }
244 }
245 return found_veh;
246 }
247
248
add_convoi(bool local_execution)249 convoihandle_t depot_t::add_convoi(bool local_execution)
250 {
251 convoi_t* new_cnv = new convoi_t(get_owner());
252 new_cnv->set_home_depot(get_pos());
253 convois.append(new_cnv->self);
254 depot_frame_t *win = dynamic_cast<depot_frame_t *>(win_get_magic( (ptrdiff_t)this ));
255 if( win && local_execution ) {
256 win->activate_convoi( new_cnv->self );
257 }
258 return new_cnv->self;
259 }
260
261
check_obsolete_inventory(convoihandle_t cnv)262 bool depot_t::check_obsolete_inventory(convoihandle_t cnv)
263 {
264 bool ok = true;
265 slist_tpl<vehicle_t*> veh_tmp_list;
266
267 for( int i = 0; i < cnv->get_vehicle_count(); i++ ) {
268 const vehicle_desc_t* const vb = cnv->get_vehikel(i)->get_desc();
269 if( vb ) {
270 // search storage for matching vehicle
271 vehicle_t* veh = NULL;
272 for( slist_tpl<vehicle_t*>::iterator i = vehicles.begin(); i != vehicles.end(); ++i ) {
273 if( (*i)->get_desc() == vb ) {
274 // found in storage, remove to temp list while searching for next vehicle
275 veh = *i;
276 vehicles.erase(i);
277 veh_tmp_list.append( veh );
278 break;
279 }
280 }
281 if( !veh ) {
282 // need to buy new
283 if( vb->is_retired( welt->get_timeline_year_month() ) ) {
284 // is obsolete, return false
285 ok = false;
286 break;
287 }
288 }
289 }
290 }
291
292 // put vehicles back into storage
293 vehicles.append_list( veh_tmp_list );
294
295 return ok;
296 }
297
298
copy_convoi(convoihandle_t old_cnv,bool local_execution)299 convoihandle_t depot_t::copy_convoi(convoihandle_t old_cnv, bool local_execution)
300 {
301 if( old_cnv.is_bound() && !convoihandle_t::is_exhausted() &&
302 old_cnv->get_vehicle_count() > 0 && get_waytype() == old_cnv->front()->get_desc()->get_waytype() ) {
303
304 convoihandle_t new_cnv = add_convoi( false );
305 new_cnv->set_name(old_cnv->get_internal_name());
306 int vehicle_count = old_cnv->get_vehicle_count();
307 for (int i = 0; i<vehicle_count; i++) {
308 const vehicle_desc_t * info = old_cnv->get_vehikel(i)->get_desc();
309 if (info != NULL) {
310 // search in depot for an existing vehicle of correct type
311 vehicle_t* oldest_vehicle = get_oldest_vehicle(info);
312 if (oldest_vehicle != NULL) {
313 // append existing vehicle
314 append_vehicle( new_cnv, oldest_vehicle, false, local_execution );
315 }
316 else {
317 // buy new vehicle
318 vehicle_t* veh = vehicle_builder_t::build(get_pos(), get_owner(), NULL, info );
319 veh->set_pos(get_pos());
320 new_cnv->add_vehikel(veh, false);
321 }
322 }
323 }
324 if (old_cnv->get_line().is_bound()) {
325 new_cnv->set_line(old_cnv->get_line());
326 new_cnv->get_schedule()->set_current_stop( old_cnv->get_schedule()->get_current_stop() );
327 }
328 else {
329 if (old_cnv->get_schedule() != NULL) {
330 new_cnv->set_schedule(old_cnv->get_schedule()->copy());
331 }
332 }
333
334 // make this the current selected convoi
335 depot_frame_t *win = dynamic_cast<depot_frame_t *>(win_get_magic( (ptrdiff_t)this ));
336 if( win ) {
337 if( local_execution ) {
338 win->activate_convoi( new_cnv );
339 }
340 else {
341 win->update_data();
342 }
343 }
344
345 return new_cnv;
346 }
347 return convoihandle_t();
348 }
349
350
disassemble_convoi(convoihandle_t cnv,bool sell)351 bool depot_t::disassemble_convoi(convoihandle_t cnv, bool sell)
352 {
353 if( cnv.is_bound() ) {
354 if( !sell ) {
355 // store vehicles in depot
356 while( vehicle_t* const v = cnv->remove_vehikel_bei(0) ) {
357 v->discard_cargo();
358 v->set_leading(false);
359 v->set_last(false);
360 vehicles.append(v);
361 }
362 }
363
364 // remove from depot lists
365 remove_convoi( cnv );
366
367 // and remove from welt
368 cnv->self_destruct();
369 return true;
370 }
371 return false;
372 }
373
374
start_all_convoys()375 bool depot_t::start_all_convoys()
376 {
377 uint32 i = 0;
378 while( i < convois.get_count() ) {
379 if( !start_convoi( convois.at(i), false ) ) {
380 i++;
381 }
382 }
383 return (convois.get_count() == 0);
384 }
385
386 // implementation in simtool.cc
387 bool scenario_check_convoy(karte_t *welt, player_t *player, convoihandle_t cnv, depot_t* depot, bool local);
388
389
start_convoi(convoihandle_t cnv,bool local_execution)390 bool depot_t::start_convoi(convoihandle_t cnv, bool local_execution)
391 {
392 // close schedule window if not yet closed
393 if(cnv.is_bound() && cnv->get_schedule()!=NULL) {
394 if(!cnv->get_schedule()->is_editing_finished()) {
395 // close the schedule window
396 destroy_win((ptrdiff_t)cnv->get_schedule());
397 }
398 }
399
400 // convoi not in depot anymore, maybe user double-clicked on start-button
401 if(!convois.is_contained(cnv)) {
402 return false;
403 }
404
405 if (cnv.is_bound() && cnv->get_schedule() && !cnv->get_schedule()->empty()) {
406 // if next schedule entry is this depot => advance to next entry
407 const koord3d& cur_pos = cnv->get_schedule()->get_current_entry().pos;
408 if (cur_pos == get_pos()) {
409 cnv->get_schedule()->advance();
410 }
411
412 // check if convoi is complete
413 if(cnv->get_sum_power() == 0 || !cnv->pruefe_alle()) {
414 if (local_execution) {
415 create_win( new news_img("Diese Zusammenstellung kann nicht fahren!\n"), w_time_delete, magic_none);
416 }
417 }
418 else if( !cnv->front()->calc_route(this->get_pos(), cur_pos, cnv->get_min_top_speed(), cnv->access_route()) ) {
419 // no route to go ...
420 if(local_execution) {
421 static cbuffer_t buf;
422 buf.clear();
423 buf.printf( translator::translate("Vehicle %s can't find a route!"), cnv->get_name() );
424 create_win( new news_img(buf), w_time_delete, magic_none);
425 }
426 }
427 else if (!scenario_check_convoy(welt, get_owner(), cnv, this, local_execution) ) {
428 // not allowed by scenario
429 }
430 else {
431 // convoi can start now
432 cnv->start();
433
434 // remove from depot lists
435 remove_convoi( cnv );
436
437 return true;
438 }
439 }
440 else {
441 if (local_execution) {
442 create_win( new news_img("Noch kein Fahrzeug\nmit Fahrplan\nvorhanden\n"), w_time_delete, magic_none);
443 }
444
445 if (!cnv.is_bound()) {
446 dbg->warning("depot_t::start_convoi()","No convoi to start!");
447 } else if (!cnv->get_schedule()) {
448 dbg->warning("depot_t::start_convoi()","No schedule for convoi.");
449 } else if (!cnv->get_schedule()->is_editing_finished()) {
450 dbg->warning("depot_t::start_convoi()","Schedule is incomplete/not finished");
451 }
452 }
453 return false;
454 }
455
456
remove_convoi(convoihandle_t cnv)457 void depot_t::remove_convoi( convoihandle_t cnv )
458 {
459 depot_frame_t *win = dynamic_cast<depot_frame_t *>(win_get_magic( (ptrdiff_t)this ));
460 if( win ) {
461 // get currently selected convoi to restore selection if not removed
462 int icnv = win->get_icnv();
463 convoihandle_t c = icnv > -1 ? get_convoi( icnv ) : convoihandle_t();
464
465 icnv = convois.index_of( cnv );
466 convois.remove( cnv );
467
468 if( c == cnv ) {
469 // removing currently selected, select next in list or last instead
470 c = !convois.empty() ? convois.at( min((uint32)icnv, convois.get_count() - 1) ) : convoihandle_t();
471 }
472 win->activate_convoi( c );
473 }
474 else {
475 convois.remove( cnv );
476 }
477 }
478
479
480 // attention! this will not be used for railway depots! They will be loaded by hand ...
rdwr(loadsave_t * file)481 void depot_t::rdwr(loadsave_t *file)
482 {
483 gebaeude_t::rdwr(file);
484
485 rdwr_vehikel(vehicles, file);
486 if (file->is_version_less(81, 33)) {
487 // wagons are stored extra, just add them to vehicles
488 assert(file->is_loading());
489 rdwr_vehikel(vehicles, file);
490 }
491 }
492
493
rdwr_vehikel(slist_tpl<vehicle_t * > & list,loadsave_t * file)494 void depot_t::rdwr_vehikel(slist_tpl<vehicle_t *> &list, loadsave_t *file)
495 {
496 sint32 count;
497
498 if(file->is_saving()) {
499 count = list.get_count();
500 DBG_MESSAGE("depot_t::vehikel_laden()","saving %d vehicles",count);
501 }
502 file->rdwr_long(count);
503
504 if(file->is_loading()) {
505
506 // no house definition for this => use a normal hut ...
507 if( this->get_tile()==NULL ) {
508 dbg->error( "depot_t::rdwr()", "tile for depot not found!" );
509 set_tile( (*hausbauer_t::get_citybuilding_list( building_desc_t::city_res ))[0]->get_tile(0), true );
510 }
511
512 DBG_MESSAGE("depot_t::vehikel_laden()","loading %d vehicles",count);
513 for(int i=0; i<count; i++) {
514 obj_t::typ typ = (obj_t::typ)file->rd_obj_id();
515
516 vehicle_t *v = NULL;
517 const bool first = false;
518 const bool last = false;
519
520 switch( typ ) {
521 case old_automobil:
522 case road_vehicle: v = new road_vehicle_t(file, first, last); break;
523 case old_waggon:
524 case rail_vehicle: v = new rail_vehicle_t(file, first, last); break;
525 case old_schiff:
526 case water_vehicle: v = new water_vehicle_t(file, first, last); break;
527 case old_aircraft:
528 case air_vehicle: v = new air_vehicle_t(file, first, last); break;
529 case old_monorailwaggon:
530 case monorail_vehicle: v = new monorail_vehicle_t(file, first, last); break;
531 case maglev_vehicle: v = new maglev_vehicle_t(file, first, last); break;
532 case narrowgauge_vehicle: v = new narrowgauge_vehicle_t(file, first, last); break;
533 default:
534 dbg->fatal("depot_t::vehikel_laden()","invalid vehicle type $%X", typ);
535 }
536 if(v->get_desc()) {
537 DBG_MESSAGE("depot_t::vehikel_laden()","loaded %s", v->get_desc()->get_name());
538 list.insert( v );
539 }
540 else {
541 dbg->error("depot_t::vehikel_laden()","vehicle has no desc => ignored");
542 }
543 }
544 }
545 else {
546 FOR(slist_tpl<vehicle_t*>, const v, list) {
547 file->wr_obj_id(v->get_typ());
548 v->rdwr_from_convoi(file);
549 }
550 }
551 }
552
553
554 /**
555 * @return NULL when OK, otherwise an error message
556 * @author Hj. Malthaner
557 */
is_deletable(const player_t * player)558 const char * depot_t::is_deletable(const player_t *player)
559 {
560 if(player!=get_owner() && player!=welt->get_public_player()) {
561 return "Das Feld gehoert\neinem anderen Spieler\n";
562 }
563 if (!vehicles.empty()) {
564 return "There are still vehicles\nstored in this depot!\n";
565 }
566
567 FOR(slist_tpl<convoihandle_t>, const c, convois) {
568 if (c->get_vehicle_count() > 0) {
569 return "There are still vehicles\nstored in this depot!\n";
570 }
571 }
572 return NULL;
573 }
574
575
get_vehicle_type(uint8 sortkey) const576 slist_tpl<vehicle_desc_t const*> const& depot_t::get_vehicle_type(uint8 sortkey) const
577 {
578 return vehicle_builder_t::get_info(get_waytype(), sortkey);
579 }
580
581
get_oldest_vehicle(const vehicle_desc_t * desc)582 vehicle_t* depot_t::get_oldest_vehicle(const vehicle_desc_t* desc)
583 {
584 vehicle_t* oldest_veh = NULL;
585 FOR(slist_tpl<vehicle_t*>, const veh, get_vehicle_list()) {
586 if (veh->get_desc() == desc) {
587 if (oldest_veh == NULL ||
588 oldest_veh->get_purchase_time() > veh->get_purchase_time()) {
589 oldest_veh = veh;
590 }
591 }
592 }
593 return oldest_veh;
594 }
595
596
update_win()597 void depot_t::update_win()
598 {
599 depot_frame_t *depot_frame = dynamic_cast<depot_frame_t *>(win_get_magic( (ptrdiff_t)this ));
600 if(depot_frame) {
601 depot_frame->build_vehicle_lists();
602 }
603 }
604
605
new_month()606 void depot_t::new_month()
607 {
608 // since vehicles may have become obsolete
609 update_all_win();
610 }
611
612
update_all_win()613 void depot_t::update_all_win()
614 {
615 FOR(slist_tpl<depot_t*>, const d, all_depots) {
616 d->update_win();
617 }
618 }
619
620
get_max_convoi_length() const621 unsigned bahndepot_t::get_max_convoi_length() const
622 {
623 return welt->get_settings().get_max_rail_convoi_length();
624 }
get_max_convoi_length() const625 unsigned strassendepot_t::get_max_convoi_length() const
626 {
627 return welt->get_settings().get_max_road_convoi_length();
628 }
get_max_convoi_length() const629 unsigned schiffdepot_t::get_max_convoi_length() const
630 {
631 return welt->get_settings().get_max_ship_convoi_length();
632 }
get_max_convoi_length() const633 unsigned airdepot_t::get_max_convoi_length() const
634 {
635 return welt->get_settings().get_max_air_convoi_length();
636 }
637