1 /*
2 * Copyright (c) 1997 - 2001 Hansj�rg Malthaner
3 *
4 * This file is part of the Simutrans project under the artistic licence.
5 * (see licence.txt)
6 *
7 * Renovation in dec 2004 for other vehicles, timeline
8 * @author prissi
9 */
10
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <math.h>
14
15 #include "../simconvoi.h"
16 #include "../simdebug.h"
17 #include "../simdepot.h"
18 #include "../simhalt.h"
19 #include "../simintr.h"
20 #include "../simline.h"
21 #include "../simmesg.h"
22 #include "../simsound.h"
23 #include "../simticker.h"
24 #include "../simtool.h"
25 #include "../gui/simwin.h"
26 #include "../simworld.h"
27 #include "../display/viewport.h"
28
29 #include "../bauer/brueckenbauer.h"
30 #include "../bauer/hausbauer.h"
31 #include "../bauer/tunnelbauer.h"
32
33 #include "../descriptor/tunnel_desc.h"
34 #include "../descriptor/way_desc.h"
35
36 #include "../boden/grund.h"
37
38 #include "../dataobj/settings.h"
39 #include "../dataobj/scenario.h"
40 #include "../dataobj/loadsave.h"
41 #include "../dataobj/translator.h"
42 #include "../dataobj/environment.h"
43
44 #include "../obj/bruecke.h"
45 #include "../obj/gebaeude.h"
46 #include "../obj/leitung2.h"
47 #include "../obj/tunnel.h"
48
49 #include "../gui/messagebox.h"
50 #include "../gui/player_frame_t.h"
51
52 #include "../utils/cbuffer_t.h"
53 #include "../utils/simstring.h"
54
55 #include "../vehicle/simvehicle.h"
56
57 #include "simplay.h"
58 #include "finance.h"
59
60 karte_ptr_t player_t::welt;
61
62 #ifdef MULTI_THREAD
63 #include "../utils/simthread.h"
64 static pthread_mutex_t load_mutex = PTHREAD_MUTEX_INITIALIZER;
65 #endif
66
player_t(uint8 nr)67 player_t::player_t(uint8 nr) :
68 simlinemgmt()
69 {
70 finance = new finance_t(this, welt);
71 player_nr = nr;
72 player_age = 0;
73 active = false; // Don't start as an AI player
74 locked = false; // allowed to change anything
75 unlock_pending = false;
76
77 headquarter_pos = koord::invalid;
78 headquarter_level = 0;
79
80
81 welt->get_settings().set_player_color_to_default(this);
82
83 // we have different AI, try to find out our type:
84 sprintf(player_name_buf,"player %i",player_nr-1);
85 }
86
87
~player_t()88 player_t::~player_t()
89 {
90 while( !messages.empty() ) {
91 delete messages.remove_first();
92 }
93 destroy_win(magic_finances_t + get_player_nr());
94 delete finance;
95 finance = NULL;
96 }
97
98
book_construction_costs(player_t * const player,const sint64 amount,const koord k,const waytype_t wt)99 void player_t::book_construction_costs(player_t * const player, const sint64 amount, const koord k, const waytype_t wt)
100 {
101 if(player!=NULL) {
102 player->finance->book_construction_costs(amount, wt);
103 if(k != koord::invalid) {
104 player->add_money_message(amount, k);
105 }
106 }
107 }
108
109
add_maintenance(sint64 change,waytype_t const wt)110 sint64 player_t::add_maintenance(sint64 change, waytype_t const wt)
111 {
112 int tmp = 0;
113 #ifdef MULTI_THREAD
114 pthread_mutex_lock( &load_mutex );
115 #endif
116 tmp = finance->book_maintenance(change, wt);
117 #ifdef MULTI_THREAD
118 pthread_mutex_unlock( &load_mutex );
119 #endif
120 return tmp;
121 }
122
123
add_money_message(const sint64 amount,const koord pos)124 void player_t::add_money_message(const sint64 amount, const koord pos)
125 {
126 if(amount != 0 && player_nr != 1) {
127 if( koord_distance(welt->get_viewport()->get_world_position(),pos)<2*(uint32)(display_get_width()/get_tile_raster_width())+3 ) {
128 // only display, if near the screen ...
129 add_message(amount, pos);
130
131 // and same for sound too ...
132 if( amount>=10000 && !welt->is_fast_forward() ) {
133 welt->play_sound_area_clipped(pos, SFX_CASH);
134 }
135 }
136 }
137 }
138
139
book_new_vehicle(const sint64 amount,const koord k,const waytype_t wt)140 void player_t::book_new_vehicle(const sint64 amount, const koord k, const waytype_t wt)
141 {
142 finance->book_new_vehicle(amount, wt);
143 add_money_message(amount, k);
144 }
145
146
book_revenue(const sint64 amount,const koord k,const waytype_t wt,sint32 index)147 void player_t::book_revenue(const sint64 amount, const koord k, const waytype_t wt, sint32 index)
148 {
149 finance->book_revenue(amount, wt, index);
150 add_money_message(amount, k);
151 }
152
153
book_running_costs(const sint64 amount,const waytype_t wt)154 void player_t::book_running_costs(const sint64 amount, const waytype_t wt)
155 {
156 finance->book_running_costs(amount, wt);
157 }
158
159
book_toll_paid(const sint64 amount,const waytype_t wt)160 void player_t::book_toll_paid(const sint64 amount, const waytype_t wt)
161 {
162 finance->book_toll_paid(amount, wt);
163 }
164
165
book_toll_received(const sint64 amount,const waytype_t wt)166 void player_t::book_toll_received(const sint64 amount, const waytype_t wt)
167 {
168 finance->book_toll_received(amount, wt);
169 }
170
171
book_transported(const sint64 amount,const waytype_t wt,int index)172 void player_t::book_transported(const sint64 amount, const waytype_t wt, int index)
173 {
174 finance->book_transported(amount, wt, index);
175 }
176
book_delivered(const sint64 amount,const waytype_t wt,int index)177 void player_t::book_delivered(const sint64 amount, const waytype_t wt, int index)
178 {
179 finance->book_delivered(amount, wt, index);
180 }
181
182
get_name() const183 const char* player_t::get_name() const
184 {
185 return translator::translate(player_name_buf);
186 }
187
188
set_name(const char * new_name)189 void player_t::set_name(const char *new_name)
190 {
191 tstrncpy( player_name_buf, new_name, lengthof(player_name_buf) );
192
193 // update player window
194 if (ki_kontroll_t *frame = dynamic_cast<ki_kontroll_t *>( win_get_magic(magic_ki_kontroll_t) ) ) {
195 frame->update_data();
196 }
197 }
198
199
income_message_t(sint64 betrag,koord p)200 player_t::income_message_t::income_message_t( sint64 betrag, koord p )
201 {
202 money_to_string(str, betrag/100.0);
203 alter = 127;
204 pos = p;
205 amount = betrag;
206 }
207
208
operator new(size_t)209 void *player_t::income_message_t::operator new(size_t /*s*/)
210 {
211 return freelist_t::gimme_node(sizeof(player_t::income_message_t));
212 }
213
214
operator delete(void * p)215 void player_t::income_message_t::operator delete(void *p)
216 {
217 freelist_t::putback_node(sizeof(player_t::income_message_t),p);
218 }
219
220
display_messages()221 void player_t::display_messages()
222 {
223 const viewport_t *vp = welt->get_viewport();
224
225 FOR(slist_tpl<income_message_t*>, const m, messages) {
226
227 const scr_coord scr_pos = vp->get_screen_coord(koord3d(m->pos,welt->lookup_hgt(m->pos)),koord(0,m->alter >> 4));
228
229 display_shadow_proportional_rgb( scr_pos.x, scr_pos.y, PLAYER_FLAG|color_idx_to_rgb(player_color_1+3), color_idx_to_rgb(COL_BLACK), m->str, true);
230 if( m->pos.x < 3 || m->pos.y < 3 ) {
231 // very close to border => renew background
232 welt->set_background_dirty();
233 }
234 }
235 }
236
237
age_messages(uint32)238 void player_t::age_messages(uint32 /*delta_t*/)
239 {
240 for(slist_tpl<income_message_t *>::iterator iter = messages.begin(); iter != messages.end(); ) {
241 income_message_t *m = *iter;
242 m->alter -= 5;
243
244 if(m->alter<-80) {
245 iter = messages.erase(iter);
246 delete m;
247 }
248 else {
249 ++iter;
250 }
251 }
252 }
253
254
add_message(sint64 betrag,koord k)255 void player_t::add_message(sint64 betrag, koord k)
256 {
257 if( !messages.empty() && messages.back()->pos==k && messages.back()->alter==127 ) {
258 // last message exactly at same place, not aged
259 messages.back()->amount += betrag;
260 money_to_string(messages.back()->str, messages.back()->amount/100.0);
261 }
262 else {
263 // otherwise new message
264 income_message_t *m = new income_message_t(betrag,k);
265 messages.append( m );
266 }
267 }
268
269
set_player_color(uint8 col1,uint8 col2)270 void player_t::set_player_color(uint8 col1, uint8 col2)
271 {
272 player_color_1 = col1;
273 player_color_2 = col2;
274 display_set_player_color_scheme( player_nr, col1, col2 );
275 }
276
277
step()278 void player_t::step()
279 {
280 }
281
282
new_month()283 bool player_t::new_month()
284 {
285 // since the messages must remain on the screen longer ...
286 static cbuffer_t buf;
287
288 finance->new_month();
289
290 // new month has started => recalculate vehicle value
291 calc_assets();
292
293 finance->calc_finance_history();
294
295 simlinemgmt.new_month();
296
297 // Bankrupt ?
298 if( finance->get_account_balance() < 0 ) {
299 finance->increase_account_overdrawn();
300 if( !welt->get_settings().is_freeplay() && player_nr != 1 ) {
301 if( welt->get_active_player_nr()==player_nr && !env_t::networkmode ) {
302 if( finance->get_netwealth() < 0 ) {
303 destroy_all_win(true);
304 create_win( display_get_width()/2-128, 40, new news_img("Bankrott:\n\nDu bist bankrott.\n"), w_info, magic_none);
305 ticker::add_msg( translator::translate("Bankrott:\n\nDu bist bankrott.\n"), koord::invalid, PLAYER_FLAG + player_color_1 + 1 );
306 welt->stop(false);
307 }
308 else if( finance->get_netwealth()*10 < welt->get_settings().get_starting_money(welt->get_current_month()/12) ){
309 // tell the player (problem!)
310 welt->get_message()->add_message( translator::translate("Net wealth less than 10% of starting capital!"), koord::invalid, message_t::problems, player_nr, IMG_EMPTY );
311 }
312 else {
313 // tell the player (just warning)
314 buf.clear();
315 buf.printf( translator::translate("On loan since %i month(s)"), finance->get_account_overdrawn() );
316 welt->get_message()->add_message( buf, koord::invalid, message_t::ai, player_nr, IMG_EMPTY );
317 }
318 }
319 // no assets => nothing to go bankrupt about again
320 else if( finance->get_maintenance(TT_ALL) != 0 || finance->has_convoi() ) {
321
322 // for AI, we only declare bankrupt, if total assets are below zero
323 if( finance->get_netwealth() < 0 ) {
324 return false;
325 }
326 // tell the current player (even during networkgames)
327 if( welt->get_active_player_nr()==player_nr ) {
328 if( finance->get_netwealth()*10 < welt->get_settings().get_starting_money(welt->get_current_month()/12) ){
329 // net wealth nearly spent (problem!)
330 welt->get_message()->add_message( translator::translate("Net wealth near zero"), koord::invalid, message_t::problems, player_nr, IMG_EMPTY );
331 }
332 else {
333 // just minus in account (just tell)
334 buf.clear();
335 buf.printf( translator::translate("On loan since %i month(s)"), finance->get_account_overdrawn() );
336 welt->get_message()->add_message( buf, koord::invalid, message_t::ai, player_nr, IMG_EMPTY );
337 }
338 }
339 }
340 }
341 }
342 else {
343 finance->set_account_overdrawn( 0 );
344 }
345
346 if( env_t::networkmode && player_nr>1 && !active ) {
347 // find out dummy companies (i.e. no vehicle running within x months)
348 if( welt->get_settings().get_remove_dummy_player_months() && player_age >= welt->get_settings().get_remove_dummy_player_months() ) {
349 bool no_cnv = true;
350 const uint16 months = min( MAX_PLAYER_HISTORY_MONTHS, welt->get_settings().get_remove_dummy_player_months() );
351 for( uint16 m=0; m<months && no_cnv; m++ ) {
352 no_cnv &= finance->get_history_com_month(m, ATC_ALL_CONVOIS) ==0;
353 }
354 const uint16 years = max( MAX_PLAYER_HISTORY_YEARS, (welt->get_settings().get_remove_dummy_player_months() - 1) / 12 );
355 for( uint16 y=0; y<years && no_cnv; y++ ) {
356 no_cnv &= finance->get_history_com_year(y, ATC_ALL_CONVOIS)==0;
357 }
358 // never run a convoi => dummy
359 if( no_cnv ) {
360 return false; // remove immediately
361 }
362 }
363
364 // find out abandoned companies (no activity within x months)
365 if( welt->get_settings().get_unprotect_abandoned_player_months() && player_age >= welt->get_settings().get_unprotect_abandoned_player_months() ) {
366 bool abandoned = true;
367 const uint16 months = min( MAX_PLAYER_HISTORY_MONTHS, welt->get_settings().get_unprotect_abandoned_player_months() );
368 for( uint16 m = 0; m < months && abandoned; m++ ) {
369 abandoned &= finance->get_history_veh_month(TT_ALL, m, ATV_NEW_VEHICLE)==0 && finance->get_history_veh_month(TT_ALL, m, ATV_CONSTRUCTION_COST)==0;
370 }
371 const uint16 years = min( MAX_PLAYER_HISTORY_YEARS, (welt->get_settings().get_unprotect_abandoned_player_months() - 1) / 12);
372 for( uint16 y = 0; y < years && abandoned; y++ ) {
373 abandoned &= finance->get_history_veh_year(TT_ALL, y, ATV_NEW_VEHICLE)==0 && finance->get_history_veh_year(TT_ALL, y, ATV_CONSTRUCTION_COST)==0;
374 }
375 // never changed convoi, never built => abandoned
376 if( abandoned ) {
377 pwd_hash.clear();
378 locked = false;
379 unlock_pending = false;
380 }
381 }
382 }
383
384 // subtract maintenance after bankruptcy check
385 finance->book_account( -finance->get_maintenance_with_bits(TT_ALL) );
386 // company gets older ...
387 player_age ++;
388
389 return true; // still active
390 }
391
392
calc_assets()393 void player_t::calc_assets()
394 {
395 sint64 assets[TT_MAX];
396 for(int i=0; i < TT_MAX; ++i){
397 assets[i] = 0;
398 }
399 // all convois
400 FOR(vector_tpl<convoihandle_t>, const cnv, welt->convoys()) {
401 if( cnv->get_owner() == this ) {
402 sint64 restwert = cnv->calc_restwert();
403 assets[TT_ALL] += restwert;
404 assets[finance->translate_waytype_to_tt(cnv->front()->get_desc()->get_waytype())] += restwert;
405 }
406 }
407
408 // all vehikels stored in depot not part of a convoi
409 FOR(slist_tpl<depot_t*>, const depot, depot_t::get_depot_list()) {
410 if( depot->get_player_nr() == player_nr ) {
411 FOR(slist_tpl<vehicle_t*>, const veh, depot->get_vehicle_list()) {
412 sint64 restwert = veh->calc_sale_value();
413 assets[TT_ALL] += restwert;
414 assets[finance->translate_waytype_to_tt(veh->get_desc()->get_waytype())] += restwert;
415 }
416 }
417 }
418
419 finance->set_assets(assets);
420 }
421
422
update_assets(sint64 const delta,const waytype_t wt)423 void player_t::update_assets(sint64 const delta, const waytype_t wt)
424 {
425 finance->update_assets(delta, wt);
426 }
427
428
get_scenario_completion() const429 sint32 player_t::get_scenario_completion() const
430 {
431 return finance->get_scenario_completed();
432 }
433
434
set_scenario_completion(sint32 percent)435 void player_t::set_scenario_completion(sint32 percent)
436 {
437 finance->set_scenario_completed(percent);
438 }
439
440
check_owner(const player_t * owner,const player_t * test)441 bool player_t::check_owner( const player_t *owner, const player_t *test )
442 {
443 return owner == test || owner == NULL || test == welt->get_public_player();
444 }
445
446
ai_bankrupt()447 void player_t::ai_bankrupt()
448 {
449 player_t *const psplayer = welt->get_public_player();
450
451 DBG_MESSAGE("player_t::ai_bankrupt()","Removing convois");
452
453 for (size_t i = welt->convoys().get_count(); i-- != 0;) {
454 convoihandle_t const cnv = welt->convoys()[i];
455 if(cnv->get_owner()!=this) {
456 continue;
457 }
458
459 linehandle_t line = cnv->get_line();
460
461 cnv->self_destruct();
462 // convois not in depots must step to really get rid of them
463 if( cnv.is_bound() && cnv->get_state() != convoi_t::INITIAL ) {
464 cnv->step();
465 }
466
467 // last vehicle on that connection (no line => railroad)
468 if( !line.is_bound() || line->count_convoys()==0 ) {
469 simlinemgmt.delete_line( line );
470 }
471 }
472
473 // remove headquarters pos
474 headquarter_pos = koord::invalid;
475
476 // remove all stops
477 // first generate list of our stops
478 slist_tpl<halthandle_t> halt_list;
479 FOR(vector_tpl<halthandle_t>, const halt, haltestelle_t::get_alle_haltestellen()) {
480 if( halt->get_owner()==this ) {
481 halt_list.append(halt);
482 }
483 }
484 // ... and destroy them
485 while (!halt_list.empty()) {
486 halthandle_t h = halt_list.remove_first();
487 haltestelle_t::destroy( h );
488 }
489
490 // transfer all ways in public stops belonging to me to no one
491 FOR(vector_tpl<halthandle_t>, const halt, haltestelle_t::get_alle_haltestellen()) {
492 if( halt->get_owner()==welt->get_public_player() ) {
493 // only concerns public stops tiles
494 FOR(slist_tpl<haltestelle_t::tile_t>, const& i, halt->get_tiles()) {
495 grund_t const* const gr = i.grund;
496 for( uint8 wnr=0; wnr<2; wnr++ ) {
497 weg_t *w = gr->get_weg_nr(wnr);
498 // make public
499 if( w && w->get_owner()==this ) {
500 // tunnels and bridges are handled later? (logic needs to be checked for correct maintenance costs)
501 if (!gr->ist_bruecke() && !gr->ist_tunnel()) {
502 sint32 const costs = w->get_desc()->get_maintenance();
503 waytype_t const wt = w->get_desc()->get_finance_waytype();
504 player_t::add_maintenance(this, -costs, wt);
505 player_t::add_maintenance(psplayer, costs, wt);
506 }
507 w->set_owner(psplayer);
508 }
509 }
510 }
511 }
512 }
513
514 // deactivate active tool (remove dummy grounds)
515 welt->set_tool(tool_t::general_tool[TOOL_QUERY], this);
516
517 // next remove all ways, depot etc, that are not road or channels
518 for( int y=0; y<welt->get_size().y; y++ ) {
519 for( int x=0; x<welt->get_size().x; x++ ) {
520 planquadrat_t *plan = welt->access(x,y);
521 for (size_t b = plan->get_boden_count(); b-- != 0;) {
522 grund_t *gr = plan->get_boden_bei(b);
523 // remove tunnel and bridges first
524 if( gr->get_top()>0 && gr->obj_bei(0)->get_owner()==this && (gr->ist_bruecke() || gr->ist_tunnel()) ) {
525 koord3d pos = gr->get_pos();
526
527 waytype_t wt = gr->hat_wege() ? gr->get_weg_nr(0)->get_waytype() : powerline_wt;
528 if (gr->ist_bruecke()) {
529 bridge_builder_t::remove( this, pos, wt );
530 // fails if powerline bridge somehow connected to powerline bridge of another player
531 }
532 else {
533 tunnel_builder_t::remove( this, pos, wt, true );
534 }
535 // maybe there are some objects left (station on bridge head etc)
536 gr = plan->get_boden_in_hoehe(pos.z);
537 if (gr == NULL) {
538 continue;
539 }
540 }
541 for (uint8 i = gr->get_top(); i-- != 0;) {
542 obj_t *obj = gr->obj_bei(i);
543 if(obj->get_owner()==this) {
544 sint32 costs = 0;
545 waytype_t wt = ignore_wt;
546 switch(obj->get_typ()) {
547 case obj_t::roadsign:
548 case obj_t::signal:
549 case obj_t::airdepot:
550 case obj_t::bahndepot:
551 case obj_t::monoraildepot:
552 case obj_t::tramdepot:
553 case obj_t::strassendepot:
554 case obj_t::schiffdepot:
555 case obj_t::senke:
556 case obj_t::pumpe:
557 case obj_t::wayobj:
558 case obj_t::label:
559 obj->cleanup(this);
560 delete obj;
561 break;
562 case obj_t::leitung:
563 // do not remove powerline from bridges
564 if(gr->ist_bruecke()) {
565 costs = ((leitung_t*)obj)->get_desc()->get_maintenance();
566 add_maintenance(-costs, powerline_wt);
567 psplayer->add_maintenance(costs, powerline_wt);
568 obj->set_owner(psplayer);
569 }
570 else {
571 obj->cleanup(this);
572 delete obj;
573 }
574 break;
575 case obj_t::gebaeude:
576 hausbauer_t::remove( this, (gebaeude_t *)obj );
577 break;
578 case obj_t::way:
579 {
580 weg_t *w=(weg_t *)obj;
581 // tunnels and bridges made public
582 if (gr->ist_bruecke() || gr->ist_tunnel()) {
583 w->set_owner(psplayer);
584 }
585 // roads and water ways also made public
586 else if(w->get_waytype()==road_wt || w->get_waytype()==water_wt) {
587 costs = w->get_desc()->get_maintenance();
588 wt = w->get_waytype();
589 add_maintenance(-costs, wt);
590 psplayer->add_maintenance(costs, wt);
591 w->set_owner(psplayer);
592 }
593 else {
594 gr->weg_entfernen( w->get_waytype(), true );
595 }
596 break;
597 }
598 case obj_t::bruecke:
599 costs = ((bruecke_t*)obj)->get_desc()->get_maintenance();
600 wt = obj->get_waytype();
601 add_maintenance(-costs, wt);
602 psplayer->add_maintenance(costs, wt);
603 obj->set_owner(psplayer);
604 break;
605 case obj_t::tunnel:
606 costs = ((tunnel_t*)obj)->get_desc()->get_maintenance();
607 wt = ((tunnel_t*)obj)->get_desc()->get_finance_waytype();
608 add_maintenance(-costs, wt);
609 psplayer->add_maintenance(costs, wt);
610 obj->set_owner(psplayer);
611 break;
612
613 default:
614 obj->set_owner(psplayer);
615 }
616 }
617 }
618 // remove empty tiles (elevated ways)
619 if (!gr->ist_karten_boden() && gr->get_top()==0) {
620 plan->boden_entfernen(gr);
621 }
622 }
623 }
624 }
625
626 active = false;
627 // make account negative
628 if (finance->get_account_balance() > 0) {
629 finance->book_account( -finance->get_account_balance() -1 );
630 }
631
632 cbuffer_t buf;
633 buf.printf( translator::translate("%s\nwas liquidated."), get_name() );
634 welt->get_message()->add_message( buf, koord::invalid, message_t::ai, PLAYER_FLAG|player_nr );
635 }
636
637
rdwr(loadsave_t * file)638 void player_t::rdwr(loadsave_t *file)
639 {
640 xml_tag_t sss( file, "spieler_t" );
641
642 if(file->is_version_less(112, 5)) {
643 sint64 konto = finance->get_account_balance();
644 file->rdwr_longlong(konto);
645 finance->set_account_balance(konto);
646
647 sint32 account_overdrawn = finance->get_account_overdrawn();
648 file->rdwr_long(account_overdrawn);
649 finance->set_account_overdrawn( account_overdrawn );
650 }
651
652 if(file->is_version_less(101, 0)) {
653 // ignore steps
654 sint32 ldummy=0;
655 file->rdwr_long(ldummy);
656 }
657
658 if(file->is_version_less(99, 9)) {
659 sint32 farbe;
660 file->rdwr_long(farbe);
661 player_color_1 = (uint8)farbe*2;
662 player_color_2 = player_color_1+24;
663 }
664 else {
665 file->rdwr_byte(player_color_1);
666 file->rdwr_byte(player_color_2);
667 }
668
669 sint32 halt_count=0;
670 if(file->is_version_less(99, 8)) {
671 file->rdwr_long(halt_count);
672 }
673 if(file->is_version_less(112, 3)) {
674 sint32 haltcount = 0;
675 file->rdwr_long(haltcount);
676 }
677
678 // save all the financial statistics
679 finance->rdwr( file );
680
681 file->rdwr_bool(active);
682
683 // state is not saved anymore
684 if(file->is_version_less(99, 14)) {
685 sint32 ldummy=0;
686 file->rdwr_long(ldummy);
687 file->rdwr_long(ldummy);
688 }
689
690 // the AI stuff is now saved directly by the different AI
691 if( file->is_version_less(101, 0)) {
692 sint32 ldummy = -1;
693 file->rdwr_long(ldummy);
694 file->rdwr_long(ldummy);
695 file->rdwr_long(ldummy);
696 file->rdwr_long(ldummy);
697 koord k(-1,-1);
698 k.rdwr( file );
699 k.rdwr( file );
700 }
701
702 if(file->is_loading()) {
703
704 // halt_count will be zero for newer savegames
705 DBG_DEBUG("player_t::rdwr()","player %i: loading %i halts.",welt->sp2num( this ),halt_count);
706 for(int i=0; i<halt_count; i++) {
707 haltestelle_t::create( file );
708 }
709 // empty undo buffer
710 init_undo(road_wt,0);
711 }
712
713 // headquarters stuff
714 if (file->is_version_less(86, 4))
715 {
716 headquarter_level = 0;
717 headquarter_pos = koord::invalid;
718 }
719 else {
720 file->rdwr_long(headquarter_level);
721 headquarter_pos.rdwr( file );
722 if(file->is_loading()) {
723 if(headquarter_level<0) {
724 headquarter_pos = koord::invalid;
725 headquarter_level = 0;
726 }
727 }
728 }
729
730 // line management
731 if(file->is_version_atleast(88, 3)) {
732 simlinemgmt.rdwr(file,this);
733 }
734
735 if(file->is_version_atleast(102, 3)) {
736 // password hash
737 for( int i=0; i<20; i++ ) {
738 file->rdwr_byte(pwd_hash[i]);
739 }
740 if( file->is_loading() ) {
741 // disallow all actions, if password set (might be unlocked by password gui )
742 locked = !pwd_hash.empty();
743 }
744 }
745
746 // save the name too
747 if( file->is_version_atleast(102, 4) ) {
748 file->rdwr_str( player_name_buf, lengthof(player_name_buf) );
749 }
750
751 // save age
752 if( file->is_version_atleast(112, 2) ) {
753 file->rdwr_short( player_age );
754 }
755 }
756
757
finish_rd()758 void player_t::finish_rd()
759 {
760 simlinemgmt.finish_rd();
761 display_set_player_color_scheme( player_nr, player_color_1, player_color_2 );
762 // recalculate vehicle value
763 calc_assets();
764
765 finance->calc_finance_history();
766 }
767
768
rotate90(const sint16 y_size)769 void player_t::rotate90( const sint16 y_size )
770 {
771 simlinemgmt.rotate90( y_size );
772 headquarter_pos.rotate90( y_size );
773 }
774
775
report_vehicle_problem(convoihandle_t cnv,const koord3d position)776 void player_t::report_vehicle_problem(convoihandle_t cnv,const koord3d position)
777 {
778 switch(cnv->get_state()) {
779
780 case convoi_t::NO_ROUTE:
781 DBG_MESSAGE("player_t::report_vehicle_problem","Vehicle %s can't find a route to (%i,%i)!", cnv->get_name(),position.x,position.y);
782 {
783 cbuffer_t buf;
784 buf.printf( translator::translate("Vehicle %s can't find a route!"), cnv->get_name());
785 welt->get_message()->add_message( (const char *)buf, cnv->get_pos().get_2d(), message_t::problems, PLAYER_FLAG | player_nr, cnv->front()->get_base_image());
786 }
787 break;
788
789 case convoi_t::WAITING_FOR_CLEARANCE_ONE_MONTH:
790 case convoi_t::CAN_START_ONE_MONTH:
791 case convoi_t::CAN_START_TWO_MONTHS:
792 DBG_MESSAGE("player_t::report_vehicle_problem","Vehicle %s stuck!", cnv->get_name(),position.x,position.y);
793 {
794 cbuffer_t buf;
795 buf.printf( translator::translate("Vehicle %s is stucked!"), cnv->get_name());
796 welt->get_message()->add_message( (const char *)buf, cnv->get_pos().get_2d(), message_t::warnings, PLAYER_FLAG | player_nr, cnv->front()->get_base_image());
797 }
798 break;
799
800 default:
801 DBG_MESSAGE("player_t::report_vehicle_problem","Vehicle %s, state %i!", cnv->get_name(), cnv->get_state());
802 }
803 (void)position;
804 }
805
806
init_undo(waytype_t wtype,unsigned short max)807 void player_t::init_undo( waytype_t wtype, unsigned short max )
808 {
809 // only human player
810 // prissi: allow for UNDO for real player
811 DBG_MESSAGE("player_t::int_undo()","undo tiles %i",max);
812 last_built.clear();
813 last_built.resize(max+1);
814 if(max>0) {
815 undo_type = wtype;
816 }
817
818 }
819
820
add_undo(koord3d k)821 void player_t::add_undo(koord3d k)
822 {
823 if(last_built.get_size()>0) {
824 //DBG_DEBUG("player_t::add_undo()","tile at (%i,%i)",k.x,k.y);
825 last_built.append(k);
826 }
827 }
828
829
undo()830 sint64 player_t::undo()
831 {
832 if (last_built.empty()) {
833 // nothing to UNDO
834 return false;
835 }
836 // check, if we can still do undo
837 FOR(vector_tpl<koord3d>, const& i, last_built) {
838 grund_t* const gr = welt->lookup(i);
839 if(gr==NULL || gr->get_typ()!=grund_t::boden) {
840 // well, something was built here ... so no undo
841 last_built.clear();
842 return false;
843 }
844 // we allow ways, unimportant stuff but no vehicles, signals, wayobjs etc
845 if(gr->obj_count()>0) {
846 for( unsigned i=0; i<gr->get_top(); i++ ) {
847 switch(gr->obj_bei(i)->get_typ()) {
848 // these are allowed
849 case obj_t::zeiger:
850 case obj_t::wolke:
851 case obj_t::leitung:
852 case obj_t::pillar:
853 case obj_t::way:
854 case obj_t::label:
855 case obj_t::crossing:
856 case obj_t::pedestrian:
857 case obj_t::road_user:
858 case obj_t::movingobj:
859 break;
860 // special case airplane
861 // they can be everywhere, so we allow for everything but runway undo
862 case obj_t::air_vehicle: {
863 if(undo_type!=air_wt) {
864 break;
865 }
866 const air_vehicle_t* aircraft = obj_cast<air_vehicle_t>(gr->obj_bei(i));
867 // flying aircrafts are ok
868 if(!aircraft->is_on_ground()) {
869 break;
870 }
871 }
872 /* FALLTHROUGH */
873 // all other are forbidden => no undo any more
874 default:
875 last_built.clear();
876 return false;
877 }
878 }
879 }
880 }
881
882 // ok, now remove everything last built
883 sint64 cost=0;
884 FOR(vector_tpl<koord3d>, const& i, last_built) {
885 grund_t* const gr = welt->lookup(i);
886 if( undo_type != powerline_wt ) {
887 cost += gr->weg_entfernen(undo_type,true);
888 }
889 else {
890 if (leitung_t* lt = gr->get_leitung()) {
891 cost += lt->get_desc()->get_price();
892 lt->cleanup(NULL);
893 delete lt;
894 }
895 }
896 }
897 last_built.clear();
898 return cost;
899 }
900
901
tell_tool_result(tool_t * tool,koord3d,const char * err)902 void player_t::tell_tool_result(tool_t *tool, koord3d, const char *err)
903 {
904 /* tools can return three kinds of messages
905 * NULL = success
906 * "" = failure, but just do not try again
907 * "bla" error message, which should be shown
908 */
909 if (welt->get_active_player()==this) {
910 if(err==NULL) {
911 if(tool->ok_sound!=NO_SOUND) {
912 sound_play(tool->ok_sound);
913 }
914 }
915 else if(*err!=0) {
916 // something went really wrong
917 sound_play(SFX_FAILURE);
918 // look for coordinate in error message
919 // syntax: either @x,y or (x,y)
920 open_error_msg_win(err);
921 }
922 }
923 }
924
925
book_convoi_number(int count)926 void player_t::book_convoi_number(int count)
927 {
928 finance->book_convoi_number(count);
929 }
930
931
get_account_balance_as_double() const932 double player_t::get_account_balance_as_double() const
933 {
934 return finance->get_account_balance() / 100.0;
935 }
936
937
get_account_overdrawn() const938 int player_t::get_account_overdrawn() const
939 {
940 return finance->get_account_overdrawn();
941 }
942
943
has_money_or_assets() const944 bool player_t::has_money_or_assets() const
945 {
946 return finance->has_money_or_assets();
947 }
948
can_afford(sint64 cost) const949 bool player_t::can_afford(sint64 cost) const
950 {
951 return welt->get_settings().is_freeplay() ||
952 is_public_service() ||
953 get_finance()->get_netwealth() >= -cost;
954 }
955
is_public_service() const956 bool player_t::is_public_service() const
957 {
958 return get_player_nr() == 1;
959 }
960