1 /*
2 * Tools for the players
3 *
4 * Copyright (c) 1997 - 2001 Hj. Malthaner
5 *
6 * This file is part of the Simutrans project under the artistic license.
7 * (see license.txt)
8 */
9
10 #include <stdio.h>
11 #include <string.h>
12 #include <math.h>
13
14 #include "simdebug.h"
15 #include "simevent.h"
16 #include "simcity.h"
17 #include "simmesg.h"
18 #include "simconvoi.h"
19 #include "gui/simwin.h"
20 #include "display/viewport.h"
21
22 #include "bauer/fabrikbauer.h"
23 #include "bauer/vehikelbauer.h"
24
25 #include "boden/grund.h"
26 #include "boden/wasser.h"
27 #include "boden/wege/schiene.h"
28 #include "boden/tunnelboden.h"
29 #include "boden/monorailboden.h"
30
31 #include "simdepot.h"
32 #include "simfab.h"
33 #include "display/simimg.h"
34 #include "simintr.h"
35 #include "simhalt.h"
36 #include "simskin.h"
37
38 #include "descriptor/ground_desc.h"
39 #include "descriptor/building_desc.h"
40 #include "descriptor/roadsign_desc.h"
41 #include "descriptor/tunnel_desc.h"
42
43 #include "vehicle/simvehicle.h"
44 #include "vehicle/simroadtraffic.h"
45 #include "vehicle/simpeople.h"
46
47 #include "gui/line_management_gui.h"
48 #include "gui/tool_selector.h"
49 #include "gui/station_building_select.h"
50 #include "gui/minimap.h" // to update map after construction of new industry
51 #include "gui/depot_frame.h"
52 #include "gui/schedule_gui.h"
53 #include "gui/player_frame_t.h"
54 #include "gui/schedule_list.h"
55 #include "gui/signal_spacing.h"
56 #include "gui/city_info.h"
57 #include "gui/trafficlight_info.h"
58 #include "gui/privatesign_info.h"
59 #include "gui/messagebox.h"
60
61 #include "obj/zeiger.h"
62 #include "obj/bruecke.h"
63 #include "obj/tunnel.h"
64 #include "obj/groundobj.h"
65 #include "obj/signal.h"
66 #include "obj/crossing.h"
67 #include "obj/roadsign.h"
68 #include "obj/wayobj.h"
69 #include "obj/leitung2.h"
70 #include "obj/baum.h"
71 #include "obj/field.h"
72 #include "obj/label.h"
73
74 #include "dataobj/koord.h"
75 #include "dataobj/settings.h"
76 #include "dataobj/environment.h"
77 #include "dataobj/schedule.h"
78 #include "dataobj/route.h"
79 #include "dataobj/scenario.h"
80 #include "network/network_cmd_ingame.h" // for dragging raise / lower tools
81
82 #include "bauer/tunnelbauer.h"
83 #include "bauer/brueckenbauer.h"
84 #include "bauer/wegbauer.h"
85 #include "bauer/hausbauer.h"
86
87 #include "descriptor/way_desc.h"
88 #include "descriptor/roadsign_desc.h"
89
90 #include "tpl/vector_tpl.h"
91
92 #include "network/memory_rw.h"
93 #include "utils/simrandom.h"
94 #include "utils/simstring.h"
95
96 #include "simtool.h"
97 #include "player/finance.h"
98
99
100 #define is_scenario() welt->get_scenario()->is_scripted()
101
102 #define CHECK_FUNDS() \
103 /* do not allow, if out of money */ \
104 if( !welt->get_settings().is_freeplay() && player->get_player_nr()!=1 && !player->has_money_or_assets() ) {\
105 return "Out of funds";\
106 }\
107
108 /****************************************** notification strings **************************************/
109
110 /**
111 * Translated notification text identifiers used by tools are placed here.
112 * This is because they are not simple well structured internal identifiers.
113 * Instead they can be complex sentences intended to be read untranslated.
114 * Using these constants assues a valid and correct text identifier is choosen.
115 */
116
117
118
119 /**
120 * Message returned when a player cannot afford to complete an action.
121 */
122 char const *const NOTICE_INSUFFICIENT_FUNDS = "Insufficient funds!";
123
124 /**
125 * Message returned when a player tries to place trees when trees are disabled.
126 */
127 char const *const NOTICE_NO_TREES = "Trees disabled!";
128
129 /**
130 * Message returned when valid terrain cannot be found for a tool to use.
131 */
132 char const *const NOTICE_UNSUITABLE_GROUND = "No suitable ground!";
133
134 /**
135 * Message returned when a depot cannot be placed.
136 */
137 char const *const NOTICE_DEPOT_BAD_POS = "Cannot built depot here!";
138
139 /**
140 * Message returned when a tool fails due to the target tile being occupied.
141 */
142 char const *const NOTICE_TILE_FULL = "Tile not empty.";
143
144 /**
145 * Message returned when a company tries to make a public way when public ways are disabled.
146 */
147 char const *const NOTICE_DISABLED_PUBLIC_WAY = "Not allowed to make publicly owned ways!";
148
149 /****************************************** static helper functions **************************************/
150
151 /**
152 * Creates a tooltip from tip text and money value
153 * @author Hj. Malthaner
154 */
tooltip_with_price(const char * tip,sint64 price)155 char *tooltip_with_price(const char * tip, sint64 price)
156 {
157 const int n = sprintf(tool_t::toolstr, "%s, ", translator::translate(tip) );
158 money_to_string(tool_t::toolstr+n, (double)price/-100.0);
159 return tool_t::toolstr;
160 }
161
162
163
164
165 /**
166 * Creates a tooltip from tip text, money value and way/object length
167 * @author captain crunch
168 */
tooltip_with_price_length(const char * tip,sint64 price,sint64 length)169 char *tooltip_with_price_length(const char * tip, sint64 price, sint64 length)
170 {
171 int n;
172 n = sprintf(tool_t::toolstr, translator::translate("length: %d"), length);
173 n += sprintf(tool_t::toolstr+n, ", %s: ", translator::translate(tip));
174 money_to_string(tool_t::toolstr+n, (double)price/-100.0);
175 return tool_t::toolstr;
176 }
177
178
179
180
181 /**
182 * Creates a tooltip from tip text and money value
183 * @author Hj. Malthaner
184 */
tooltip_with_price_maintenance(karte_t * welt,const char * tip,sint64 price,sint64 maintenance)185 char *tooltip_with_price_maintenance(karte_t *welt, const char *tip, sint64 price, sint64 maintenance)
186 {
187 int n = sprintf(tool_t::toolstr, "%s, ", translator::translate(tip) );
188 money_to_string(tool_t::toolstr+n, (double)price/-100.0);
189 strcat( tool_t::toolstr, " (" );
190 n = strlen(tool_t::toolstr);
191
192 money_to_string(tool_t::toolstr+n, (double)(welt->scale_with_month_length(maintenance) ) / 100.0);
193 strcat( tool_t::toolstr, ")" );
194 return tool_t::toolstr;
195 }
196
197
198
199 /**
200 * Creates a tooltip from tip text and money value
201 */
tooltip_with_price_maintenance_capacity(karte_t * const welt,char const * const tip,sint64 const price,sint64 const maintenance,uint32 const capacity,uint8 const enables)202 static char const* tooltip_with_price_maintenance_capacity(karte_t* const welt, char const* const tip, sint64 const price, sint64 const maintenance, uint32 const capacity, uint8 const enables)
203 {
204 int n = sprintf(tool_t::toolstr, "%s, ", translator::translate(tip) );
205 money_to_string(tool_t::toolstr+n, (double)price/-100.0);
206 strcat( tool_t::toolstr, " (" );
207 n = strlen(tool_t::toolstr);
208
209 money_to_string(tool_t::toolstr+n, (double)(welt->scale_with_month_length(maintenance) ) / 100.0);
210 strcat( tool_t::toolstr, ")" );
211 n = strlen(tool_t::toolstr);
212
213 if((enables&7)!=0) {
214 n += sprintf( tool_t::toolstr+n, ", %d", capacity );
215 if(enables&1) {
216 n += sprintf( tool_t::toolstr+n, " %s", translator::translate("Passagiere") );
217 }
218 if(enables&2) {
219 n += sprintf( tool_t::toolstr+n, " %s", translator::translate("Post") );
220 }
221 if(enables&4) {
222 n += sprintf( tool_t::toolstr+n, " %s", translator::translate("Fracht") );
223 }
224 }
225 else if (!welt->get_settings().is_separate_halt_capacities()) {
226 n += sprintf( tool_t::toolstr+n, ", %s %d", translator::translate("Storage capacity"), capacity );
227 }
228
229 return tool_t::toolstr;
230 }
231
232
open_error_msg_win(const char * error)233 void open_error_msg_win(const char* error)
234 {
235 koord pos = message_t::get_coord_from_text(error);
236 if (pos != koord::invalid) {
237 create_win( new news_loc(error, pos), w_time_delete, magic_none);
238 }
239 else {
240 create_win( new news_img(error), w_time_delete, magic_none);
241 }
242 }
243
244
245 /**
246 * sucht Haltestelle um Umkreis +1/-1 um (pos, b, h)
247 * extended to search first in our direction
248 * @author Hj. Malthaner, V.Meyer, prissi
249 */
suche_nahe_haltestelle(player_t * player,karte_t * welt,koord3d pos,sint16 b=1,sint16 h=1)250 static halthandle_t suche_nahe_haltestelle(player_t *player, karte_t *welt, koord3d pos, sint16 b=1, sint16 h=1)
251 {
252 koord k(pos.get_2d());
253
254 // any other ground with a valid stop here?
255 halthandle_t my_halt;
256 if( planquadrat_t* plan=welt->access(pos.get_2d()) ) {
257 my_halt = plan->get_halt( player );
258 if( my_halt.is_bound() ) {
259 return my_halt;
260 }
261 }
262
263 grund_t *bd = welt->lookup(pos);
264 if( bd==NULL ) {
265 bd = welt->lookup_kartenboden(k);
266 }
267
268 // first we try to connect to a stop straight in our direction; otherwise our station may break during construction
269 if( bd->hat_wege() ) {
270 ribi_t::ribi ribi = bd->get_weg_nr(0)->get_ribi_unmasked();
271 for( int i=0; i<4; i++ ) {
272 if( ribi_t::nsew[i] & ribi ) {
273 if( planquadrat_t* plan=welt->access(k+koord::nsew[i]) ) {
274 my_halt = plan->get_halt( player );
275 if( my_halt.is_bound() ) {
276 return my_halt;
277 }
278 }
279 }
280 }
281 }
282
283 // now just search all neighbours
284 for( sint16 y=-1; y<=h; y++ ) {
285 for( sint16 x=-1; x<=b; (x==-1 && y>-1 && y<h) ? x=b:x++ ) {
286 if( planquadrat_t* plan=welt->access(k+koord(x,y)) ) {
287 my_halt = plan->get_halt( player );
288 if( my_halt.is_bound() ) {
289 return my_halt;
290 }
291 }
292 }
293 }
294
295 #if AUTOJOIN_PUBLIC
296 // now search everything for public stops
297 for( int i=0; i<8; i++ ) {
298 if( planquadrat_t* plan=welt->access(k+koord::neighbours[i]) ) {
299 my_halt = plan->get_halt( welt->get_public_player() );
300 if( my_halt.is_bound() ) {
301 return my_halt;
302 }
303 }
304 }
305 #endif
306
307 // here we reach only with a non-found station, i.e. a non-bounded handle
308 return halthandle_t();
309 }
310
311
312 // converts a 2d koord to a suitable ground pointer
tool_intern_koord_to_weg_grund(player_t * player,karte_t * welt,koord3d pos,waytype_t wt)313 static grund_t *tool_intern_koord_to_weg_grund(player_t *player, karte_t *welt, koord3d pos, waytype_t wt)
314 {
315 // check for valid ground
316 grund_t *gr=welt->lookup(pos);
317 if (gr==NULL) {
318 return NULL;
319 }
320
321 if( wt==powerline_wt && gr->get_leitung() ) {
322 // check for ownership
323 if(gr->get_leitung()->is_deletable(player)!=NULL) {
324 return NULL;
325 }
326 // ok
327 else {
328 return gr;
329 }
330 }
331
332 // tram
333 if(wt==tram_wt) {
334 weg_t *way = gr->get_weg(track_wt);
335 if (way && way->get_desc()->get_styp() == type_tram && way->is_deletable(player)==NULL) {
336 return gr;
337 }
338 else {
339 return NULL;
340 }
341 }
342
343
344 // has some rail or monorail?
345 if( !gr->hat_weg(wt) ) {
346 return NULL;
347 }
348 // check for ownership
349 if(gr->get_weg(wt)->is_deletable(player)!=NULL){
350 return NULL;
351 }
352 // ok, now we have a valid ground
353 return gr;
354 }
355
356
357
358 /****************************************** now the actual tools **************************************/
work(player_t *,koord3d pos)359 const char *tool_query_t::work( player_t *, koord3d pos )
360 {
361 grund_t *gr = welt->lookup(pos);
362 if(gr) {
363 // not single_info: show least important first
364 const bool reverse = !env_t::single_info || is_ctrl_pressed();
365
366 // iterate through different stages of importance
367 const uint8 max_stages = 4;
368 for(uint8 stage = 0; stage<max_stages; stage++) {
369
370 int old_count = win_get_open_count();
371
372 switch (reverse ? max_stages-1-stage: stage) {
373 case 0: { // halts
374 if( gr->get_halt().is_bound() ) {
375 gr->get_halt()->open_info_window();
376 }
377 break;
378 }
379 case 1: // labels
380 if( gr->get_flag(grund_t::marked) ) {
381 label_t *lb = gr->find<label_t>();
382 if( lb ) {
383 lb->show_info();
384 if( old_count < win_get_open_count() ) {
385 return NULL;
386 }
387 }
388 }
389 break;
390 case 2: { // objects
391 convoihandle_t cnv;
392 for (size_t n = gr->get_top(); n-- != 0;) {
393 obj_t *obj = gr->obj_bei(reverse ? gr->get_top()-1-n : n);
394
395 if (vehicle_t* veh = dynamic_cast<vehicle_t*>(obj)) {
396 if (veh->get_convoi()->self == cnv) {
397 continue; // do not try to open the same window twice, does not work so great with env_t::second_open_closes_win
398 }
399 cnv = veh->get_convoi()->self;
400 }
401 if( obj && obj->get_typ()!=obj_t::wayobj && obj->get_typ()!=obj_t::pillar && obj->get_typ()!=obj_t::label ) {
402 DBG_MESSAGE("tool_query_t()", "index %u", (unsigned)n);
403 obj->show_info();
404 // did some new window open?
405 if(env_t::single_info && old_count < win_get_open_count()) {
406 return NULL;
407 }
408 old_count = win_get_open_count(); // click may have closed a window, open a new one if possible
409 }
410 }
411 break;
412 }
413 case 3:
414 default: // ground
415 gr->open_info_window();
416 break;
417 }
418
419 if( env_t::single_info && old_count < win_get_open_count() ) {
420 return NULL;
421 }
422 }
423 }
424 return NULL;
425 }
426
427
428 /* delete things from a tile
429 * citycars and pedestrian first and then go up to queue to more important objects
430 */
tool_remover_intern(player_t * player,koord3d pos,sint8 type,const char * & msg)431 bool tool_remover_t::tool_remover_intern(player_t *player, koord3d pos, sint8 type, const char *&msg)
432 {
433 DBG_MESSAGE("tool_remover_intern()","at (%s)", pos.get_str());
434 // check if there is something to remove from here ...
435 grund_t *gr = welt->lookup(pos);
436 if (!gr || gr->get_top()==0) {
437 msg = "";
438 return false;
439 }
440
441 // marker?
442 if (type == obj_t::label || type == obj_t::undefined) {
443 if (label_t* l = gr->find<label_t>()) {
444 msg = l->is_deletable(player);
445 if(msg==NULL) {
446 delete l;
447 return true;
448 }
449 else if( gr->get_top()==1 || type == obj_t::label ) {
450 // only complain if this is the last object on this tile ...
451 return false;
452 }
453 msg = NULL;
454 // not deletable: skip it
455 }
456 }
457
458 // citycar? (we allow always)
459 if (type == obj_t::road_vehicle || type == obj_t::undefined) {
460 if (private_car_t* citycar = gr->find<private_car_t>()) {
461 delete citycar;
462 return true;
463 }
464 }
465 // pedestrians?
466 if (type == obj_t::pedestrian || type == obj_t::undefined) {
467 if (pedestrian_t* pedestrian = gr->find<pedestrian_t>()) {
468 delete pedestrian;
469 return true;
470 }
471 }
472
473 koord k(pos.get_2d());
474
475 // prissi: check powerline (can cross ground of another player)
476 leitung_t* lt = gr->get_leitung();
477 // check whether powerline related stuff should be removed, and if there is any to remove
478 if ( (type == obj_t::leitung || type == obj_t::pumpe || type == obj_t::senke || type == obj_t::undefined)
479 && lt != NULL && lt->is_deletable(player) == NULL) {
480 if( gr->ist_bruecke() ) {
481 bruecke_t* br = gr->find<bruecke_t>();
482 if( br == NULL ) {
483 // no bridge? most likely transformer on a former bridge tile...
484 grund_t *gr_new = new boden_t(pos, gr->get_grund_hang());
485 gr_new->take_obj_from( gr );
486 welt->access(k)->kartenboden_setzen( gr_new );
487 gr = gr_new;
488 }
489 else if( br->get_desc()->get_waytype() == powerline_wt ) {
490 msg = bridge_builder_t::remove(player, gr->get_pos(), powerline_wt );
491 return msg == NULL;
492 }
493 }
494 if(gr->ist_tunnel() && gr->ist_karten_boden()) {
495 if (gr->find<tunnel_t>()->get_desc()->get_waytype()==powerline_wt) {
496 msg = tunnel_builder_t::remove(player, gr->get_pos(), powerline_wt, is_ctrl_pressed() );
497 return msg == NULL;
498 }
499 }
500 if( gr->ist_im_tunnel() ) {
501 lt->cleanup(player);
502 delete lt;
503 // now everything gone?
504 if( gr->get_top() == 1 ) {
505 // delete tunnel too
506 tunnel_t *t = gr->find<tunnel_t>();
507 t->cleanup(player);
508 delete t;
509 }
510 // unmark kartenboden (is marked during underground mode deletion)
511 welt->lookup_kartenboden(k)->clear_flag(grund_t::marked);
512 // remove upper or lower ground
513 welt->access(k)->boden_entfernen(gr);
514 delete gr;
515 }
516 else {
517 lt->cleanup(player);
518 delete lt;
519 }
520 return true;
521 }
522
523 // check for signal
524 roadsign_t* rs = gr->find<signal_t>();
525 if (rs == NULL) rs = gr->find<roadsign_t>();
526 if ( (type == obj_t::signal || type == obj_t::roadsign || type == obj_t::undefined) && rs!=NULL) {
527 msg = rs->is_deletable(player);
528 if(msg) {
529 return false;
530 }
531 DBG_MESSAGE("tool_remover()", "removing roadsign at (%s)", pos.get_str());
532 weg_t *weg = gr->get_weg(rs->get_desc()->get_wtyp());
533 if( weg==NULL && rs->get_desc()->get_wtyp()==tram_wt ) {
534 weg = gr->get_weg(track_wt);
535 }
536 rs->cleanup(player);
537 delete rs;
538 assert( weg );
539 weg->count_sign();
540 return true;
541 }
542
543 // check stations
544 halthandle_t halt = gr->get_halt();
545 DBG_MESSAGE("tool_remover()", "bound=%i",halt.is_bound());
546 if (gr->is_halt() && halt.is_bound() && fabrik_t::get_fab(k)==NULL && type == obj_t::undefined) {
547 // halt and not a factory (oil rig etc.)
548 const player_t* owner = halt->get_owner();
549 if( player_t::check_owner( owner, player ) ) {
550 return haltestelle_t::remove(player, gr->get_pos());
551 }
552 }
553
554 // catenary or something like this
555 wayobj_t* wo = gr->find<wayobj_t>();
556 if(wo && (type == obj_t::wayobj || type == obj_t::undefined)) {
557 msg = wo->is_deletable(player);
558 if(msg) {
559 return false;
560 }
561 wo->cleanup(player);
562 delete wo;
563 depot_t *dep = gr->get_depot();
564 if( dep ) {
565 dep->update_win();
566 }
567 return true;
568 }
569
570 DBG_MESSAGE("tool_remover()", "check tunnel/bridge");
571
572 // bridge?
573 if(gr->ist_bruecke() && (type == obj_t::bruecke || type == obj_t::undefined)) {
574 DBG_MESSAGE("tool_remover()", "removing bridge from %d,%d,%d",gr->get_pos().x, gr->get_pos().y, gr->get_pos().z);
575 bruecke_t* br = gr->find<bruecke_t>();
576 msg = bridge_builder_t::remove(player, gr->get_pos(), br->get_desc()->get_waytype());
577 return msg == NULL;
578 }
579
580 // beginning/end of tunnel
581 if(gr->ist_tunnel() && gr->ist_karten_boden() && (type == obj_t::tunnel || type == obj_t::undefined)) {
582 DBG_MESSAGE("tool_remover()", "removing tunnel from %d,%d,%d",gr->get_pos().x, gr->get_pos().y, gr->get_pos().z);
583 waytype_t wegtyp = gr->get_leitung() ? powerline_wt : gr->get_weg_nr(0)->get_waytype();
584 msg = tunnel_builder_t::remove(player, gr->get_pos(), wegtyp, is_ctrl_pressed());
585 return msg == NULL;
586 }
587
588 // fields
589 field_t* f = gr->find<field_t>();
590 if (f && (type == obj_t::field || type == obj_t::undefined)) {
591 msg = f->is_deletable(player);
592 if(msg==NULL) {
593 f->cleanup(player);
594 delete f;
595 // fields have foundations ...
596 sint8 dummy;
597 welt->access(k)->boden_ersetzen( gr, new boden_t(gr->get_pos(), welt->recalc_natural_slope(k,dummy) ) );
598 welt->lookup_kartenboden(k)->calc_image();
599 welt->lookup_kartenboden(k)->set_flag( grund_t::dirty );
600 }
601 return msg == NULL;
602 }
603
604 // depots
605 depot_t* dep = gr->get_depot();
606 if (dep && (type == obj_t::bahndepot || type == obj_t::undefined)) {
607 // type == bahndepot to remove any type of depot
608 msg = dep->is_deletable(player);
609 if(msg) {
610 return false;
611 }
612 dep->cleanup(player);
613 delete dep;
614 return true;
615 }
616
617 // since buildings can have more than one tile, we must handle them together
618 gebaeude_t* gb = gr->find<gebaeude_t>();
619 if(gb != NULL && (type == obj_t::gebaeude || type == obj_t::undefined)) {
620 msg = gb->is_deletable(player);
621 if(msg) {
622 return false;
623 }
624 if(!gb->get_tile()->get_desc()->can_rotate() && welt->cannot_save()) {
625 msg = "Not possible in this rotation!";
626 return false;
627 }
628 DBG_MESSAGE("tool_remover()", "removing building" );
629
630 // remove town? (when removing townhall)
631 if(gb->is_townhall()) {
632 stadt_t *stadt = welt->find_nearest_city(k);
633 if(!welt->remove_city( stadt )) {
634 msg = "Das Feld gehoert\neinem anderen Spieler\n";
635 return false;
636 }
637 }
638 else {
639 // townhall is also removed during town removal
640 hausbauer_t::remove( player, gb );
641 }
642 return true;
643 }
644
645 // if type is given, then leave here. Below other stuff and ways gets removed.
646 if (type != obj_t::undefined) {
647 msg = "Requested object not found.";
648 return false;
649 }
650
651 // there is a powerline above this tile, but we do not own it
652 // so we take it out and add it later again
653 if(lt) {
654 DBG_MESSAGE("tool_remover()", "took out powerline");
655 gr->obj_remove(lt);
656 }
657
658 // do not delete crossing, so we remove it
659 crossing_t *cr = gr->find<crossing_t>(2);
660 if(cr) {
661 gr->obj_remove(cr);
662 }
663 // do not delete pointers - they may come from players on other clients
664 zeiger_t *zeiger = gr->find<zeiger_t>();
665 if(zeiger) {
666 gr->obj_remove(zeiger);
667 }
668 // do not delete other players label
669 label_t *label = gr->find<label_t>();
670 if(label) {
671 gr->obj_remove(label);
672 }
673
674 // remove all other stuff (clouds, ...)
675 bool return_ok = false;
676 uint8 num_obj = gr->obj_count();
677 if(num_obj>0) {
678 msg = gr->kann_alle_obj_entfernen(player);
679 return_ok = (msg==NULL && !(gr->get_typ()==grund_t::brueckenboden || gr->get_typ()==grund_t::tunnelboden) && gr->obj_loesche_alle(player));
680 DBG_MESSAGE("tool_remover()", "removing everything from %d,%d,%d",gr->get_pos().x, gr->get_pos().y, gr->get_pos().z);
681 }
682
683 if(lt) {
684 DBG_MESSAGE("tool_remover()", "add again powerline");
685 gr->obj_add(lt);
686 }
687 if(cr) {
688 gr->obj_add(cr);
689 }
690 if(zeiger) {
691 gr->obj_add(zeiger);
692 }
693 if(label) {
694 gr->obj_add(label);
695 }
696
697 // could not delete everything
698 if(msg) {
699 return false;
700 }
701 if(return_ok) {
702 // no sound
703 msg = "";
704 return true;
705 }
706
707 // ok, now we remove every object that should be removed - one by one.
708 // the following objects will be removed together
709 DBG_MESSAGE("tool_remover()", "removing way");
710
711 waytype_t wt = ignore_wt;
712 if(gr->get_typ()!=grund_t::tunnelboden || gr->has_two_ways()) {
713 weg_t *w = gr->get_weg_nr(1);
714 if(gr->get_typ()==grund_t::brueckenboden && w==NULL) {
715 // do not delete the middle of a bridge
716 return false;
717 }
718 if( w && w->get_waytype()==water_wt ) {
719 // remove the other way first
720 w = NULL;
721 }
722 if(w==NULL || w->is_deletable(player)!=NULL) {
723 w = gr->get_weg_nr(0);
724 if(w==NULL) {
725 // no way at all ...
726 return true;
727 }
728 if(w->is_deletable(player)!=NULL){
729 msg = w->is_deletable(player);
730 return false;
731 }
732 }
733 wt = w->get_desc()->get_finance_waytype();
734 sint32 cost_sum = gr->weg_entfernen(w->get_waytype(), true);
735 player_t::book_construction_costs(player, -cost_sum, k, wt);
736 }
737 else {
738 // remove ways and tunnel
739 if( weg_t *weg = gr->get_weg_nr(0) ) {
740 gr->remove_everything_from_way(player, weg->get_waytype(), ribi_t::none);
741 }
742 // tunnel without way: delete anything else
743 if( !gr->hat_wege() ) {
744 gr->obj_loesche_alle(player);
745 }
746 }
747
748 // remove empty tile
749 if( !gr->ist_karten_boden() && gr->get_top()==0 ) {
750 // unmark kartenboden (is marked during underground mode deletion)
751 welt->lookup_kartenboden(k)->clear_flag(grund_t::marked);
752 // remove upper or lower ground
753 welt->access(k)->boden_entfernen(gr);
754 delete gr;
755 }
756
757 return true;
758 }
759
760
761
work(player_t * player,koord3d pos)762 const char *tool_remover_t::work( player_t *player, koord3d pos )
763 {
764 DBG_MESSAGE("tool_remover()","at %d,%d", pos.x, pos.y);
765
766 obj_t::typ type = obj_t::undefined;
767
768 if (default_param) {
769 int t = atoi(default_param);
770 if (t != 0 && -1 <= t && t <= 200) {
771 type = (obj_t::typ)t;
772 }
773 }
774
775 const char *fail = NULL;
776 if(!tool_remover_intern(player, pos, type, fail)) {
777 return fail;
778 }
779
780 // must recalc neighbourhood for slopes etc.
781 if(pos.x>1) {
782 welt->lookup_kartenboden(pos.get_2d()+koord::west)->calc_image();
783 }
784 if(pos.y>1) {
785 welt->lookup_kartenboden(pos.get_2d()+koord::north)->calc_image();
786 }
787
788 if(pos.x<welt->get_size().x-1) {
789 welt->lookup_kartenboden(pos.get_2d()+koord::east)->calc_image();
790 }
791 if(pos.y<welt->get_size().y-1) {
792 welt->lookup_kartenboden(pos.get_2d()+koord::south)->calc_image();
793 }
794
795 return NULL;
796 }
797
798
799
move(player_t * player,uint16 buttonstate,koord3d pos)800 const char *tool_raise_lower_base_t::move( player_t *player, uint16 buttonstate, koord3d pos )
801 {
802 CHECK_FUNDS();
803
804 const char *result = NULL;
805 if( buttonstate==1 ) {
806 char buf[16];
807 if(!is_dragging) {
808 drag_height = get_drag_height(pos.get_2d());
809 }
810 is_dragging = true;
811 sprintf( buf, "%i", drag_height );
812 default_param = buf;
813 if (env_t::networkmode) {
814 // queue tool for network
815 nwc_tool_t *nwc = new nwc_tool_t(player, this, pos, welt->get_steps(), welt->get_map_counter(), false);
816 network_send_server(nwc);
817 }
818 else {
819 result = work( player, pos );
820 }
821 default_param = NULL;
822 }
823 return result;
824 }
825
826
drag(player_t * player,koord k,sint16 height,int & n)827 const char* tool_raise_lower_base_t::drag(player_t *player, koord k, sint16 height, int &n)
828 {
829 if( !welt->is_within_grid_limits(k) ) {
830 return "";
831 }
832 const char* err = NULL;
833
834 // dragging may be going up or down!
835 while( welt->lookup_hgt(k) < height && height <= welt->get_maximumheight() ) {
836 int diff = welt->grid_raise( player, k, err );
837 if( diff == 0 ) {
838 break;
839 }
840 n += diff;
841 }
842
843 // when going down need to check here we will not be going below sea level
844 // cannot rely on check within lower as water height can be recalculated
845 while( height >= welt->get_water_hgt(k) && welt->lookup_hgt(k) > height && height >= welt->get_minimumheight() ) {
846 int diff = welt->grid_lower( player, k, err );
847 if( diff == 0 ) {
848 break;
849 }
850 n += diff;
851 }
852
853 return err; //height == welt->lookup_hgt(k);
854 }
855
856
check_dragging()857 bool tool_raise_lower_base_t::check_dragging()
858 {
859 // reset dragging
860 if( is_dragging && strempty(default_param) ) {
861 is_dragging = false;
862 return false;
863 }
864 return true;
865 }
866
867
get_drag_height(koord k)868 sint16 tool_raise_t::get_drag_height(koord k)
869 {
870 const grund_t *gr = welt->lookup_kartenboden_gridcoords(k);
871
872 return gr->get_hoehe(welt->get_corner_to_operate(k)) + 1;
873 }
874
875
check_pos(player_t *,koord3d pos)876 const char *tool_raise_t::check_pos(player_t *, koord3d pos )
877 {
878 // check for underground mode
879 if( is_dragging && drag_height-1 > grund_t::underground_level ) {
880 is_dragging = false;
881 return "";
882 }
883 if( !welt->is_within_grid_limits(pos.get_2d()) ) {
884 return "";
885 }
886 sint8 h = (sint8) get_drag_height(pos.get_2d());
887 if( h > grund_t::underground_level ) {
888 return "Terraforming not possible\nhere in underground view";
889 }
890 return NULL;
891 }
892
893
work(player_t * player,koord3d pos)894 const char *tool_raise_t::work(player_t* player, koord3d pos )
895 {
896 if (!check_dragging()) {
897 return NULL;
898 }
899
900 const char* err = NULL;
901 koord k = pos.get_2d();
902
903 CHECK_FUNDS();
904
905 if(welt->is_within_grid_limits(k)) {
906
907 const sint8 hgt = (sint8) get_drag_height(k);
908
909 if( hgt <= welt->get_maximumheight() ) {
910
911 int n = 0; // tiles changed
912 if( !strempty(default_param) ) {
913 // called by dragging or by AI
914 err = drag(player, k, atoi(default_param), n);
915 }
916 else {
917 n = welt->grid_raise(player, k, err);
918 }
919 if(n>0) {
920 player_t::book_construction_costs(player, welt->get_settings().cst_alter_land * n, k, ignore_wt);
921 }
922 return err == NULL ? (n ? NULL : "")
923 : (*err == 0 ? NOTICE_TILE_FULL : err);
924 }
925 else {
926 // no mountains higher than welt->get_maximumheight() ...
927 return "Maximum tile height difference reached.";
928 }
929 }
930 return "Zu nah am Kartenrand";
931 }
932
933
get_drag_height(koord k)934 sint16 tool_lower_t::get_drag_height(koord k)
935 {
936 const grund_t *gr = welt->lookup_kartenboden_gridcoords(k);
937
938 return gr->get_hoehe(welt->get_corner_to_operate(k)) - 1;
939 }
940
941
check_pos(player_t *,koord3d pos)942 const char *tool_lower_t::check_pos( player_t *, koord3d pos )
943 {
944 // check for underground mode
945 if (is_dragging && drag_height+1 > grund_t::underground_level) {
946 is_dragging = false;
947 return "";
948 }
949 if (! welt->is_within_grid_limits(pos.get_2d())) {
950 return "";
951 }
952 sint8 h = (sint8) get_drag_height(pos.get_2d());
953 if (h > grund_t::underground_level) {
954 return "Terraforming not possible\nhere in underground view";
955 }
956 return NULL;
957 }
958
959
work(player_t * player,koord3d pos)960 const char *tool_lower_t::work( player_t *player, koord3d pos )
961 {
962 if (!check_dragging()) {
963 return NULL;
964 }
965
966 const char* err = NULL;
967 koord k = pos.get_2d();
968
969 CHECK_FUNDS();
970
971 if(welt->is_within_grid_limits(k)) {
972 const sint8 hgt = (sint8) get_drag_height(k);
973
974 if( hgt >= welt->get_water_hgt( k ) ) {
975 int n = 0; // tiles changed
976 if (!strempty(default_param)) {
977 // called by dragging or by AI
978 err = drag(player, k, atoi(default_param), n);
979 }
980 else {
981 n = welt->grid_lower(player, k, err);
982 }
983 if(n>0) {
984 player_t::book_construction_costs(player, welt->get_settings().cst_alter_land * n, k, ignore_wt);
985 }
986 return err == NULL ? (n ? NULL : "")
987 : (*err == 0 ? NOTICE_TILE_FULL : err);
988 }
989 else {
990 // below water level
991 return "";
992 }
993 }
994 return "Zu nah am Kartenrand";
995 }
996
997
check_pos(player_t *,koord3d pos)998 const char *tool_setslope_t::check_pos( player_t *, koord3d pos)
999 {
1000 grund_t *gr1 = welt->lookup(pos);
1001 if(gr1) {
1002 // check for underground mode
1003 if( grund_t::underground_mode == grund_t::ugm_all && !gr1->ist_tunnel() ) {
1004 return "Terraforming not possible\nhere in underground view";
1005 }
1006 }
1007 else {
1008 return "";
1009 }
1010 return NULL;
1011 }
1012
check_pos(player_t *,koord3d pos)1013 const char *tool_restoreslope_t::check_pos( player_t *, koord3d pos)
1014 {
1015 grund_t *gr1 = welt->lookup(pos);
1016 if(gr1) {
1017 // check for underground mode
1018 if( grund_t::underground_mode == grund_t::ugm_all && !gr1->ist_tunnel() ) {
1019 return "Terraforming not possible\nhere in underground view";
1020 }
1021 }
1022 else {
1023 return "";
1024 }
1025 return NULL;
1026 }
1027
tool_set_slope_work(player_t * player,koord3d pos,int new_slope)1028 const char *tool_setslope_t::tool_set_slope_work( player_t *player, koord3d pos, int new_slope )
1029 {
1030 if( !ground_desc_t::double_grounds ) {
1031 // translate old single slope parameter to new double slope
1032 if( 0 < new_slope && new_slope < ALL_UP_SLOPE_SINGLE ) {
1033 new_slope = scorner_sw(new_slope) + scorner_se(new_slope) * 3 + scorner_ne(new_slope) * 9 + scorner_nw(new_slope) * 27;
1034 }
1035 else {
1036 switch( new_slope ) {
1037 case ALL_UP_SLOPE:
1038 case ALL_UP_SLOPE_SINGLE: new_slope = ALL_UP_SLOPE; break;
1039 case ALL_DOWN_SLOPE:
1040 case ALL_DOWN_SLOPE_SINGLE: new_slope = ALL_DOWN_SLOPE; break;
1041 case RESTORE_SLOPE:
1042 case RESTORE_SLOPE_SINGLE: new_slope = RESTORE_SLOPE; break;
1043 default:
1044 return ""; // invalid parameter
1045 }
1046 }
1047 }
1048
1049 bool ok = false;
1050
1051 grund_t *gr1 = welt->lookup(pos);
1052 if( gr1 ) {
1053 koord k(pos.get_2d());
1054
1055 sint8 water_hgt = welt->get_water_hgt( k );
1056
1057 const uint8 max_hdiff = ground_desc_t::double_grounds ? 2 : 1;
1058
1059 // at least a pixel away from the border?
1060 if( pos.z < water_hgt && !gr1->ist_tunnel() ) {
1061 return "Maximum tile height difference reached.";
1062 }
1063
1064 if( new_slope==RESTORE_SLOPE && !(gr1->get_typ()==grund_t::boden || gr1->get_typ()==grund_t::wasser) ) {
1065 return NOTICE_UNSUITABLE_GROUND;
1066 }
1067
1068 // finally: empty enough
1069 if( gr1->get_grund_hang()!=gr1->get_weg_hang() || gr1->get_halt().is_bound() || gr1->kann_alle_obj_entfernen(player) ||
1070 gr1->find<gebaeude_t>() || gr1->get_depot() || (gr1->get_leitung() && gr1->hat_wege()) || gr1->get_weg(air_wt) || gr1->find<label_t>() || gr1->get_typ()==grund_t::brueckenboden) {
1071 return NOTICE_TILE_FULL;
1072 }
1073
1074 if( !welt->is_within_limits(k+koord(1,1)) || !welt->is_within_limits(k+koord(-1,-1))) {
1075 return "Zu nah am Kartenrand";
1076 }
1077
1078 // slopes may affect the position and the total height!
1079 koord3d new_pos = pos;
1080
1081 if( gr1->hat_wege() || gr1->get_leitung() ) {
1082 // check the resulting slope
1083 ribi_t::ribi ribis = 0;
1084 if( gr1->hat_wege()) {
1085 ribis |= gr1->get_weg_nr(0)->get_ribi_unmasked();
1086 if( gr1->get_weg_nr(1) ) {
1087 ribis |= gr1->get_weg_nr(1)->get_ribi_unmasked();
1088 }
1089 }
1090 if( gr1->get_leitung()) {
1091 ribis |= gr1->get_leitung()->get_ribi();
1092 }
1093
1094 if( new_slope==RESTORE_SLOPE || !ribi_t::is_single(ribis) || (new_slope<slope_t::raised && ribi_t::backward(ribi_type(new_slope))!=ribis) ) {
1095 // has the wrong tilt
1096 return NOTICE_TILE_FULL;
1097 }
1098 // reverse ribis: up to here was direction leaving the tile,
1099 // now it will be the direction on the tile when moving onto the slope
1100 ribis = ribi_t::reverse_single(ribis);
1101 /* new things getting tricky:
1102 * A single way on an all up or down slope will result in
1103 * a slope with the way as hinge.
1104 */
1105 if( new_slope==ALL_UP_SLOPE ) {
1106 if( gr1->get_weg_hang()==slope_t::flat ) {
1107 new_slope = slope_type(ribis);
1108 }
1109 else if( gr1->get_weg_hang() == slope_type(ribis) ) {
1110 // check that way_desc supports such steep slopes
1111 if( (gr1->get_weg_nr(0) && !gr1->get_weg_nr(0)->get_desc()->has_double_slopes())
1112 || (gr1->get_weg_nr(1) && !gr1->get_weg_nr(1)->get_desc()->has_double_slopes())
1113 || (gr1->get_leitung() && !gr1->get_leitung()->get_desc()->has_double_slopes()) ) {
1114 return NOTICE_TILE_FULL;
1115 }
1116 new_slope = slope_type(ribis) * 2;
1117 }
1118 else if( gr1->get_weg_hang() == slope_type( ribi_t::backward(ribis) ) * 2 ) {
1119 new_pos.z++;
1120 if( welt->lookup(new_pos) ) {
1121 return NOTICE_TILE_FULL;
1122 }
1123 new_slope = slope_type( ribi_t::backward(ribis) );
1124 }
1125 else if( gr1->get_weg_hang() != slope_type( ribi_t::backward(ribis) ) ) {
1126 return "Maximum tile height difference reached.";
1127 }
1128 }
1129 else if( new_slope==ALL_DOWN_SLOPE ) {
1130 if( gr1->get_grund_hang()==slope_type(ribis) ) {
1131 // do not lower tiles to sea
1132 if( pos.z == water_hgt && !gr1->ist_tunnel() ) {
1133 return NOTICE_TILE_FULL;
1134 }
1135 }
1136 else if( gr1->get_grund_hang() == slope_type(ribis) * 2 ) {
1137 if( pos.z == water_hgt && !gr1->ist_tunnel() ) {
1138 return NOTICE_TILE_FULL;
1139 }
1140 new_slope = slope_type(ribis);
1141 }
1142 else if( gr1->get_grund_hang() == slope_t::flat ) {
1143 new_slope = slope_type( ribi_t::backward(ribis) );
1144 new_pos.z--;
1145 if( welt->lookup(new_pos) ) {
1146 return NOTICE_TILE_FULL;
1147 }
1148 }
1149 else if( gr1->get_grund_hang() == slope_type( ribi_t::backward(ribis) ) ) {
1150 // check that way_desc supports such steep slopes
1151 if( (gr1->get_weg_nr(0) && !gr1->get_weg_nr(0)->get_desc()->has_double_slopes())
1152 || (gr1->get_weg_nr(1) && !gr1->get_weg_nr(1)->get_desc()->has_double_slopes())
1153 || (gr1->get_leitung() && !gr1->get_leitung()->get_desc()->has_double_slopes()) ) {
1154 return NOTICE_TILE_FULL;
1155 }
1156 new_slope = slope_type( ribi_t::backward(ribis) ) * 2;
1157 new_pos.z--;
1158 if( welt->lookup(new_pos) ) {
1159 return NOTICE_TILE_FULL;
1160 }
1161 }
1162 else {
1163 return "Maximum tile height difference reached.";
1164 }
1165 }
1166 }
1167
1168 if( new_slope == ALL_DOWN_SLOPE || new_slope == RESTORE_SLOPE ) {
1169 if( new_slope == RESTORE_SLOPE ) {
1170 // prissi: special action: set to natural slope
1171 sint8 min_hgt;
1172 new_slope = welt->recalc_natural_slope( k, min_hgt );
1173 new_pos = koord3d( k, min_hgt );
1174 DBG_MESSAGE("natural_slope","%i",new_slope);
1175 }
1176 else {
1177 new_slope = slope_t::flat;
1178 // is more intuitive: if there is a slope, first downgrade it
1179 if( gr1->get_grund_hang() == 0 ) {
1180 new_pos.z--;
1181 }
1182 }
1183
1184 // now prevent being lowered below neighbouring water
1185 sint8 water_table = (water_hgt >= (gr1->get_hoehe() + (gr1->get_grund_hang() ? 1 : 0))) ? water_hgt : welt->get_groundwater() - 4;
1186 sint8 min_neighbour_height = gr1->get_hoehe();
1187
1188 for( sint16 i = 0 ; i < 8 ; i++ ) {
1189 const koord neighbour = k + koord::neighbours[i];
1190
1191 if( welt->is_within_grid_limits( neighbour ) ) {
1192 grund_t *gr2 = welt->lookup_kartenboden( neighbour );
1193 const sint8 water_hgt_neighbour = welt->get_water_hgt( neighbour );
1194 if( gr2 && (water_hgt_neighbour >= (gr2->get_hoehe() + (gr2->get_grund_hang() ? 1 : 0))) ) {
1195 water_table = max( water_table, water_hgt_neighbour );
1196 }
1197 if( gr2 && gr2->get_hoehe() < min_neighbour_height ) {
1198 min_neighbour_height = gr2->get_hoehe();
1199 }
1200 }
1201 }
1202
1203 if( water_table>new_pos.z || (water_table == new_pos.z && min_neighbour_height < new_pos.z) ) {
1204 // do not lower tiles when it will be below water level
1205 return NOTICE_TILE_FULL;
1206 }
1207 welt->set_water_hgt( k, water_table );
1208 water_hgt = water_table;
1209 }
1210 else if( new_slope == ALL_UP_SLOPE ) {
1211 new_slope = slope_t::flat;
1212 new_pos.z++;
1213 }
1214
1215 // already some ground here (tunnel, bridge, monorail?)
1216 if( new_pos.z != pos.z && welt->lookup(new_pos) != NULL ) {
1217 return NOTICE_TILE_FULL;
1218 }
1219 // check for grounds above / below
1220 if( new_pos.z >= pos.z ) {
1221 grund_t *gr2 = welt->lookup( new_pos + koord3d(0, 0, 1) );
1222 if( !gr2 ) {
1223 gr2 = welt->lookup( new_pos + koord3d(0, 0, 2) );
1224 }
1225 if( !gr2 && welt->get_settings().get_way_height_clearance()==2 && (gr1->hat_wege() || gr1->get_leitung()) ) {
1226 gr2 = welt->lookup( new_pos + koord3d(0, 0, 3) );
1227 }
1228 // slope may alter amount of clearance required
1229 if( gr2 && gr2->get_pos().z - new_pos.z + slope_t::min_diff( gr2->get_weg_hang(), new_slope ) < welt->get_settings().get_way_height_clearance() ) {
1230 return NOTICE_TILE_FULL;
1231 }
1232 }
1233 if( new_pos.z <= pos.z ) {
1234 grund_t *gr2 = welt->lookup( new_pos + koord3d(0, 0, -1) );
1235 if( !gr2 ) {
1236 gr2 = welt->lookup( new_pos + koord3d(0, 0, -2) );
1237 }
1238 if( !gr2 && welt->get_settings().get_way_height_clearance()==2 ) {
1239 gr2 = welt->lookup( new_pos + koord3d(0, 0, -3) );
1240 }
1241 // slope may alter amount of clearance required
1242 if( gr2 && new_pos.z - gr2->get_pos().z + slope_t::min_diff( new_slope, gr2->get_weg_hang() ) < welt->get_settings().get_way_height_clearance() ) {
1243 return NOTICE_TILE_FULL;
1244 }
1245 }
1246
1247 // check, if action is valid ...
1248 const sint16 hgt=new_pos.z;
1249 // maximum difference check with tiles to north, south east and west
1250 const sint8 test_hgt = hgt+(new_slope!=0);
1251
1252 if( gr1->get_typ()==grund_t::boden ) {
1253 for( sint16 i = 0 ; i < 4 ; i++ ) {
1254 const koord neighbour = k + koord::nsew[i];
1255
1256 const grund_t *gr_neighbour=welt->lookup_kartenboden(neighbour);
1257 if(gr_neighbour) {
1258 const sint16 gr_neighbour_hgt=gr_neighbour->get_hoehe() + (new_slope==ALL_DOWN_SLOPE && gr_neighbour->get_grund_hang()? 1 : 0);
1259 const sint8 diff_from_ground = abs(gr_neighbour_hgt-test_hgt);
1260 if( diff_from_ground > 2 * max_hdiff ) {
1261 return "Maximum tile height difference reached.";
1262 }
1263 }
1264 }
1265 }
1266
1267 // ok, now we set the slope ...
1268 ok = (new_pos!=pos);
1269 bool slope_changed = new_slope!=gr1->get_grund_hang();
1270 ok |= slope_changed;
1271
1272 if(ok) {
1273 // check if clear
1274 if( gr1->kann_alle_obj_entfernen(player) ) {
1275 return NOTICE_TILE_FULL;
1276 }
1277
1278 // check way ownership
1279 if(gr1->hat_wege()) {
1280 if(gr1->get_weg_nr(0)->is_deletable(player)!=NULL) {
1281 return NOTICE_TILE_FULL;
1282 }
1283 if(gr1->has_two_ways() && gr1->get_weg_nr(1)->is_deletable(player)!=NULL) {
1284 return NOTICE_TILE_FULL;
1285 }
1286 }
1287
1288 // check funds
1289 settings_t const& s = welt->get_settings();
1290 sint64 const cost = new_slope == RESTORE_SLOPE ? s.cst_alter_land : s.cst_set_slope;
1291 if( !player->can_afford(cost) ) {
1292 return NOTICE_INSUFFICIENT_FUNDS;
1293 }
1294
1295 // ok, it was a success
1296 if( !gr1->is_water() && new_slope == 0 && hgt == water_hgt && gr1->get_typ() != grund_t::tunnelboden ) {
1297 // now water
1298 gr1->obj_loesche_alle(player);
1299 welt->access(k)->kartenboden_setzen( new wasser_t(new_pos) );
1300 gr1 = welt->lookup_kartenboden(k);
1301 }
1302 else if( gr1->is_water() && (new_pos.z > water_hgt || new_slope != 0) ) {
1303 // build underwater hill first
1304 if( !welt->flatten_tile( player, k, water_hgt, false, true ) ) {
1305 return NOTICE_TILE_FULL;
1306 }
1307 gr1->obj_loesche_alle(player);
1308 welt->access(k)->kartenboden_setzen( new boden_t(new_pos,new_slope) );
1309 gr1 = welt->lookup_kartenboden(k);
1310 welt->set_water_hgt(k, welt->get_groundwater()-4);
1311 }
1312 else {
1313 gr1->set_grund_hang(new_slope);
1314 gr1->set_pos(new_pos);
1315 gr1->clear_flag(grund_t::marked);
1316 gr1->set_flag(grund_t::dirty);
1317 // update new positions if changed
1318 if( new_pos!=pos ) {
1319 for( int i=0; i<gr1->get_top(); i++ ) {
1320 gr1->obj_bei(i)->set_pos( new_pos );
1321 }
1322 }
1323 // correct tree offsets if slope has changed
1324 if( slope_changed ) {
1325 for( int i=0; i<gr1->get_top(); i++ ) {
1326 baum_t *tree = obj_cast<baum_t>(gr1->obj_bei(i));
1327 if (tree) {
1328 tree->recalc_off();
1329 }
1330 }
1331 }
1332 if( !gr1->ist_karten_boden() ) {
1333 gr1->calc_image();
1334 }
1335 }
1336
1337 // if there is a powerline here we need to treat it as newly built as it may connect to neighbours
1338 leitung_t *lt = gr1->get_leitung();
1339 if( lt ) {
1340 // remove maintenance for existing powerline
1341 player_t::add_maintenance(lt->get_owner(), -lt->get_desc()->get_maintenance(), powerline_wt);
1342 lt->finish_rd();
1343 }
1344
1345 if( gr1->ist_karten_boden() ) {
1346 if( new_slope!=slope_t::flat ) {
1347 // no lakes on slopes ...
1348 groundobj_t *obj = gr1->find<groundobj_t>();
1349 if( obj && obj->get_desc()->get_phases()!=16 ) {
1350 obj->cleanup(player);
1351 delete obj;
1352 }
1353 // connect canals to sea
1354 if( gr1->get_hoehe() == water_hgt && gr1->hat_weg(water_wt) ) {
1355 grund_t *sea = welt->lookup_kartenboden(k - koord( ribi_type(new_slope ) ));
1356 if (sea && sea->is_water()) {
1357 gr1->weg_erweitern(water_wt, ribi_t::backward(ribi_type(new_slope)));
1358 sea->calc_image();
1359 }
1360 }
1361 }
1362 // recalc slope walls on neighbours
1363 for(int y=-1; y<=1; y++) {
1364 for(int x=-1; x<=1; x++) {
1365 grund_t *gr = welt->lookup_kartenboden(k+koord(x,y));
1366 gr->calc_image();
1367 }
1368 }
1369 // correct the grid height
1370 if( gr1->is_water() ) {
1371 sint8 grid_hgt = min( water_hgt, welt->lookup_hgt( k ) );
1372 welt->set_grid_hgt(k, grid_hgt );
1373 }
1374 else {
1375 welt->set_grid_hgt(k, gr1->get_hoehe()+ corner_nw(gr1->get_grund_hang()) );
1376 }
1377 minimap_t::get_instance()->calc_map_pixel(k);
1378
1379 welt->calc_climate( k, true );
1380 }
1381 player_t::book_construction_costs(player, cost, k, ignore_wt);
1382 }
1383 // update limits
1384 if( welt->min_height > gr1->get_hoehe() ) {
1385 welt->min_height = gr1->get_hoehe();
1386 }
1387 else if( welt->max_height < gr1->get_hoehe() ) {
1388 welt->max_height = gr1->get_hoehe();
1389 }
1390 }
1391 return ok ? NULL : "";
1392 }
1393
1394
1395
1396 // set marker
work(player_t * player,koord3d pos)1397 const char *tool_marker_t::work( player_t *player, koord3d pos )
1398 {
1399 grund_t *gr = welt->lookup_kartenboden(pos.get_2d());
1400 if (gr) {
1401 if(!gr->get_text()) {
1402 const obj_t* thing = gr->obj_bei(0);
1403 if(thing == NULL || thing->get_owner() == player || (player_t::check_owner(thing->get_owner(), player) && (thing->get_typ() != obj_t::gebaeude))) {
1404 gr->obj_add(new label_t(gr->get_pos(), player, default_param ? default_param : "\0"));
1405 if (can_use_gui()) {
1406 gr->find<label_t>()->show_info();
1407 }
1408 return NULL;
1409 }
1410 }
1411 }
1412 return "Das Feld gehoert\neinem anderen Spieler\n";
1413 }
1414
1415
1416
1417 // show/repair blocks
init(player_t *)1418 bool tool_clear_reservation_t::init( player_t * )
1419 {
1420 if (can_use_gui()) {
1421 schiene_t::show_reservations = true;
1422 welt->set_dirty();
1423 }
1424 return true;
1425 }
1426
exit(player_t *)1427 bool tool_clear_reservation_t::exit( player_t * )
1428 {
1429 if (can_use_gui()) {
1430 schiene_t::show_reservations = false;
1431 welt->set_dirty();
1432 }
1433 return true;
1434 }
1435
work(player_t *,koord3d pos)1436 const char *tool_clear_reservation_t::work( player_t *, koord3d pos )
1437 {
1438 grund_t *gr = welt->lookup(pos);
1439 if(gr) {
1440 for(unsigned wnr=0; wnr<2; wnr++ ) {
1441
1442 schiene_t const* const w = obj_cast<schiene_t>(gr->get_weg_nr(wnr));
1443 // is this a reserved track?
1444 if(w!=NULL && w->is_reserved()) {
1445 /* now we do a very crude procedure:
1446 * - we search all ways for reservations of this convoi and remove them
1447 * - we set the convoi state to ROUTING_1; it must reserve again its ways then
1448 */
1449 const waytype_t waytype = w->get_waytype();
1450 const convoihandle_t cnv = w->get_reserved_convoi();
1451 if(cnv->get_state()==convoi_t::DRIVING) {
1452 // reset driving state
1453 cnv->suche_neue_route();
1454 }
1455 FOR(slist_tpl<weg_t*>, const w, weg_t::get_alle_wege()) {
1456 if (w->get_waytype() == waytype) {
1457 schiene_t* const sch = obj_cast<schiene_t>(w);
1458 if (sch->get_reserved_convoi() == cnv) {
1459 vehicle_t& v = *cnv->front();
1460 if (!gr->suche_obj(v.get_typ())) {
1461 // force free
1462 sch->unreserve(&v);
1463 }
1464 }
1465 }
1466 }
1467 }
1468 }
1469 }
1470 return NULL;
1471 }
1472
1473
1474 // transformer for electricity supply
get_tooltip(const player_t *) const1475 const char* tool_transformer_t::get_tooltip(const player_t *) const
1476 {
1477 settings_t const& s = welt->get_settings();
1478 sprintf(toolstr, "%s, %ld$ (%ld$)", translator::translate("Build drain"), (long)(s.cst_transformer / -100), (long)(welt->scale_with_month_length(s.cst_maintain_transformer)) / -100);
1479 return toolstr;
1480 }
1481
get_icon(player_t *) const1482 image_id tool_transformer_t::get_icon(player_t*) const
1483 {
1484 return way_builder_t::waytype_available( powerline_wt, welt->get_timeline_year_month() ) ? icon : IMG_EMPTY;
1485 }
1486
init(player_t *)1487 bool tool_transformer_t::init( player_t *)
1488 {
1489 return way_builder_t::waytype_available( powerline_wt, welt->get_timeline_year_month() );
1490 }
1491
1492
check_pos(player_t *,koord3d pos)1493 const char *tool_transformer_t::check_pos( player_t *, koord3d pos )
1494 {
1495 if(grund_t::underground_mode == grund_t::ugm_all && env_t::networkmode) {
1496 // clients cannot guess at which height transformer should be build
1497 return "Cannot built this station/building\nin underground mode here.";
1498 }
1499 if(grund_t::underground_mode == grund_t::ugm_level) {
1500 // only above or directly under surface
1501 // taking into account way clearance requirements
1502 grund_t *gr = welt->lookup_kartenboden(pos.get_2d());
1503 return (gr->get_pos() == pos || gr->get_hoehe() == grund_t::underground_level + welt->get_settings().get_way_height_clearance()) ? NULL : "";
1504 }
1505 return NULL;
1506 }
1507
1508
work(player_t * player,koord3d pos)1509 const char *tool_transformer_t::work( player_t *player, koord3d pos )
1510 {
1511 DBG_MESSAGE("tool_transformer_t()","called on %d,%d", pos.x, pos.y);
1512
1513 koord k(pos.get_2d());
1514
1515 grund_t *gr = welt->lookup_kartenboden(k);
1516 if( !welt->get_settings().get_allow_underground_transformers() && pos.z!=gr->get_hoehe() ) {
1517 // no underground transformers allowed
1518 return "Cannot built this station/building\nin underground mode here.";
1519 }
1520
1521 bool underground = false;
1522 fabrik_t *fab = NULL;
1523 // full underground mode: coordinate is on ground, adjust it to one level below ground
1524 // not possible in network mode!
1525 if (!env_t::networkmode && grund_t::underground_mode == grund_t::ugm_all) {
1526 pos = gr->get_pos() - koord3d( 0, 0, welt->get_settings().get_way_height_clearance() );
1527 }
1528 // search for factory
1529 // must be independent of network mode
1530 if (gr->get_pos().z <= pos.z) {
1531 fab = leitung_t::suche_fab_4(k);
1532 }
1533 else if( gr->get_pos().z == pos.z+welt->get_settings().get_way_height_clearance() ) {
1534 fab = fabrik_t::get_fab( k);
1535 underground = true;
1536 }
1537
1538 if( !fab ) {
1539 return "Transformer only next to factory!";
1540 }
1541 if( fab->is_transformer_connected() ) {
1542 return "Only one transformer per factory!";
1543 }
1544
1545 // underground: first build tunnel tile at coordinate pos
1546 if(underground) {
1547 if(gr->is_water()) {
1548 return "Transformer only next to factory!";
1549 }
1550
1551 if(welt->lookup(pos)) {
1552 return NOTICE_TILE_FULL;
1553 }
1554
1555 if( welt->get_settings().get_way_height_clearance()==2 && welt->lookup(pos + koord3d( 0, 0, 1 )) ) {
1556 return NOTICE_TILE_FULL;
1557 }
1558
1559 const tunnel_desc_t *tunnel_desc = tunnel_builder_t::get_tunnel_desc(powerline_wt, 0, 0);
1560 if( tunnel_desc==NULL ) {
1561 return "Cannot built this station/building\nin underground mode here.";
1562 }
1563
1564 tunnelboden_t* tunnel = new tunnelboden_t(pos, 0);
1565 welt->access(k)->boden_hinzufuegen(tunnel);
1566 tunnel->obj_add(new tunnel_t(pos, player, tunnel_desc));
1567 player_t::add_maintenance( player, tunnel_desc->get_maintenance(), tunnel_desc->get_finance_waytype() );
1568 gr = tunnel;
1569 }
1570 else {
1571 // above ground: check for clear tile
1572 if(gr->get_grund_hang()!=0 || !gr->ist_natur()) {
1573 return "Transformer only on flat bare land!";
1574 }
1575 // remove everything on that spot
1576 if(const char *fail = gr->kann_alle_obj_entfernen(player)) {
1577 return fail;
1578 }
1579 gr->obj_loesche_alle(player);
1580 }
1581 // transformer will be build on tile pointed to by gr
1582
1583 // build source or drain depending on factory type
1584 if(fab->get_desc()->is_electricity_producer()) {
1585 pumpe_t *p = new pumpe_t(gr->get_pos(), player);
1586 gr->obj_add( p );
1587 p->finish_rd();
1588 }
1589 else {
1590 senke_t *s = new senke_t(gr->get_pos(), player);
1591 gr->obj_add(s);
1592 s->finish_rd();
1593 }
1594
1595 return NULL; // ok
1596 }
1597
1598
1599
1600 /**
1601 * found a new city
1602 * @author Hj. Malthaner
1603 */
work(player_t * player,koord3d pos)1604 const char *tool_add_city_t::work( player_t *player, koord3d pos )
1605 {
1606 // check funds
1607 const sint64 cost = welt->get_settings().cst_found_city;
1608 if( !player->can_afford(cost) ) {
1609 return NOTICE_INSUFFICIENT_FUNDS;
1610 }
1611
1612 koord k(pos.get_2d());
1613
1614 grund_t *gr = welt->lookup_kartenboden(k);
1615 if(gr) {
1616 if(gr->ist_natur() &&
1617 !gr->is_water() &&
1618 gr->get_grund_hang() == 0 &&
1619 hausbauer_t::get_special( 0, building_desc_t::townhall, welt->get_timeline_year_month(), 0, welt->get_climate( k ) ) != NULL ) {
1620
1621 gebaeude_t const* const gb = obj_cast<gebaeude_t>(gr->first_obj());
1622 if(gb && gb->is_townhall()) {
1623 dbg->warning("tool_add_city()", "Already a city here");
1624 return NOTICE_TILE_FULL;
1625 }
1626 else {
1627
1628 // Hajo: if city is owned by player and player removes special
1629 // buildings the game crashes. To avoid this problem cities
1630 // always belong to player 1
1631
1632 int const citizens = (int)(welt->get_settings().get_mean_citizen_count() * 0.9);
1633 // stadt_t *stadt = new stadt_t(welt->get_public_player(), pos,citizens/10+simrand(2*citizens+1));
1634
1635 // always start with 1/10 citizens
1636 stadt_t* stadt = new stadt_t(welt->get_public_player(), k, citizens / 10);
1637 if (stadt->get_buildings() == 0) {
1638 delete stadt;
1639 return NOTICE_UNSUITABLE_GROUND;
1640 }
1641
1642 welt->add_city(stadt);
1643 stadt->finish_rd();
1644 stadt->verbinde_fabriken();
1645
1646 player_t::book_construction_costs(player, cost, k, ignore_wt);
1647 minimap_t::get_instance()->calc_map();
1648 return NULL;
1649 }
1650 }
1651 else {
1652 return NOTICE_UNSUITABLE_GROUND;
1653 }
1654 }
1655 return "";
1656 }
1657
1658 // buy a house
work(player_t * player,koord3d pos)1659 const char *tool_buy_house_t::work( player_t *player, koord3d pos)
1660 {
1661 if ( player == welt->get_public_player() ) {
1662 return "";
1663 }
1664 grund_t* gr = welt->lookup_kartenboden(pos.get_2d());
1665 if(!gr || gr->hat_wege() || gr->get_halt().is_bound()) {
1666 return "";
1667 }
1668
1669 // since buildings can have more than one tile, we must handle them together
1670 gebaeude_t* gb = gr->find<gebaeude_t>();
1671 if( gb== NULL || !gb->is_city_building() || !player_t::check_owner(gb->get_owner(),player) ) {
1672 return "Das Feld gehoert\neinem anderen Spieler\n";
1673 }
1674
1675 if( gb->get_owner()==player ) {
1676 // I bought this already ...
1677 return "";
1678 }
1679
1680 player_t *old_owner = gb->get_owner();
1681 const building_tile_desc_t *tile = gb->get_tile();
1682 const building_desc_t * bdsc = tile->get_desc();
1683 koord size = bdsc->get_size( tile->get_layout() );
1684
1685 koord k;
1686 for(k.y = 0; k.y < size.y; k.y ++) {
1687 for(k.x = 0; k.x < size.x; k.x ++) {
1688 grund_t *gr = welt->lookup(koord3d(k,0)+pos);
1689 if(gr) {
1690 gebaeude_t *gb_part = gr->find<gebaeude_t>();
1691 // there may be buildings with holes
1692 if( gb_part && gb_part->get_tile()->get_desc()==bdsc && player_t::check_owner(gb_part->get_owner(),player) ) {
1693 sint32 const maint = welt->get_settings().maint_building * bdsc->get_level();
1694 player_t::add_maintenance(old_owner, -maint, gb->get_waytype());
1695 player_t::add_maintenance(player, +maint, gb->get_waytype());
1696 gb->set_owner(player);
1697 player_t::book_construction_costs(player, -maint, k + pos.get_2d(), gb->get_waytype());
1698 }
1699 }
1700 }
1701 }
1702 return NULL;
1703 }
1704
1705 /* change city size
1706 * @author prissi
1707 */
init(player_t *)1708 bool tool_change_city_size_t::init( player_t * )
1709 {
1710 cursor = atoi(default_param)>0 ? tool_t::general_tool[TOOL_RAISE_LAND]->cursor : tool_t::general_tool[TOOL_LOWER_LAND]->cursor;
1711 return true;
1712 }
1713
work(player_t *,koord3d pos)1714 const char *tool_change_city_size_t::work( player_t *, koord3d pos )
1715 {
1716 stadt_t *city = welt->find_nearest_city(pos.get_2d());
1717 if(city!=NULL) {
1718 city->change_size( atoi(default_param) );
1719 // Knightly : update the links from other cities to this city
1720 FOR(weighted_vector_tpl<stadt_t*>, const c, welt->get_cities()) {
1721 c->remove_target_city(city);
1722 c->add_target_city(city);
1723 }
1724 return NULL;
1725 }
1726 return "";
1727 }
1728
1729
1730 /* change climate
1731 * @author kieron
1732 */
get_tooltip(player_t const *) const1733 const char *tool_set_climate_t::get_tooltip(player_t const*) const
1734 {
1735 char temp[1024];
1736 sprintf( temp, translator::translate( "Set tile climate" ), translator::translate( ground_desc_t::get_climate_name_from_bit((climate)atoi(default_param)) ) );
1737 return tooltip_with_price( temp, welt->get_settings().cst_alter_climate );
1738 }
1739
is_valid_pos(player_t * player,const koord3d &,const char * & error,const koord3d &)1740 uint8 tool_set_climate_t::is_valid_pos(player_t *player, const koord3d &, const char *& error, const koord3d &)
1741 {
1742 error = NULL;
1743 // no dragging in networkmode but for admin
1744 return env_t::networkmode && !player->is_public_service() ? 1 /*no dragging*/ : 2 /*dragging allowed*/;
1745 }
1746
mark_tiles(player_t *,const koord3d & start,const koord3d & end)1747 void tool_set_climate_t::mark_tiles(player_t *, const koord3d &start, const koord3d &end)
1748 {
1749 koord k1, k2;
1750 k1.x = start.x < end.x ? start.x : end.x;
1751 k1.y = start.y < end.y ? start.y : end.y;
1752 k2.x = start.x + end.x - k1.x;
1753 k2.y = start.y + end.y - k1.y;
1754 koord k;
1755 for( k.x = k1.x; k.x <= k2.x; k.x++ ) {
1756 for( k.y = k1.y; k.y <= k2.y; k.y++ ) {
1757 grund_t *gr = welt->lookup_kartenboden( k );
1758
1759 zeiger_t *marker = new zeiger_t(gr->get_pos(), NULL );
1760
1761 const uint8 grund_hang = gr->get_grund_hang();
1762 const uint8 weg_hang = gr->get_weg_hang();
1763 const uint8 hang = max( corner_sw(grund_hang), corner_sw(weg_hang) ) + 3 * max( corner_se(grund_hang), corner_se(weg_hang) ) + 9 * max( corner_ne(grund_hang), corner_ne(weg_hang) ) + 27 * max( corner_nw(grund_hang), corner_nw(weg_hang) );
1764 uint8 back_hang = (hang % 3) + 3 * ((uint8)(hang / 9)) + 27;
1765 marker->set_foreground_image( ground_desc_t::marker->get_image( grund_hang % 27 ) );
1766 marker->set_image( ground_desc_t::marker->get_image( back_hang ) );
1767
1768 marker->mark_image_dirty( marker->get_image(), 0 );
1769 gr->obj_add( marker );
1770 marked.insert( marker );
1771 }
1772 }
1773 }
1774
1775
do_work(player_t * player,const koord3d & start,const koord3d & end)1776 const char *tool_set_climate_t::do_work( player_t *player, const koord3d &start, const koord3d &end )
1777 {
1778 int n = 0; // tiles altered
1779 climate cl = (climate) atoi(default_param);
1780 koord k1, k2;
1781 if( end == koord3d::invalid ) {
1782 k1.x = k2.x = start.x;
1783 k1.y = k2.y = start.y;
1784 }
1785 else {
1786 k1.x = start.x < end.x ? start.x : end.x;
1787 k1.y = start.y < end.y ? start.y : end.y;
1788 k2.x = start.x + end.x - k1.x;
1789 k2.y = start.y + end.y - k1.y;
1790 }
1791 koord k;
1792 for( k.x = k1.x; k.x <= k2.x; k.x++ ) {
1793 for( k.y = k1.y; k.y <= k2.y; k.y++ ) {
1794 if( grund_t *gr=welt->lookup_kartenboden(k) ) {
1795 if( cl != water_climate ) {
1796 bool ok = true;
1797 if( gr->is_water() ) {
1798 const sint8 hgt = welt->lookup_hgt(k);
1799 ok = welt->get_water_hgt(k) == hgt && welt->is_plan_height_changeable( k.x, k.y );
1800 // check s, se, e - these must not be deep water!
1801 for( int i = 3 ; i < 6 && ok ; i++ ) {
1802 koord k_neighbour(k + koord::neighbours[i]);
1803 if( welt->is_within_grid_limits(k_neighbour) ) {
1804 ok = welt->lookup_hgt(k_neighbour) >= hgt;
1805 }
1806 }
1807 if( ok ) {
1808 gr->obj_loesche_alle( NULL );
1809 welt->set_water_hgt( k, hgt - 1 );
1810 welt->access(k)->correct_water();
1811 }
1812 }
1813 if( ok ) {
1814 welt->set_climate( k, cl, true );
1815 minimap_t::get_instance()->calc_map_pixel( k );
1816 n ++;
1817 }
1818 }
1819 else if( !gr->is_water() && gr->get_grund_hang() == slope_t::flat && welt->is_plan_height_changeable( k.x, k.y ) ) {
1820 bool ok = true;
1821 for( int i = 0 ; i < 8; i++ ) {
1822 grund_t *gr2 = welt->lookup_kartenboden( k + koord::neighbours[i] );
1823 if( gr2 && ok ) {
1824 ok = gr2->get_pos().z >= gr->get_pos().z;
1825 }
1826 }
1827 if( ok ) {
1828 gr->obj_loesche_alle( NULL );
1829 welt->set_water_hgt( k, gr->get_pos().z );
1830 welt->access(k)->correct_water();
1831 welt->set_climate( k, water_climate, true );
1832 minimap_t::get_instance()->calc_map_pixel( k );
1833 n ++;
1834 }
1835 }
1836
1837 }
1838 }
1839 }
1840 if(n>0) {
1841 player_t::book_construction_costs(player, welt->get_settings().cst_alter_climate * n, k, ignore_wt);
1842 }
1843 return NULL;
1844 }
1845
1846
1847 /* change water height
1848 * @author kieron
1849 */
init(player_t * player)1850 bool tool_change_water_height_t::init( player_t *player )
1851 {
1852 cursor = atoi(default_param) > 0 ? tool_t::general_tool[TOOL_RAISE_LAND]->cursor : tool_t::general_tool[TOOL_LOWER_LAND]->cursor;
1853 return !env_t::networkmode || player->is_public_service();
1854 }
1855
1856
work(player_t *,koord3d pos)1857 const char *tool_change_water_height_t::work( player_t *, koord3d pos )
1858 {
1859 if( pos == koord3d::invalid ) {
1860 return "Cannot alter water";
1861 }
1862
1863 // calculate new height to use:
1864 bool raising = atoi(default_param) > 0;
1865 koord k = pos.get_2d();
1866 sint8 new_water_height;
1867 grund_t *gr = welt->lookup_kartenboden(k);
1868
1869 if( gr->is_water() ) {
1870 // lower + control removes shallow water only. If this tile is deep water this will fail
1871 if( !raising && is_ctrl_pressed() && welt->min_hgt(k)!=gr->get_hoehe() ) {
1872 return "Cannot alter water";
1873 }
1874
1875 // if currently water, raise = +1, lower = -1
1876 new_water_height = gr->get_hoehe() + (raising ? 1 : -1);
1877 }
1878 // if not water then raise = set water height to ground height, lower = error
1879 else if( raising ) {
1880 slope_t::type slope = gr->get_grund_hang();
1881 new_water_height = gr->get_hoehe() + max( max( corner_sw(slope), corner_se(slope) ),max( corner_ne(slope), corner_nw(slope) ) );
1882 }
1883 else {
1884 return "Cannot alter water";
1885 }
1886 if( new_water_height < welt->get_groundwater() - 3 ) {
1887 return "Cannot alter water";
1888 }
1889 sint8 test_height = max( new_water_height, gr->get_hoehe() );
1890
1891 // make a list of tiles to change
1892 // cannot use a recursive method as stack is not large enough!
1893
1894 sint8 *from_dir = new sint8[welt->get_size().x * welt->get_size().y];
1895 sint8 *stage = new sint8[welt->get_size().x * welt->get_size().y];
1896 memset( from_dir, -1, sizeof(sint8) * welt->get_size().x * welt->get_size().y );
1897 memset( stage, -1, sizeof(sint8) * welt->get_size().x * welt->get_size().y );
1898 #define array_koord(px,py) (px + py * welt->get_size().x)
1899 stage[array_koord(k.x,k.y)]=0;
1900 do {
1901 // firstly we must be able to change ground height
1902 bool ok = welt->is_plan_height_changeable( k.x, k.y ) && k.x > 0 && k.y > 0 && k.x < welt->get_size().x - 1 && k.y < welt->get_size().y - 1;
1903 const planquadrat_t *plan = welt->access(k);
1904
1905 // next there cannot be any grounds directly above this tile
1906 sint8 h = plan->get_kartenboden()->get_hoehe() + 1;
1907 while( ok && h < new_water_height + welt->get_settings().get_way_height_clearance() ) {
1908 if( plan->get_boden_in_hoehe(h) ) {
1909 ok = false;
1910 }
1911 h++;
1912 }
1913
1914 if( !ok ) {
1915 delete [] from_dir;
1916 delete [] stage;
1917 return "Cannot alter water";
1918 }
1919
1920 // get neighbour corner heights
1921 sint8 neighbour_heights[8][4];
1922 welt->get_neighbour_heights( k, neighbour_heights );
1923
1924 for( int i = stage[array_koord(k.x,k.y)]; i < 8; i++ ) {
1925 koord k_neighbour = k + koord::neighbours[i];
1926 grund_t *gr2 = welt->lookup_kartenboden(k_neighbour);
1927 if( gr2 ) {
1928 sint8 neighbour_height = gr2->get_hoehe();
1929
1930 // move onto this tile if it hasn't been processed yet
1931 bool ok = stage[array_koord(k_neighbour.x, k_neighbour.y)] == -1;
1932
1933 if( raising ) {
1934 // test whether points adjacent to current tile will be flooded
1935 // if control key modifier pressed, level ground will be left alone, but then need to check for spills
1936
1937 // for neighbour i test corners adjacent to tile
1938 // nw (i = 0), test se (corner 1)
1939 // w (i = 1), test se (corner 1) and ne (corner 2)
1940 // sw (i = 2), test ne (corner 2)
1941 // s (i = 3), test ne (corner 2) and nw (corner 3)
1942 // se (i = 4), test nw (corner 3)
1943 // e (i = 5), test nw (corner 3) and sw (corner 0)
1944 // ne (i = 6), test sw (corner 0)
1945 // n (i = 7), test sw (corner 0) and se (corner 1)
1946
1947 if( is_ctrl_pressed() ) {
1948 ok = ok && ( (gr2->get_grund_hang()!=slope_t::flat && welt->max_hgt(k_neighbour) <= test_height) ||
1949 neighbour_heights[i][((i >> 1) + 1) & 3] < test_height ||
1950 ( (i & 1) && neighbour_heights[i][((i >> 1) + 2) & 3] < test_height) );
1951 }
1952 else {
1953 ok = ok && (neighbour_heights[i][((i >> 1) + 1) & 3] <= test_height ||
1954 ( (i & 1) && neighbour_heights[i][((i >> 1) + 2) & 3] <= test_height));
1955 }
1956
1957 // move onto this tile unless it already has water at new level, or the land level is above new level
1958 ok = ok && welt->get_water_hgt(k_neighbour) < new_water_height;
1959 }
1960 else {
1961 if( is_ctrl_pressed() ) {
1962 ok = ok && welt->min_hgt(k_neighbour) == test_height;
1963 }
1964 else {
1965 ok = ok && neighbour_height <= test_height;
1966 }
1967
1968 // move onto this tile unless it already has water at new level, or the land level is above new level
1969 ok = ok && welt->get_water_hgt(k_neighbour) > new_water_height;
1970 }
1971
1972 if( ok ) {
1973 //move on to next tile
1974 from_dir[array_koord(k_neighbour.x,k_neighbour.y)] = i;
1975 stage[array_koord(k_neighbour.x,k_neighbour.y)] = 0;
1976 stage[array_koord(k.x,k.y)] = i;
1977 k = k_neighbour;
1978 break;
1979 }
1980 }
1981 //return back to previous tile
1982 if( i==7 ) {
1983 stage[array_koord(k.x,k.y)] = 8;
1984 if( from_dir[array_koord(k.x,k.y)] != -1 ) {
1985 k = k - koord::neighbours[from_dir[array_koord(k.x,k.y)]];
1986 }
1987 }
1988 }
1989 } while( from_dir[array_koord(k.x,k.y)] != -1 || stage[array_koord(k.x,k.y)] < 7 );
1990
1991 delete [] from_dir;
1992
1993 // loop over map to find marked tiles
1994 for( int y = 1; y<welt->get_size().y - 1; y++ ) {
1995 for( int x = 1; x<welt->get_size().x - 1; x++ ) {
1996 if( stage[array_koord(x,y)] > -1 ) {
1997 // calculate new height, slope and climate and set water height
1998 grund_t *gr2 =welt->lookup_kartenboden(x, y);
1999
2000 // remove any objects on this tile
2001 gr2->obj_loesche_alle( NULL );
2002
2003 const sint8 h0 = gr2->get_hoehe();
2004 const sint8 min_grid_hgt = welt->min_hgt( koord( x, y ) );
2005
2006 sint8 h0_nw, h0_ne, h0_se, h0_sw;
2007
2008 if( gr2->is_water() ) {
2009 // water - maximum existing height can be is old water height no matter what surrounding grids are
2010 h0_nw = min(h0, welt->lookup_hgt(x, y));
2011 h0_ne = min(h0, welt->lookup_hgt(x+1, y));
2012 h0_se = min(h0, welt->lookup_hgt(x+1, y+1));
2013 h0_sw = min(h0, welt->lookup_hgt(x, y+1));
2014 }
2015 else if( h0 > min_grid_hgt ) {
2016 // if min grid height here is less than ground height it will be because we are partially water
2017 h0_nw = welt->lookup_hgt(x, y);
2018 h0_ne = welt->lookup_hgt(x+1, y);
2019 h0_se = welt->lookup_hgt(x+1, y+1);
2020 h0_sw = welt->lookup_hgt(x, y+1);
2021 if( !gr2->is_water() ) {
2022 // while this appears to be a single height slope actually it is a double height slope half underwater
2023 const sint8 water_hgt = welt->get_water_hgt(x, y);
2024 h0_nw >= water_hgt ? h0_nw = h0 + corner_nw( gr2->get_grund_hang() ) : 0;
2025 h0_ne >= water_hgt ? h0_ne = h0 + corner_ne( gr2->get_grund_hang() ) : 0;
2026 h0_se >= water_hgt ? h0_se = h0 + corner_se( gr2->get_grund_hang() ) : 0;
2027 h0_sw >= water_hgt ? h0_sw = h0 + corner_sw( gr2->get_grund_hang() ) : 0;
2028 }
2029 }
2030 else {
2031 // fully land
2032 h0_nw = h0 + corner_nw( gr2->get_grund_hang() );
2033 h0_ne = h0 + corner_ne( gr2->get_grund_hang() );
2034 h0_se = h0 + corner_se( gr2->get_grund_hang() );
2035 h0_sw = h0 + corner_sw( gr2->get_grund_hang() );
2036 }
2037
2038
2039 const sint8 hneu_nw = max( new_water_height, h0_nw );
2040 const sint8 hneu_ne = max( new_water_height, h0_ne );
2041 const sint8 hneu_se = max( new_water_height, h0_se );
2042 const sint8 hneu_sw = max( new_water_height, h0_sw );
2043 const sint8 hneu = min( min( hneu_nw, hneu_ne ), min( hneu_se, hneu_sw ) );
2044
2045 gr2->set_hoehe( hneu );
2046
2047 const uint8 sneu = (hneu_sw - hneu > 2 ? 2 : hneu_sw - hneu) + ((hneu_se - hneu > 2 ? 2 : hneu_se-hneu) * 3) + ((hneu_ne - hneu > 2 ? 2 : hneu_ne - hneu) * 9) + ((hneu_nw - hneu > 2 ? 2 : hneu_nw - hneu) * 27);
2048 gr2->set_grund_hang( sneu );
2049
2050 welt->set_water_hgt(x, y, new_water_height );
2051 welt->access(x, y)->correct_water();
2052 welt->calc_climate( koord( x, y ), true );
2053 }
2054 }
2055 }
2056
2057 delete [] stage;
2058
2059 return NULL;
2060 }
2061
2062
move(player_t * const player,uint16 const b,koord3d const pos)2063 char const* tool_plant_tree_t::move(player_t* const player, uint16 const b, koord3d const pos)
2064 {
2065 if (b==0) {
2066 return NULL;
2067 }
2068 if (env_t::networkmode) {
2069 // queue tool for network
2070 nwc_tool_t *nwc = new nwc_tool_t(player, this, pos, welt->get_steps(), welt->get_map_counter(), false);
2071 network_send_server(nwc);
2072 return NULL;
2073 }
2074 else {
2075 return work( player, pos );
2076 }
2077 }
2078
2079
work(player_t * player,koord3d pos)2080 const char *tool_plant_tree_t::work( player_t *player, koord3d pos )
2081 {
2082 koord k(pos.get_2d());
2083
2084 grund_t *gr = welt->lookup_kartenboden(k);
2085 if(gr) {
2086 // check if trees are allowed
2087 if( welt->get_settings().get_no_trees() && !player->is_public_service() ) {
2088 return NOTICE_NO_TREES;
2089 }
2090
2091 // check funds
2092 sint64 const cost = welt->get_settings().cst_remove_tree;
2093 if( !player->can_afford(cost) ) {
2094 return NOTICE_INSUFFICIENT_FUNDS;
2095 }
2096
2097 const tree_desc_t *desc = NULL;
2098 bool check_climates = true;
2099 bool random_age = false;
2100 if(default_param==NULL || strlen(default_param)==0) {
2101 desc = baum_t::random_tree_for_climate( welt->get_climate( k ) );
2102 }
2103 else {
2104 // parse default_param: bbdesc_nr b=1 ignore climate b=1 random age
2105 check_climates = default_param[0]=='0';
2106 random_age = default_param[1]=='1';
2107 desc = baum_t::find_tree(default_param+3);
2108 }
2109 if(desc && baum_t::plant_tree_on_coordinate( k, desc, check_climates, random_age ) ) {
2110 player_t::book_construction_costs(player, cost, k, ignore_wt);
2111 return NULL;
2112 }
2113 return "";
2114 }
2115 return NULL;
2116 }
2117
2118
2119
2120 /* the following routines add waypoints/halts to a schedule
2121 * because we do not like to stop at AIs stop, but we still want to force the truck to use AI roads
2122 * So if there is a halt, then it must be either public or ours!
2123 * @author prissi
2124 */
tool_schedule_insert_aux(karte_t * welt,player_t * player,koord3d pos,schedule_t * schedule,bool append)2125 static const char *tool_schedule_insert_aux(karte_t *welt, player_t *player, koord3d pos, schedule_t *schedule, bool append)
2126 {
2127 if(schedule == NULL) {
2128 dbg->warning("tool_schedule_insert_aux()","Schedule is (null), doing nothing");
2129 return 0;
2130 }
2131 grund_t *bd = welt->lookup(pos);
2132 if (bd) {
2133 // now just for error messages, we're assuming a valid ground
2134 // check for right way type
2135 if(!schedule->is_stop_allowed(bd)) {
2136 return schedule->get_error_msg();
2137 }
2138 // and check for ownership
2139 if( !bd->is_halt() ) {
2140 weg_t *w = bd->get_weg( schedule->get_waytype() );
2141 if( w==NULL && schedule->get_waytype()==tram_wt ) {
2142 w = bd->get_weg( track_wt );
2143 }
2144 if( w!=NULL && w->get_owner()!=welt->get_public_player() && !player_t::check_owner(w->get_owner(),player) ) {
2145 return "Das Feld gehoert\neinem anderen Spieler\n";
2146 }
2147 if( bd->get_depot() && !player_t::check_owner( bd->get_depot()->get_owner(), player ) ) {
2148 return "Das Feld gehoert\neinem anderen Spieler\n";
2149 }
2150 }
2151 if( bd->is_halt() && !player_t::check_owner( player, bd->get_halt()->get_owner()) ) {
2152 return "Das Feld gehoert\neinem anderen Spieler\n";
2153 }
2154 // ok, now we have a valid ground
2155 if(append) {
2156 schedule->append(bd);
2157 }
2158 else {
2159 schedule->insert(bd);
2160 }
2161 }
2162 return NULL;
2163 }
2164
work(player_t * player,koord3d pos)2165 const char *tool_schedule_add_t::work( player_t *player, koord3d pos )
2166 {
2167 return tool_schedule_insert_aux( welt, player, pos, (schedule_t*)const_cast<char *>(default_param), true );
2168 }
2169
work(player_t * player,koord3d pos)2170 const char *tool_schedule_ins_t::work( player_t *player, koord3d pos )
2171 {
2172 return tool_schedule_insert_aux( welt, player, pos, (schedule_t*)const_cast<char *>(default_param), false );
2173 }
2174
2175
2176 /* way construction */
2177 const way_desc_t *tool_build_way_t::defaults[17] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
2178
get_desc(uint16 timeline_year_month,bool remember) const2179 const way_desc_t *tool_build_way_t::get_desc( uint16 timeline_year_month, bool remember ) const
2180 {
2181 const way_desc_t *desc = default_param ? way_builder_t::get_desc(default_param,0) :NULL;
2182 if( desc==NULL && default_param ) {
2183 waytype_t wt = (waytype_t)atoi(default_param);
2184 desc = defaults[wt&63];
2185 if(desc==NULL || !desc->is_available(timeline_year_month)) {
2186 // search fastest way.
2187 if( wt == tram_wt || wt == powerline_wt ) {
2188 desc = way_builder_t::weg_search(wt, 0xffffffff, timeline_year_month, type_flat);
2189 }
2190 else {
2191 // this triggers an assertion if wt == powerline_wt
2192 weg_t *w = weg_t::alloc(wt);
2193 desc = w->get_desc();
2194 delete w;
2195 }
2196 }
2197 }
2198 if( desc && remember ) {
2199 if( desc->get_styp() == type_tram ) {
2200 defaults[ tram_wt ] = desc;
2201 }
2202 else {
2203 defaults[desc->get_wtyp()&63] = desc;
2204 }
2205 }
2206 return desc;
2207 }
2208
get_icon(player_t *) const2209 image_id tool_build_way_t::get_icon(player_t *) const
2210 {
2211 const way_desc_t *desc = way_builder_t::get_desc(default_param,0);
2212 image_id image = icon;
2213 bool is_tram = false;
2214 if( desc ) {
2215 is_tram = (desc->get_wtyp()==tram_wt) || (desc->get_styp() == type_tram);
2216 if( image == IMG_EMPTY ) {
2217 image = desc->get_cursor()->get_image_id(1);
2218 }
2219 if( !desc->is_available( world()->get_timeline_year_month() ) ) {
2220 return IMG_EMPTY;
2221 }
2222 }
2223 if( grund_t::underground_mode==grund_t::ugm_all && !is_tram ) {
2224 return IMG_EMPTY;
2225 }
2226 return image;
2227 }
2228
get_tooltip(const player_t *) const2229 const char* tool_build_way_t::get_tooltip(const player_t *) const
2230 {
2231 const way_desc_t *desc = get_desc(welt->get_timeline_year_month(),false);
2232 if (desc == NULL) {
2233 return "";
2234 }
2235 tooltip_with_price_maintenance( welt, desc->get_name(), -desc->get_price(), desc->get_maintenance() );
2236 size_t n= strlen(toolstr);
2237 sprintf(toolstr+n, ", %dkm/h", desc->get_topspeed() );
2238 return toolstr;
2239 }
2240
2241 // default ways are not initialized synchronously for different clients
2242 // always return the name of a way, never the string containing the waytype
get_default_param(player_t * player) const2243 const char* tool_build_way_t::get_default_param(player_t *player) const
2244 {
2245 if (player==NULL) {
2246 return default_param;
2247 }
2248 if (desc) {
2249 return desc->get_name();
2250 }
2251 else {
2252 if (default_param == NULL) {
2253 // no chance to guess anything sensible
2254 return NULL;
2255 }
2256 const way_desc_t* test_desc = get_desc(0, false);
2257 if (test_desc) {
2258 return test_desc->get_name();
2259 }
2260 else {
2261 return default_param;
2262 }
2263 }
2264 }
2265
is_selected() const2266 bool tool_build_way_t::is_selected() const
2267 {
2268 tool_t const* const tool = welt->get_tool(welt->get_active_player_nr());
2269 if (tool->get_id() != get_id()) {
2270 return false;
2271 }
2272 tool_build_way_t const* const selected = dynamic_cast<tool_build_way_t const*>(tool);
2273 return (selected && selected->get_desc(welt->get_timeline_year_month(),false) == get_desc(welt->get_timeline_year_month(),false));
2274 }
2275
init(player_t * player)2276 bool tool_build_way_t::init( player_t *player )
2277 {
2278 two_click_tool_t::init( player );
2279 if( ok_sound == NO_SOUND ) {
2280 ok_sound = SFX_CASH;
2281 }
2282
2283 // now get current desc
2284 desc = get_desc( welt->get_timeline_year_month(), can_use_gui() );
2285 if( desc && desc->get_cursor()->get_image_id(0) != IMG_EMPTY ) {
2286 cursor = desc->get_cursor()->get_image_id(0);
2287 }
2288 if( desc && !desc->is_available(welt->get_timeline_year_month()) && player!=NULL && player!=welt->get_public_player() ) {
2289 // non available way => fail
2290 return false;
2291 }
2292 return desc!=NULL;
2293 }
2294
get_waytype() const2295 waytype_t tool_build_way_t::get_waytype() const
2296 {
2297 const way_desc_t *desc = get_desc( welt->get_timeline_year_month(), false );
2298 if (desc) {
2299 return desc->is_tram() ? tram_wt : desc->get_wtyp();
2300 }
2301 return invalid_wt;
2302 }
2303
start_at(koord3d & new_start)2304 void tool_build_way_t::start_at( koord3d &new_start )
2305 {
2306 if( is_shift_pressed() && (desc->get_styp() == type_elevated && desc->get_wtyp() != air_wt) ) {
2307 grund_t *gr=welt->lookup(new_start);
2308 if( gr->get_weg( desc->get_waytype() ) ) {
2309 new_start.z -= welt->get_settings().get_way_height_clearance();
2310 }
2311 }
2312 // elevated ways with SHIFT will selected the current layer, when already on an elevated way
2313 two_click_tool_t::start_at( new_start );
2314 }
2315
is_valid_pos(player_t * player,const koord3d & pos,const char * & error,const koord3d &)2316 uint8 tool_build_way_t::is_valid_pos( player_t *player, const koord3d &pos, const char *&error, const koord3d & )
2317 {
2318 error = NULL;
2319 grund_t *gr=welt->lookup(pos);
2320 if( gr && slope_t::is_way(gr->get_weg_hang()) ) {
2321 // ignore tunnel tiles (except road tunnel for tram track building ..)
2322 if( gr->get_typ() == grund_t::tunnelboden && !gr->ist_karten_boden() && !(desc->is_tram() && gr->hat_weg(road_wt)) ) {
2323 return 0;
2324 }
2325 bool const elevated = desc->get_styp() == type_elevated && desc->get_wtyp() != air_wt;
2326 // ignore water
2327 if( desc->get_wtyp() != water_wt && gr->get_typ() == grund_t::wasser ) {
2328 if( !elevated || welt->lookup_hgt( pos.get_2d() ) < welt->get_water_hgt( pos.get_2d() ) ) {
2329 return 0;
2330 }
2331 // here either channel or elevated way over not too deep water
2332 }
2333 // elevated ways have to check tile above
2334 if( elevated ) {
2335 gr = welt->lookup( pos + koord3d( 0, 0, welt->get_settings().get_way_height_clearance() ) );
2336 if( gr == NULL ) {
2337 return 2;
2338 }
2339 if( gr->get_typ() != grund_t::monorailboden ) {
2340 return 0;
2341 }
2342 }
2343 // test if way already exists on the way and if we are allowed to connect
2344 weg_t *way = gr->get_weg(desc->get_wtyp());
2345 if( way ) {
2346 // allow to connect to any road
2347 if( desc->get_wtyp() == road_wt || desc->get_wtyp() == water_wt ) {
2348 return 2;
2349 }
2350 error = way->is_deletable(player);
2351 return error==NULL ? 2 : 0;
2352 }
2353 // check for ownership but ignore moving things
2354 if(player!=NULL) {
2355 for(uint8 i=0; i<gr->obj_count(); i++) {
2356 obj_t* dt = gr->obj_bei(i);
2357 if (!dt->is_moving() && dt->is_deletable(player)!=NULL && dt->get_typ()!=obj_t::label) {
2358 error = dt->is_deletable(player); // "Das Feld gehoert\neinem anderen Spieler\n";
2359 return 0;
2360 }
2361 }
2362 }
2363 }
2364 else {
2365 return 0;
2366 }
2367 return 2;
2368 }
2369
calc_route(way_builder_t & bauigel,const koord3d & start,const koord3d & end)2370 void tool_build_way_t::calc_route( way_builder_t &bauigel, const koord3d &start, const koord3d &end )
2371 {
2372 // recalc type of construction
2373 way_builder_t::bautyp_t bautyp = (way_builder_t::bautyp_t)desc->get_wtyp();
2374 if(desc->is_tram()) {
2375 bautyp = way_builder_t::schiene_tram;
2376 }
2377 // elevated track?
2378 if(desc->get_styp()==type_elevated && desc->get_wtyp()!=air_wt) {
2379 bautyp |= way_builder_t::elevated_flag;
2380 }
2381
2382 bauigel.init_builder(bautyp, desc);
2383 if( is_ctrl_pressed() ) {
2384 bauigel.set_keep_existing_ways( false );
2385 }
2386 else {
2387 bauigel.set_keep_existing_faster_ways( true );
2388 }
2389
2390 koord3d my_end = end;
2391 // ending point is applied that elevated ways with SHIFT selects the current layer, when already on an elevated way
2392 if( is_shift_pressed() && (desc->get_styp() == type_elevated && desc->get_wtyp() != air_wt) ) {
2393 grund_t *gr=welt->lookup(my_end);
2394 if( gr->get_weg( desc->get_waytype() ) ) {
2395 my_end.z -= welt->get_settings().get_way_height_clearance();
2396 }
2397 }
2398 // and continue as normal ...
2399 if( is_ctrl_pressed() || (env_t::straight_way_without_control && !env_t::networkmode && !is_scripted()) ) {
2400 DBG_MESSAGE("tool_build_way_t()", "try straight route");
2401 bauigel.calc_straight_route(start,my_end);
2402 }
2403 else {
2404 bauigel.calc_route(start,my_end);
2405 }
2406 DBG_MESSAGE("tool_build_way_t()", "builder found route with %d squares length.", bauigel.get_count());
2407 }
2408
do_work(player_t * player,const koord3d & start,const koord3d & end)2409 const char *tool_build_way_t::do_work( player_t *player, const koord3d &start, const koord3d &end )
2410 {
2411 way_builder_t bauigel(player);
2412 calc_route( bauigel, start, end );
2413 if( bauigel.get_route().get_count()>1 ) {
2414 welt->mute_sound(true);
2415 bauigel.build();
2416 welt->mute_sound(false);
2417
2418 return NULL;
2419 }
2420 return "";
2421 }
2422
mark_tiles(player_t * player,const koord3d & start,const koord3d & end)2423 void tool_build_way_t::mark_tiles( player_t *player, const koord3d &start, const koord3d &end )
2424 {
2425 way_builder_t bauigel(player);
2426 calc_route( bauigel, start, end );
2427
2428 uint8 offset = (desc->get_styp() == type_elevated && desc->get_wtyp() != air_wt) ? welt->get_settings().get_way_height_clearance() : 0;
2429
2430 if( bauigel.get_count()>1 ) {
2431 // Set tooltip first (no dummygrounds, if bauigel.calc_casts() is called).
2432 win_set_static_tooltip( tooltip_with_price_length("Building costs estimates", bauigel.calc_costs(), bauigel.get_count() ) );
2433
2434 // make dummy route from bauigel
2435 for( uint32 j=0; j<bauigel.get_count(); j++ ) {
2436 koord3d pos = bauigel.get_route()[j] + koord3d(0,0,offset);
2437 grund_t *gr = welt->lookup( pos );
2438 if( !gr ) {
2439 gr = new monorailboden_t(pos, 0);
2440 // should only be here when elevated/monorail, therefore will be at height offset above ground
2441 gr->set_grund_hang( welt->lookup( pos - koord3d( 0, 0, offset ) )->get_grund_hang() );
2442 welt->access(pos.get_2d())->boden_hinzufuegen(gr);
2443 }
2444 if (gr->is_water()) {
2445 continue;
2446 }
2447 ribi_t::ribi zeige = gr->get_weg_ribi_unmasked(desc->get_wtyp()) | bauigel.get_route().get_ribi( j );
2448
2449 zeiger_t *way = new zeiger_t(pos, player);
2450 if(gr->get_weg_hang()) {
2451 way->set_image( desc->get_slope_image_id(gr->get_weg_hang(),0) );
2452 }
2453 else if(desc->get_wtyp()!=powerline_wt && ribi_t::is_bend(zeige) && desc->has_diagonal_image()) {
2454 way->set_image( desc->get_diagonal_image_id(zeige,0) );
2455 }
2456 else {
2457 way->set_image( desc->get_image_id(zeige,0) );
2458 }
2459 gr->obj_add( way );
2460 way->set_yoff(-gr->get_weg_yoff() );
2461 marked.insert( way );
2462 way->mark_image_dirty( way->get_image(), 0 );
2463 }
2464 }
2465 }
2466
2467
2468 /* city road construction */
get_desc(uint16,bool) const2469 const way_desc_t *tool_build_cityroad::get_desc(uint16,bool) const
2470 {
2471 return welt->get_city_road();
2472 }
2473
do_work(player_t * player,const koord3d & start,const koord3d & end)2474 const char *tool_build_cityroad::do_work( player_t *player, const koord3d &start, const koord3d &end )
2475 {
2476 way_builder_t bauigel(player);
2477 bauigel.set_build_sidewalk(true);
2478 calc_route( bauigel, start, end );
2479 if( bauigel.get_route().get_count()>1 ) {
2480 welt->mute_sound(true);
2481 bauigel.build();
2482 welt->mute_sound(false);
2483
2484 return NULL;
2485 }
2486 return "";
2487 }
2488
2489
2490 /* bridge construction */
get_tooltip(const player_t *) const2491 const char* tool_build_bridge_t::get_tooltip(const player_t *) const
2492 {
2493 const bridge_desc_t * desc = bridge_builder_t::get_desc(default_param);
2494 if (desc == NULL) {
2495 return "";
2496 }
2497 tooltip_with_price_maintenance( welt, desc->get_name(), -desc->get_price(), desc->get_maintenance() );
2498 size_t n= strlen(toolstr);
2499 if(desc->get_waytype()!=powerline_wt) {
2500 n += sprintf(toolstr+n, ", %dkm/h", desc->get_topspeed());
2501 }
2502 if(desc->get_max_length()>0) {
2503 n += sprintf(toolstr+n, ", %dkm", desc->get_max_length());
2504 }
2505 return toolstr;
2506 }
2507
get_waytype() const2508 waytype_t tool_build_bridge_t::get_waytype() const
2509 {
2510 const bridge_desc_t * desc = bridge_builder_t::get_desc(default_param);
2511 return desc ? desc->get_waytype() : invalid_wt;
2512 }
2513
2514
init(player_t * player)2515 bool tool_build_bridge_t::init( player_t *player )
2516 {
2517 two_click_tool_t::init( player );
2518 // now get current desc
2519 const bridge_desc_t *desc = bridge_builder_t::get_desc(default_param);
2520 if( desc && !desc->is_available(welt->get_timeline_year_month()) && player!=NULL && player!=welt->get_public_player() ) {
2521 return false;
2522 }
2523 return desc!=NULL;
2524 }
2525
2526
do_work(player_t * player,const koord3d & start,const koord3d & end)2527 const char *tool_build_bridge_t::do_work( player_t *player, const koord3d &start, const koord3d &end )
2528 {
2529 const bridge_desc_t *desc = bridge_builder_t::get_desc(default_param);
2530 if (end==koord3d::invalid) {
2531 return bridge_builder_t::build( player, start, desc );
2532 }
2533 else {
2534 const koord zv(ribi_type(end-start));
2535 sint8 bridge_height;
2536 const char *error;
2537 koord3d end2 = bridge_builder_t::find_end_pos(player, start, zv, desc, error, bridge_height, false, koord_distance(start, end), is_ctrl_pressed());
2538 if (end2 != end) {
2539 return "End position is not valid"; // could only happen for scripts
2540 }
2541 bridge_builder_t::build_bridge( player, start, end, zv, bridge_height, desc, way_builder_t::weg_search(desc->get_waytype(), desc->get_topspeed(), welt->get_timeline_year_month(), type_flat));
2542 return NULL; // all checks are performed before building.
2543 }
2544 }
2545
rdwr_custom_data(memory_rw_t * packet)2546 void tool_build_bridge_t::rdwr_custom_data(memory_rw_t *packet)
2547 {
2548 two_click_tool_t::rdwr_custom_data(packet);
2549 uint8 i = ribi;
2550 packet->rdwr_byte(i);
2551 ribi = (ribi_t::ribi)i;
2552 }
2553
mark_tiles(player_t * player,const koord3d & start,const koord3d & end)2554 void tool_build_bridge_t::mark_tiles( player_t *player, const koord3d &start, const koord3d &end )
2555 {
2556 const ribi_t::ribi ribi_mark = ribi_type(end-start);
2557 const koord zv(ribi_mark);
2558 const bridge_desc_t *desc = bridge_builder_t::get_desc(default_param);
2559 const char *error;
2560 sint8 bridge_height;
2561 koord3d end2 = bridge_builder_t::find_end_pos(player, start, zv, desc, error, bridge_height, false, koord_distance(start, end), is_ctrl_pressed());
2562 assert(end2 == end); (void)end2;
2563
2564 sint64 costs = 0;
2565 // start
2566 grund_t *gr = welt->lookup(start);
2567
2568 // get initial height of bridge from start tile
2569 // flat -> height is 1 if conversion factor 1, 2 if conversion factor 2
2570 // single height -> height is 1
2571 // double height -> height is 2
2572 const slope_t::type slope = gr->get_weg_hang();
2573 uint8 max_height = slope ? slope_t::max_diff(slope) : bridge_height;
2574
2575 zeiger_t *way = new zeiger_t(start, player );
2576 const bridge_desc_t::img_t img0 = desc->get_end( slope, slope, slope_type(zv)*max_height );
2577
2578 gr->obj_add( way );
2579 way->set_image( desc->get_background( img0, 0 ) );
2580 way->set_foreground_image( desc->get_foreground( img0, 0 ) );
2581
2582 if( gr->get_grund_hang() != 0 ) {
2583 way->set_yoff( -TILE_HEIGHT_STEP * max_height );
2584 }
2585 // eventually we have to remove trees on start tile
2586 if (desc->get_waytype() != powerline_wt) {
2587 for( uint8 i=0; i<gr->get_top(); i++ ) {
2588 obj_t *obj = gr->obj_bei(i);
2589 switch(obj->get_typ()) {
2590 case obj_t::baum:
2591 costs -= welt->get_settings().cst_remove_tree;
2592 break;
2593 case obj_t::groundobj:
2594 costs += ((groundobj_t *)obj)->get_desc()->get_price();
2595 break;
2596 default: break;
2597 }
2598 }
2599 }
2600 marked.insert( way );
2601 way->mark_image_dirty( way->get_image(), 0 );
2602 // loop
2603 koord3d pos( start + zv + koord3d( 0, 0, max_height ) );
2604 while (pos.get_2d()!=end.get_2d()) {
2605 grund_t *gr = welt->lookup( pos );
2606 if( !gr ) {
2607 gr = new monorailboden_t(pos, 0);
2608 gr->set_grund_hang( 0 );
2609 welt->access(pos.get_2d())->boden_hinzufuegen(gr);
2610 }
2611 zeiger_t *way = new zeiger_t(pos, player );
2612 gr->obj_add( way );
2613 grund_t *kb = welt->lookup_kartenboden(pos.get_2d());
2614 sint16 height = pos.z - kb->get_pos().z;
2615 way->set_image(desc->get_background(desc->get_straight(ribi_mark,height-slope_t::max_diff(kb->get_grund_hang())),0));
2616 way->set_foreground_image(desc->get_foreground(desc->get_straight(ribi_mark,height-slope_t::max_diff(kb->get_grund_hang())), 0));
2617 marked.insert( way );
2618 way->mark_image_dirty( way->get_image(), 0 );
2619 pos = pos + zv;
2620 }
2621 costs += desc->get_price() * koord_distance(start, pos);
2622 // end
2623 gr = welt->lookup(end);
2624
2625 // get initial height of bridge from start tile
2626 // flat -> height is 1 if conversion factor 1, 2 if conversion factor 2
2627 // single height -> height is 1
2628 // double height -> height is 2
2629 const slope_t::type end_slope = gr->get_weg_hang();
2630 const uint8 end_max_height = end_slope ? ((end_slope & 7) ? 1 : 2) : (pos.z-end.z);
2631
2632 if( gr->ist_karten_boden() && end.z + end_max_height == start.z + max_height ) {
2633 zeiger_t *way = new zeiger_t(end, player );
2634 const bridge_desc_t::img_t img1 = desc->get_end( end_slope, end_slope, end_slope?0:(pos.z-end.z)*slope_type(-zv) );
2635 gr->obj_add( way );
2636 way->set_image(desc->get_background(img1, 0));
2637 way->set_foreground_image(desc->get_foreground(img1, 0));
2638 if( gr->get_grund_hang() != 0 ) {
2639 way->set_yoff( -TILE_HEIGHT_STEP * end_max_height );
2640 }
2641 marked.insert( way );
2642 way->mark_image_dirty( way->get_image(), 0 );
2643 costs += desc->get_price();
2644 }
2645 else {
2646 if (desc->get_waytype() == powerline_wt ? !gr->find<leitung_t>() : !gr->hat_weg(desc->get_waytype())) {
2647 const way_desc_t *way_desc = way_builder_t::weg_search(desc->get_waytype(), desc->get_topspeed(), welt->get_timeline_year_month(), type_flat);
2648 costs += way_desc->get_price();
2649 }
2650 }
2651 // eventually we have to remove trees on end tile
2652 if (desc->get_waytype() != powerline_wt) {
2653 for( uint8 i=0; i<gr->get_top(); i++ ) {
2654 obj_t *obj = gr->obj_bei(i);
2655 switch(obj->get_typ()) {
2656 case obj_t::baum:
2657 costs -= welt->get_settings().cst_remove_tree;
2658 break;
2659 case obj_t::groundobj:
2660 costs += ((groundobj_t *)obj)->get_desc()->get_price();
2661 break;
2662 default: break;
2663 }
2664 }
2665 }
2666 win_set_static_tooltip( tooltip_with_price_length("Building costs estimates", costs, koord_distance(start, pos) ) );
2667 }
2668
is_valid_pos(player_t * player,const koord3d & pos,const char * & error,const koord3d & start)2669 uint8 tool_build_bridge_t::is_valid_pos( player_t *player, const koord3d &pos, const char *&error, const koord3d &start )
2670 {
2671 const bridge_desc_t *desc = bridge_builder_t::get_desc(default_param);
2672 const waytype_t wt = desc->get_waytype();
2673
2674 error = NULL;
2675 grund_t *gr = welt->lookup(pos);
2676 if( gr==NULL || !slope_t::is_way(gr->get_grund_hang()) || !bridge_builder_t::can_place_ramp( player, gr, wt, (is_first_click() ? 0 : ribi_type(pos-start)) ) ) {
2677 return 0;
2678 }
2679
2680 if( welt->lookup( pos + koord3d(0, 0, 1)) || (welt->get_settings().get_way_height_clearance()==2 && welt->lookup( pos + koord3d(0, 0, 2) )) ) {
2681 return 0;
2682 }
2683
2684 if( is_first_click() ) {
2685 if( gr->ist_karten_boden() ) {
2686 // first click
2687 ribi_t::ribi rw = ribi_t::none;
2688 if (wt==powerline_wt) {
2689 if (gr->hat_wege()) {
2690 return 0;
2691 }
2692 if (gr->find<leitung_t>()) {
2693 rw |= gr->find<leitung_t>()->get_ribi();
2694 }
2695 }
2696 else {
2697 if (gr->find<leitung_t>()) {
2698 return 0;
2699 }
2700 if(wt!=road_wt) {
2701 // only road bridges can have other ways on it (ie trams)
2702 if(gr->has_two_ways() || (gr->hat_wege() && gr->get_weg_nr(0)->get_waytype()!=wt) ) {
2703 return 0;
2704 }
2705 if(gr->hat_wege()){
2706 rw |= gr->get_weg_nr(0)->get_ribi_unmasked();
2707 }
2708 }
2709 else {
2710 // If road and tram, we have to check both ribis.
2711 for(int i=0;i<2;i++) {
2712 const weg_t *w = gr->get_weg_nr(i);
2713 if (w) {
2714 if (w->get_waytype()!=road_wt && !w->get_desc()->is_tram()) {
2715 return 0;
2716 }
2717 rw |= w->get_ribi_unmasked();
2718 }
2719 else break;
2720 }
2721 }
2722 }
2723 // ribi from slope
2724 rw |= ribi_type(gr->get_grund_hang());
2725 if( rw!=ribi_t::none && !ribi_t::is_single(rw) ) {
2726 return 0;
2727 }
2728 // determine possible directions
2729 ribi = ribi_t::backward(rw);
2730 return (ribi!=ribi_t::none ? 2 : 0) | (ribi_t::is_single(ribi) ? 1 : 0);
2731 }
2732 else {
2733 if( gr->get_weg_hang() ) {
2734 return 0;
2735 }
2736
2737 if( gr->get_typ() != grund_t::monorailboden &&
2738 gr->get_typ() != grund_t::tunnelboden ) {
2739 return 0;
2740 }
2741
2742 if(!gr->get_weg_nr(0)) {
2743 return 0;
2744 }
2745
2746 ribi = ~gr->get_weg_nr(0)->get_ribi_unmasked();
2747
2748 return 2;
2749 }
2750 }
2751 else {
2752 // second click
2753
2754 // dragging in the right direction?
2755 ribi_t::ribi test = ribi_type(pos - start);
2756 if (!ribi_t::is_single(test) || ((test & (~ribi))!=0) ) {
2757 return 0;
2758 }
2759
2760 // check whether we can build a bridge here
2761 const char *error = NULL;
2762 sint8 bridge_height;
2763 koord3d end = bridge_builder_t::find_end_pos(player, start, koord(test), desc, error, bridge_height, false, koord_distance(start, pos), is_ctrl_pressed());
2764 if (end!=pos) {
2765 return 0;
2766 }
2767 return 2;
2768 }
2769 }
2770
2771
2772 /* more difficult, since this builds also underground ways */
get_tooltip(const player_t *) const2773 const char* tool_build_tunnel_t::get_tooltip(const player_t *) const
2774 {
2775 const tunnel_desc_t * desc = tunnel_builder_t::get_desc(default_param);
2776 tooltip_with_price_maintenance( welt, desc->get_name(), -desc->get_price(), desc->get_maintenance() );
2777 size_t n= strlen(toolstr);
2778 if(desc->get_waytype()!=powerline_wt) {
2779 n += sprintf(toolstr+n, ", %dkm/h", desc->get_topspeed());
2780 }
2781 return toolstr;
2782 }
2783
2784
get_waytype() const2785 waytype_t tool_build_tunnel_t::get_waytype() const
2786 {
2787 const tunnel_desc_t * desc = tunnel_builder_t::get_desc(default_param);
2788 return desc ? desc->get_waytype() : invalid_wt;
2789 }
2790
2791
init(player_t * player)2792 bool tool_build_tunnel_t::init( player_t *player )
2793 {
2794 two_click_tool_t::init( player );
2795 // now get current desc
2796 const tunnel_desc_t *desc = tunnel_builder_t::get_desc(default_param);
2797 if( desc && !desc->is_available(welt->get_timeline_year_month()) && player!=NULL && player!=welt->get_public_player() ) {
2798 return false;
2799 }
2800 return desc!=NULL;
2801 }
2802
2803
check_pos(player_t * player,koord3d pos)2804 const char *tool_build_tunnel_t::check_pos( player_t *player, koord3d pos)
2805 {
2806 if (grund_t::underground_mode == grund_t::ugm_all) {
2807 return NULL;
2808 }
2809 else {
2810 return two_click_tool_t::check_pos(player, pos);
2811 }
2812 }
2813
calc_route(way_builder_t & bauigel,const koord3d & start,const koord3d & end)2814 void tool_build_tunnel_t::calc_route( way_builder_t &bauigel, const koord3d &start, const koord3d &end)
2815 {
2816 const tunnel_desc_t *desc = tunnel_builder_t::get_desc(default_param);
2817 way_builder_t::bautyp_t bt = (way_builder_t::bautyp_t)(desc->get_waytype());
2818
2819 const way_desc_t *wb = desc->get_way_desc();
2820 if(wb==NULL) {
2821 // ignore timeline to get consistent results
2822 wb = way_builder_t::weg_search( desc->get_waytype(), desc->get_topspeed(), 0, type_flat );
2823 }
2824
2825 bauigel.init_builder(bt | way_builder_t::tunnel_flag, wb, desc);
2826 bauigel.set_keep_existing_faster_ways( !is_ctrl_pressed() );
2827 // wegbauer (way builder) tries to find route to 3d coordinate if no ground at end exists or is not kartenboden (map ground)
2828 bauigel.calc_straight_route(start,end);
2829 }
2830
do_work(player_t * player,const koord3d & start,const koord3d & end)2831 const char *tool_build_tunnel_t::do_work( player_t *player, const koord3d &start, const koord3d &end )
2832 {
2833 if( end == koord3d::invalid ) {
2834 // Build tunnel mouths
2835 if (welt->lookup_kartenboden(start.get_2d())->get_hoehe() == start.z) {
2836 const tunnel_desc_t *desc = tunnel_builder_t::get_desc(default_param);
2837 return tunnel_builder_t::build( player, start.get_2d(), desc, !is_ctrl_pressed() );
2838 }
2839 else {
2840 return "";
2841 }
2842 }
2843 else {
2844 // Build tunnels
2845 way_builder_t bauigel(player);
2846 calc_route( bauigel, start, end );
2847 welt->mute_sound(true);
2848 bauigel.build();
2849 welt->mute_sound(false);
2850 welt->lookup_kartenboden(end.get_2d())->clear_flag(grund_t::marked);
2851 return NULL;
2852 }
2853 }
2854
is_valid_pos(player_t * player,const koord3d & pos,const char * & error,const koord3d &)2855 uint8 tool_build_tunnel_t::is_valid_pos( player_t *player, const koord3d &pos, const char *&error, const koord3d & )
2856 {
2857 if( !is_first_click() ) {
2858 error = NULL;
2859 // All pos are valid for the second click!
2860 return 2;
2861 }
2862 // search for ground
2863 // start needs valid tile!
2864 grund_t *gr = welt->lookup(pos);
2865 if( gr ) {
2866 if( gr->hat_wege() ) {
2867 const tunnel_desc_t *desc = tunnel_builder_t::get_desc(default_param);
2868 // use the check_owner routine of way_builder_t (not player_t!), needs an instance
2869 weg_t *w = gr->get_weg_nr(0);
2870 if( w==NULL || w->get_desc()->get_wtyp()!=desc->get_waytype() ) {
2871 error = NOTICE_UNSUITABLE_GROUND;
2872 return 0;
2873 }
2874 way_builder_t bauigel(player);
2875 if(!bauigel.check_owner( w->get_owner(), player )) {
2876 error = "Das Feld gehoert\neinem anderen Spieler\n";
2877 return 0;
2878 }
2879 }
2880 }
2881 else {
2882 error = NOTICE_UNSUITABLE_GROUND;
2883 return 0;
2884 }
2885 // if starting tile is tunnel .. build underground tracks
2886 error = NULL;
2887 if(gr->ist_tunnel()) {
2888 return 2;
2889 }
2890 // .. otherwise build tunnel mouths (and tunnel behind)
2891 else {
2892 return 1;
2893 }
2894 }
2895
mark_tiles(player_t * player,const koord3d & start,const koord3d & end)2896 void tool_build_tunnel_t::mark_tiles( player_t *player, const koord3d &start, const koord3d &end )
2897 {
2898 way_builder_t bauigel(player);
2899 calc_route( bauigel, start, end );
2900
2901 const tunnel_desc_t *desc = tunnel_builder_t::get_desc(default_param);
2902 // now we search a matching way for the tunnels top speed
2903 const way_desc_t *wb = desc->get_way_desc();
2904 if(wb==NULL) {
2905 // ignore timeline to get consistent results
2906 wb = way_builder_t::weg_search( desc->get_waytype(), desc->get_topspeed(), 0, type_flat );
2907 }
2908
2909 welt->lookup_kartenboden(end.get_2d())->clear_flag(grund_t::marked);
2910
2911 if( bauigel.get_count()>1 ) {
2912 // Set tooltip first (no dummygrounds, if bauigel.calc_casts() is called).
2913 win_set_static_tooltip( tooltip_with_price_length("Building costs estimates", -bauigel.calc_costs(), bauigel.get_count() ) );
2914
2915 // make dummy route from bauigel
2916 for( uint32 j=0; j<bauigel.get_count(); j++ ) {
2917 koord3d pos = bauigel.get_route()[j];
2918 grund_t *gr = welt->lookup(pos);
2919 if( !gr ) {
2920 // We need to create a dummy ground.
2921 gr = new tunnelboden_t(pos, 0);
2922 welt->access(pos.get_2d())->boden_hinzufuegen(gr);
2923 }
2924 ribi_t::ribi zeige = gr->get_weg_ribi_unmasked(wb->get_wtyp()) | bauigel.get_route().get_ribi( j );
2925
2926 zeiger_t *way = new zeiger_t(pos, player );
2927 if(gr->get_weg_hang()) {
2928 way->set_image( wb->get_slope_image_id(gr->get_weg_hang(),0) );
2929 }
2930 else if(wb->get_wtyp()!=powerline_wt && ribi_t::is_bend(zeige) && wb->has_diagonal_image()) {
2931 way->set_image( wb->get_diagonal_image_id(zeige,0) );
2932 }
2933 else {
2934 way->set_image( wb->get_image_id(zeige,0) );
2935 }
2936 gr->obj_add( way );
2937 marked.insert( way );
2938 way->mark_image_dirty( way->get_image(), 0 );
2939 }
2940 welt->lookup(end)->set_flag(grund_t::marked);
2941 }
2942 }
2943
2944
2945 /* removes a way like a driving car ... */
get_tooltip(player_t const *) const2946 char const* tool_wayremover_t::get_tooltip(player_t const*) const
2947 {
2948 switch(atoi(default_param)) {
2949 case road_wt: return translator::translate("remove roads");
2950 case tram_wt:
2951 case track_wt: return translator::translate("remove tracks");
2952 case maglev_wt: return translator::translate("remove maglev tracks");
2953 case narrowgauge_wt: return translator::translate("remove narrowgauge tracks");
2954 case monorail_wt: return translator::translate("remove monorails");
2955 case water_wt: return translator::translate("remove channels");
2956 case air_wt: return translator::translate("remove airstrips");
2957 case powerline_wt: return translator::translate("remove powerlines");
2958 }
2959 return NULL;
2960 }
2961
get_icon(player_t *) const2962 image_id tool_wayremover_t::get_icon(player_t *) const
2963 {
2964 if( default_param && way_builder_t::waytype_available( (waytype_t)atoi(default_param), welt->get_timeline_year_month() ) ) {
2965 return icon;
2966 }
2967 return IMG_EMPTY;
2968 }
2969
get_waytype() const2970 waytype_t tool_wayremover_t::get_waytype() const
2971 {
2972 return default_param ? (waytype_t)atoi(default_param) : invalid_wt;
2973 }
2974
2975 class electron_t : public test_driver_t {
check_next_tile(const grund_t * gr) const2976 bool check_next_tile(const grund_t* gr) const OVERRIDE { return gr->get_leitung()!=NULL; }
get_ribi(const grund_t * gr) const2977 ribi_t::ribi get_ribi(const grund_t* gr) const OVERRIDE { return gr->get_leitung()->get_ribi(); }
get_waytype() const2978 waytype_t get_waytype() const OVERRIDE { return invalid_wt; }
get_cost(const grund_t *,const weg_t *,const sint32,ribi_t::ribi) const2979 int get_cost(const grund_t *, const weg_t *, const sint32, ribi_t::ribi) const OVERRIDE { return 1; }
is_target(const grund_t *,const grund_t *) const2980 bool is_target(const grund_t *,const grund_t *) const OVERRIDE { return false; }
2981 };
2982
2983 class scenario_checker_t : public test_driver_t {
2984 public:
2985 test_driver_t *other;
2986 scenario_t *scenario;
2987 uint16 id;
2988 player_t *player;
~scenario_checker_t()2989 ~scenario_checker_t() { delete other; }
2990
2991 /**
2992 * checks for active scenario,
2993 * @returns scenario_checker_t if scenario active, the supplied test_driver otherwise
2994 */
apply(test_driver_t * test_driver,player_t * player,tool_t * tool)2995 static test_driver_t* apply(test_driver_t *test_driver, player_t *player, tool_t *tool) {
2996 karte_t *welt = world();
2997 if (is_scenario()) {
2998 scenario_checker_t *td2 = new scenario_checker_t();
2999 td2->other = test_driver;
3000 td2->scenario = welt->get_scenario();
3001 td2->id = tool->get_id();
3002 td2->player = player;
3003 return td2;
3004 }
3005 return test_driver;
3006 }
3007 private:
check_next_tile(const grund_t * gr) const3008 bool check_next_tile(const grund_t* gr) const OVERRIDE { return other->check_next_tile(gr) && scenario->is_work_allowed_here(player, id, other->get_waytype(), gr->get_pos())==NULL;}
get_ribi(const grund_t * gr) const3009 ribi_t::ribi get_ribi(const grund_t* gr) const OVERRIDE { return other->get_ribi(gr); }
get_waytype() const3010 waytype_t get_waytype() const OVERRIDE { return other->get_waytype(); }
get_cost(const grund_t * gr,const weg_t * w,const sint32 max_speed,ribi_t::ribi from) const3011 int get_cost(const grund_t *gr, const weg_t *w, const sint32 max_speed, ribi_t::ribi from) const OVERRIDE { return other->get_cost(gr, w, max_speed, from); }
is_target(const grund_t * gr,const grund_t * gr2) const3012 bool is_target(const grund_t *gr,const grund_t *gr2) const OVERRIDE { return other->is_target(gr,gr2); }
3013 };
3014
mark_tiles(player_t * player,const koord3d & start,const koord3d & end)3015 void tool_wayremover_t::mark_tiles( player_t *player, const koord3d &start, const koord3d &end )
3016 {
3017 route_t verbindung;
3018 bool can_built = calc_route( verbindung, player, start, end );
3019 if( can_built ) {
3020 FOR(vector_tpl<koord3d>, const& pos, verbindung.get_route()) {
3021 zeiger_t *marker = new zeiger_t(pos, NULL );
3022 marker->set_image( cursor );
3023 marker->mark_image_dirty( marker->get_image(), 0 );
3024 marked.insert( marker );
3025 welt->lookup(pos)->obj_add( marker );
3026 }
3027 }
3028 }
3029
is_valid_pos(player_t * player,const koord3d & pos,const char * & error,const koord3d &)3030 uint8 tool_wayremover_t::is_valid_pos( player_t *player, const koord3d &pos, const char *&error, const koord3d & )
3031 {
3032 // search for starting ground
3033 waytype_t wt = get_waytype();
3034 grund_t *gr=tool_intern_koord_to_weg_grund(player,welt,pos,wt);
3035 if(gr==NULL) {
3036 DBG_MESSAGE("tool_wayremover_t()", "no ground on %i,%i",pos.x, pos.y);
3037 // wrong ground or not this way here => exit
3038 return 0;
3039 }
3040 // do not remove ground from depot
3041 if(gr->get_depot()) {
3042 error = NOTICE_UNSUITABLE_GROUND;
3043 return 0;
3044 }
3045 if(is_scenario()) {
3046 error = welt->get_scenario()->is_work_allowed_here(player, get_id(), wt, pos);
3047 if (error) {
3048 dbg->warning("tool_wayremover_t::is_within_limits()", error);
3049 return 0;
3050 }
3051 }
3052 error = NULL;
3053 return 2;
3054 }
3055
calc_route(route_t & verbindung,player_t * player,const koord3d & start,const koord3d & end)3056 bool tool_wayremover_t::calc_route( route_t &verbindung, player_t *player, const koord3d &start, const koord3d &end )
3057 {
3058 waytype_t wt = get_waytype();
3059 if (wt == tram_wt) {
3060 wt = track_wt;
3061 }
3062
3063 if( start == end ) {
3064 verbindung.clear();
3065 grund_t *gr=welt->lookup(start);
3066 if( gr && (wt!=powerline_wt ? gr->get_weg(wt)!=NULL : gr->get_leitung()!=NULL) ) {
3067 verbindung.append( start );
3068 }
3069 }
3070 else {
3071 // get a default vehikel
3072 test_driver_t* test_driver;
3073 if( wt!=powerline_wt ) {
3074 vehicle_desc_t remover_desc(wt, 500, vehicle_desc_t::diesel );
3075 vehicle_t *driver = vehicle_builder_t::build(start, player, NULL, &remover_desc);
3076 driver->set_flag( obj_t::not_on_map );
3077 test_driver = driver;
3078 }
3079 else {
3080 test_driver = new electron_t();
3081 }
3082 test_driver = scenario_checker_t::apply(test_driver, player, this);
3083
3084 verbindung.calc_route(welt, start, end, test_driver, 0, 0);
3085 delete test_driver;
3086 }
3087 DBG_MESSAGE("tool_wayremover_t()","route with %d tile found",verbindung.get_count());
3088
3089 calc_route_error = NULL;
3090 bool can_delete = start == end || verbindung.get_count()>1;
3091 if( can_delete ) {
3092 // found a route => check if I can delete anything on it
3093 FOR(koord3d_vector_t, const& i, verbindung.get_route()) {
3094 if (!can_delete) break;
3095 grund_t const* const gr = welt->lookup(i);
3096 if( wt!=powerline_wt ) {
3097 // no way found
3098 if( gr==NULL || gr->get_weg(wt)==NULL ) {
3099 can_delete = false;
3100 break;
3101 }
3102 // check all if we want to delete the first on a no-ground tile
3103 bool check_all = !gr->ist_karten_boden() && gr->has_two_ways() && gr->get_weg_nr(0)->get_waytype()==wt;
3104 // we have to do a fine check
3105 for( uint i=0; i<gr->get_top() && can_delete; i++ ) {
3106 obj_t *obj = gr->obj_bei(i);
3107 const uint8 type = obj->get_typ();
3108 // ignore pillars, powerlines
3109 if (type == obj_t::pillar || type==obj_t::leitung) {
3110 continue;
3111 }
3112 // ignore flying aircraft
3113 if (type == obj_t::air_vehicle && !(static_cast<air_vehicle_t*>(obj)->is_on_ground())) {
3114 continue;
3115 }
3116 const waytype_t obj_wt = obj->get_waytype();
3117 // way-related things
3118 if (obj_wt != invalid_wt) {
3119 // check this thing if it has the same waytype or if we want to remove the whole bridge/tunnel tile
3120 // special case: stations - take care not to produce station without any way
3121 const bool lonely_station = type==obj_t::gebaeude && !gr->has_two_ways();
3122 if (check_all || obj_wt == wt || lonely_station) {
3123 can_delete = (calc_route_error = obj->is_deletable(player)) == NULL;
3124 }
3125 }
3126 // all other stuff
3127 else {
3128 can_delete = (calc_route_error = obj->is_deletable(player)) == NULL;
3129 }
3130 }
3131 }
3132 else {
3133 // for powerline: only a ground and a powerline to remove
3134 if( gr==NULL || gr->get_leitung()==NULL || (calc_route_error = gr->get_leitung()->is_deletable(player))!=NULL ) {
3135 can_delete = false;
3136 break;
3137 }
3138 }
3139 }
3140 }
3141 DBG_MESSAGE("tool_wayremover_t()", "route search returned %d", can_delete);
3142
3143 return can_delete;
3144 }
3145
do_work(player_t * player,const koord3d & start,const koord3d & end)3146 const char *tool_wayremover_t::do_work( player_t *player, const koord3d &start, const koord3d &end )
3147 {
3148 waytype_t wt = get_waytype();
3149 route_t verbindung;
3150 if( !calc_route( verbindung, player, start, end ) ) {
3151 DBG_MESSAGE("tool_wayremover_t()","no route found");
3152 if (calc_route_error && *calc_route_error) {
3153 return calc_route_error;
3154 }
3155 else {
3156 return "Ways not connected";
3157 }
3158 }
3159 bool can_delete = true; // assume success
3160
3161 // if successful => delete everything
3162 for( uint32 i=0; i<verbindung.get_count(); i++ ) {
3163
3164 grund_t *gr=welt->lookup(verbindung.at(i));
3165
3166 // ground can be missing after deleting a bridge ...
3167 if(gr && !gr->is_water()) {
3168
3169 if(gr->ist_bruecke()) {
3170 if(gr->find<bruecke_t>()->get_desc()->get_waytype()==wt) {
3171 if( bridge_builder_t::is_start_of_bridge(gr) ) {
3172 const char *err = NULL;
3173 err = bridge_builder_t::remove(player,verbindung.at(i),wt);
3174 if(err) {
3175 return err;
3176 }
3177 gr = welt->lookup(verbindung.at(i));
3178 if( !gr ) {
3179 // happens with bridges without ramps
3180 continue;
3181 }
3182 }
3183 else {
3184 // do not remove asphalt from a bridge ...
3185 continue;
3186 }
3187 }
3188 }
3189
3190 // now the tricky part: delete just part of a way (or everything, if possible)
3191 // calculate remaining directions
3192 ribi_t::ribi rem = 15 ^ ( verbindung.get_route().get_ribi(i) );
3193 // if start=end tile then delete every direction
3194 if( verbindung.get_count() <= 1 ) {
3195 rem = 0;
3196 }
3197
3198 if( wt!=powerline_wt ) {
3199 if(!gr->get_flag(grund_t::is_kartenboden) && (gr->get_typ()==grund_t::tunnelboden || gr->get_typ()==grund_t::monorailboden) && gr->get_weg_nr(0)->get_waytype()==wt) {
3200 can_delete &= gr->remove_everything_from_way(player,wt,rem);
3201 if(can_delete && gr->get_weg(wt)==NULL) {
3202 if(gr->get_weg_nr(0)!=0) {
3203 gr->remove_everything_from_way(player,gr->get_weg_nr(0)->get_waytype(),ribi_t::none);
3204 }
3205 gr->obj_loesche_alle(player);
3206 gr->mark_image_dirty();
3207 if (gr->is_visible() && gr->get_typ()==grund_t::tunnelboden && i>0) { // visibility test does not influence execution
3208 grund_t *bd = welt->access(verbindung.at(i-1).get_2d())->get_kartenboden();
3209 bd->calc_image();
3210 bd->set_flag(grund_t::dirty);
3211 }
3212 // delete tunnel ground too, if empty
3213 welt->access(gr->get_pos().get_2d())->boden_entfernen(gr);
3214 delete gr;
3215 }
3216 }
3217 else {
3218 can_delete &= gr->remove_everything_from_way(player,wt,rem);
3219 }
3220 }
3221 else {
3222 leitung_t *lt = gr->get_leitung();
3223 if( lt && (rem<->get_ribi())==0 ) {
3224 // remove only single connections
3225 lt->cleanup(player);
3226 delete lt;
3227 // delete tunnel ground too, if empty
3228 if (gr->get_typ()==grund_t::tunnelboden) {
3229 gr->obj_loesche_alle(player);
3230 gr->mark_image_dirty();
3231 if (!gr->get_flag(grund_t::is_kartenboden)) {
3232 welt->access(gr->get_pos().get_2d())->boden_entfernen(gr);
3233 delete gr;
3234 }
3235 else {
3236 grund_t *gr_new = new boden_t(gr->get_pos(), gr->get_grund_hang());
3237 welt->access(gr->get_pos().get_2d())->boden_ersetzen(gr, gr_new);
3238 gr_new->calc_image();
3239 }
3240 }
3241 }
3242 // otherwise it is a crossing ...
3243 }
3244 }
3245 // ok, now everything removed ...
3246 }
3247
3248 // return success
3249 return can_delete ? NULL : "";
3250 }
3251
3252
3253
3254 /* add catenary during construction */
3255 const way_obj_desc_t *tool_build_wayobj_t::default_electric = NULL;
3256
get_tooltip(const player_t *) const3257 const char* tool_build_wayobj_t::get_tooltip(const player_t *) const
3258 {
3259 if( build ) {
3260 const way_obj_desc_t *desc = get_desc();
3261 if(desc) {
3262 tooltip_with_price_maintenance( welt, desc->get_name(), -desc->get_price(), desc->get_maintenance() );
3263 size_t n= strlen(toolstr);
3264 if (desc->is_overhead_line()) {
3265 // only overheadlines impose topspeed
3266 sprintf(toolstr+n, ", %dkm/h", desc->get_topspeed());
3267 }
3268 return toolstr;
3269 }
3270 return NULL;
3271 }
3272 else {
3273 waytype_t wt = (waytype_t)atoi( default_param );
3274 sprintf( toolstr, translator::translate("Remove wayobj %s"), translator::translate(weg_t::waytype_to_string(wt)) );
3275 return toolstr;
3276 }
3277 }
3278
get_desc() const3279 const way_obj_desc_t *tool_build_wayobj_t::get_desc() const
3280 {
3281 const way_obj_desc_t *desc = default_param ? wayobj_t::find_desc(default_param) : NULL;
3282 if(desc==NULL) {
3283 desc = default_electric;
3284 if(desc==NULL) {
3285 desc = wayobj_t::get_overhead_line( track_wt, welt->get_timeline_year_month() );
3286 }
3287 }
3288 return desc;
3289 }
3290
get_waytype() const3291 waytype_t tool_build_wayobj_t::get_waytype() const
3292 {
3293 if( build ) {
3294 const way_obj_desc_t *desc = get_desc();
3295 return desc ? desc->get_wtyp() : invalid_wt;
3296 }
3297 else {
3298 return default_param ? (waytype_t)atoi( default_param ) : invalid_wt;
3299 }
3300 }
3301
is_selected() const3302 bool tool_build_wayobj_t::is_selected() const
3303 {
3304 const tool_build_wayobj_t *selected = dynamic_cast<const tool_build_wayobj_t *>(welt->get_tool(welt->get_active_player_nr()));
3305 return (selected && selected->build==build && selected->get_desc() == get_desc());
3306 }
3307
init(player_t * player)3308 bool tool_build_wayobj_t::init( player_t *player )
3309 {
3310 two_click_tool_t::init( player );
3311
3312 if( build ) {
3313 desc = get_desc();
3314 if( desc ) {
3315 cursor = desc->get_cursor()->get_image_id(0);
3316 wt = desc->get_wtyp();
3317 default_electric = desc;
3318 }
3319 return desc!=NULL;
3320 }
3321 else {
3322 desc = NULL;
3323 wt = (waytype_t)atoi( default_param );
3324 return wt != 0;
3325 }
3326 }
3327
calc_route(route_t & verbindung,player_t * player,const koord3d & start,const koord3d & to)3328 bool tool_build_wayobj_t::calc_route( route_t &verbindung, player_t *player, const koord3d& start, const koord3d& to )
3329 {
3330 waytype_t waytype = wt;
3331 if( waytype == any_wt ) {
3332 waytype = welt->lookup(start)->get_weg(wt)->get_waytype();
3333 }
3334 // special treatment for deports, since track electrication cannot "drive" into tram depot
3335 if( waytype == track_wt ) {
3336 if( depot_t *dp = welt->lookup(start)->get_depot() ) {
3337 waytype = dp->get_waytype();
3338 }
3339 else if( depot_t *dp = welt->lookup(to)->get_depot() ) {
3340 waytype = dp->get_waytype();
3341 }
3342 }
3343 // get a default vehikel
3344 vehicle_desc_t remover_desc( waytype, 500, vehicle_desc_t::diesel );
3345 vehicle_t* test_vehicle = vehicle_builder_t::build(start, player, NULL, &remover_desc);
3346 test_vehicle->set_flag( obj_t::not_on_map );
3347 test_driver_t* test_driver = scenario_checker_t::apply(test_vehicle, player, this);
3348
3349 bool can_built;
3350 if( start != to ) {
3351 can_built = verbindung.calc_route(welt, start, to, test_driver, 0, 0);
3352 }
3353 else {
3354 verbindung.clear();
3355 verbindung.append( start );
3356 can_built = true;
3357 }
3358 delete test_driver;
3359 return can_built;
3360 }
3361
is_valid_pos(player_t * player,const koord3d & pos,const char * & error,const koord3d &)3362 uint8 tool_build_wayobj_t::is_valid_pos( player_t* player, const koord3d& pos, const char *&error, const koord3d & )
3363 {
3364 // search for starting ground
3365 grund_t *gr=tool_intern_koord_to_weg_grund(player, welt, pos, wt );
3366 if( gr == NULL ) {
3367 DBG_MESSAGE("tool_build_wayobj_t::is_within_limits()", "no ground on %s",pos.get_str());
3368 // wrong ground or not this way here => exit
3369 return 0;
3370 }
3371 error = NULL;
3372 return 2;
3373 }
3374
mark_tiles(player_t * player,const koord3d & start,const koord3d & end)3375 void tool_build_wayobj_t::mark_tiles( player_t* player, const koord3d &start, const koord3d &end )
3376 {
3377 route_t verbindung;
3378 bool can_built = calc_route( verbindung, player, start, end );
3379 if( can_built ) {
3380 sint32 cost_estimate = 0;
3381
3382 bool keep_existing_faster_ways = !is_ctrl_pressed();
3383
3384 for( uint32 j = 0; j < verbindung.get_count(); j++ ) {
3385 koord3d pos = verbindung.at(j);
3386 grund_t *gr = welt->lookup(pos);
3387
3388 ribi_t::ribi show = verbindung.get_route().get_ribi(j);
3389 // Search a matching catenary on gr.
3390 wayobj_t *wayobj = gr->get_wayobj( wt );
3391 if( build ) {
3392 cost_estimate += desc->get_price();
3393 if( wayobj ) {
3394 show = show | wayobj->get_dir();
3395 // Already a catenary here -> costs only, if new catenary is faster
3396 if( (wayobj->get_desc()->get_topspeed() >= desc->get_topspeed() && keep_existing_faster_ways) || wayobj->get_desc() == desc ) {
3397 cost_estimate -= desc->get_price();
3398 }
3399 }
3400 }
3401 else if( wayobj ) {
3402 cost_estimate += wayobj->get_desc()->get_price();
3403 }
3404
3405 zeiger_t *way_obj = NULL;
3406 if( build ) {
3407 way_obj = new zeiger_t(pos, player );
3408 if( gr->get_weg_hang() ) {
3409 way_obj->set_foreground_image( desc->get_front_slope_image_id(gr->get_weg_hang()) );
3410 way_obj->set_image( desc->get_back_slope_image_id(gr->get_weg_hang()) );
3411 }
3412 else if( ribi_t::is_bend(show) && desc->has_diagonal_image() ) {
3413 way_obj->set_foreground_image( desc->get_front_diagonal_image_id(show) );
3414 way_obj->set_image( desc->get_back_diagonal_image_id(show) );
3415 }
3416 else {
3417 way_obj->set_foreground_image( desc->get_front_image_id(show) );
3418 way_obj->set_image( desc->get_back_image_id(show) );
3419 }
3420 }
3421 else {
3422 if( gr->get_wayobj( wt ) ) {
3423 way_obj = new zeiger_t(pos, player );
3424 way_obj->set_image( cursor ); //skinverwaltung_t::bauigelsymbol->get_image_id(0));
3425 }
3426 }
3427 if( way_obj ) {
3428 way_obj->mark_image_dirty( way_obj->get_image(), 0 );
3429 gr->obj_add( way_obj );
3430 marked.insert( way_obj );
3431 }
3432 }
3433 win_set_static_tooltip( tooltip_with_price_length("Building costs estimates", -cost_estimate, verbindung.get_count() ) );
3434 }
3435 }
3436
do_work(player_t * player,const koord3d & start,const koord3d & end)3437 const char *tool_build_wayobj_t::do_work( player_t* player, const koord3d &start, const koord3d &end )
3438 {
3439 route_t verbindung;
3440 bool can_built = calc_route( verbindung, player, start, end );
3441 DBG_MESSAGE("tool_build_wayobj_t::work()","route search returned %d",can_built);
3442
3443 const char *err = NULL;
3444
3445 if(!can_built) {
3446 return "Ways not connected";
3447 }
3448
3449 bool keep_existing_faster_ways = !is_ctrl_pressed();
3450
3451 // build wayobj ...
3452 koord3d_vector_t const& r = verbindung.get_route();
3453 for(uint32 i=0; i<verbindung.get_count(); i++ ) {
3454 if( build ) {
3455 wayobj_t::extend_wayobj(r[i], player, r.get_ribi(i), desc, keep_existing_faster_ways);
3456 }
3457 else {
3458 grund_t *gr = welt->lookup(r[i]);
3459 for(int n=0; n<gr->get_top(); n++ ) {
3460 obj_t *obj = gr->obj_bei(n);
3461 if( obj && obj->get_typ()==obj_t::wayobj ) {
3462 wayobj_t *wo = static_cast<wayobj_t *>(obj);
3463 if( wo->get_waytype() == wt ) {
3464 // only remove matching waytype
3465 const char *err = wo->is_deletable( player );
3466 if( !err ) {
3467 wo->cleanup( player );
3468 delete wo;
3469 }
3470 else {
3471 break;
3472 }
3473 }
3474 }
3475 }
3476 }
3477 }
3478
3479 // Update depots (maybe remove electric tab?). Depots can only be on first and last tile.
3480 if( depot_t *dep = welt->lookup(r[0])->get_depot() ) {
3481 dep->update_win();
3482 }
3483 if( depot_t *dep = welt->lookup(r.back())->get_depot() ) {
3484 dep->update_win();
3485 }
3486
3487 return err;
3488 }
3489
3490
3491 /* build all kind of station extension buildings */
tool_station_building_aux(player_t * player,bool extend_public_halt,koord3d pos,const building_desc_t * desc,sint8 rotation)3492 const char *tool_build_station_t::tool_station_building_aux(player_t *player, bool extend_public_halt, koord3d pos, const building_desc_t *desc, sint8 rotation )
3493 {
3494 koord k = pos.get_2d();
3495
3496 // need kartenboden (map ground)
3497 if (welt->lookup_kartenboden(k)->get_hoehe() != pos.z) {
3498 return "";
3499 }
3500 DBG_MESSAGE("tool_station_building_aux()", "building mail office/station building on square %d,%d", k.x, k.y);
3501
3502 // Player player pays for the construction
3503 // but we try to extend stations of Player new_owner that may be the public player
3504 player_t *new_owner = extend_public_halt ? welt->get_public_player() : player;
3505
3506 koord offsets;
3507 halthandle_t halt;
3508 const char *msg = NOTICE_TILE_FULL;
3509
3510 if( rotation==-1 ) {
3511 //no predefined rotation
3512
3513 int best_halt = 0;
3514 int any_halt = 0;
3515
3516 // find valid rotations (since halt extensions are symmetric, we need to check only two)
3517 bool any_ok = false;
3518 for( int r=0; r<2; r++ ) {
3519 koord testsize = desc->get_size(r);
3520 for( sint8 j=3; j>=0; j-- ) {
3521 bool ok = true;
3522 koord offset(((j&1)^1)*(testsize.x-1),((j>>1)&1)*(testsize.y-1));
3523 if(welt->square_is_free(k-offset, testsize.x, testsize.y, NULL, desc->get_allowed_climate_bits())) {
3524 // first we must check over/under halt
3525 halthandle_t last_halt;
3526 for( sint16 x=0; x<testsize.x; x++ ) {
3527 for( sint16 y=0; y<testsize.y; y++ ) {
3528 const planquadrat_t *pl = welt->access( k-offset+koord(x,y) );
3529 if (pl) {
3530 for( uint8 i=0; i < pl->get_boden_count(); i++ ) {
3531 halthandle_t test_halt = pl->get_boden_bei(i)->get_halt();
3532 if(test_halt.is_bound()) {
3533 if(!player_t::check_owner( new_owner, test_halt->get_owner())) {
3534 // there is another player's halt
3535 ok = false;
3536 msg = "Das Feld gehoert\neinem anderen Spieler\n";
3537 }
3538 else if(!last_halt.is_bound()) {
3539 last_halt = test_halt;
3540 }
3541 else if(last_halt != test_halt) {
3542 // there are several halts
3543 ok = false;
3544 msg = "Several halts found.";
3545 }
3546 }
3547 }
3548 }
3549 }
3550 }
3551 if(!ok) {
3552 continue;
3553 }
3554 // well, at least this is theoretical possible here
3555 any_ok = true;
3556 if(rotation==-1) {
3557 // we can build it. reserve this one
3558 // This needs to build a building at under/over a halt.
3559 rotation = r;
3560 offsets = offset;
3561 }
3562 koord test_start = k-offset;
3563
3564 // find all surrounding tiles with a stop
3565 // for following section of code arrays are arranged such that
3566 // 0 - facing north
3567 // 1 - facing west
3568 // 2 - facing south
3569 // 3 - facing east
3570 int neighbour_halts[4] = { 0, 0, 0, 0 };
3571 int best_halts[4] = { 0, 0, 0, 0 };
3572
3573 // test also diagonal corners (that is why from -1 to size!)
3574 for( sint16 y=-1; y<=testsize.y; y++ ) {
3575 for( sint16 x=-1; x<=testsize.x; (x==-1 && y>-1 && y<testsize.y)?x=testsize.x:x++ ) {
3576 const planquadrat_t *pl = welt->access( test_start+koord(x,y) );
3577 if( pl ) {
3578 for( uint b=0; b < pl->get_boden_count(); b++ ) {
3579 grund_t *gr = pl->get_boden_bei(b);
3580 if( gr->is_halt() && gr->get_halt().is_bound() && new_owner == gr->get_halt()->get_owner() ) {
3581 halt = gr->get_halt();
3582 gebaeude_t *gb = gr->find<gebaeude_t>();
3583 bool best = gr->hat_wege() && gb && gb->get_tile()->get_desc()->get_extra()==desc->get_extra();
3584
3585 // north
3586 if( y==-1 ) {
3587 neighbour_halts[0] ++;
3588 if( best ) {
3589 best_halts[0] ++;
3590 }
3591 }
3592
3593 // west
3594 if( x==-1 ) {
3595 neighbour_halts[1] ++;
3596 if( best ) {
3597 best_halts[1] ++;
3598 }
3599 }
3600
3601 // south
3602 if( y==testsize.y ) {
3603 neighbour_halts[2] ++;
3604 if( best ) {
3605 best_halts[2] ++;
3606 }
3607 }
3608
3609 // east
3610 if( x==testsize.x ) {
3611 neighbour_halts[3] ++;
3612 if( best ) {
3613 best_halts[3] ++;
3614 }
3615 }
3616 }
3617 }
3618 }
3619 }
3620 }
3621
3622 // now find out, if this offset/rotation is better ... (i.e. matches more fitting buildings)
3623 // for r=0 we check north and south, for r=1 we check east and west
3624 for( int i=r; i<4; i+=2 ) {
3625 if( best_halts[i]>best_halt || (best_halt==0 && neighbour_halts[i]>any_halt) ) {
3626 best_halt = best_halts[i];
3627 any_halt = neighbour_halts[i];
3628 rotation = i;
3629 offsets = offset;
3630 }
3631 }
3632 }
3633 }
3634 }
3635
3636 // no suitable ground here ...
3637 if( !any_ok ) {
3638 return msg;
3639 }
3640 // check over/under halt again
3641 for( sint16 x=0; x<desc->get_x(rotation); x++ ) {
3642 for( sint16 y=0; y<desc->get_y(rotation); y++ ) {
3643 const planquadrat_t *pl = welt->access( k-offsets+koord(x,y) );
3644 for( uint8 i=0; i < pl->get_boden_count(); i++ ) {
3645 halthandle_t test_halt = pl->get_boden_bei(i)->get_halt();
3646 if( test_halt.is_bound() && player_t::check_owner( new_owner, test_halt->get_owner()) ) {
3647 halt = test_halt;
3648 break;
3649 }
3650 }
3651 }
3652 }
3653 // is there no halt to connect?
3654 if( !halt.is_bound() ) {
3655 return "Post muss neben\nHaltestelle\nliegen!\n";
3656 }
3657 }
3658 else {
3659 // rotation was pre-selected; just search for stop now
3660 assert( rotation < desc->get_all_layouts() );
3661 koord testsize = desc->get_size(rotation);
3662 offsets = koord(0,0);
3663
3664 if( !welt->square_is_free(k, testsize.x, testsize.y, NULL, desc->get_allowed_climate_bits()) ) {
3665 return NOTICE_TILE_FULL;
3666 }
3667 // check over/under halt
3668 for( sint16 x=0; x<testsize.x; x++ ) {
3669 for( sint16 y=0; y<testsize.y; y++ ) {
3670 const planquadrat_t *pl = welt->access(k+koord(x,y));
3671 for( uint8 i=0; i < pl->get_boden_count(); i++ ) {
3672 halthandle_t test_halt = pl->get_boden_bei(i)->get_halt();
3673 if(test_halt.is_bound()) {
3674 if(!player_t::check_owner( new_owner, test_halt->get_owner())) {
3675 return "Das Feld gehoert\neinem anderen Spieler\n";
3676 }
3677 else if(!halt.is_bound()) {
3678 halt = test_halt;
3679 }
3680 else if(halt != test_halt) {
3681 return "Several halts found.";
3682 }
3683 }
3684 }
3685 }
3686 }
3687 if(!halt.is_bound()) {
3688 halt = suche_nahe_haltestelle(new_owner, welt, welt->lookup_kartenboden(k)->get_pos(), desc->get_x(rotation), desc->get_y(rotation) );
3689 // is there no halt to connect?
3690 if( !halt.is_bound() ) {
3691 return "Post muss neben\nHaltestelle\nliegen!\n";
3692 }
3693 }
3694 }
3695
3696 if( rotation>desc->get_all_layouts() ) {
3697 rotation %= desc->get_all_layouts();
3698 }
3699
3700 hausbauer_t::build(halt->get_owner(), pos-offsets, rotation, desc, &halt);
3701
3702 sint32 const factor = desc->get_x() * desc->get_y();
3703 sint64 cost = -desc->get_price(welt) * factor;
3704
3705 if( player!=halt->get_owner() && halt->get_owner()==welt->get_public_player() ) {
3706 // public stops are expensive!
3707 cost -= (desc->get_maintenance(welt) * factor * 60);
3708 }
3709 // difficult to distinguish correctly most suitable waytype
3710 player_t::book_construction_costs(player, cost, k, desc->get_finance_waytype());
3711 halt->recalc_station_type();
3712
3713 return NULL;
3714 }
3715
3716 /* build a dock either small or large */
tool_station_dock_aux(player_t * player,koord3d pos,const building_desc_t * desc)3717 const char *tool_build_station_t::tool_station_dock_aux(player_t *player, koord3d pos, const building_desc_t *desc)
3718 {
3719 // the cursor cannot be outside the map from here on
3720 koord k = pos.get_2d();
3721 grund_t *gr = welt->lookup_kartenboden(k);
3722 if (gr->get_hoehe()!= pos.z) {
3723 return "";
3724 }
3725 slope_t::type hang = gr->get_grund_hang();
3726 // first get the size
3727 int len = desc->get_y()-1;
3728 koord dx(hang);
3729 koord last_k = k - dx*len;
3730 halthandle_t halt;
3731
3732 // check, if we can build here ...
3733 if(!slope_t::is_single(hang)) {
3734 return "Dock must be built on single slope!";
3735 }
3736 else {
3737 // iterate up to max(len,1) to ensure that there is at least one tile of water
3738 // in front of the dock
3739 for(int i=0; i<=max(len,1); i++ ) {
3740 if(!welt->is_within_limits(k-dx*i)) {
3741 // need at least a single tile to navigate ...
3742 return "Zu nah am Kartenrand";
3743 }
3744 // search for nearby stops
3745 const planquadrat_t* pl = welt->access(k-dx*i);
3746 for( uint8 j=0; j < pl->get_boden_count(); j++ ) {
3747 halthandle_t test_halt = pl->get_boden_bei(j)->get_halt();
3748 if(test_halt.is_bound()) {
3749 if(!player_t::check_owner( player, test_halt->get_owner())) {
3750 return "Das Feld gehoert\neinem anderen Spieler\n";
3751 }
3752 else if(!halt.is_bound()) {
3753 halt = test_halt;
3754 }
3755 else if(halt != test_halt) {
3756 return "Several halts found.";
3757 }
3758 }
3759 }
3760
3761 // check whether we can build something
3762 const grund_t *gr=welt->lookup_kartenboden(k-dx*i);
3763 if (gr->get_hoehe() != pos.z) {
3764 return NOTICE_UNSUITABLE_GROUND;
3765 }
3766 if (i <= len) {
3767 if (const char *msg = gr->kann_alle_obj_entfernen(player)) {
3768 return msg;
3769 }
3770 }
3771
3772 if (i==0) {
3773 // start tile on slope near water
3774 if(gr->hat_wege() || gr->get_typ()!=grund_t::boden || gr->is_halt()) {
3775 return NOTICE_TILE_FULL;
3776 }
3777 }
3778 else {
3779 // all other tiles in water (allowing one-tile docks on rivers)
3780 if (!gr->is_water() && !(len==0 && i==1 && gr->hat_weg(water_wt))) {
3781 return NOTICE_UNSUITABLE_GROUND;
3782 }
3783 if (gr->find<gebaeude_t>() || gr->get_depot() || gr->is_halt()) {
3784 return NOTICE_TILE_FULL;
3785 }
3786 }
3787 }
3788 }
3789
3790 // remove everything from tile
3791 gr->obj_loesche_alle(player);
3792
3793 int layout = 0;
3794 koord3d bau_pos = welt->lookup_kartenboden(k)->get_pos();
3795 koord dx2;
3796 switch(hang) {
3797 case slope_t::south:
3798 case slope_t::south*2:
3799 layout = 0;
3800 dx2 = koord::west;
3801 break;
3802 case slope_t::east:
3803 case slope_t::east*2:
3804 layout = 1;
3805 dx2 = koord::north;
3806 break;
3807 case slope_t::north:
3808 case slope_t::north*2:
3809 layout = 2;
3810 dx2 = koord::west;
3811 bau_pos = welt->lookup_kartenboden(last_k)->get_pos();
3812 break;
3813 case slope_t::west:
3814 case slope_t::west*2:
3815 layout = 3;
3816 dx2 = koord::north;
3817 bau_pos = welt->lookup_kartenboden(last_k)->get_pos();
3818 break;
3819 }
3820
3821 // handle 16 layouts
3822 bool change_layout = false;
3823 if(desc->get_all_layouts()==16) {
3824 if( layout<2 ) {
3825 layout = 15-layout;
3826 }
3827 else {
3828 layout = 9-layout;
3829 }
3830 change_layout = true;
3831 }
3832
3833 // oriented buildings here - get neighbouring layouts
3834 const grund_t* gr_neigh = welt->lookup_kartenboden(k+dx2);
3835
3836 // find out if middle end or start tile
3837 if(gr_neigh && gr_neigh->is_halt() && player_t::check_owner( player, gr_neigh->get_halt()->get_owner() )) {
3838 gebaeude_t *gb = gr_neigh->find<gebaeude_t>();
3839 if(gb && (gb->get_tile()->get_desc()->get_type()==building_desc_t::dock || gb->get_tile()->get_desc()->get_type()==building_desc_t::flat_dock) ) {
3840 if(change_layout) {
3841 layout -= 4;
3842 }
3843 if(gb->get_tile()->get_desc()->get_all_layouts()==16) {
3844 sint8 ly = gb->get_tile()->get_layout();
3845 if(ly&0x02) {
3846 gb->set_tile( gb->get_tile()->get_desc()->get_tile(ly&0xFD,0,0), false );
3847 }
3848 }
3849 }
3850 }
3851
3852 gr_neigh = welt->lookup_kartenboden(k-dx2);
3853 if(gr_neigh && gr_neigh->is_halt() && player_t::check_owner( player, gr_neigh->get_halt()->get_owner() )) {
3854 gebaeude_t *gb = gr_neigh->find<gebaeude_t>();
3855 if(gb && (gb->get_tile()->get_desc()->get_type()==building_desc_t::dock || gb->get_tile()->get_desc()->get_type()==building_desc_t::flat_dock) ) {
3856 if(change_layout) {
3857 layout -= 2;
3858 }
3859 if(gb->get_tile()->get_desc()->get_all_layouts()==16) {
3860 sint8 ly = gb->get_tile()->get_layout();
3861 if(ly&0x04) {
3862 gb->set_tile( gb->get_tile()->get_desc()->get_tile(ly&0xFB,0,0), false );
3863 }
3864 }
3865 }
3866 }
3867
3868 if(!halt.is_bound()) {
3869 halt = suche_nahe_haltestelle(player, welt, welt->lookup_kartenboden(k)->get_pos() );
3870 }
3871 bool neu = !halt.is_bound();
3872
3873 if(neu) {
3874 if( gr && gr->get_halt().is_bound() ) {
3875 return "Das Feld gehoert\neinem anderen Spieler\n";
3876 }
3877 // ok, really new stop on this tile then
3878 halt = haltestelle_t::create(k, player);
3879 }
3880 hausbauer_t::build(halt->get_owner(), bau_pos, layout, desc, &halt);
3881 sint64 costs = -desc->get_price(welt);
3882 if( player!=halt->get_owner() && player != welt->get_public_player() ) {
3883 // public stops are expensive!
3884 // (Except for the public player itself)
3885 costs -= (desc->get_maintenance(welt) * 60);
3886 }
3887 for( int i=0; i<=len; i++ ) {
3888 koord p=k-dx*i;
3889 player_t::book_construction_costs(player, costs, p, water_wt);
3890 }
3891
3892 halt->recalc_station_type();
3893 if( env_t::station_coverage_show && welt->get_zeiger()->get_pos().get_2d()==k ) {
3894 // since we are larger now ...
3895 halt->mark_unmark_coverage( true );
3896 }
3897
3898 if(neu) {
3899 char* const name = halt->create_name(k, "Dock");
3900 halt->set_name( name );
3901 free(name);
3902 }
3903 return NULL;
3904 }
3905
3906 /* build a dock either small or large */
tool_station_flat_dock_aux(player_t * player,koord3d pos,const building_desc_t * desc,sint8 rotation)3907 const char *tool_build_station_t::tool_station_flat_dock_aux(player_t *player, koord3d pos, const building_desc_t *desc, sint8 rotation)
3908 {
3909 // the cursor cannot be outside the map from here on
3910 koord k = pos.get_2d();
3911 grund_t *gr = welt->lookup_kartenboden(k);
3912 if (gr->get_hoehe()!= pos.z) {
3913 return "";
3914 }
3915 // first get the size
3916 int len = desc->get_size().y-1;
3917
3918 // check, if we can build here ...
3919 if( !gr->ist_natur() || gr->get_grund_hang() != slope_t::flat ) {
3920 return NOTICE_UNSUITABLE_GROUND;
3921 }
3922
3923 // now find the direction
3924 // first: find the next water
3925 ribi_t::ribi water_dir = 0;
3926 uint8 total_dir = 0;
3927 for( uint8 i=0; i<4; i++ ) {
3928 if( grund_t *gr = welt->lookup_kartenboden(k+koord::nsew[i]) ) {
3929 if( gr->is_water() && gr->get_hoehe() == pos.z) {
3930 water_dir |= ribi_t::nsew[i];
3931 total_dir ++;
3932 }
3933 }
3934 }
3935
3936 // not surrounded by water => fail
3937 if( total_dir == 0 ) {
3938 return NOTICE_UNSUITABLE_GROUND;
3939 }
3940
3941 // prefer layouts that reach an existing halt
3942 ribi_t::ribi halt_dir = 0;
3943 halthandle_t test_halt[4];
3944
3945 for( uint8 ii=0; ii<4; ii++ ) {
3946
3947 if( (water_dir & ribi_t::nsew[ii]) == 0 ) {
3948 continue;
3949 }
3950 const koord dx = koord::nsew[ii];
3951 const char *last_error = NULL;
3952
3953 for(int i=0; i<=len; i++ ) {
3954
3955 // check whether we can build something
3956 const grund_t *gr = welt->lookup_kartenboden(k+dx*i);
3957 if( !gr ) {
3958 // need at least a single tile to navigate ...
3959 last_error = "Zu nah am Kartenrand";
3960 break;
3961 }
3962
3963 if (gr->get_hoehe() != pos.z) {
3964 return NOTICE_UNSUITABLE_GROUND;
3965 break;
3966 }
3967
3968 // search for nearby stops
3969 const planquadrat_t* pl = welt->access(k+dx*i);
3970 for( uint8 j=0; j < pl->get_boden_count() && !test_halt[ii].is_bound(); j++ ) {
3971 halthandle_t halt = pl->get_boden_bei(j)->get_halt();
3972 if (halt.is_bound() && player_t::check_owner( player, halt->get_owner()) ) {
3973 test_halt[ii] = halt;
3974 halt_dir |= ribi_t::nsew[ii];
3975 }
3976 }
3977
3978 if( (last_error = gr->kann_alle_obj_entfernen(player)) ) {
3979 break;
3980 }
3981
3982 if (i>0) {
3983 // all other tiles in water
3984 if (!gr->is_water() || gr->find<gebaeude_t>() || gr->get_depot() || gr->is_halt()) {
3985 last_error = NOTICE_TILE_FULL;
3986 }
3987 }
3988 }
3989
3990 // error: then remove this direction
3991 if( last_error ) {
3992 water_dir &= ~ribi_t::nsew[ii];
3993 if( --total_dir == 0 ) {
3994 // no duitable directions found
3995 return last_error;
3996 }
3997 }
3998 }
3999
4000 // now we may have more than one dir left
4001 if (rotation == -1 && total_dir > 1 && !ribi_t::is_single(water_dir & halt_dir) ) {
4002 return "More than one possibility to build this dock found.";
4003 }
4004
4005 // remove everything from tile
4006 gr->obj_loesche_alle(player);
4007
4008 koord3d bau_pos = welt->lookup_kartenboden(k)->get_pos();
4009 koord dx = koord::invalid;
4010 koord last_k;
4011 uint8 layout = 0; // building orientation
4012 halthandle_t halt;
4013
4014 for( uint8 i=0; i<4; i++ ) {
4015 if( water_dir & ribi_t::nsew[i] ) {
4016 dx = koord::nsew[i];
4017 halt = test_halt[i];
4018 koord last_k = k + dx*len;
4019 // layout: north 2, west 3, south 0, east 1
4020 static const uint8 nsew_to_layout[4] = { 2, 0, 1, 3 };
4021 layout = nsew_to_layout[i];
4022 if( layout>=2 ) {
4023 // reverse construction in these directions
4024 bau_pos = welt->lookup_kartenboden(last_k)->get_pos();
4025 }
4026 if (rotation == layout) {
4027 // desired rotation works
4028 break;
4029 }
4030 if (rotation != -1) {
4031 // desired rotation not possible, try others
4032 if( --total_dir == 0 ) {
4033 return NOTICE_UNSUITABLE_GROUND;
4034 }
4035 }
4036 }
4037 }
4038
4039 // handle 16 layouts
4040 bool change_layout = false;
4041 if(desc->get_all_layouts()==16) {
4042 if( layout<2 ) {
4043 layout = 15-layout;
4044 }
4045 else {
4046 layout = 9-layout;
4047 }
4048 change_layout = true;
4049 }
4050
4051 // oriented buildings here - get neighbouring layouts
4052 koord dx2 = dx;
4053 dx2.rotate90(0);
4054 gr = welt->lookup_kartenboden(k+dx2);
4055
4056 // find out if middle end or start tile
4057 if(gr && gr->is_halt() && player_t::check_owner( player, gr->get_halt()->get_owner() )) {
4058 gebaeude_t *gb = gr->find<gebaeude_t>();
4059 if(gb && (gb->get_tile()->get_desc()->get_type()==building_desc_t::dock || gb->get_tile()->get_desc()->get_type()==building_desc_t::flat_dock) ) {
4060 if(change_layout) {
4061 layout -= 4;
4062 }
4063 if(gb->get_tile()->get_desc()->get_all_layouts()==16) {
4064 sint8 ly = gb->get_tile()->get_layout();
4065 if(ly&0x02) {
4066 gb->set_tile( gb->get_tile()->get_desc()->get_tile(ly&0xFD,0,0), false );
4067 }
4068 }
4069 }
4070 }
4071
4072 gr = welt->lookup_kartenboden(k-dx2);
4073 if(gr && gr->is_halt() && player_t::check_owner( player, gr->get_halt()->get_owner() )) {
4074 gebaeude_t *gb = gr->find<gebaeude_t>();
4075 if(gb && (gb->get_tile()->get_desc()->get_type()==building_desc_t::dock || gb->get_tile()->get_desc()->get_type()==building_desc_t::flat_dock) ) {
4076 if(change_layout) {
4077 layout -= 2;
4078 }
4079 if(gb->get_tile()->get_desc()->get_all_layouts()==16) {
4080 sint8 ly = gb->get_tile()->get_layout();
4081 if(ly&0x04) {
4082 gb->set_tile( gb->get_tile()->get_desc()->get_tile(ly&0xFB,0,0), false );
4083 }
4084 }
4085 }
4086 }
4087
4088 DBG_MESSAGE("tool_station_flat_dock_aux()","building dock from square (%d,%d) to (%d,%d) layout=%i", k.x, k.y, last_k.x, last_k.y, layout );
4089
4090 if(!halt.is_bound()) {
4091 halt = suche_nahe_haltestelle(player, welt, welt->lookup_kartenboden(k)->get_pos() );
4092 }
4093 bool neu = !halt.is_bound();
4094
4095 if(neu) {
4096 if( gr && gr->get_halt().is_bound() ) {
4097 return "Das Feld gehoert\neinem anderen Spieler\n";
4098 }
4099 // ok, really new stop on this tile then
4100 halt = haltestelle_t::create(k, player);
4101 }
4102
4103 hausbauer_t::build(halt->get_owner(), bau_pos, layout, desc, &halt);
4104 sint64 costs = -desc->get_price(welt);
4105 if( player!=halt->get_owner() && player != welt->get_public_player() ) {
4106 // public stops are expensive!
4107 // (Except for the public player itself)
4108 costs -= (desc->get_maintenance(welt) * 60);
4109 }
4110 for( int i=0; i<=len; i++ ) {
4111 koord p=k-dx*i;
4112 player_t::book_construction_costs(player, costs, p, water_wt);
4113 }
4114
4115 halt->recalc_station_type();
4116
4117 if( env_t::station_coverage_show && welt->get_zeiger()->get_pos().get_2d()==k ) {
4118 // since we are larger now ...
4119 halt->mark_unmark_coverage( true );
4120 }
4121
4122 if(neu) {
4123 char* const name = halt->create_name(k, "Dock");
4124 halt->set_name( name );
4125 free(name);
4126 }
4127 else {
4128 halt->recalc_basis_pos();
4129 }
4130 return NULL;
4131 }
4132
4133 // build all types of stops but sea harbours
tool_station_aux(player_t * player,koord3d pos,const building_desc_t * desc,waytype_t wegtype,const char * type_name)4134 const char *tool_build_station_t::tool_station_aux(player_t *player, koord3d pos, const building_desc_t *desc, waytype_t wegtype, const char *type_name )
4135 {
4136 koord k = pos.get_2d();
4137 DBG_MESSAGE("tool_station_aux()", "building %s on square %d,%d for waytype %x", desc->get_name(), k.x, k.y, wegtype);
4138 const char *p_error=(desc->get_all_layouts()==4) ? "No terminal station here!" : "No through station here!";
4139
4140 // underground is checked in work(); if underground only simple stations are allowed
4141 // get valid ground
4142 grund_t *bd = tool_intern_koord_to_weg_grund(player, welt, pos, wegtype);
4143
4144 if( !bd || bd->get_weg_hang()!=slope_t::flat ) {
4145 // only flat tiles, only one stop per map square
4146 return "No suitable way on the ground!";
4147 }
4148
4149 if( bd->ist_tunnel() && bd->ist_karten_boden() ) {
4150 // do not build on tunnel entries
4151 return "No suitable way on the ground!";
4152 }
4153
4154 if( bd->get_depot() ) {
4155 // not on depots
4156 return NOTICE_UNSUITABLE_GROUND;
4157 }
4158
4159 if( bd->hat_weg(air_wt) && bd->get_weg(air_wt)->get_desc()->get_styp()!=type_flat ) {
4160 return "Flugzeughalt muss auf\nRunway liegen!\n";
4161 }
4162
4163 // find out orientation ...
4164 uint32 layout = 0;
4165 ribi_t::ribi ribi=ribi_t::none;
4166 if( desc->get_all_layouts()==2 || desc->get_all_layouts()==8 || desc->get_all_layouts()==16 ) {
4167 // through station
4168 if( bd->has_two_ways() ) {
4169 // a crossing or maybe just a tram track on a road ...
4170 ribi = bd->get_weg_nr(0)->get_ribi_unmasked() | bd->get_weg_nr(1)->get_ribi_unmasked();
4171 }
4172 else if( bd->hat_wege() ) {
4173 ribi = bd->get_weg_nr(0)->get_ribi_unmasked();
4174 }
4175 // not straight: sorry cannot build here ...
4176 if( !ribi_t::is_straight(ribi) ) {
4177 return p_error;
4178 }
4179 layout = (ribi & ribi_t::northsouth)?0 :1;
4180 }
4181 else if( desc->get_all_layouts()==4 ) {
4182 // terminal station
4183 if( bd->hat_wege() ) {
4184 ribi = bd->get_weg_nr(0)->get_ribi_unmasked();
4185 }
4186 // sorry cannot build here ... (not a terminal tile)
4187 if( !ribi_t::is_single(ribi) ) {
4188 return p_error;
4189 }
4190
4191 switch(ribi) {
4192 //case ribi_t::south:layout = 0; break;
4193 case ribi_t::east: layout = 1; break;
4194 case ribi_t::north: layout = 2; break;
4195 case ribi_t::west: layout = 3; break;
4196 }
4197 }
4198 else {
4199 // something wrong with station number of layouts
4200 dbg->fatal( "tool_station_t::tool_station_aux", "%s has wrong number of layouts (must be 2,4,8,16!)", desc->get_name() );
4201 return p_error;
4202 }
4203
4204 if( desc->get_all_layouts() == 8 || desc->get_all_layouts() == 16 ) {
4205 // through station - complex layout
4206 // bits
4207 // 1 = north south/east west (as simple layout)
4208 // 2 = use far end image \ can be combined
4209 // 3 = use near end image / to use both end image
4210 // 4 = platform face - 0 = far, 1 = near
4211
4212 // bit 1 has already been set
4213
4214 ribi_t::ribi next_own = ribi_t::none;
4215
4216 sint8 offset = bd->get_hoehe()+bd->get_weg_yoff()/TILE_HEIGHT_STEP;
4217
4218 grund_t *gr;
4219 sint32 neighbour_layout[] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
4220 for( unsigned i=0; i<4; i++ ) {
4221 // oriented buildings here - get neighbouring layouts
4222 gr = welt->lookup(koord3d(k+koord::nsew[i],offset));
4223 if(!gr) {
4224 // check whether bridge end tile
4225 grund_t * gr_tmp = welt->lookup(koord3d(k+koord::nsew[i],offset-1));
4226 if(gr_tmp && gr_tmp->get_weg_yoff()/TILE_HEIGHT_STEP == 1) {
4227 gr = gr_tmp;
4228 }
4229 else {
4230 grund_t * gr_tmp = welt->lookup(koord3d(k+koord::nsew[i],offset-2));
4231 if(gr_tmp && gr_tmp->get_weg_yoff()/TILE_HEIGHT_STEP == 2) {
4232 gr = gr_tmp;
4233 }
4234 }
4235 }
4236 if( gr && gr->get_halt().is_bound() ) {
4237 // check, if there is an oriented stop
4238 const gebaeude_t* gb = gr->find<gebaeude_t>();
4239 if(gb && gb->get_tile()->get_desc()->get_all_layouts()>4 && (gb->get_tile()->get_desc()->get_type()>building_desc_t::dock || gb->get_tile()->get_desc()->get_type()>building_desc_t::flat_dock) ) {
4240 next_own |= ribi_t::nsew[i];
4241 neighbour_layout[ribi_t::nsew[i]] = gb->get_tile()->get_layout();
4242 }
4243 }
4244 }
4245
4246 // now for the details
4247 ribi_t::ribi senkrecht = ~ribi_t::doubles(ribi);
4248 ribi_t::ribi waagerecht = ribi_t::doubles(ribi);
4249 if(next_own!=ribi_t::none) {
4250 // oriented buildings here
4251 if(ribi_t::is_single(ribi & next_own)) {
4252 // only a single next neighbour on the same track
4253 layout |= neighbour_layout[ribi & next_own] & 8;
4254 }
4255 else if(ribi_t::is_straight(ribi & next_own)) {
4256 // two neighbours on the same track, use the north/west one
4257 layout |= neighbour_layout[ribi & next_own & ribi_t::northwest] & 8;
4258 }
4259 else if(ribi_t::is_single((~ribi) & waagerecht & next_own)) {
4260 // neighbour across break in track
4261 layout |= neighbour_layout[(~ribi) & waagerecht & next_own] & 8;
4262 }
4263 else {
4264 // no buildings left and right
4265 // oriented buildings left and right
4266 if(neighbour_layout[senkrecht & next_own & ribi_t::northwest] != -1) {
4267 // just rotate layout
4268 layout |= 8-(neighbour_layout[senkrecht & next_own & ribi_t::northwest]&8);
4269 }
4270 else {
4271 if(neighbour_layout[senkrecht & next_own & ribi_t::southeast] != -1) {
4272 layout |= 8-(neighbour_layout[senkrecht & next_own & ribi_t::southeast]&8);
4273 }
4274 }
4275 }
4276 }
4277 // avoid orientation on 8 tiled buildings
4278 layout &= (desc->get_all_layouts()-1);
4279 }
4280
4281 halthandle_t old_halt = bd->get_halt();
4282 sint64 old_cost = 0;
4283 bool recalc_schedule = false;
4284
4285 halthandle_t halt;
4286
4287 if( old_halt.is_bound() ) {
4288 gebaeude_t* gb = bd->find<gebaeude_t>();
4289 const building_desc_t *old_desc = gb->get_tile()->get_desc();
4290 if( old_desc == desc ) {
4291 // already has the same station
4292 return NULL;
4293 }
4294 if( old_desc->get_capacity() >= desc->get_capacity() && !is_ctrl_pressed() ) {
4295 return "Upgrade must have\na higher level";
4296 }
4297 old_cost = old_desc->get_price(welt)*old_desc->get_x()*old_desc->get_y();
4298 gb->cleanup( NULL );
4299 delete gb;
4300 halt = old_halt;
4301 if( old_desc->get_enabled() != desc->get_enabled() ) {
4302 recalc_schedule = true;
4303 }
4304 }
4305 else {
4306 halt = suche_nahe_haltestelle(player,welt,bd->get_pos());
4307 }
4308
4309 // seems everything ok, lets build
4310 bool neu = !halt.is_bound();
4311
4312 if(neu) {
4313 if( bd && bd->get_halt().is_bound() ) {
4314 return "Das Feld gehoert\neinem anderen Spieler\n";
4315 }
4316 halt = haltestelle_t::create(k, player);
4317 }
4318 hausbauer_t::build_station_extension_depot(halt->get_owner(), bd->get_pos(), layout, desc, &halt);
4319 halt->recalc_station_type();
4320
4321 if(neu) {
4322 char* const name = halt->create_name(k, type_name);
4323 halt->set_name(name);
4324 free(name);
4325 }
4326 else {
4327 halt->recalc_basis_pos();
4328 }
4329
4330 // cost to build new station
4331 sint64 cost = -desc->get_price(welt)*desc->get_x()*desc->get_y();
4332 // discount for existing station
4333 cost += old_cost/2;
4334 if( player!=halt->get_owner() && player != welt->get_public_player() ) {
4335 // public stops are expensive!
4336 // (Except for the public player itself)
4337 cost -= (desc->get_maintenance(welt) * desc->get_x() * desc->get_y() * 60);
4338 }
4339 player_t::book_construction_costs(player, cost, k, wegtype);
4340
4341 if( env_t::station_coverage_show && welt->get_zeiger()->get_pos().get_2d()==k ) {
4342 // since we are larger now ...
4343 halt->mark_unmark_coverage( true );
4344 }
4345
4346 // the new station (after upgrading) might accept different goods => needs new schedule
4347 if( recalc_schedule ) {
4348 welt->set_schedule_counter();
4349 }
4350
4351 return NULL;
4352 }
4353
4354 // gives the description and sets the rotation value
get_desc(sint8 & rotation) const4355 const building_desc_t *tool_build_station_t::get_desc( sint8 &rotation ) const
4356 {
4357 char *building = strdup( default_param );
4358 const building_tile_desc_t *tdsc = NULL;
4359 if( building ) {
4360 char *p = strrchr( building, ',' );
4361 if( p ) {
4362 *p++ = 0;
4363 rotation = atoi( p );
4364 }
4365 else {
4366 rotation = -1;
4367 }
4368 tdsc = hausbauer_t::find_tile(building,0);
4369 free( building );
4370 }
4371 if( tdsc==NULL ) {
4372 return NULL;
4373 }
4374 return tdsc->get_desc();
4375 }
4376
init(player_t *)4377 bool tool_build_station_t::init( player_t * )
4378 {
4379 sint8 rotation = -1;
4380 const building_desc_t *bdsc = get_desc( rotation );
4381 if( bdsc==NULL ) {
4382 return false;
4383 }
4384 cursor = bdsc->get_cursor()->get_image_id(0);
4385 if( !can_use_gui() ) {
4386 // do not change cursor
4387 return true;
4388 }
4389 if( (bdsc->get_type()==building_desc_t::generic_extension || bdsc->get_type()==building_desc_t::flat_dock) && bdsc->get_all_layouts()>1 ) {
4390 if( is_ctrl_pressed() && rotation==-1 ) {
4391 // call station dialog instead
4392 destroy_win( magic_station_building_select );
4393 create_win( new station_building_select_t(bdsc), w_info, magic_station_building_select);
4394 // we do not activate building yet; else uncomment the return statement
4395 return false;
4396 }
4397 else if( rotation>=0 ) {
4398 // rotation is already fixed
4399 cursor_area = koord( bdsc->get_x(rotation), bdsc->get_y(rotation) );
4400 cursor_centered = false;
4401 cursor_offset = koord(0,0);
4402 if (bdsc->get_type()==building_desc_t::flat_dock && rotation >= 2 ) {
4403 cursor_offset = cursor_area - koord(1,1);
4404 }
4405 }
4406 else {
4407 goto set_area_cov;
4408 }
4409 }
4410 else {
4411 set_area_cov:
4412 uint16 const cov = welt->get_settings().get_station_coverage() * 2 + 1;
4413 cursor_area = koord(cov, cov);
4414 cursor_centered = true;
4415 cursor_offset = koord(0,0);
4416 }
4417 return true;
4418 }
4419
4420
get_icon(player_t *) const4421 image_id tool_build_station_t::get_icon( player_t * ) const
4422 {
4423 sint8 dummy;
4424 const building_desc_t *desc=get_desc(dummy);
4425 if (desc == NULL) {
4426 return IMG_EMPTY;
4427 }
4428 if( grund_t::underground_mode==grund_t::ugm_all ) {
4429 // in underground mode, buildings will be done invisible above ground => disallow such confusion
4430 if( desc->get_type()!=building_desc_t::generic_stop || desc->get_extra()==air_wt) {
4431 return IMG_EMPTY;
4432 }
4433 if( desc->get_type()==building_desc_t::generic_stop && !desc->can_be_built_underground()) {
4434 return IMG_EMPTY;
4435 }
4436 }
4437 if( grund_t::underground_mode==grund_t::ugm_none ) {
4438 if( desc->get_type()==building_desc_t::generic_stop && !desc->can_be_built_aboveground()) {
4439 return IMG_EMPTY;
4440 }
4441 }
4442 return icon;
4443 }
4444
4445
get_tooltip(const player_t *) const4446 const char* tool_build_station_t::get_tooltip(const player_t *) const
4447 {
4448 sint8 dummy;
4449 building_desc_t const* desc = get_desc(dummy);
4450 if (desc == NULL) {
4451 return "";
4452 }
4453
4454 sint64 price = 0;
4455 sint64 maint = 0;
4456 uint32 cap = desc->get_capacity(); // This is always correct in the desc object.
4457
4458 maint = desc->get_maintenance(welt);
4459
4460 if( desc->get_type()==building_desc_t::generic_stop || desc->get_type()==building_desc_t::generic_extension || desc->get_type()==building_desc_t::dock || desc->get_type()==building_desc_t::flat_dock ) {
4461 price = -desc->get_price(welt);
4462 }
4463 else {
4464 return "Illegal description";
4465 }
4466
4467 if(desc->get_type()==building_desc_t::generic_extension || desc->get_type()==building_desc_t::dock || desc->get_type()==building_desc_t::flat_dock) {
4468 const sint16 size_multiplier = desc->get_size().x * desc->get_size().y;
4469 price *= size_multiplier;
4470 cap *= size_multiplier;
4471 maint *= size_multiplier;
4472 }
4473
4474 return tooltip_with_price_maintenance_capacity(welt, desc->get_name(), price, maint, cap, desc->get_enabled());
4475 }
4476
get_waytype() const4477 waytype_t tool_build_station_t::get_waytype() const
4478 {
4479 sint8 dummy;
4480 building_desc_t const* desc = get_desc(dummy);
4481 switch (desc ? desc->get_type() : building_desc_t::generic_extension) {
4482 case building_desc_t::generic_stop:
4483 return (waytype_t)desc->get_extra();
4484 case building_desc_t::dock:
4485 case building_desc_t::flat_dock:
4486 return water_wt;
4487 case building_desc_t::generic_extension:
4488 default:
4489 return invalid_wt;
4490 }
4491 }
4492
4493
check_pos(player_t *,koord3d pos)4494 const char *tool_build_station_t::check_pos( player_t*, koord3d pos )
4495 {
4496 if( grund_t *gr = welt->lookup( pos ) ) {
4497 sint8 rotation;
4498 const building_desc_t *desc = get_desc(rotation);
4499 if(desc == NULL) {
4500 // tool is in bad state, eg due to invalid tool parameters
4501 DBG_DEBUG("tool_build_station_t::check_pos()", "Cannot resolve building descriptor, default_param=\"%s\".", default_param);
4502 return "ENGINE ERROR: Build station tool cannot resolve a building descriptor.";
4503 }
4504
4505 if( grund_t *bd = welt->lookup_kartenboden( pos.get_2d() ) ) {
4506 const bool underground = bd->get_hoehe()>gr->get_hoehe();
4507 if( underground ) {
4508 // in underground mode, buildings will be done invisible above ground => disallow such confusion
4509 if( desc->get_type()!=building_desc_t::generic_stop || desc->get_extra()==air_wt) {
4510 return "Cannot built this station/building\nin underground mode here.";
4511 }
4512 if( desc->get_type()==building_desc_t::generic_stop && !desc->can_be_built_underground()) {
4513 return "Cannot built this station/building\nin underground mode here.";
4514 }
4515 }
4516 else if( desc->get_type()==building_desc_t::generic_stop && !desc->can_be_built_aboveground()) {
4517 return "This station/building\ncan only be built underground.";
4518 }
4519 return NULL;
4520 }
4521 }
4522 // no ground here???
4523 return "Missing ground (fatal!)";
4524 }
4525
4526
move(player_t * player,uint16 buttonstate,koord3d pos)4527 const char *tool_build_station_t::move( player_t *player, uint16 buttonstate, koord3d pos )
4528 {
4529 CHECK_FUNDS();
4530
4531 const char *result = NULL;
4532 if( buttonstate==1 ) {
4533 const grund_t *gr = welt->lookup(pos);
4534 if(!gr) {
4535 return "";
4536 }
4537
4538 // ownership allowed?
4539 halthandle_t halt = gr->get_halt();
4540 if(halt.is_bound() && !player_t::check_owner( player, halt->get_owner())) {
4541 return "";
4542 }
4543
4544 if( env_t::networkmode ) {
4545 // queue tool for network
4546 nwc_tool_t *nwc = new nwc_tool_t(player, this, pos, welt->get_steps(), welt->get_map_counter(), false);
4547 network_send_server(nwc);
4548 }
4549 else {
4550 result = work( player, pos );
4551 }
4552 }
4553 return result;
4554 }
4555
4556
work(player_t * player,koord3d pos)4557 const char *tool_build_station_t::work( player_t *player, koord3d pos )
4558 {
4559 const grund_t *gr = welt->lookup(pos);
4560 if(!gr) {
4561 return "";
4562 }
4563
4564 // ownership allowed?
4565 halthandle_t halt = gr->get_halt();
4566 if(halt.is_bound() && !player_t::check_owner( player, halt->get_owner())) {
4567 return "Das Feld gehoert\neinem anderen Spieler\n";
4568 }
4569
4570 // check underground / above ground
4571 if (const char* msg = check_pos(player, pos)) {
4572 return msg;
4573 }
4574
4575 sint8 rotation = 0;
4576 const building_desc_t *desc=get_desc(rotation);
4577 const char *msg = NULL;
4578 switch (desc->get_type()) {
4579 case building_desc_t::dock:
4580 msg = tool_build_station_t::tool_station_dock_aux(player, pos, desc );
4581 break;
4582 case building_desc_t::flat_dock:
4583 msg = tool_build_station_t::tool_station_flat_dock_aux(player, pos, desc, rotation );
4584 break;
4585 case building_desc_t::generic_extension:
4586 msg = tool_build_station_t::tool_station_building_aux(player, false, pos, desc, rotation );
4587 if (msg) {
4588 // try to build near a public halt
4589 msg = tool_build_station_t::tool_station_building_aux(player, true, pos, desc, rotation );
4590 }
4591 break;
4592 case building_desc_t::generic_stop: {
4593 switch(desc->get_extra()) {
4594 case road_wt:
4595 msg = tool_build_station_t::tool_station_aux(player, pos, desc, road_wt, "H");
4596 break;
4597 case track_wt:
4598 case monorail_wt:
4599 case maglev_wt:
4600 case narrowgauge_wt:
4601 case tram_wt:
4602 msg = tool_build_station_t::tool_station_aux(player, pos, desc, (waytype_t)desc->get_extra(), "BF");
4603 break;
4604 case water_wt:
4605 msg = tool_build_station_t::tool_station_aux(player, pos, desc, water_wt, "Dock");
4606 break;
4607 case air_wt:
4608 msg = tool_build_station_t::tool_station_aux(player, pos, desc, air_wt, "Airport");
4609 break;
4610 }
4611 break;
4612 }
4613
4614 default:
4615 dbg->warning("tool_build_station_t::work()","tool called for illegal desc \"%\"", default_param );
4616 msg = "Illegal station tool";
4617 }
4618 return msg;
4619 }
4620
4621
4622
work(player_t * player,koord3d pos)4623 const char *tool_rotate_building_t::work( player_t *player, koord3d pos )
4624 {
4625 const grund_t *gr = welt->lookup(pos);
4626 if(!gr) {
4627 return "";
4628 }
4629
4630 if( gebaeude_t* gb = gr->find<gebaeude_t>() ) {
4631
4632 if( !player_t::check_owner( gb->get_owner(), player ) ) {
4633 return "Das Feld gehoert\neinem anderen Spieler\n";
4634 }
4635
4636 // check for harbour (must not rotate)
4637 const building_desc_t *desc = gb->get_tile()->get_desc();
4638 if( desc->get_all_layouts() == 1 ) {
4639 // non rotatable => finish
4640 return NULL;
4641 }
4642 if( desc->get_type() == building_desc_t::dock ) {
4643 // cannot rotate a harbour
4644 return "Cannot rotate this building!";
4645 }
4646 if( desc->get_all_layouts()==2 && desc->get_x()!=desc->get_y() ) {
4647 // cannot rotate an asymmetric building with only two rotations
4648 return "Cannot rotate this building!";
4649 }
4650
4651 if( gr->hat_wege() ) {
4652 // this is almost certainly a station ...
4653 if( desc->get_all_layouts()<16 ) {
4654 // either symmetrical (==2, ==8) or freight loading station, so do not rotate!
4655 return "Cannot rotate this building!";
4656 }
4657 int layout = gb->get_tile()->get_layout();
4658 gb->set_tile( gb->get_tile()->get_desc()->get_tile( layout^8, 0, 0 ), false );
4659 }
4660 else {
4661 // single and multitile buildings from here, include factories with holes etc.
4662 bool rotate180 = desc->get_x() != desc->get_y();
4663
4664 if( desc->get_x() != desc->get_y() && desc->get_all_layouts()==2 ) {
4665 // asymmetrical with only one rotation so do not rotate!
4666 return "Cannot rotate this building!";
4667 }
4668
4669 gb = gb->get_first_tile();
4670 uint8 layout = gb->get_tile()->get_layout();
4671 uint8 newlayout = (layout+1+rotate180) % desc->get_all_layouts();
4672
4673 // first test if all tiles are present (check for holes)
4674 koord k;
4675 for(k.x=0; k.x<desc->get_x(layout); k.x++) {
4676 for(k.y=0; k.y<desc->get_y(layout); k.y++) {
4677 grund_t *gr = welt->lookup( gb->get_pos()+k );
4678 if( !gr ) {
4679 return "Cannot rotate this building!";
4680 }
4681 const building_tile_desc_t *tile = desc->get_tile(newlayout, k.x, k.y);
4682 gebaeude_t *gbt = gr->find<gebaeude_t>();
4683 if( tile==NULL && gbt ) {
4684 return "Cannot rotate this building!";
4685 }
4686 if( tile && gbt==NULL ) {
4687 return "Cannot rotate this building!";
4688 }
4689 }
4690 }
4691 // ok, we can rotate it
4692 for(k.x=0; k.x<desc->get_x(layout); k.x++) {
4693 for(k.y=0; k.y<desc->get_y(layout); k.y++) {
4694 grund_t *gr = welt->lookup( gb->get_pos()+k );
4695 // there could be still holes, so the if is needed
4696 if( gebaeude_t *gb = gr->find<gebaeude_t>() ) {
4697 const building_tile_desc_t *tile = desc->get_tile(newlayout, k.x, k.y);
4698 gb->set_tile( tile, false );
4699 }
4700 }
4701 }
4702 }
4703 }
4704 return NULL;
4705 }
4706
4707
4708
get_tooltip(player_t const *) const4709 char const* tool_build_roadsign_t::get_tooltip(player_t const*) const
4710 {
4711 const roadsign_desc_t * desc = roadsign_t::find_desc(default_param);
4712 if(desc) {
4713 return tooltip_with_price( desc->get_name(), -desc->get_price() );
4714 }
4715 return NULL;
4716 }
4717
draw_after(scr_coord k,bool dirty) const4718 void tool_build_roadsign_t::draw_after(scr_coord k, bool dirty) const
4719 {
4720 if( icon!=IMG_EMPTY && is_selected() ) {
4721 display_img_blend( icon, k.x, k.y, TRANSPARENT50_FLAG|OUTLINE_FLAG|color_idx_to_rgb(COL_BLACK), false, dirty );
4722 char level_str[16];
4723 sprintf(level_str, "%i", signal[welt->get_active_player_nr()].spacing);
4724 display_proportional_rgb( k.x+4, k.y+4, level_str, ALIGN_LEFT, color_idx_to_rgb(COL_YELLOW), true );
4725 }
4726 }
4727
check_pos_intern(player_t * player,koord3d pos)4728 const char* tool_build_roadsign_t::check_pos_intern(player_t *player, koord3d pos)
4729 {
4730 const char * error = "Hier kann kein\nSignal aufge-\nstellt werden!\n";
4731 if (desc==NULL) {
4732 // read data from string
4733 desc = roadsign_t::find_desc(default_param);
4734 }
4735 if (desc==NULL) {
4736 return error;
4737 }
4738 // search for starting ground
4739 grund_t *gr = tool_intern_koord_to_weg_grund(player, welt, pos, desc->get_wtyp());
4740 if(gr) {
4741
4742 signal_t *s = gr->find<signal_t>();
4743 if(s && s->get_desc()!=desc) {
4744 // only one sign per tile
4745 return error;
4746 }
4747
4748 if(desc->is_signal_type() && gr->find<roadsign_t>()) {
4749 // only one sign per tile
4750 return error;
4751 }
4752
4753 // get the sign direction
4754 weg_t *weg = gr->get_weg( desc->get_wtyp()!=tram_wt ? desc->get_wtyp() : track_wt);
4755 ribi_t::ribi dir = weg->get_ribi_unmasked();
4756
4757 // no signs on runways
4758 if( weg->get_waytype() == air_wt && weg->get_desc()->get_styp() == type_runway ) {
4759 return error;
4760 }
4761
4762 // no signals on switches
4763 if( ribi_t::is_threeway(dir) && desc->is_signal_type() ) {
4764 return error;
4765 }
4766
4767 if( desc->is_private_way() && !ribi_t::is_straight(dir) ) {
4768 // only on straight tiles ...
4769 return error;
4770 }
4771
4772 const bool two_way = desc->is_single_way() || desc->is_signal_type();
4773
4774 if(!(desc->is_traffic_light() || two_way) || (two_way && ribi_t::is_twoway(dir)) || (desc->is_traffic_light() && ribi_t::is_threeway(dir))) {
4775 roadsign_t* rs;
4776 if( desc->is_signal_type() ) {
4777 // if there is already a signal, we might need to inverse the direction
4778 rs = gr->find<signal_t>();
4779 if (rs) {
4780 if( !player_t::check_owner( rs->get_owner(), player ) ) {
4781 return "Das Feld gehoert\neinem anderen Spieler\n";
4782 }
4783 }
4784 }
4785 else {
4786 // if there is already a sign, we might need to inverse the direction
4787 rs = gr->find<roadsign_t>();
4788 if (rs) {
4789 if( !player_t::check_owner( rs->get_owner(), player ) ) {
4790 return "Das Feld gehoert\neinem anderen Spieler\n";
4791 }
4792 }
4793 }
4794 error = NULL;
4795 }
4796 }
4797 return error;
4798 }
4799
4800
rdwr_custom_data(memory_rw_t * packet)4801 void tool_build_roadsign_t::rdwr_custom_data(memory_rw_t *packet)
4802 {
4803 two_click_tool_t::rdwr_custom_data(packet);
4804 packet->rdwr_byte(current.spacing);
4805 packet->rdwr_bool(current.remove_intermediate);
4806 packet->rdwr_bool(current.replace_other);
4807 }
4808
4809
get_waytype() const4810 waytype_t tool_build_roadsign_t::get_waytype() const
4811 {
4812 return desc ? desc->get_wtyp() : invalid_wt;
4813 }
4814
4815
init(player_t * player)4816 bool tool_build_roadsign_t::init( player_t *player)
4817 {
4818 desc = roadsign_t::find_desc(default_param);
4819 // take default values from players settings
4820 current = signal[player->get_player_nr()];
4821
4822 if (is_ctrl_pressed() && can_use_gui()) {
4823 create_win(new signal_spacing_frame_t(player, this), w_info, (ptrdiff_t)this);
4824 }
4825 return two_click_tool_t::init(player) && (desc!=NULL);
4826 }
4827
exit(player_t * player)4828 bool tool_build_roadsign_t::exit( player_t *player )
4829 {
4830 destroy_win((ptrdiff_t)this);
4831 return two_click_tool_t::exit(player);
4832 }
4833
is_valid_pos(player_t * player,const koord3d & pos,const char * & error,const koord3d & start)4834 uint8 tool_build_roadsign_t::is_valid_pos( player_t *player, const koord3d &pos, const char *&error, const koord3d &start)
4835 {
4836 // first click
4837 if (start==koord3d::invalid) {
4838 error = check_pos_intern(player, pos);
4839 return (error==NULL ? 3 : 0);
4840 }
4841 // second click
4842 else {
4843 error = NULL;
4844 return 2;
4845 }
4846 }
4847
4848
calc_route(route_t & verbindung,player_t * player,const koord3d & start,const koord3d & to)4849 bool tool_build_roadsign_t::calc_route( route_t &verbindung, player_t *player, const koord3d& start, const koord3d& to )
4850 {
4851 // get a default vehikel
4852 vehicle_desc_t rs_desc( desc->get_wtyp(), 500, vehicle_desc_t::diesel );
4853 vehicle_t* test_vehicle = vehicle_builder_t::build(start, player, NULL, &rs_desc);
4854 test_vehicle->set_flag(obj_t::not_on_map);
4855 test_driver_t* test_driver = scenario_checker_t::apply(test_vehicle, player, this);
4856
4857 bool can_built;
4858 if( start != to ) {
4859 can_built = verbindung.calc_route(welt, start, to, test_driver, 0, 0);
4860 // prevent building of many signals if start and to are adjacent
4861 // but the step start->to is now allowed
4862 if (can_built && koord_distance(start, to)==1 && verbindung.get_count()>2) {
4863 grund_t *gr, *grto = welt->lookup(to);
4864 if( welt->lookup(start)->get_neighbour(gr, desc->get_wtyp(), ribi_type(to-start) ) && gr==grto) {
4865 can_built = false;
4866 }
4867 }
4868 }
4869 else {
4870 verbindung.clear();
4871 verbindung.append( start );
4872 can_built = true;
4873 }
4874 delete test_driver;
4875 return can_built;
4876 }
4877
mark_tiles(player_t * player,const koord3d & start,const koord3d & ziel)4878 void tool_build_roadsign_t::mark_tiles( player_t *player, const koord3d &start, const koord3d &ziel )
4879 {
4880 route_t route;
4881 if (!calc_route(route, player, start, ziel)) {
4882 return;
4883 }
4884 signal_info const& s = current;
4885 uint8 const signal_density = 2 * s.spacing; // measured in half tiles (straight track count as 2, diagonal as 1, since sqrt(1/2) = 1/2 ;)
4886 uint8 next_signal = signal_density + 1; // to place a sign asap
4887 sint32 cost = 0;
4888 directions.clear();
4889 // dummy roadsign to get images for preview
4890 roadsign_t *dummy_rs;
4891 if (desc->is_signal_type()) {
4892 dummy_rs = new signal_t(player, koord3d::invalid, ribi_t::none, desc, true);
4893 }
4894 else {
4895 dummy_rs = new roadsign_t(player, koord3d::invalid, ribi_t::none, desc, true);
4896 }
4897 dummy_rs->set_flag(obj_t::not_on_map);
4898
4899 bool single_ribi = desc->is_signal_type() || desc->is_single_way() || desc->is_choose_sign();
4900 for( uint16 i = 0; i < route.get_count(); i++ ) {
4901 grund_t* gr = welt->lookup( route.at(i) );
4902
4903 weg_t *weg = gr->get_weg(desc->get_wtyp());
4904 ribi_t::ribi ribi=weg->get_ribi_unmasked(); // set full ribi when signal is on a crossing.
4905 if( single_ribi ) {
4906 if(i>0) {
4907 // take backward direction
4908 ribi = ribi_type(route.at(i), route.at(i-1));
4909 }
4910 else {
4911 // clear one direction bit to get single direction for signal
4912 ribi &= ~ribi_type(route.at(i), route.at(i+1));
4913 }
4914 }
4915
4916 roadsign_t *rs = gr->find<signal_t>();
4917 if (rs==NULL) {
4918 rs = gr->find<roadsign_t>();
4919 }
4920
4921 if (rs && rs->get_waytype() != desc->get_waytype()) {
4922 // do not delete signs from other ways
4923 continue;
4924 }
4925
4926 // check owner .. other signals...
4927 bool straight = (i == 0) || (i == route.get_count()-1) || ribi_t::is_straight(ribi_type(route.at(i-1), route.at(i+1)));
4928 next_signal += straight ? 2 : 1;
4929 if( next_signal >= signal_density ) {
4930 // can we place signal here?
4931 if (check_pos_intern(player, route.at(i))==NULL ||
4932 (s.replace_other && rs && !rs->is_deletable(player))) {
4933 zeiger_t* zeiger = new zeiger_t(gr->get_pos(), player );
4934 marked.append(zeiger);
4935 zeiger->set_image( skinverwaltung_t::bauigelsymbol->get_image_id(0) );
4936 gr->obj_add( zeiger );
4937 directions.append(ribi /* !=0 -> place sign*/);
4938 next_signal = 0;
4939 dummy_rs->set_pos(gr->get_pos());
4940 dummy_rs->set_dir(ribi); // calls calc_image()
4941 zeiger->set_foreground_image(dummy_rs->get_front_image());
4942 zeiger->set_image(dummy_rs->get_image());
4943 cost += rs ? (rs->get_desc()==desc ? 0 : desc->get_price()+rs->get_desc()->get_price()) : desc->get_price();
4944 }
4945 }
4946 else if (s.remove_intermediate && rs && !rs->is_deletable(player)) {
4947 zeiger_t* zeiger = new zeiger_t(gr->get_pos(), player );
4948 marked.append(zeiger);
4949 zeiger->set_image( tool_t::general_tool[TOOL_REMOVER]->cursor );
4950 gr->obj_add( zeiger );
4951 directions.append(ribi_t::none /*remove sign*/);
4952 cost += rs->get_desc()->get_price();
4953 }
4954 }
4955 delete dummy_rs;
4956 win_set_static_tooltip( tooltip_with_price_length("Building costs estimates", cost, route.get_count() ) );
4957 }
4958
do_work(player_t * player,const koord3d & start,const koord3d & end)4959 const char *tool_build_roadsign_t::do_work( player_t *player, const koord3d &start, const koord3d &end)
4960 {
4961 // read data from string
4962 desc = roadsign_t::find_desc(default_param);
4963 // single click ->place signal
4964 if( end == koord3d::invalid || start == end ) {
4965 grund_t *gr = welt->lookup(start);
4966 return place_sign_intern( player, gr );
4967 }
4968 // mark tiles to calculate positions of signals
4969 mark_tiles(player, start, end);
4970 // only search the marked tiles
4971 uint32 j=0;
4972 FOR(slist_tpl<zeiger_t*>, const i, marked) {
4973 grund_t* const gr = welt->lookup(i->get_pos());
4974 weg_t *weg = gr->get_weg(desc->get_wtyp());
4975 ribi_t::ribi dir = directions[j++];
4976 if (dir) {
4977 // try to place signal
4978 const char* error_text = place_sign_intern( player, gr );
4979 if( error_text ) {
4980 if (signal[player->get_player_nr()].replace_other) {
4981 roadsign_t* rs = gr->find<signal_t>();
4982 if(rs == NULL) rs = gr->find<roadsign_t>();
4983 if( rs != NULL && rs->is_deletable(player) == NULL ) {
4984 rs->cleanup(player);
4985 delete rs;
4986 error_text = place_sign_intern( player, gr );
4987 }
4988 }
4989 }
4990 if( error_text ) {
4991 return error_text;
4992 }
4993 roadsign_t* rs = gr->find<signal_t>();
4994 if(rs == NULL) rs = gr->find<roadsign_t>();
4995 assert(rs);
4996 rs->set_dir(dir);
4997 }
4998 else {
4999 // Place no signal -> remove existing signal
5000 roadsign_t* rs = gr->find<signal_t>();
5001 if(rs == NULL) rs = gr->find<roadsign_t>();
5002 if( rs != NULL && rs->is_deletable(player) == NULL ) {
5003 rs->cleanup(player);
5004 delete rs;
5005 };
5006 }
5007 weg->count_sign();
5008 gr->calc_image();
5009 }
5010 cleanup();
5011 directions.clear();
5012 return NULL;
5013 }
5014
5015 /*
5016 * Called by the GUI (gui/signal_spacing.*)
5017 */
set_values(player_t * player,uint8 spacing,bool remove,bool replace)5018 void tool_build_roadsign_t::set_values( player_t *player, uint8 spacing, bool remove, bool replace )
5019 {
5020 signal_info& s = signal[player->get_player_nr()];
5021 s.spacing = spacing;
5022 s.remove_intermediate = remove;
5023 s.replace_other = replace;
5024 current = s;
5025 }
5026
5027
get_values(player_t * player,uint8 & spacing,bool & remove,bool & replace)5028 void tool_build_roadsign_t::get_values( player_t *player, uint8 &spacing, bool &remove, bool &replace )
5029 {
5030 signal_info const& s = signal[player->get_player_nr()];
5031 spacing = s.spacing;
5032 remove = s.remove_intermediate;
5033 replace = s.replace_other;
5034 }
5035
5036
place_sign_intern(player_t * player,grund_t * gr,const roadsign_desc_t *)5037 const char *tool_build_roadsign_t::place_sign_intern( player_t *player, grund_t* gr, const roadsign_desc_t*)
5038 {
5039 const char *error = "Hier kann kein\nSignal aufge-\nstellt werden!\n";
5040 // search for starting ground
5041 if(gr) {
5042 // get the sign direction
5043 weg_t *weg = gr->get_weg( desc->get_wtyp()!=tram_wt ? desc->get_wtyp() : track_wt);
5044 roadsign_t *s = gr->find<signal_t>();
5045 if(s==NULL) {
5046 s = gr->find<roadsign_t>();
5047 }
5048 if(s && s->get_desc()!=desc) {
5049 // only one sign per tile
5050 return error;
5051 }
5052 ribi_t::ribi dir = weg->get_ribi_unmasked();
5053
5054 const bool two_way = desc->is_single_way() || desc->is_signal_type();
5055
5056 if(!(desc->is_traffic_light() || two_way) || (two_way && ribi_t::is_twoway(dir)) || (desc->is_traffic_light() && ribi_t::is_threeway(dir))) {
5057 roadsign_t* rs;
5058 if (desc->is_signal_type()) {
5059 // if there is already a signal, we might need to inverse the direction
5060 rs = gr->find<signal_t>();
5061 if (rs) {
5062 if( !player_t::check_owner( rs->get_owner(), player ) ) {
5063 return "Das Feld gehoert\neinem anderen Spieler\n";
5064 }
5065 // signals have three options
5066 ribi_t::ribi sig_dir = rs->get_dir();
5067 uint8 i = 0;
5068 if (!ribi_t::is_twoway(sig_dir)) {
5069 // inverse first dir
5070 for (; i < 4; i++) {
5071 if ((dir & ribi_t::nsew[i]) == sig_dir) {
5072 i++;
5073 break;
5074 }
5075 }
5076 }
5077 // find the second dir ...
5078 for (; i < 4; i++) {
5079 if ((dir & ribi_t::nsew[i]) != 0) {
5080 dir = ribi_t::nsew[i];
5081 }
5082 }
5083 // if nothing found, we have two ways again ...
5084 rs->set_dir(dir);
5085 }
5086 else {
5087 // add a new signal at position zero!
5088 rs = new signal_t(player, gr->get_pos(), dir, desc);
5089 DBG_MESSAGE("tool_roadsign()", "new signal, dir is %i", dir);
5090 goto built_sign;
5091 }
5092 }
5093 else {
5094 // if there is already a sign, we might need to inverse the direction
5095 rs = gr->find<roadsign_t>();
5096 if (rs) {
5097 if( !player_t::check_owner( rs->get_owner(), player ) ) {
5098 return "Das Feld gehoert\neinem anderen Spieler\n";
5099 }
5100 // reverse only if single way sign
5101 if (desc->is_single_way() || desc->is_choose_sign()) {
5102 dir = ~rs->get_dir() & weg->get_ribi_unmasked();
5103 rs->set_dir(dir);
5104 DBG_MESSAGE("tool_roadsign()", "reverse ribi %i", dir);
5105 }
5106 }
5107 else {
5108 // add a new roadsign at position zero!
5109 // if single way, we need to reduce the allowed ribi to one
5110 if (desc->is_single_way() || desc->is_choose_sign()) {
5111 for( int i=0; i<4; i++ ) {
5112 if ((dir & ribi_t::nsew[i]) != 0) {
5113 dir = ribi_t::nsew[i];
5114 break;
5115 }
5116 }
5117 }
5118 DBG_MESSAGE("tool_roadsign()", "new roadsign, dir is %i", dir);
5119 rs = new roadsign_t(player, gr->get_pos(), dir, desc);
5120 built_sign:
5121 gr->obj_add(rs);
5122 rs->finish_rd(); // to make them visible
5123 weg->count_sign();
5124 player_t::book_construction_costs(player, -desc->get_price(), gr->get_pos().get_2d(), weg->get_waytype());
5125 }
5126 }
5127 error = NULL;
5128 }
5129 }
5130 return error;
5131 }
5132
5133
5134
5135 // built all types of depots
tool_depot_aux(player_t * player,koord3d pos,const building_desc_t * desc,waytype_t wegtype)5136 const char *tool_build_depot_t::tool_depot_aux(player_t *player, koord3d pos, const building_desc_t *desc, waytype_t wegtype)
5137 {
5138 if(welt->is_within_limits(pos.get_2d())) {
5139 grund_t *bd=NULL;
5140 // special for the seven seas ...
5141 if(wegtype==water_wt) {
5142 bd = welt->lookup_kartenboden(pos.get_2d());
5143 if(!bd->is_water()) {
5144 bd = NULL;
5145 }
5146 }
5147 if(bd==NULL) {
5148 bd = tool_intern_koord_to_weg_grund(player,welt,pos,wegtype);
5149 }
5150 if(!bd || bd->has_two_ways()) {
5151 return NOTICE_DEPOT_BAD_POS;
5152 }
5153
5154 // no depots on runways!
5155 if(desc->get_extra()==air_wt && bd->get_weg(air_wt)->get_desc()->get_styp()!=type_flat) {
5156 return NOTICE_DEPOT_BAD_POS;
5157 }
5158
5159 const char *p=bd->kann_alle_obj_entfernen(player);
5160 if(p) {
5161 return p;
5162 }
5163
5164 // avoid building over a stop
5165 if(bd->is_halt() || bd->get_depot()!=NULL) {
5166 return NOTICE_DEPOT_BAD_POS;
5167 }
5168
5169 ribi_t::ribi ribi;
5170 if(bd->is_water()) {
5171 // assume one orientation with water
5172 ribi = ribi_t::south;
5173 }
5174 else {
5175 ribi = bd->get_weg_ribi_unmasked(wegtype);
5176 }
5177
5178 if(ribi_t::is_single(ribi) && bd->get_weg_hang()==0) {
5179
5180 int layout = 0;
5181 switch(ribi) {
5182 //case ribi_t::south:layout = 0; break;
5183 case ribi_t::east: layout = 1; break;
5184 case ribi_t::north: layout = 2; break;
5185 case ribi_t::west: layout = 3; break;
5186 }
5187 hausbauer_t::build_station_extension_depot(player, bd->get_pos(), layout, desc );
5188 player_t::book_construction_costs(player, -desc->get_price(welt), pos.get_2d(), desc->get_finance_waytype());
5189 if(can_use_gui() && player == welt->get_active_player()) {
5190 welt->set_tool( general_tool[TOOL_QUERY], player );
5191 }
5192
5193 return NULL;
5194 }
5195 return NOTICE_DEPOT_BAD_POS;
5196 }
5197 return "";
5198 }
5199
get_icon(player_t * player) const5200 image_id tool_build_depot_t::get_icon(player_t *player) const
5201 {
5202 if( player && !player->is_public_service() ) {
5203 const building_desc_t *desc = hausbauer_t::find_tile(default_param,0)->get_desc();
5204 const uint16 time = welt->get_timeline_year_month();
5205 if( desc && desc->is_available(time) ) {
5206 return desc->get_cursor()->get_image_id(1);
5207 }
5208 }
5209 return IMG_EMPTY;
5210 }
5211
init(player_t * player)5212 bool tool_build_depot_t::init( player_t *player )
5213 {
5214 building_desc_t const* desc = hausbauer_t::find_tile(default_param, 0)->get_desc();
5215 if (desc == NULL) {
5216 return false;
5217 }
5218 // no depots for player 1
5219 if(player!=welt->get_public_player()) {
5220 cursor = desc->get_cursor()->get_image_id(0);
5221 return true;
5222 }
5223 return false;
5224 }
5225
get_tooltip(const player_t *) const5226 const char* tool_build_depot_t::get_tooltip(const player_t *) const
5227 {
5228 settings_t const& settings = welt->get_settings();
5229 building_desc_t const* desc = hausbauer_t::find_tile(default_param, 0)->get_desc();
5230 if (desc == NULL) {
5231 return NULL;
5232 }
5233
5234 char const* tip;
5235 switch (desc->get_extra()) {
5236 case road_wt: tip = "Build road depot"; break;
5237 case track_wt: tip = "Build train depot"; break;
5238 case monorail_wt: tip = "Build monorail depot"; break;
5239 case maglev_wt: tip = "Build maglev depot"; break;
5240 case narrowgauge_wt: tip = "Build narrowgauge depot"; break;
5241 case tram_wt: tip = "Build tram depot"; break;
5242 case water_wt: tip = "Build ship depot"; break;
5243 case air_wt: tip = "Build air depot"; break;
5244 default: return 0;
5245 }
5246 return tooltip_with_price_maintenance(welt, tip, -desc->get_price(welt), settings.maint_building * desc->get_level());
5247 }
5248
get_waytype() const5249 waytype_t tool_build_depot_t::get_waytype() const
5250 {
5251 const building_desc_t *desc = hausbauer_t::find_tile(default_param,0)->get_desc();
5252 return desc ? (waytype_t)desc->get_extra() : invalid_wt;
5253 }
5254
work(player_t * player,koord3d pos)5255 const char *tool_build_depot_t::work( player_t *player, koord3d pos )
5256 {
5257 if(player==welt->get_public_player()) {
5258 // no depots for player 1
5259 return 0;
5260 }
5261
5262 building_desc_t const* const desc = hausbauer_t::find_tile(default_param,0)->get_desc();
5263 switch(desc->get_extra()) {
5264 case road_wt:
5265 case track_wt:
5266 case water_wt:
5267 case air_wt:
5268 case maglev_wt:
5269 case narrowgauge_wt:
5270 return tool_build_depot_t::tool_depot_aux(player, pos, desc, (waytype_t)desc->get_extra());
5271
5272 case monorail_wt:
5273 {
5274 // since it needs also a foundation, this is slightly more complex ...
5275 char const* const err = tool_build_depot_t::tool_depot_aux(player, pos, desc, monorail_wt);
5276 if(err==NULL) {
5277 grund_t *bd = welt->lookup_kartenboden(pos.get_2d());
5278 if(hausbauer_t::elevated_foundation_desc && pos.z-bd->get_pos().z==1 && bd->ist_natur()) {
5279 hausbauer_t::build(player, bd->get_pos(), 0, hausbauer_t::elevated_foundation_desc );
5280 }
5281 }
5282 return err;
5283 }
5284 case tram_wt:
5285 return tool_build_depot_t::tool_depot_aux(player, pos, desc, track_wt);
5286 default:
5287 dbg->warning("tool_build_depot()","called with unknown desc %s",desc->get_name() );
5288 return "Unknown depot object";
5289 }
5290 return NULL;
5291 }
5292
5293
5294
5295 /* builds (random) tourist attraction and maybe adds it to the next city
5296 * the parameter string is a follow:
5297 * 1#theater
5298 * first letter: ignore climates
5299 * second letter: rotation (0,1,2,3,#=random)
5300 * finally building name
5301 * @author prissi
5302 */
init(player_t *)5303 bool tool_build_house_t::init( player_t * )
5304 {
5305 if (can_use_gui() && !strempty(default_param)) {
5306 const char *c = default_param+2;
5307 const building_tile_desc_t *tile = hausbauer_t::find_tile(c,0);
5308 if(tile!=NULL) {
5309 int rotation = (default_param[1]-'0') % tile->get_desc()->get_all_layouts();
5310 cursor_area = tile->get_desc()->get_size(rotation);
5311 }
5312 }
5313 return true;
5314 }
5315
5316
work(player_t * player,koord3d pos)5317 const char *tool_build_house_t::work( player_t *player, koord3d pos )
5318 {
5319 koord k(pos.get_2d());
5320
5321 const grund_t* gr = welt->lookup_kartenboden(k);
5322 if(gr==NULL) {
5323 return "";
5324 }
5325
5326 // Parsing parameter (if there)
5327 const building_desc_t *desc = NULL;
5328 if (!strempty(default_param)) {
5329 const char *c = default_param+2;
5330 const building_tile_desc_t *tile = hausbauer_t::find_tile(c,0);
5331 if(tile) {
5332 desc = tile->get_desc();
5333 }
5334 }
5335 else {
5336 desc = hausbauer_t::get_random_attraction( welt->get_timeline_year_month(), false, welt->get_climate( k ) );
5337 }
5338
5339 if(desc==NULL) {
5340 return "";
5341 }
5342 int rotation;
5343 if( !default_param || default_param[1]=='#' ) {
5344 rotation = simrand(desc->get_all_layouts());
5345 }
5346 else if( default_param[1]=='A' ) {
5347 if( desc->get_type()!=building_desc_t::attraction_land && desc->get_type()!=building_desc_t::attraction_city ) {
5348 // auto rotation only valid for city buildings
5349 rotation = stadt_t::orient_city_building( k, desc, desc->get_size() );
5350 if( rotation < 0 ) {
5351 return NOTICE_UNSUITABLE_GROUND;
5352 }
5353 }
5354 else {
5355 rotation = simrand(desc->get_all_layouts());
5356 }
5357 }
5358 else {
5359 rotation = (default_param[1]-'0') % desc->get_all_layouts();
5360 }
5361
5362 koord size = desc->get_size(rotation);
5363
5364 // process ignore climates switch
5365 climate_bits cl = (default_param && default_param[0]=='1') ? ALL_CLIMATES : desc->get_allowed_climate_bits();
5366
5367 bool hat_platz = welt->square_is_free( k, desc->get_x(rotation), desc->get_y(rotation), NULL, cl );
5368 if(!hat_platz && size.y!=size.x && desc->get_all_layouts()>1 && (default_param==NULL || default_param[1]=='#' || default_param[1]=='A')) {
5369 // try other rotation too ...
5370 rotation = (rotation+1) % desc->get_all_layouts();
5371 hat_platz = welt->square_is_free( k, desc->get_x(rotation), desc->get_y(rotation), NULL, cl );
5372 }
5373
5374 // Place found...
5375 if(hat_platz) {
5376 player_t *gb_player = desc->is_city_building() ? NULL : welt->get_public_player();
5377 gebaeude_t *gb = hausbauer_t::build(gb_player, gr->get_pos(), rotation, desc);
5378 if(gb) {
5379 // building successful
5380 if( desc->get_type()!=building_desc_t::attraction_land && desc->get_type()!=building_desc_t::attraction_city ) {
5381 stadt_t *city = welt->find_nearest_city( k );
5382 if(city) {
5383 city->add_gebaeude_to_stadt(gb);
5384 }
5385 }
5386 player_t::book_construction_costs(player, -desc->get_price(welt) * size.x * size.y, k, gb->get_waytype());
5387 return NULL;
5388 }
5389 }
5390 return NOTICE_UNSUITABLE_GROUND;
5391 }
5392
5393
5394
5395 // show industry size in cursor (in known)
init(player_t *)5396 bool tool_build_land_chain_t::init( player_t * )
5397 {
5398 if (can_use_gui() && !strempty(default_param)) {
5399 const char *c = default_param+2;
5400 while(*c && *c++!=',') { /* do nothing */ }
5401 const factory_desc_t *fab = factory_builder_t::get_desc(c);
5402 if(fab==NULL) {
5403 // wrong tool!
5404 return false;
5405 }
5406 int rotation = (default_param[1]-'0') % fab->get_building()->get_all_layouts();
5407 cursor_area = fab->get_building()->get_size(rotation);
5408 }
5409 return true;
5410 }
5411
5412 /* builds a (if param=NULL random) industry chain starting here *
5413 * the parameter string is a follow:
5414 * 1#34,oelfeld
5415 * first letter: ignore climates
5416 * second letter: rotation (0,1,2,3,#=random)
5417 * next number is production value
5418 * finally industry name
5419 */
work(player_t * player,koord3d pos)5420 const char *tool_build_land_chain_t::work( player_t *player, koord3d pos )
5421 {
5422 const grund_t* gr = welt->lookup_kartenboden(pos.get_2d());
5423 if(gr==NULL) {
5424 return "";
5425 }
5426
5427 const factory_desc_t *fab = NULL;
5428 if (!strempty(default_param)) {
5429 const char *c = default_param+2;
5430 while(*c && *c++!=',') { /* do nothing */ }
5431 fab = factory_builder_t::get_desc(c);
5432 }
5433 else {
5434 fab = factory_builder_t::get_random_consumer( false, (climate_bits)(1 << welt->get_climate( pos.get_2d() )), welt->get_timeline_year_month() );
5435 }
5436
5437 if(fab==NULL) {
5438 return "";
5439 }
5440 int rotation = (default_param && default_param[1]!='#') ? (default_param[1]-'0') % fab->get_building()->get_all_layouts() : simrand(fab->get_building()->get_all_layouts()-1);
5441 koord size = fab->get_building()->get_size(rotation);
5442
5443 // process ignore climates switch
5444 bool ignore_climates = default_param && default_param[0]=='1';
5445 climate_bits cl = ignore_climates ? ALL_CLIMATES : fab->get_building()->get_allowed_climate_bits();
5446
5447 bool hat_platz = false;
5448 if(fab->get_placement()==factory_desc_t::Water) {
5449 // at sea
5450 hat_platz = welt->is_water( pos.get_2d(), fab->get_building()->get_size(rotation) );
5451
5452 if(!hat_platz && size.y!=size.x && fab->get_building()->get_all_layouts()>1 && (default_param==NULL || default_param[1]=='#')) {
5453 // try other rotation too ...
5454 rotation = (rotation+1) % fab->get_building()->get_all_layouts();
5455 hat_platz = welt->is_water( pos.get_2d(), fab->get_building()->get_size(rotation) );
5456 }
5457 }
5458 else {
5459 // and on solid ground
5460 hat_platz = welt->square_is_free( pos.get_2d(), fab->get_building()->get_x(rotation), fab->get_building()->get_y(rotation), NULL, cl );
5461
5462 if(!hat_platz && size.y!=size.x && fab->get_building()->get_all_layouts()>1 && (default_param==NULL || default_param[1]=='#')) {
5463 // try other rotation too ...
5464 rotation = (rotation+1) % fab->get_building()->get_all_layouts();
5465 hat_platz = welt->square_is_free( pos.get_2d(), fab->get_building()->get_x(rotation), fab->get_building()->get_y(rotation), NULL, cl );
5466 }
5467 }
5468
5469 if(hat_platz) {
5470 // eventually adjust production
5471 sint32 initial_prod = -1;
5472 if (!strempty(default_param)) {
5473 initial_prod = welt->inverse_scale_with_month_length( atol(default_param+2) );
5474 }
5475
5476 koord3d build_pos = gr->get_pos();
5477 int count = factory_builder_t::build_link(NULL, fab, initial_prod, rotation, &build_pos, welt->get_public_player(), 10000, ignore_climates);
5478
5479 if(count>0) {
5480 // at least one factory has been built
5481 welt->get_viewport()->change_world_position( build_pos );
5482 player_t::book_construction_costs(player, count * welt->get_settings().cst_multiply_found_industry, build_pos.get_2d(), ignore_wt);
5483
5484 // crossconnect all?
5485 if (welt->get_settings().is_crossconnect_factories()) {
5486 FOR(slist_tpl<fabrik_t*>, const f, welt->get_fab_list()) {
5487 f->add_all_suppliers();
5488 }
5489 }
5490 return NULL;
5491 }
5492 }
5493 return NOTICE_UNSUITABLE_GROUND;
5494 }
5495
5496
5497 // show industry size in cursor (in known)
init(player_t *)5498 bool tool_city_chain_t::init( player_t * )
5499 {
5500 if (can_use_gui() && !strempty(default_param)) {
5501 const char *c = default_param+2;
5502 while(*c && *c++!=',') { /* do nothing */ }
5503 const factory_desc_t *fab = factory_builder_t::get_desc(c);
5504 if(fab==NULL) {
5505 // wrong tool!
5506 return false;
5507 }
5508 int rotation = (default_param[1]-'0') % fab->get_building()->get_all_layouts();
5509 cursor_area = fab->get_building()->get_size(rotation);
5510 }
5511 return true;
5512 }
5513
5514 /* builds a industry chain in the next town
5515 * defaukt_param see previous function
5516 */
work(player_t * player,koord3d pos)5517 const char *tool_city_chain_t::work( player_t *player, koord3d pos )
5518 {
5519 const grund_t* gr = welt->lookup_kartenboden(pos.get_2d());
5520 if(gr==NULL) {
5521 return "";
5522 }
5523
5524 const factory_desc_t *fab = NULL;
5525 if (!strempty(default_param)) {
5526 const char *c = default_param+2;
5527 while(*c && *c++!=',') { /* do nothing */ }
5528 fab = factory_builder_t::get_desc(c);
5529 }
5530 else {
5531 fab = factory_builder_t::get_random_consumer( false, (climate_bits)(1 << welt->get_climate( pos.get_2d() )), welt->get_timeline_year_month() );
5532 }
5533
5534 if(fab==NULL) {
5535 return "";
5536 }
5537
5538 // eventually adjust production
5539 sint32 initial_prod = -1;
5540 if (!strempty(default_param)) {
5541 initial_prod = welt->inverse_scale_with_month_length( atol(default_param+2) );
5542 }
5543
5544 // process ignore climates switch
5545 bool ignore_climates = default_param && default_param[0]=='1';
5546
5547 pos = gr->get_pos();
5548 int count = factory_builder_t::build_link(NULL, fab, initial_prod, 0, &pos, welt->get_public_player(), 10000, ignore_climates);
5549 if(count>0) {
5550 // at least one factory has been built
5551 welt->get_viewport()->change_world_position( pos );
5552
5553 // crossconnect all?
5554 if (welt->get_settings().is_crossconnect_factories()) {
5555 FOR(slist_tpl<fabrik_t*>, const f, welt->get_fab_list()) {
5556 f->add_all_suppliers();
5557 }
5558 }
5559 // ain't going to be cheap
5560 player_t::book_construction_costs(player, count * welt->get_settings().cst_multiply_found_industry, pos.get_2d(), ignore_wt);
5561 return NULL;
5562 }
5563 return NOTICE_UNSUITABLE_GROUND;
5564 }
5565
5566
5567
5568 // show industry size in cursor (must be known!)
init(player_t *)5569 bool tool_build_factory_t::init( player_t * )
5570 {
5571 if (can_use_gui() && !strempty(default_param)) {
5572 const char *c = default_param+2;
5573 while(*c && *c++!=',') { /* do nothing */ }
5574 const factory_desc_t *fab = factory_builder_t::get_desc(c);
5575 if(fab==NULL) {
5576 // wrong tool!
5577 return false;
5578 }
5579 int rotation = (default_param[1]-'0') % fab->get_building()->get_all_layouts();
5580 cursor_area = fab->get_building()->get_size(rotation);
5581 return true;
5582 }
5583 return true;
5584 }
5585
5586 /* builds an industry next to the cursor (default_param see above) */
work(player_t * player,koord3d pos)5587 const char *tool_build_factory_t::work( player_t *player, koord3d pos )
5588 {
5589 const grund_t* gr = welt->lookup_kartenboden(pos.get_2d());
5590 if(gr==NULL) {
5591 return "";
5592 }
5593
5594 const factory_desc_t *fab = NULL;
5595 if (!strempty(default_param)) {
5596 const char *c = default_param+2;
5597 while(*c && *c++!=',') { /* do nothing */ }
5598 fab = factory_builder_t::get_desc(c);
5599 }
5600 else {
5601 fab = factory_builder_t::get_random_consumer( false, (climate_bits)(1 << welt->get_climate( pos.get_2d() )), welt->get_timeline_year_month() );
5602 }
5603
5604 if(fab==NULL) {
5605 return "";
5606 }
5607 int rotation = (default_param && default_param[1]!='#') ? (default_param[1]-'0') % fab->get_building()->get_all_layouts() : simrand(fab->get_building()->get_all_layouts());
5608 koord size = fab->get_building()->get_size(rotation);
5609
5610 // process ignore climates switch
5611 climate_bits cl = (default_param && default_param[0]=='1') ? ALL_CLIMATES : fab->get_building()->get_allowed_climate_bits();
5612
5613 bool hat_platz = false;
5614 if(fab->get_placement()==factory_desc_t::Water) {
5615 // at sea
5616 hat_platz = welt->is_water( pos.get_2d(), fab->get_building()->get_size(rotation) );
5617
5618 if(!hat_platz && size.y!=size.x && fab->get_building()->get_all_layouts()>1 && (default_param==NULL || default_param[1]=='#')) {
5619 // try other rotation too ...
5620 rotation = (rotation+1) % fab->get_building()->get_all_layouts();
5621 hat_platz = welt->is_water( pos.get_2d(), fab->get_building()->get_size(rotation) );
5622 }
5623 }
5624 else {
5625 // and on solid ground
5626 hat_platz = welt->square_is_free( pos.get_2d(), fab->get_building()->get_x(rotation), fab->get_building()->get_y(rotation), NULL, cl );
5627
5628 if(!hat_platz && size.y!=size.x && fab->get_building()->get_all_layouts()>1 && (default_param==NULL || default_param[1]=='#')) {
5629 // try other rotation too ...
5630 rotation = (rotation+1) % fab->get_building()->get_all_layouts();
5631 hat_platz = welt->square_is_free( pos.get_2d(), fab->get_building()->get_x(rotation), fab->get_building()->get_y(rotation), NULL, cl );
5632 }
5633 }
5634
5635 if(hat_platz) {
5636 // eventually adjust production
5637 sint32 initial_prod = -1;
5638 if (!strempty(default_param)) {
5639 initial_prod = welt->inverse_scale_with_month_length( atol(default_param+2) );
5640 }
5641
5642 fabrik_t *f = factory_builder_t::build_factory(NULL, fab, initial_prod, rotation, gr->get_pos(), welt->get_public_player());
5643 if(f) {
5644 // at least one factory has been built
5645 welt->get_viewport()->change_world_position( pos );
5646 player_t::book_construction_costs(player, welt->get_settings().cst_multiply_found_industry, pos.get_2d(), ignore_wt);
5647
5648 // crossconnect all?
5649 if (welt->get_settings().is_crossconnect_factories()) {
5650 FOR(slist_tpl<fabrik_t*>, const f, welt->get_fab_list()) {
5651 f->add_all_suppliers();
5652 }
5653 }
5654 return NULL;
5655 }
5656 }
5657 return NOTICE_UNSUITABLE_GROUND;
5658 }
5659
5660
5661
5662 /** link tool: links products of factory one with factory two (if possible)
5663 */
get_marker_image()5664 image_id tool_link_factory_t::get_marker_image()
5665 {
5666 return cursor;
5667 }
5668
5669
is_valid_pos(player_t *,const koord3d & pos,const char * & error,const koord3d &)5670 uint8 tool_link_factory_t::is_valid_pos( player_t *, const koord3d &pos, const char *&error, const koord3d & )
5671 {
5672 fabrik_t *fab = fabrik_t::get_fab( pos.get_2d() );
5673 if (fab == NULL) {
5674 error = "";
5675 return 0;
5676 }
5677 return 2;
5678 }
5679
5680
do_work(player_t *,const koord3d & start,const koord3d & pos)5681 const char *tool_link_factory_t::do_work( player_t *, const koord3d &start, const koord3d &pos )
5682 {
5683 fabrik_t *last_fab = fabrik_t::get_fab( start.get_2d() );
5684 fabrik_t *fab = fabrik_t::get_fab( pos.get_2d() );
5685
5686 if(fab!=NULL && last_fab!=NULL && last_fab!=fab) {
5687 // It's a factory
5688 if(!is_ctrl_pressed()) {
5689 if(fab->add_supplier(last_fab) || last_fab->add_supplier(fab)) {
5690 //ok! they are connected
5691 return NULL;
5692 }
5693 }
5694 else {
5695 // remove connections
5696 fab->rem_supplier(last_fab->get_pos().get_2d());
5697 fab->rem_lieferziel(last_fab->get_pos().get_2d());
5698 last_fab->rem_supplier(fab->get_pos().get_2d());
5699 last_fab->rem_lieferziel(fab->get_pos().get_2d());
5700 return NULL;
5701 }
5702 }
5703 return "";
5704 }
5705
5706
5707 /* builds company headquarters
5708 * @author prissi
5709 */
next_level(const player_t * player) const5710 const building_desc_t *tool_headquarter_t::next_level( const player_t *player ) const
5711 {
5712 return hausbauer_t::get_headquarters(player->get_headquarter_level(), welt->get_timeline_year_month());
5713 }
5714
get_tooltip(const player_t * player) const5715 const char* tool_headquarter_t::get_tooltip(const player_t *player) const
5716 {
5717 if (building_desc_t const* const desc = next_level(player)) {
5718 settings_t const& s = welt->get_settings();
5719 char const* const tip = player->get_headquarter_level() == 0 ? "build HQ" : "upgrade HQ";
5720 sint64 const factor = desc->get_x() * desc->get_y();
5721 return tooltip_with_price_maintenance(welt, tip, -factor * desc->get_price(welt), factor * desc->get_level() * s.maint_building);
5722 }
5723 return NULL;
5724 }
5725
init(player_t * player)5726 bool tool_headquarter_t::init( player_t *player )
5727 {
5728 // do no use this, if there is no next level to build ...
5729 const building_desc_t *desc = next_level(player);
5730 if (desc) {
5731 if (can_use_gui()) {
5732 const int rotation = 0;
5733 cursor_area = desc->get_size(rotation);
5734 }
5735 return true;
5736 }
5737 return false;
5738 }
5739
5740
work(player_t * player,koord3d pos)5741 const char *tool_headquarter_t::work( player_t *player, koord3d pos )
5742 {
5743 bool ok=false;
5744 bool built = false;
5745 DBG_MESSAGE("tool_headquarter()", "building headquarters at (%d,%d)", pos.x, pos.y);
5746
5747 const building_desc_t* desc = next_level(player);
5748 if(desc==NULL) {
5749 // no further headquarters level
5750 dbg->message( "tool_headquarter()", "Already at maximum level!" );
5751 return "";
5752 }
5753
5754 // check funds
5755 koord size = desc->get_size();
5756 sint64 const cost = -desc->get_price(welt) * size.x * size.y;
5757 if( !player->can_afford(cost) ) {
5758 return NOTICE_INSUFFICIENT_FUNDS;
5759 }
5760
5761 koord k(pos.get_2d());
5762 grund_t *gr = welt->lookup_kartenboden(k);
5763
5764 if(gr) {
5765 gebaeude_t *hq = NULL;
5766 // check for current head quarter
5767 koord previous = player->get_headquarter_pos();
5768 if(previous!=koord::invalid) {
5769 grund_t *gr_hq = welt->lookup_kartenboden(previous);
5770 gebaeude_t *prev_hq = gr_hq->find<gebaeude_t>();
5771 // check if upgrade should be built at same place as current one
5772 gebaeude_t *gb = gr->find<gebaeude_t>();
5773 if (gb && gb->get_owner()==player && prev_hq->get_tile()->get_desc()==gb->get_tile()->get_desc()) {
5774 const building_desc_t* prev_desc = prev_hq->get_tile()->get_desc();
5775 // check if sizes fit
5776 uint8 prev_layout = prev_hq->get_tile()->get_layout();
5777 uint8 layout = prev_layout % desc->get_all_layouts();
5778 koord size = desc->get_size(layout);
5779 if (prev_desc->get_size(prev_layout) == size) {
5780 // check for same tile structure
5781 ok = true;
5782 for (sint16 x=0; x<size.x && ok; x++) {
5783 for (sint16 y=0; y<size.y && ok; y++) {
5784 ok = (prev_desc->get_tile(prev_layout, x, y)==NULL)==(desc->get_tile(layout, x, y)==NULL);
5785 }
5786 }
5787 hq = gb;
5788 if( ok ) {
5789 // upgrade the tiles
5790 koord k_hq = k - gb->get_tile()->get_offset();
5791 for( sint16 x = 0; x < size.x; x++ ) {
5792 for( sint16 y = 0; y < size.y; y++ ) {
5793 if( const building_tile_desc_t *tile = desc->get_tile(layout, x, y) ) {
5794 if( grund_t *gr2 = welt->lookup_kartenboden(k_hq + koord(x, y)) ) {
5795 if( gebaeude_t *gb = gr2->find<gebaeude_t>() ) {
5796 if( gb && gb->get_owner() == player && prev_desc == gb->get_tile()->get_desc() ) {
5797 player_t::add_maintenance( player, -prev_desc->get_maintenance(welt), prev_desc->get_finance_waytype() );
5798 gb->set_tile( tile, true );
5799 gb->calc_image();
5800 player_t::add_maintenance( player, desc->get_maintenance(welt), desc->get_finance_waytype() );
5801 }
5802 }
5803 }
5804 }
5805 }
5806 }
5807 built = true;
5808 }
5809 }
5810 }
5811 // did not upgrade old one, need to remove it
5812 if( !built ) {
5813 // remove previous one
5814 hausbauer_t::remove( player, prev_hq );
5815 // resize cursor
5816 init(player);
5817 }
5818 }
5819
5820
5821 // build new one
5822 if (!built) {
5823 int rotate = 0;
5824
5825 if(welt->square_is_free(k, size.x, size.y, NULL, desc->get_allowed_climate_bits())) {
5826 ok = true;
5827 }
5828 if(!ok && desc->get_all_layouts()>1 && size.y != size.x && welt->square_is_free(k, size.y, size.x, NULL, desc->get_allowed_climate_bits())) {
5829 rotate = 1;
5830 ok = true;
5831 }
5832
5833 if(ok) {
5834 // then built it
5835 hq = hausbauer_t::build(player, gr->get_pos(), rotate, desc, NULL);
5836 stadt_t *city = welt->find_nearest_city( k );
5837 if(city) {
5838 city->add_gebaeude_to_stadt( hq );
5839 }
5840 built = true;
5841 }
5842 else {
5843 return NOTICE_UNSUITABLE_GROUND;
5844 }
5845 }
5846
5847
5848 if( built ) {
5849 // sometimes those are not correct after rotation ...
5850 player->add_headquarter( desc->get_extra() + 1, hq->get_pos().get_2d() - hq->get_tile()->get_offset() );
5851 player_t::book_construction_costs(player, cost, k, ignore_wt);
5852 // tell the world of it ...
5853 cbuffer_t buf;
5854 buf.printf( translator::translate("%s s\nheadquarter now\nat (%i,%i)."), player->get_name(), pos.x, pos.y );
5855 welt->get_message()->add_message( buf, k, message_t::ai, PLAYER_FLAG|player->get_player_nr(), hq->get_tile()->get_background(0,0,0) );
5856 // reset to query tool, since costly relocations should be avoided
5857 if(can_use_gui() && player == welt->get_active_player()) {
5858 welt->set_tool( tool_t::general_tool[TOOL_QUERY], player );
5859 }
5860 return NULL;
5861 }
5862 }
5863 return "";
5864 }
5865
work(player_t *,koord3d)5866 const char *tool_lock_game_t::work( player_t *, koord3d )
5867 {
5868 // tool can never be executed in network mode
5869 if (env_t::networkmode) {
5870 return "";
5871 }
5872 // as the result depends on the local locked state of public player
5873 if (welt->get_public_player()->is_locked() || !welt->get_settings().get_allow_player_change()) {
5874 return "Only public player can lock games!";
5875 }
5876 welt->clear_player_password_hashes();
5877 if( !welt->get_public_player()->is_locked() ) {
5878 return "In order to lock the game, you have to protect the public player by password!";
5879 }
5880 destroy_all_win( true );
5881 welt->switch_active_player( 0, true );
5882 welt->get_settings().set_allow_player_change(false);
5883 welt->set_tool( general_tool[TOOL_QUERY], welt->get_player(0) );
5884 return NULL;
5885 }
5886
5887
work(player_t * player,koord3d pos)5888 const char *tool_add_citycar_t::work( player_t *player, koord3d pos )
5889 {
5890 if( private_car_t::list_empty() ) {
5891 // No citycar
5892 return "";
5893 }
5894 grund_t *gr = tool_intern_koord_to_weg_grund( player, welt, pos, road_wt );
5895
5896 if( gr != NULL && ribi_t::is_twoway(gr->get_weg_ribi_unmasked(road_wt)) && gr->find<private_car_t>() == NULL) {
5897 // add citycar
5898 private_car_t* vt = new private_car_t(gr, koord::invalid);
5899 gr->obj_add(vt);
5900 welt->sync.add(vt);
5901 return NULL;
5902 }
5903 return "";
5904 }
5905
5906
is_valid_pos(player_t *,const koord3d &,const char * &,const koord3d &)5907 uint8 tool_forest_t::is_valid_pos( player_t *, const koord3d &, const char *&, const koord3d & )
5908 {
5909 // do really nothing ...
5910 return 2;
5911 }
5912
5913
mark_tiles(player_t *,const koord3d & start,const koord3d & end)5914 void tool_forest_t::mark_tiles( player_t *, const koord3d &start, const koord3d &end )
5915 {
5916 koord k1, k2;
5917 k1.x = start.x < end.x ? start.x : end.x;
5918 k1.y = start.y < end.y ? start.y : end.y;
5919 k2.x = start.x + end.x - k1.x;
5920 k2.y = start.y + end.y - k1.y;
5921 koord k;
5922 for( k.x = k1.x; k.x <= k2.x; k.x++ ) {
5923 for( k.y = k1.y; k.y <= k2.y; k.y++ ) {
5924 grund_t *gr = welt->lookup_kartenboden( k );
5925
5926 zeiger_t *marker = new zeiger_t(gr->get_pos(), NULL );
5927
5928 const uint8 grund_hang = gr->get_grund_hang();
5929 const uint8 weg_hang = gr->get_weg_hang();
5930 const uint8 hang = max( corner_sw(grund_hang), corner_sw(weg_hang)) +
5931 3 * max( corner_se(grund_hang), corner_se(weg_hang)) +
5932 9 * max( corner_ne(grund_hang), corner_ne(weg_hang)) +
5933 27 * max( corner_nw(grund_hang), corner_nw(weg_hang));
5934 uint8 back_hang = (hang % 3) + 3 * ((uint8)(hang / 9)) + 27;
5935 marker->set_foreground_image( ground_desc_t::marker->get_image( grund_hang % 27 ) );
5936 marker->set_image( ground_desc_t::marker->get_image( back_hang ) );
5937
5938 marker->mark_image_dirty( marker->get_image(), 0 );
5939 gr->obj_add( marker );
5940 marked.insert( marker );
5941 }
5942 }
5943 }
5944
5945
do_work(player_t * player,const koord3d & start,const koord3d & end)5946 const char *tool_forest_t::do_work( player_t *player, const koord3d &start, const koord3d &end )
5947 {
5948 koord wh, nw;
5949 wh.x = abs(end.x-start.x)+1;
5950 wh.y = abs(end.y-start.y)+1;
5951 nw.x = min(start.x, end.x)+(wh.x/2);
5952 nw.y = min(start.y, end.y)+(wh.y/2);
5953
5954 sint64 costs = baum_t::create_forest( nw, wh );
5955 player_t::book_construction_costs(player, costs * welt->get_settings().cst_remove_tree, end.get_2d(), ignore_wt);
5956
5957 return NULL;
5958 }
5959
5960
get_marker_image()5961 image_id tool_stop_mover_t::get_marker_image()
5962 {
5963 return cursor;
5964 }
5965
5966
read_start_position(player_t * player,const koord3d & pos)5967 void tool_stop_mover_t::read_start_position(player_t *player, const koord3d &pos)
5968 {
5969 waytype[0] = invalid_wt;
5970 waytype[1] = invalid_wt;
5971 last_halt = halthandle_t();
5972
5973 grund_t *bd = welt->lookup(pos);
5974 if (bd==NULL) {
5975 return;
5976 }
5977 // now assign waytypes
5978 if(bd->is_water()) {
5979 waytype[0] = water_wt;
5980 }
5981 else {
5982 waytype[0] = bd->get_weg_nr(0)->get_waytype();
5983 if(bd->get_weg_nr(1)) {
5984 waytype[1] = bd->get_weg_nr(1)->get_waytype();
5985 }
5986 }
5987 // .. and halt
5988 last_halt = haltestelle_t::get_halt(pos,player);
5989 }
5990
5991
is_valid_pos(player_t * player,const koord3d & pos,const char * & error,const koord3d & start)5992 uint8 tool_stop_mover_t::is_valid_pos( player_t *player, const koord3d &pos, const char *&error, const koord3d &start)
5993 {
5994 grund_t *bd = welt->lookup(pos);
5995 if (bd==NULL) {
5996 error = "";
5997 return 0;
5998 }
5999 // check halt ownership
6000 halthandle_t h = haltestelle_t::get_halt(pos,player);
6001 if( h.is_bound() && !player_t::check_owner( player, h->get_owner() ) ) {
6002 error = "Das Feld gehoert\neinem anderen Spieler\n";
6003 return 0;
6004 }
6005 // check for halt on the tile
6006 if( h.is_bound() && !(bd->is_halt() || (h->get_station_type()&haltestelle_t::dock && bd->is_water()) ) ) {
6007 error = NOTICE_UNSUITABLE_GROUND;
6008 return 0;
6009 }
6010
6011 if (start==koord3d::invalid) {
6012 // check for existing ways
6013 if (bd->is_water() || bd->hat_wege()) {
6014 return 2;
6015 }
6016 else {
6017 error = NOTICE_UNSUITABLE_GROUND;
6018 return 0;
6019 }
6020 }
6021 else {
6022 // read conditions at start point
6023 read_start_position(player, start);
6024 // check halts vs waypoints
6025 if(h.is_bound() ^ last_halt.is_bound()) {
6026 error = "Can only move from halt to halt or waypoint to waypoint.";
6027 return 0;
6028 }
6029 // check waytypes
6030 if( (waytype[0] == water_wt && bd->is_water()) || bd->hat_weg(waytype[0]) || bd->hat_weg(waytype[1]) ) {
6031 // ok
6032 return 2;
6033 }
6034 else {
6035 error = NOTICE_UNSUITABLE_GROUND;
6036 return 0;
6037 }
6038 }
6039 }
6040
do_work(player_t * player,const koord3d & last_pos,const koord3d & pos)6041 const char *tool_stop_mover_t::do_work( player_t *player, const koord3d &last_pos, const koord3d &pos)
6042 {
6043 // read conditions at start point
6044 read_start_position(player, last_pos);
6045
6046 // second click
6047 grund_t *bd = welt->lookup(pos);
6048 halthandle_t h = haltestelle_t::get_halt(pos,player);
6049
6050 if (bd) {
6051 const halthandle_t new_halt = h;
6052 // depending on the waytype we simply build replacements lists
6053 // in the worst case we have to iterate over all tiles twice ...
6054 for( uint i=0; i<2; i++ ) {
6055 const waytype_t wt = waytype[i];
6056 slist_tpl <koord3d>old_platform;
6057
6058 if(bd->is_water()) {
6059 if(wt!=water_wt) {
6060 break;
6061 }
6062 }
6063 else if(!bd->hat_weg(wt)) {
6064 continue;
6065 }
6066 // platform, stop or just tile moving?
6067 const bool catch_all_halt = (wt==water_wt || wt==air_wt) && last_halt.is_bound();
6068 if(!last_halt.is_bound()) {
6069 old_platform.append(last_pos);
6070 }
6071 else if(!catch_all_halt) {
6072 // builds a coordinate list
6073 if(wt==road_wt) {
6074 old_platform.append(last_pos);
6075 }
6076 else {
6077 // all connected tiles for start pos
6078 uint8 ribi = welt->lookup(last_pos)->get_weg_ribi_unmasked(wt);
6079 koord delta = ribi_t::is_straight_ns(ribi) ? koord(0,1) : koord(1,0);
6080 koord3d start_pos=last_pos;
6081 while(ribi&12) {
6082 koord3d test_pos = start_pos+delta;
6083 grund_t *gr = welt->lookup(test_pos);
6084 if(!gr || !gr->is_halt() || (ribi=gr->get_weg_ribi_unmasked(wt))==0) {
6085 break;
6086 }
6087 start_pos = test_pos;
6088 }
6089 // now add all of them
6090 while(ribi&3) {
6091 koord3d test_pos = start_pos-delta;
6092 grund_t *gr = welt->lookup(test_pos);
6093 old_platform.append(start_pos);
6094 if(!gr || !gr->is_halt() || (ribi=gr->get_weg_ribi_unmasked(wt))==0) {
6095 break;
6096 }
6097 start_pos = test_pos;
6098 }
6099 }
6100 }
6101
6102 // first, check convoi without line
6103 FOR(vector_tpl<convoihandle_t>, const cnv, welt->convoys()) {
6104 // check line and owner
6105 if(!cnv->get_line().is_bound() && cnv->get_owner()==player) {
6106 schedule_t *schedule = cnv->get_schedule();
6107 // check waytype
6108 if(schedule && schedule->is_stop_allowed(bd)) {
6109 bool updated = false;
6110 FOR(minivec_tpl<schedule_entry_t>, & k, schedule->entries) {
6111 if ((catch_all_halt && haltestelle_t::get_halt( k.pos, cnv->get_owner()) == last_halt) ||
6112 old_platform.is_contained(k.pos)) {
6113 k.pos = pos;
6114 updated = true;
6115 }
6116 }
6117 if(updated) {
6118 schedule->cleanup();
6119 // Knightly : remove lineless convoy from old stop
6120 if( last_halt.is_bound() ) {
6121 last_halt->remove_convoy(cnv);
6122 }
6123 // Knightly : register lineless convoy at new stop
6124 if( new_halt.is_bound() ) {
6125 new_halt->add_convoy(cnv);
6126 }
6127 if( !schedule->is_editing_finished() ) {
6128 // schedule is not owned by schedule window ...
6129 // ... thus we can set this schedule
6130 cnv->set_schedule(schedule);
6131 // otherwise the schedule window will reset it
6132 }
6133 }
6134 }
6135 }
6136 }
6137 // next, check lines serving old_halt (no owner check needed for own lines ...
6138 vector_tpl<linehandle_t>lines;
6139 player->simlinemgmt.get_lines(simline_t::line,&lines);
6140 FOR(vector_tpl<linehandle_t>, const line, lines) {
6141 schedule_t *schedule = line->get_schedule();
6142 // check waytype
6143 if(schedule->is_stop_allowed(bd)) {
6144 bool updated = false;
6145 FOR(minivec_tpl<schedule_entry_t>, & k, schedule->entries) {
6146 // ok!
6147 if ((catch_all_halt && haltestelle_t::get_halt( k.pos, line->get_owner()) == last_halt) ||
6148 old_platform.is_contained(k.pos)) {
6149 k.pos = pos;
6150 updated = true;
6151 }
6152 }
6153 // update line
6154 if(updated) {
6155 schedule->cleanup();
6156 // remove line from old stop is needed at here
6157 if(last_halt.is_bound()) {
6158 last_halt->remove_line(line);
6159 }
6160 player->simlinemgmt.update_line(line);
6161 }
6162 }
6163 }
6164 }
6165 // since factory connections may have changed
6166 welt->set_schedule_counter();
6167 }
6168 return NULL;
6169 }
6170
6171
get_tooltip(player_t const *) const6172 char const* tool_daynight_level_t::get_tooltip(player_t const*) const
6173 {
6174 if (!strempty(default_param)) {
6175 if(default_param[0]=='+' || default_param[0]=='-') {
6176 sprintf(toolstr, "%s %s",
6177 translator::translate("1LIGHT_CHOOSE"),
6178 &default_param[0]);
6179 return toolstr;
6180 }
6181 else {
6182 return translator::translate("Toggle day/night view");
6183 }
6184 }
6185 else {
6186 return "";
6187 }
6188 }
6189
init(player_t *)6190 bool tool_daynight_level_t::init( player_t * ) {
6191 if(grund_t::underground_mode==grund_t::ugm_all || env_t::night_shift) {
6192 return false;
6193 }
6194 if (!strempty(default_param)) {
6195 if(default_param[0]=='+' && env_t::daynight_level > 0) {
6196 // '+': fade in one level
6197 env_t::daynight_level = env_t::daynight_level-1;
6198 }
6199 else if (default_param[0]=='-') {
6200 // '-': fade out one level
6201 env_t::daynight_level = env_t::daynight_level+1;
6202 }
6203 else {
6204 // number: toggle number/0. 4 or 5 is good for night
6205 const sint8 level = atoi(default_param);
6206 env_t::daynight_level = (env_t::daynight_level==0) ? level : 0;
6207 }
6208 }
6209 return false;
6210 }
6211
6212
6213 /* make all tiles of this player a public stop
6214 * if this player is public, make all connected tiles a public stop */
init(player_t *)6215 bool tool_make_stop_public_t::init( player_t * )
6216 {
6217 win_set_static_tooltip( NULL );
6218 return true;
6219 }
6220
get_tooltip(const player_t *) const6221 const char* tool_make_stop_public_t::get_tooltip(const player_t *) const
6222 {
6223 sprintf(toolstr, translator::translate("Make way or stop public (will join with neighbours), %i times maintainance"), welt->get_settings().cst_make_public_months);
6224 return toolstr;
6225 }
6226
move(player_t * player,uint16,koord3d p)6227 const char *tool_make_stop_public_t::move( player_t *player, uint16, koord3d p )
6228 {
6229 // queue tool for network
6230 if (env_t::networkmode) {
6231 nwc_tool_t *nwc = new nwc_tool_t(player, this, p, welt->get_steps(), welt->get_map_counter(), false);
6232 network_send_server(nwc);
6233 return NULL;
6234 }
6235
6236 return work( player, p );
6237 }
6238
work(player_t * player,koord3d p)6239 const char *tool_make_stop_public_t::work( player_t *player, koord3d p )
6240 {
6241 // target halt must exist
6242 halthandle_t halt = haltestelle_t::get_halt(p,player);
6243 if( !halt.is_bound() ) {
6244 return NOTICE_UNSUITABLE_GROUND;
6245 }
6246
6247 // check funds
6248 sint64 workcost = -welt->scale_with_month_length(halt->calc_maintenance() * welt->get_settings().cst_make_public_months);
6249
6250 // check waycost and soem forbidden cases too
6251 FOR(slist_tpl<haltestelle_t::tile_t>, const& i, halt->get_tiles()) {
6252 // make way public if any suitable
6253 for( int j=0; j<2; j++ ) {
6254 if( weg_t *w=i.grund->get_weg_nr(0) ) {
6255 if( w->get_owner() != welt->get_public_player() ) {
6256 // no public ways?
6257 if( welt->get_settings().get_disable_make_way_public() ) {
6258 return NOTICE_DISABLED_PUBLIC_WAY;
6259 }
6260 // no public way with signs
6261 if( w->has_sign() ) {
6262 return NOTICE_UNSUITABLE_GROUND;
6263 }
6264 // compute maintainance cost
6265 sint32 cost = w->get_desc()->get_maintenance();
6266 // tunnel cost overwrites way cost
6267 if( tunnel_t *t = i.grund->find<tunnel_t>() ) {
6268 cost = t->get_desc()->get_maintenance();
6269 }
6270 workcost -= welt->scale_with_month_length(cost * welt->get_settings().cst_make_public_months);
6271 // not making ways public on bridges
6272 if( i.grund->get_typ()==grund_t::brueckenboden ) {
6273 return NOTICE_UNSUITABLE_GROUND;
6274 }
6275 }
6276 }
6277 }
6278 }
6279
6280 if( !player->can_afford(workcost) ) {
6281 return NOTICE_INSUFFICIENT_FUNDS;
6282 }
6283
6284 // now find an adjacent stop
6285 vector_tpl<halthandle_t>checked_halts;
6286 checked_halts.append(halt);
6287 halthandle_t merge_to;
6288 FOR(slist_tpl<haltestelle_t::tile_t>, const& i, halt->get_tiles()) {
6289 const koord pos = i.grund->get_pos().get_2d();
6290 planquadrat_t *pl = welt->access( pos );
6291 for( int i=0; i<pl->get_haltlist_count(); i++ ) {
6292 halthandle_t h = pl->get_haltlist()[i];
6293 if( !checked_halts.is_contained(h) && h->get_owner()==welt->get_public_player() ) {
6294 koord testpos = h->get_ground_closest_to( pos )->get_pos().get_2d();
6295 if( abs(pos.x-testpos.x) <= 1 && abs(pos.y-testpos.y) <= 1 ) {
6296 merge_to = h;
6297 break;
6298 }
6299 }
6300 }
6301 if( merge_to.is_bound() ) {
6302 break;
6303 }
6304 }
6305
6306 // change ownership
6307 halt->change_owner( welt->get_public_player() );
6308 halt->merge_halt( merge_to );
6309
6310 return NULL;
6311 }
6312
6313
6314 /* merge stop */
get_marker_image()6315 image_id tool_merge_stop_t::get_marker_image()
6316 {
6317 return cursor;
6318 }
6319
is_valid_pos(player_t * player,const koord3d & pos,const char * & error,const koord3d &)6320 uint8 tool_merge_stop_t::is_valid_pos( player_t *player, const koord3d &pos, const char *&error, const koord3d &)
6321 {
6322 grund_t *bd = welt->lookup(pos);
6323 if (bd==NULL) {
6324 error = "";
6325 return 0;
6326 }
6327
6328 // check halt ownership
6329 halthandle_t h = haltestelle_t::get_halt(pos,player);
6330 if( h.is_bound() ) {
6331 // allow to merge two public stops too
6332 // so no need to check for ownership
6333
6334 // check for halt on the tile
6335 if( bd->is_halt() || (h->get_station_type()&haltestelle_t::dock && bd->is_water()) ) {
6336 return 2;
6337 }
6338 }
6339
6340 // not a halt at all ...
6341 error = NOTICE_UNSUITABLE_GROUND;
6342 return 0;
6343 }
6344
mark_tiles(player_t * player,const koord3d & start,const koord3d & end)6345 void tool_merge_stop_t::mark_tiles( player_t *player, const koord3d &start, const koord3d &end )
6346 {
6347 halt_be_merged_from = haltestelle_t::get_halt(start,player);
6348 halt_be_merged_to = haltestelle_t::get_halt(end,player);
6349 sint64 workcost = 0;
6350 uint32 distance = 0x7FFFFFFFu;
6351
6352 FOR(slist_tpl<haltestelle_t::tile_t>, const& i, halt_be_merged_from->get_tiles()) {
6353 FOR(slist_tpl<haltestelle_t::tile_t>, const& j, halt_be_merged_to->get_tiles()) {
6354 uint32 dist = koord_distance( i.grund->get_pos(), j.grund->get_pos() );
6355 if( dist < distance ) {
6356 distance = dist;
6357 if( distance <= 1 ) {
6358 break;
6359 }
6360 }
6361 }
6362 if( distance <= 1 ) {
6363 break;
6364 }
6365 }
6366
6367 if( distance < welt->get_settings().allow_merge_distant_halt ) {
6368 distance = clamp(distance,2,33)-2;
6369 workcost = welt->scale_with_month_length( (1<<distance) * welt->get_settings().cst_multiply_merge_halt );
6370 win_set_static_tooltip( tooltip_with_price("Building costs estimates", workcost) );
6371 }
6372 else {
6373 win_set_static_tooltip( "Too far away to merge stations!" );
6374 }
6375 }
6376
do_work(player_t * player,const koord3d & last_pos,const koord3d & pos)6377 const char *tool_merge_stop_t::do_work( player_t *player, const koord3d &last_pos, const koord3d &pos)
6378 {
6379 halt_be_merged_from = haltestelle_t::get_halt(pos,player);
6380 halt_be_merged_to = haltestelle_t::get_halt(last_pos,player);
6381 sint64 workcost = 0;
6382 uint32 distance = 0x7FFFFFFFu;
6383
6384 FOR(slist_tpl<haltestelle_t::tile_t>, const& i, halt_be_merged_from->get_tiles()) {
6385 FOR(slist_tpl<haltestelle_t::tile_t>, const& j, halt_be_merged_to->get_tiles()) {
6386 uint32 dist = koord_distance( i.grund->get_pos(), j.grund->get_pos() );
6387 if( dist < distance ) {
6388 distance = dist;
6389 if( distance <= 1 ) {
6390 break;
6391 }
6392 }
6393 }
6394 if( distance <= 1 ) {
6395 break;
6396 }
6397 }
6398
6399 if( distance < welt->get_settings().allow_merge_distant_halt ) {
6400 distance = clamp(distance,2,33)-2;
6401 workcost = welt->scale_with_month_length( (1<<distance) * welt->get_settings().cst_multiply_merge_halt );
6402 win_set_static_tooltip( tooltip_with_price("Building costs estimates", workcost) );
6403 if( player != welt->get_public_player() && !player->can_afford(workcost) ) {
6404 return NOTICE_INSUFFICIENT_FUNDS;
6405 }
6406 }
6407 else {
6408 return "Too far away to merge stations!";
6409 }
6410
6411 // and now just do it ...
6412 halt_be_merged_to->merge_halt(halt_be_merged_from);
6413 player_t::book_construction_costs( player, workcost, halt_be_merged_to->get_basis_pos(), ignore_wt );
6414
6415 // nothing to do
6416 return NULL;
6417 }
6418
init(player_t *)6419 bool tool_show_trees_t::init( player_t * )
6420 {
6421 env_t::hide_trees = !env_t::hide_trees;
6422 baum_t::recalc_outline_color();
6423 welt->set_dirty();
6424 return false;
6425 }
6426
6427
6428 sint8 tool_show_underground_t::save_underground_level = -128;
6429
init(player_t *)6430 bool tool_show_underground_t::init( player_t * )
6431 {
6432 koord3d zpos = welt->get_zeiger()->get_pos();
6433 // move zeiger (pointer) to invalid position -> unmark tiles
6434 welt->get_zeiger()->change_pos( koord3d::invalid);
6435
6436 sint8 old_underground_level = grund_t::underground_level;
6437
6438 // map needs update?
6439 bool ok = true;
6440 // need an extra click?
6441 bool needs_click = false;
6442
6443 // default default-param = U for backward compatibility
6444 if (default_param == NULL) {
6445 default_param = strdup("U");
6446 }
6447 // now check the default parameter
6448 switch(default_param[0]) {
6449 // toggle sliced view by toolbar - height taken from extra mouse click
6450 case 'C':
6451 if(grund_t::underground_mode==grund_t::ugm_level) {
6452 grund_t::set_underground_mode( grund_t::ugm_none, 0);
6453 }
6454 else if(grund_t::underground_mode==grund_t::ugm_none) {
6455 needs_click = true;
6456 ok = false;
6457 }
6458 else {
6459 ok = false;
6460 }
6461 break;
6462 // decrease slice level
6463 case 'D':
6464 if(grund_t::underground_mode==grund_t::ugm_level) {
6465 if( grund_t::underground_level > welt->min_height ) {
6466 grund_t::underground_level --;
6467 }
6468 }
6469 else {
6470 ok = false;
6471 }
6472 break;
6473 // increase slice level
6474 case 'I':
6475 if(grund_t::underground_mode==grund_t::ugm_level) {
6476 if( grund_t::underground_level < welt->max_height ) {
6477 grund_t::underground_level ++;
6478 }
6479 }
6480 else {
6481 ok = false;
6482 }
6483 break;
6484
6485 // toggle sliced view by keyboard - height taken from cursor
6486 case 'K':
6487 if(grund_t::underground_mode==grund_t::ugm_level) {
6488 // switch to normal or full-underground
6489 grund_t::set_underground_mode( grund_t::ugm_none, 0);
6490 }
6491 else if(grund_t::underground_mode==grund_t::ugm_none) {
6492 grund_t::set_underground_mode( grund_t::ugm_level, zpos.z);
6493 }
6494 else {
6495 ok = false;
6496 }
6497 break;
6498
6499 // switch between full underground or normal/sliced view
6500 case 'U':
6501 if (grund_t::underground_mode==grund_t::ugm_all) {
6502 // check if the old level is valid then switch back to sliced view
6503 if (-128<save_underground_level && save_underground_level<127) {
6504 grund_t::set_underground_mode(grund_t::ugm_level, save_underground_level);
6505 }
6506 else {
6507 grund_t::set_underground_mode(grund_t::ugm_none, 0);
6508 }
6509 }
6510 else {
6511 grund_t::set_underground_mode( grund_t::ugm_all, 0);
6512 }
6513 break;
6514
6515 default:
6516 ok = false;
6517 dbg->error( "tool_show_underground_t::init()", "Unknown command string \"%s\"", default_param );
6518
6519 }
6520
6521 // move zeiger (pointer) back
6522 welt->get_zeiger()->change_pos( zpos);
6523
6524 if (ok) {
6525 save_underground_level = old_underground_level;
6526
6527 // renew toolbar
6528 tool_t::update_toolbars();
6529
6530 // recalc all images on map
6531 welt->update_underground();
6532 }
6533 return needs_click;
6534 }
6535
work(player_t * player,koord3d pos)6536 const char *tool_show_underground_t::work( player_t *player, koord3d pos)
6537 {
6538 koord3d zpos = welt->get_zeiger()->get_pos();
6539 // move zeiger (pointer) to invalid position -> unmark tiles
6540 welt->get_zeiger()->change_pos( koord3d::invalid);
6541
6542 save_underground_level = grund_t::underground_level;
6543 grund_t::set_underground_mode( grund_t::ugm_level, pos.z);
6544
6545 // move zeiger (pointer) back
6546 welt->get_zeiger()->change_pos( zpos);
6547
6548 // renew toolbar
6549 tool_t::update_toolbars();
6550
6551 // recalc all images on map
6552 welt->update_underground();
6553
6554 if(player == welt->get_active_player()) {
6555 welt->set_tool( general_tool[TOOL_QUERY], player );
6556 }
6557
6558 return NULL;
6559 }
6560
6561
get_tooltip(player_t const *) const6562 char const* tool_show_underground_t::get_tooltip(player_t const*) const
6563 {
6564 // no default-param == U for backward compatibility
6565 if( default_param == NULL ) {
6566 return translator::translate("underground mode");
6567 }
6568 // now check the default parameter
6569 switch(default_param[0]) {
6570 // toggle sliced view by toolbar - height taken from extra mouse click
6571 case 'C':
6572 return translator::translate("sliced underground mode");
6573 // decrease slice level
6574 case 'D':
6575 return translator::translate("decrease underground view level");
6576 // increase slice level
6577 case 'I':
6578 return translator::translate("increase underground view level");
6579 // toggle sliced view by keyboard - height taken from cursor
6580 case 'K':
6581 return translator::translate("sliced underground mode");
6582 // switch between full underground or normal/sliced view
6583 case 'U':
6584 default:
6585 return translator::translate("underground mode");
6586 }
6587 }
6588
is_selected() const6589 bool tool_show_underground_t::is_selected() const
6590 {
6591 // default default-param = U for backward compatibility
6592 if( default_param == NULL ) {
6593 return grund_t::underground_mode==grund_t::ugm_all;
6594 }
6595 // now check the default parameter
6596 switch(default_param[0]) {
6597 // toggle sliced view by toolbar - height taken from extra mouse click
6598 case 'C':
6599 return grund_t::underground_mode==grund_t::ugm_level;
6600 // decrease slice level
6601 case 'D':
6602 return false;
6603 // increase slice level
6604 case 'I':
6605 return false;
6606 // toggle sliced view by keyboard - height taken from cursor
6607 case 'K':
6608 return grund_t::underground_mode==grund_t::ugm_level;
6609 // switch between full underground or normal/sliced view
6610 case 'U':
6611 return grund_t::underground_mode==grund_t::ugm_all;
6612 }
6613 return false;
6614 }
6615
draw_after(scr_coord k,bool dirty) const6616 void tool_show_underground_t::draw_after(scr_coord k, bool dirty) const
6617 {
6618 if( icon!=IMG_EMPTY && is_selected() ) {
6619 display_img_blend( icon, k.x, k.y, TRANSPARENT50_FLAG|OUTLINE_FLAG|color_idx_to_rgb(COL_BLACK), false, dirty );
6620 // additionally show level in sliced mode
6621 if( default_param!=NULL && grund_t::underground_mode==grund_t::ugm_level ) {
6622 char level_str[16];
6623 sprintf( level_str, "%i", grund_t::underground_level );
6624 display_proportional_rgb( k.x+4, k.y+4, level_str, ALIGN_LEFT, color_idx_to_rgb(COL_YELLOW), true );
6625 }
6626 }
6627 }
6628
6629
draw_after(scr_coord pos,bool dirty) const6630 void tool_rotate90_t::draw_after(scr_coord pos, bool dirty) const
6631 {
6632 if( !env_t::networkmode ) {
6633 if( skinverwaltung_t::compass_map ) {
6634 display_img_aligned( skinverwaltung_t::compass_map->get_image_id( welt->get_settings().get_rotation()+4 ), scr_rect(pos,env_t::iconsize), ALIGN_CENTER_V|ALIGN_CENTER_H, false );
6635 }
6636 tool_t::draw_after( pos, dirty );
6637 }
6638 }
6639
init(player_t *)6640 bool tool_rotate90_t::init( player_t * )
6641 {
6642 if( !env_t::networkmode ) {
6643 welt->rotate90();
6644 welt->update_map();
6645 }
6646 return false;
6647 }
6648
6649
init(player_t *)6650 bool tool_quit_t::init( player_t * )
6651 {
6652 destroy_all_win( true );
6653 welt->stop( true );
6654 return false;
6655 }
6656
6657
init(player_t *)6658 bool tool_screenshot_t::init( player_t * )
6659 {
6660 if( is_ctrl_pressed() ) {
6661 if( const gui_frame_t * topwin = win_get_top() ) {
6662 const scr_coord k = win_get_pos(topwin);
6663 const scr_size size = topwin->get_windowsize();
6664 display_snapshot( k.x, k.y, size.w, size.h );
6665 }
6666 else {
6667 display_snapshot( 0, 0, display_get_width(), display_get_height() );
6668 }
6669 }
6670 else {
6671 display_snapshot( 0, 0, display_get_width(), display_get_height() );
6672 }
6673 create_win( new news_img("Screenshot\ngespeichert.\n"), w_time_delete, magic_none);
6674 return false;
6675 }
6676
6677
init(player_t * player)6678 bool tool_undo_t::init( player_t *player )
6679 {
6680 if(!player->undo() && can_use_gui()) {
6681 create_win( new news_img("UNDO failed!"), w_time_delete, magic_none);
6682 }
6683 return false;
6684 }
6685
6686
init(player_t *)6687 bool tool_increase_industry_t::init( player_t * )
6688 {
6689 factory_builder_t::increase_industry_density( false );
6690 return false;
6691 }
6692
6693
init(player_t *)6694 bool tool_zoom_in_t::init( player_t * )
6695 {
6696 win_change_zoom_factor(true);
6697 welt->set_dirty();
6698 return false;
6699 }
6700
6701
init(player_t *)6702 bool tool_zoom_out_t::init( player_t * )
6703 {
6704 win_change_zoom_factor(false);
6705 welt->set_dirty();
6706 return false;
6707 }
6708
6709 /************************* internal tools, only need for networking ***************/
6710
scenario_check_schedule(karte_t * welt,player_t * player,schedule_t * schedule,bool local)6711 static bool scenario_check_schedule(karte_t *welt, player_t *player, schedule_t *schedule, bool local)
6712 {
6713 if (!is_scenario()) {
6714 return true;
6715 }
6716 const char* err = welt->get_scenario()->is_schedule_allowed(player, schedule);
6717 if (err) {
6718 if (*err && local) {
6719 open_error_msg_win(err);
6720 }
6721 return false;
6722 }
6723 return true;
6724 }
6725
6726
scenario_check_convoy(karte_t * welt,player_t * player,convoihandle_t cnv,depot_t * depot,bool local)6727 bool scenario_check_convoy(karte_t *welt, player_t *player, convoihandle_t cnv, depot_t* depot, bool local)
6728 {
6729 if (!is_scenario()) {
6730 return true;
6731 }
6732 const char* err = welt->get_scenario()->is_convoy_allowed(player, cnv, depot);
6733 if (err) {
6734 if (*err && local) {
6735 open_error_msg_win(err);
6736 }
6737 return false;
6738 }
6739 return true;
6740 }
6741
6742
6743
6744 /* Handles all action of convois in depots. Needs a default param:
6745 * [function],[convoi_id],addition stuff
6746 * following simple command exists:
6747 * 'x' : self destruct
6748 * 'f' : open the schedule window
6749 * 'g' : apply a schedule
6750 * 'n' : toggle 'no load'
6751 * 'w' : toggle withdraw
6752 * 's' : change state to [number] (and maybe set open schedule flag)
6753 * 'l' : apply new line [number]
6754 * 'd' : go to nearest depot
6755 */
init(player_t * player)6756 bool tool_change_convoi_t::init( player_t *player )
6757 {
6758 char tool=0;
6759 uint16 convoi_id = 0;
6760
6761 // skip the rest of the command
6762 const char *p = default_param;
6763 while( *p && *p<=' ' ) {
6764 p++;
6765 }
6766 sscanf( p, "%c,%hi", &tool, &convoi_id );
6767
6768 // skip to the commands ...
6769 for( int z = 2; *p && z>0; p++ ) {
6770 if( *p==',' ) {
6771 z--;
6772 }
6773 }
6774
6775 convoihandle_t cnv;
6776 cnv.set_id( convoi_id );
6777 // double click on remove button will send two such commands
6778 // the first will delete the convoi, the second should not trigger an assertion
6779 // catch such commands here
6780 if( !cnv.is_bound()) {
6781 #if DEBUG>=4
6782 if (can_use_gui()) {
6783 create_win( new news_img("Convoy already deleted!"), w_time_delete, magic_none);
6784 }
6785 #endif
6786 dbg->warning("tool_change_convoi_t::init", "no convoy with id=%d found", convoi_id);
6787 return false;
6788 }
6789 // ownership check for network games
6790 if (cnv.is_bound() && env_t::networkmode && !player_t::check_owner(cnv->get_owner(), player)) {
6791 return false;
6792 }
6793
6794 // first letter is now the actual command
6795 switch( tool ) {
6796 case 'x': // self destruction ...
6797 if(cnv.is_bound()) {
6798 if (cnv->get_state()==convoi_t::INITIAL) {
6799 // delete cnv in depot
6800 if (grund_t *gr = welt->lookup(cnv->get_pos())) {
6801 if (depot_t *dep = gr->get_depot()) {
6802 dep->disassemble_convoi(cnv, true);
6803 return false;
6804 }
6805 }
6806 }
6807 cnv->self_destruct();
6808 }
6809 return false;
6810
6811 case 'f': // open schedule
6812 {
6813 // we open the window only when executed on the same client that triggered the tool
6814 // but the all clients must call the function anyway
6815 cnv->open_schedule_window( can_use_gui() );
6816 }
6817 break;
6818
6819 case 'g': // change schedule
6820 {
6821 schedule_t *schedule = cnv->create_schedule()->copy();
6822 schedule->finish_editing();
6823 if (schedule->sscanf_schedule( p ) && scenario_check_schedule(welt, player, schedule, can_use_gui())) {
6824 cnv->set_schedule( schedule );
6825 }
6826 else {
6827 // could not read schedule, do not assign
6828 delete schedule;
6829 }
6830 }
6831 break;
6832
6833 case 'l': // change line
6834 {
6835 // read out id and new current_stop index
6836 uint16 id=0, current_stop=0;
6837 int count=sscanf( p, "%hi,%hi", &id, ¤t_stop );
6838 linehandle_t l;
6839 l.set_id( id );
6840 if( l.is_bound() ) {
6841 // sanity check for right line-type (compare schedule types ..)
6842 schedule_t *schedule = cnv->create_schedule();
6843 if( schedule && l->get_schedule() && schedule->get_type()!=l->get_schedule()->get_type() ) {
6844 dbg->warning("tool_change_convoi_t::init", "types of convoi and line do not match");
6845 return false;
6846 }
6847 if( count==1 ) {
6848 // current_stop was not supplied -> take it from line schedule
6849 current_stop = l->get_schedule()->get_current_stop();
6850 }
6851 cnv->set_line( l );
6852 cnv->get_schedule()->set_current_stop((uint8)current_stop);
6853 cnv->get_schedule()->finish_editing();
6854 }
6855 }
6856 break;
6857
6858 case 'n': // change no_load
6859 cnv->set_no_load( !cnv->get_no_load() );
6860 if( !cnv->get_no_load() ) {
6861 cnv->set_withdraw( false );
6862 }
6863 break;
6864
6865 case 's': // change state
6866 {
6867 int new_state = atoi(p);
6868 if( new_state>0 ) {
6869 cnv->set_state( new_state );
6870 if( new_state==convoi_t::EDIT_SCHEDULE ) {
6871 cnv->get_schedule()->start_editing();
6872 }
6873 }
6874 }
6875 break;
6876
6877 case 'w': // change withdraw
6878 cnv->set_withdraw( !cnv->get_withdraw() );
6879 cnv->set_no_load( cnv->get_withdraw() );
6880 break;
6881
6882 case 'd': // goto depot
6883 {
6884 const char* msg = cnv->send_to_depot(is_local_execution());
6885
6886 if (is_local_execution()) {
6887 create_win( new news_img(msg), w_time_delete, magic_none);
6888 }
6889 }
6890 }
6891
6892 if( cnv->in_depot() && (tool=='g' || tool=='l') ) {
6893 const grund_t *const ground = welt->lookup( cnv->get_home_depot() );
6894 if( ground ) {
6895 const depot_t *const depot = ground->get_depot();
6896 if( depot ) {
6897 depot_frame_t *const frame = dynamic_cast<depot_frame_t *>( win_get_magic( (ptrdiff_t)depot ) );
6898 if( frame ) {
6899 frame->update_data();
6900 }
6901 }
6902 }
6903 }
6904
6905
6906 return false; // no related work tool ...
6907 }
6908
6909
6910
6911 /* Handles all action of lines. Needs a default param:
6912 * [function],[line_id],addition stuff
6913 * following simple command exists:
6914 * 'g' : apply new schedule to line [schedule follows]
6915 */
init(player_t * player)6916 bool tool_change_line_t::init( player_t *player )
6917 {
6918 uint16 line_id = 0;
6919
6920 // skip the rest of the command
6921 const char *p = default_param;
6922 while( *p && *p<=' ' ) {
6923 p++;
6924 }
6925
6926 char tool=*p++;
6927 while( *p && *p++!=',' ) {
6928 }
6929 if( *p==0 ) {
6930 dbg->warning( "tool_change_line_t::init()", "too short command \"%s\"", default_param );
6931 return false;
6932 }
6933
6934 line_id = atoi(p);
6935 while( *p && *p++!=',' ) {
6936 }
6937
6938 linehandle_t line;
6939 line.set_id( line_id );
6940
6941 // ownership check for network games
6942 if (line.is_bound() && env_t::networkmode && !player_t::check_owner(line->get_owner(), player)) {
6943 return false;
6944 }
6945
6946 // first letter is now the actual command
6947 switch( tool ) {
6948 case 'c': // create line, next parameter line type and magic of schedule window (only right window gets updated)
6949 {
6950 line = player->simlinemgmt.create_line( atoi(p), player );
6951 while( *p && *p++!=',' ) {
6952 }
6953 long t;
6954 sscanf( p, "%ld", &t );
6955 while( *p && *p++!=',' ) {
6956 }
6957
6958 line->get_schedule()->sscanf_schedule( p );
6959 // check scenario conditions
6960 if (!scenario_check_schedule(welt, player, line->get_schedule(), can_use_gui())) {
6961 player->simlinemgmt.delete_line(line);
6962 break;
6963 }
6964
6965 line->get_schedule()->finish_editing(); // just in case ...
6966 if( can_use_gui() ) {
6967 schedule_gui_t *fg = dynamic_cast<schedule_gui_t *>(win_get_magic((ptrdiff_t)t));
6968 if( fg ) {
6969 fg->init_line_selector();
6970 }
6971 schedule_list_gui_t *sl = dynamic_cast<schedule_list_gui_t *>(win_get_magic(magic_line_management_t+player->get_player_nr()));
6972 if( sl ) {
6973 sl->show_lineinfo( line );
6974 }
6975 // no schedule window open => then open one
6976 if( fg==NULL ) {
6977 create_win( new line_management_gui_t(line, player), w_info, (ptrdiff_t)line.get_rep() );
6978 }
6979 }
6980 }
6981 break;
6982
6983 case 'd': // delete line
6984 {
6985 if (line.is_bound() && line->count_convoys()==0) {
6986 // close a schedule window, if still active
6987 gui_frame_t *w = win_get_magic( (ptrdiff_t)line.get_rep() );
6988 if(w) {
6989 destroy_win( w );
6990 }
6991 player->simlinemgmt.delete_line(line);
6992 }
6993 }
6994 break;
6995
6996 case 'g': // change schedule
6997 {
6998 if (line.is_bound()) {
6999 schedule_t *schedule = line->get_schedule()->copy();
7000 if (schedule->sscanf_schedule( p ) && scenario_check_schedule(welt, player, schedule, can_use_gui()) ) {
7001 schedule->finish_editing();
7002 line->set_schedule( schedule );
7003 line->get_owner()->simlinemgmt.update_line(line);
7004 }
7005 else {
7006 // could not read schedule, do not assign
7007 delete schedule;
7008 }
7009 }
7010 }
7011 break;
7012
7013 case 't': // trims away convois on all lines of linetype with this default parameter
7014 {
7015 vector_tpl<linehandle_t> const& lines = player->simlinemgmt.get_line_list();
7016 // what kind of lines to trim
7017 const simline_t::linetype linetype = (simline_t::linetype)atoi(p);
7018 while( *p && *p++!=',' ) {
7019 }
7020 // how much as target
7021 sint32 percentage = *p ? atoi(p) : 10;
7022 if( percentage == 0 ) {
7023 break;
7024 }
7025
7026 FOR(vector_tpl<linehandle_t>,line,lines) {
7027 if( line->get_linetype() == linetype && line->get_convoys().get_count() > 2 ) {
7028 // correct waytpe and more than one,n now some up usage for the last six months
7029 sint64 transported = 0, capacity = 0;
7030 for( int i=0; i<6; i++ ) {
7031 capacity += line->get_finance_history( i , LINE_CAPACITY );
7032 transported += line->get_finance_history( i , LINE_TRANSPORTED_GOODS );
7033 }
7034 // sanity check for non-moving lines
7035 if( capacity == 0 ) {
7036 continue;
7037 }
7038
7039 if( (transported*100) / capacity < percentage ) {
7040 // less than 33 % usage => remove concois
7041 vector_tpl<convoihandle_t> const& cnvs = line->get_convoys();
7042 sint64 old_sum_capacity = 0;
7043 FOR(vector_tpl<convoihandle_t>,cnv,cnvs) {
7044 for( int i=0; i<cnv->get_vehicle_count(); i++ ) {
7045 old_sum_capacity += cnv->get_vehikel(i)->get_desc()->get_capacity();
7046 }
7047 }
7048 /* now we have the total capacity. We will now remove convois until this capacity
7049 * is reduced by the ration suggested from transported = 1/3 of total capacity
7050 * x is the percentage of used capacity
7051 * Then is the new target sum_capacity = x*old_sum_capacity
7052 */
7053 sint64 new_sum_capacity = (transported * 1000 * old_sum_capacity) / (capacity * percentage * 10);
7054
7055 // first we remove the totally empty convois (nowbody will miss them)
7056 int destroyed = 0;
7057 const int initial = line->get_convoys().get_count();
7058 const int max_left = (initial+2) / 2;
7059
7060 for( int j=initial-1; j >= 0 && initial-destroyed > max_left && new_sum_capacity < old_sum_capacity; j-- ) {
7061 convoihandle_t cnv = line->get_convoy(j);
7062 if( cnv->get_state() == convoi_t::INITIAL || cnv->get_state() >= convoi_t::WAITING_FOR_CLEARANCE_ONE_MONTH ) {
7063 for( int i=0; i<cnv->get_vehicle_count(); i++ ) {
7064 old_sum_capacity -= cnv->get_vehikel(i)->get_desc()->get_capacity();
7065 }
7066 cnv->self_destruct();
7067 destroyed ++;
7068 }
7069 }
7070
7071 // not enough? Then remove from the end ...
7072 for( uint j=0; j < line->get_convoys().get_count() && initial-destroyed > max_left && new_sum_capacity < old_sum_capacity; j++ ) {
7073 convoihandle_t cnv = line->get_convoy(j);
7074 if( cnv->get_state() != convoi_t::SELF_DESTRUCT ) {
7075 for( int i=0; i<cnv->get_vehicle_count(); i++ ) {
7076 old_sum_capacity -= cnv->get_vehikel(i)->get_desc()->get_capacity();
7077 }
7078 cnv->self_destruct();
7079 destroyed ++;
7080 }
7081 }
7082 // done
7083 if( destroyed ) {
7084 dbg->message( "tool_change_line_t::init", "trim line %s: Reduced from %d to %d convois", line->get_name(), initial, initial-destroyed );
7085 }
7086 }
7087 }
7088 }
7089 }
7090 break;
7091
7092 case 'u': // unite all lineless convois with similar schedules
7093 {
7094 array_tpl<vector_tpl<convoihandle_t> > cnvs(welt->convoys().get_count());
7095 uint32 max_cnvs=0;
7096 FOR(vector_tpl<convoihandle_t>, cnv, welt->convoys()) {
7097 // only check lineless convoys
7098 if( !cnv->get_line().is_bound() ) {
7099 bool found = false;
7100 // check, if already matches existing convois schedule
7101 for( uint32 i=0; i<max_cnvs && !found; i++ ) {
7102 FOR(vector_tpl<convoihandle_t>, cnvcomp, cnvs[i] ) {
7103 if( cnvcomp->get_schedule()->matches( welt, cnv->get_schedule() ) ) {
7104 found = true;
7105 cnvs[i].append( cnv );
7106 break;
7107 }
7108 }
7109 }
7110 // not added: then may be new line for this?
7111 if( !found ) {
7112 cnvs[max_cnvs++].append( cnv );
7113 }
7114 }
7115 }
7116 // now we have an array of one or more lineless convois
7117 for( uint32 i=0; i<max_cnvs; i++ ) {
7118 // if there is more than one convois => new line
7119 if( cnvs[i].get_count()>1 ) {
7120 line = player->simlinemgmt.create_line( cnvs[i][0]->get_schedule()->get_type(), player, cnvs[i][0]->get_schedule() );
7121 FOR(vector_tpl<convoihandle_t>, cnv, cnvs[i] ) {
7122 line->add_convoy( cnv );
7123 cnv->set_line( line );
7124 }
7125 }
7126 }
7127 }
7128 break;
7129
7130 case 'w': // change widthdraw
7131 {
7132 if (line.is_bound()) {
7133 line->set_withdraw( atoi(p) );
7134 }
7135 }
7136 break;
7137 }
7138 return false;
7139 }
7140
7141
7142
7143 /* Handles all action of convois in depots. Needs a default param:
7144 * [function],[depot_pos_3d],[convoi_id],addition stuff
7145 * following simple command exists:
7146 * 'l' : creates a new line (convoi_id might be invalid) (+printf'd initial schedule)
7147 * 'b' : starts the convoi
7148 * 'B' : starts all convoys
7149 * 'c' : copies this convoi
7150 * 'd' : disassembles convoi
7151 * 's' : sells convoi
7152 * 'a' : appends a vehicle (+vehikel_name) uses the oldest
7153 * 'i' : inserts a vehicle in front (+vehikel_name) uses the oldest
7154 * 's' : sells a vehikel (+vehikel_name) uses the newest
7155 * 'r' : removes a vehikel (+number in convoi)
7156 * 'R' : removes all vehikels including (+number in convoi) to end
7157 */
init(player_t * player)7158 bool tool_change_depot_t::init( player_t *player )
7159 {
7160 char tool=0;
7161 koord pos2d;
7162 sint16 z;
7163 uint16 convoi_id;
7164
7165 // skip the rest of the command
7166 const char *p = default_param;
7167 while( *p && *p<=' ' ) {
7168 p++;
7169 }
7170 sscanf( p, "%c,%hi,%hi,%hi,%hi", &tool, &pos2d.x, &pos2d.y, &z, &convoi_id );
7171
7172 koord3d pos(pos2d, z);
7173
7174 // skip to the commands ...
7175 z = 5;
7176 while( *p && z>0 ) {
7177 if( *p==',' ) {
7178 z--;
7179 }
7180 p++;
7181 }
7182
7183 grund_t *gr = welt->lookup(pos);
7184 depot_t *depot = gr ? gr->get_depot() : NULL;
7185 if( depot==NULL ){
7186 dbg->warning("tool_change_depot_t::init", "no depot found at (%s)", pos.get_str());
7187 return false;
7188 }
7189 if( !player_t::check_owner( depot->get_owner(), player) ) {
7190 dbg->warning("tool_change_depot_t::init", "depot at (%s) belongs to another player", pos.get_str());
7191 return false;
7192 }
7193
7194 convoihandle_t cnv;
7195 cnv.set_id( convoi_id );
7196
7197 // ok now do our stuff
7198 switch( tool ) {
7199 case 'l': { // create line schedule window
7200 linehandle_t selected_line = depot->get_owner()->simlinemgmt.create_line(depot->get_line_type(),depot->get_owner());
7201 // no need to check schedule for scenario conditions, as schedule is only copied
7202 selected_line->get_schedule()->sscanf_schedule( p );
7203
7204 depot_frame_t *depot_frame = dynamic_cast<depot_frame_t *>(win_get_magic( (ptrdiff_t)depot ));
7205 if( can_use_gui() ) {
7206 if( welt->get_active_player()==player && depot_frame ) {
7207 create_win( new line_management_gui_t( selected_line, depot->get_owner() ), w_info, (ptrdiff_t)selected_line.get_rep() );
7208 }
7209 }
7210
7211 if( depot_frame ) {
7212 if( can_use_gui() ) {
7213 depot_frame->set_selected_line( selected_line );
7214 depot_frame->apply_line();
7215 }
7216 depot_frame->update_data();
7217 }
7218
7219 schedule_list_gui_t *sl = dynamic_cast<schedule_list_gui_t *>(win_get_magic( magic_line_management_t + player->get_player_nr() ));
7220 if( sl ) {
7221 sl->update_data( selected_line );
7222 }
7223 DBG_MESSAGE("depot_frame_t::new_line()","id=%d",selected_line.get_id() );
7224 break;
7225 }
7226 case 'b': { // start a convoi from the depot
7227 if( cnv.is_bound() ) {
7228 depot->start_convoi(cnv, can_use_gui());
7229 }
7230 break;
7231 }
7232 case 'B': { // start all convoys
7233 depot->start_all_convoys();
7234 break;
7235 }
7236 case 'd': // disassemble convoi
7237 case 'v': { // sell convoi
7238 depot->disassemble_convoi( cnv, tool=='v' );
7239 break;
7240 }
7241 case 'c': { // copy this convoi
7242 if( cnv.is_bound() ) {
7243 if( convoihandle_t::is_exhausted() ) {
7244 if( can_use_gui() ) {
7245 create_win( new news_img("Convoi handles exhausted!"), w_time_delete, magic_none );
7246 }
7247 return false;
7248 }
7249 depot->copy_convoi( cnv, can_use_gui() );
7250 }
7251 break;
7252 }
7253 case 'a': // append a vehicle
7254 case 'i': // insert a vehicle in front
7255 case 's': // sells a vehicle
7256 case 'r': // removes a vehicle (assumes a valid depot)
7257 case 'R': { // removes all vehicles to end (assumes a valid depot)
7258 if( tool=='r' || tool=='R' ) {
7259 // test may fail after double-click on the button:
7260 // two remove cmds are sent, only the first will remove, the second should not trigger assertion failure
7261 if ( cnv.is_bound() ) {
7262 int start_nr = atoi(p);
7263 int nr = start_nr;
7264
7265 // find end
7266 while(nr<cnv->get_vehicle_count()) {
7267 const vehicle_desc_t *info = cnv->get_vehikel(nr)->get_desc();
7268 nr ++;
7269 if(info->get_trailer_count()!=1) {
7270 break;
7271 }
7272 }
7273 // now remove the vehicles
7274 if( cnv->get_vehicle_count()==nr-start_nr || (tool=='R' && start_nr==0) ) {
7275 depot->disassemble_convoi(cnv, false);
7276 }
7277 else if( tool=='R' ) {
7278 depot->remove_vehicles_to_end( cnv, start_nr );
7279 }
7280 else {
7281 for( int i=start_nr; i<nr; i++ ) {
7282 depot->remove_vehicle(cnv, start_nr);
7283 }
7284 }
7285 }
7286 }
7287 else {
7288 // create and append it
7289 const vehicle_desc_t *info = vehicle_builder_t::get_info( p );
7290 // we have a valid vehicle there => now check for details
7291 if( info ) {
7292 // we buy/sell all vehicles together!
7293 slist_tpl<const vehicle_desc_t *>new_vehicle_info;
7294 const vehicle_desc_t *start_info = info;
7295
7296 if(tool!='a') {
7297 // start of composition
7298 while( info->get_leader_count() == 1 && info->get_leader(0) != NULL && !new_vehicle_info.is_contained(info) ) {
7299 info = info->get_leader(0);
7300 new_vehicle_info.insert(info);
7301 }
7302 info = start_info;
7303 }
7304 while(info) {
7305 new_vehicle_info.append( info );
7306 if(info->get_trailer_count() != 1 || (tool == 'i' && info == start_info) || new_vehicle_info.is_contained(info->get_trailer(0)) ) {
7307 break;
7308 }
7309 info = info->get_trailer(0);
7310 }
7311 // now we have a valid composition together
7312 if( tool=='s' ) {
7313 while( new_vehicle_info.get_count() ) {
7314 // We sell the newest vehicle - gives most money back.
7315 vehicle_t* veh = depot->find_oldest_newest(new_vehicle_info.remove_first(), false);
7316 if(veh != NULL) {
7317 depot->sell_vehicle(veh);
7318 }
7319 }
7320 }
7321 else {
7322 // now check if we are allowed to buy this (we test only leading vehicle, so one can still buy hidden stuff)
7323 info = new_vehicle_info.front();
7324 if( !info->is_available(welt->get_timeline_year_month()) && !welt->get_settings().get_allow_buying_obsolete_vehicles() ) {
7325 // only allow append/insert, if in depot do not create new obsolete vehicles
7326 if( !depot->find_oldest_newest(info, true) ) {
7327 // just fail silent
7328 return false;
7329 }
7330 }
7331
7332 // append/insert into convoi; create one if needed
7333 depot->clear_command_pending();
7334 if( !cnv.is_bound() ) {
7335 if( convoihandle_t::is_exhausted() ) {
7336 if( can_use_gui() ) {
7337 create_win( new news_img("Convoi handles exhausted!"), w_time_delete, magic_none);
7338 }
7339 return false;
7340 }
7341 // create a new convoi
7342 cnv = depot->add_convoi( can_use_gui() );
7343 cnv->set_name( new_vehicle_info.front()->get_name() );
7344 }
7345
7346 // now we have a valid cnv
7347 if( cnv->get_vehicle_count()+new_vehicle_info.get_count() <= depot->get_max_convoi_length() ) {
7348
7349 for( unsigned i=0; i<new_vehicle_info.get_count(); i++ ) {
7350 // insert/append needs reverse order
7351 unsigned nr = (tool=='i') ? new_vehicle_info.get_count()-i-1 : i;
7352 // We add the oldest vehicle - newer stay for selling
7353 const vehicle_desc_t* vb = new_vehicle_info.at(nr);
7354 vehicle_t* veh = depot->find_oldest_newest(vb, true);
7355 if( veh == NULL ) {
7356 // nothing there => we buy it
7357 veh = depot->buy_vehicle(vb);
7358 }
7359 depot->append_vehicle( cnv, veh, tool=='i', can_use_gui() );
7360 }
7361 }
7362 }
7363 }
7364 }
7365 depot->update_win();
7366 break;
7367 }
7368 }
7369 return false;
7370 }
7371
7372
7373 /* Handles all player stuff default_param:
7374 * [function],[player_id],[state]
7375 * following command exists:
7376 * 'a' : activate/deactivate player (depends on state)
7377 * 'c' : change player color
7378 * '$' : change bank account
7379 */
init(player_t * player_in)7380 bool tool_change_player_t::init( player_t *player_in)
7381 {
7382 if( default_param==NULL ) {
7383 dbg->warning( "tool_change_player_t::init()", "nothing to do!" );
7384 return false;
7385 }
7386
7387 // skip the rest of the command
7388 const char *p = default_param;
7389 while( *p && *p<=' ' ) {
7390 p++;
7391 }
7392
7393 char tool = 0;
7394 int id = 0;
7395 if (sscanf( p, "%c,%i", &tool, &id ) < 2) {
7396 return false; // invalid command
7397 }
7398
7399 player_t *player = welt->get_player(id);
7400
7401 // ok now do our stuff
7402 switch( tool ) {
7403 case 'a': // activate/deactivate AI
7404 if( player && player->get_ai_id()!=player_t::HUMAN && (player_in==welt->get_public_player() || !env_t::networkmode) ) {
7405 int state = 0;
7406 if (sscanf( p, "%c,%i,%i", &tool, &id, &state ) == 3) {
7407 player->set_active(state);
7408 welt->get_settings().set_player_active(id, player->is_active());
7409 }
7410 }
7411 break;
7412 case 'c': // change player color
7413 if( player && player==player_in ) {
7414 int c1, c2, dummy;
7415 sscanf( p, "%c,%i,%i,%i", &tool, &dummy, &c1, &c2 );
7416 player->set_player_color( c1, c2 );
7417 }
7418 break;
7419
7420 case '$': // change bank account
7421 if( player && player_in==welt->get_public_player() ) {
7422 int delta;
7423 if (sscanf(p, "%c,%i,%i", &tool, &id, &delta) == 3) {
7424 player->get_finance()->book_account(delta);
7425 }
7426 }
7427 break;
7428
7429 case 'n': // WAS: new player with type state
7430 case 'f': // WAS: activate/deactivate freeplay
7431 dbg->error( "tool_change_player_t::init()", "deprecated command called" );
7432 break;
7433 }
7434
7435 // update the window
7436 ki_kontroll_t* playerwin = (ki_kontroll_t*)win_get_magic(magic_ki_kontroll_t);
7437 if (playerwin) {
7438 playerwin->update_data();
7439 }
7440
7441 return false;
7442 }
7443
7444
7445 /* Sets traffic light phases via default_param:
7446 * [pos],[ns_flag],[ticks]
7447 */
init(player_t * player)7448 bool tool_change_traffic_light_t::init( player_t *player )
7449 {
7450 koord pos2d;
7451 sint16 z, ns, ticks;
7452 if( 5!=sscanf( default_param, "%hi,%hi,%hi,%hi,%hi", &pos2d.x, &pos2d.y, &z, &ns, &ticks ) ) {
7453 return false;
7454 }
7455 koord3d pos(pos2d, z);
7456 if( grund_t *gr = welt->lookup(pos) ) {
7457 if( roadsign_t *rs = gr->find<roadsign_t>() ) {
7458 if( ( rs->get_desc()->is_traffic_light() || rs->get_desc()->is_private_way() ) && player_t::check_owner(rs->get_owner(),player) ) {
7459 if( ns == 1 ) {
7460 rs->set_ticks_ns( (uint8)ticks );
7461 }
7462 else if( ns == 0 ) {
7463 rs->set_ticks_ow( (uint8)ticks );
7464 }
7465 else if( ns == 2 ) {
7466 rs->set_ticks_offset( (uint8)ticks );
7467 }
7468 // update the window
7469 if( rs->get_desc()->is_traffic_light() ) {
7470 trafficlight_info_t* trafficlight_win = (trafficlight_info_t*)win_get_magic((ptrdiff_t)rs);
7471 if (trafficlight_win) {
7472 trafficlight_win->update_data();
7473 }
7474 }
7475 else {
7476 privatesign_info_t* trafficlight_win = (privatesign_info_t*)win_get_magic((ptrdiff_t)rs);
7477 if (trafficlight_win) {
7478 trafficlight_win->update_data();
7479 }
7480 }
7481 }
7482 }
7483 }
7484 return false;
7485 }
7486
7487
7488 /**
7489 * change city:
7490 * g[x],[y],[allow_city_growth]
7491 */
init(player_t * player)7492 bool tool_change_city_t::init( player_t *player )
7493 {
7494 if (player != welt->get_public_player()) {
7495 return false;
7496 }
7497 koord k;
7498 sint16 allow_growth;
7499 if( 3!=sscanf( default_param, "g%hi,%hi,%hi", &k.x, &k.y, &allow_growth ) ) {
7500 return false;
7501 }
7502 grund_t *gr = welt->lookup_kartenboden(k);
7503 if (gr) {
7504 gebaeude_t *gb = gr->find<gebaeude_t>();
7505 if (gb) {
7506 stadt_t *st = gb->get_stadt();
7507 if (st) {
7508 st->set_citygrowth_yesno(allow_growth);
7509 city_info_t *stinfo = dynamic_cast<city_info_t*>(win_get_magic((ptrdiff_t)st));
7510 if (stinfo) {
7511 stinfo->update_data();
7512 }
7513 }
7514 }
7515 }
7516 return false;
7517 }
7518
7519
7520
7521 /* Handles renaming of ingame entities. Needs a default param:
7522 * [object='c|h|l|m|t|p|f'][id|pos],[name]
7523 * c=convoi, h=halt, l=line, m=marker, t=town, p=player, f=factory
7524 * in case of marker / factory, id is a pos3d string
7525 */
init(player_t * player)7526 bool tool_rename_t::init(player_t *player)
7527 {
7528 uint16 id = 0;
7529 koord3d pos = koord3d::invalid;
7530
7531 // skip the rest of the command
7532 const char *p = default_param;
7533 const char what = *p++;
7534 switch( what ) {
7535 case 'h':
7536 case 'l':
7537 case 'c':
7538 case 't':
7539 case 'p':
7540 id = atoi(p);
7541 while( *p>0 && *p++!=',' ) {
7542 }
7543 break;
7544 case 'm':
7545 case 'f': {
7546 koord pos2d;
7547 if( 3!=sscanf( p, "%hi,%hi,%hi", &pos2d.x, &pos2d.y, &id ) ) {
7548 dbg->error( "tool_rename_t::init", "no position given for marker/factory! (%s)", default_param );
7549 return false;
7550 }
7551 while( *p>0 && *p++!=',' ) {
7552 }
7553 while( *p>0 && *p++!=',' ) {
7554 }
7555 while( *p>0 && *p++!=',' ) {
7556 }
7557 pos = koord3d(pos2d, id);
7558 id = 0;
7559 break;
7560 }
7561 default:
7562 dbg->error( "tool_rename_t::init", "illegal request! (%s)", default_param );
7563 return false;
7564 }
7565
7566 // now for action ...
7567 switch( what ) {
7568 case 'h':
7569 {
7570 halthandle_t halt;
7571 halt.set_id( id );
7572 if( halt.is_bound() && (!env_t::networkmode || player_t::check_owner(halt->get_owner(), player)) ) {
7573 halt->set_name( p );
7574 return false;
7575 }
7576 break;
7577 }
7578
7579 case 'l':
7580 {
7581 linehandle_t line;
7582 line.set_id( id );
7583 if( line.is_bound() && (!env_t::networkmode || player_t::check_owner(line->get_owner(), player)) ) {
7584 line->set_name( p );
7585
7586 schedule_list_gui_t *sl = dynamic_cast<schedule_list_gui_t *>(win_get_magic(magic_line_management_t+player->get_player_nr()));
7587 if( sl ) {
7588 sl->update_data( line );
7589 }
7590 return false;
7591 }
7592 break;
7593 }
7594
7595 case 'c':
7596 {
7597 convoihandle_t cnv;
7598 cnv.set_id( id );
7599 if( cnv.is_bound() && (!env_t::networkmode || player_t::check_owner(cnv->get_owner(), player)) ) {
7600 // set name without ID
7601 cnv->set_name( p, false );
7602 return false;
7603 }
7604 break;
7605 }
7606
7607 case 't':
7608 {
7609 if( player == welt->get_public_player() && id<welt->get_cities().get_count() ) {
7610 welt->get_cities()[id]->set_name( p );
7611 return false;
7612 }
7613 break;
7614 }
7615
7616 case 'm':
7617 if( grund_t *gr = welt->lookup(pos) ) {
7618 label_t *label = gr->find<label_t>();
7619 if (label && (!env_t::networkmode || player_t::check_owner(label->get_owner(), player)) ) {
7620 gr->set_text(p);
7621 }
7622 return false;
7623 }
7624 break;
7625
7626 case 'p': {
7627 player_t *other = welt->get_player((uint8)id);
7628 if( other && other == player ) {
7629 other->set_name(p);
7630 return false;
7631 }
7632 break;
7633 }
7634
7635 case 'f':
7636 {
7637 if( player == welt->get_public_player()) {
7638 if( grund_t *gr = welt->lookup(pos) ) {
7639 if( gebaeude_t* gb = gr->find<gebaeude_t>() ) {
7640 if ( fabrik_t *fab = gb->get_fabrik() ) {
7641 fab->set_name(p);
7642 return false;
7643 }
7644 }
7645 }
7646 }
7647 }
7648 }
7649 // we are only getting here, if we could not process this request
7650 dbg->warning( "tool_rename_t::init", "could not perform (%s)", default_param );
7651 return false;
7652 }
7653
7654
7655 /*
7656 * Add a message to the message queue
7657 */
init(player_t * player)7658 bool tool_add_message_t::init(player_t *player)
7659 {
7660 if ( *default_param ) {
7661 uint32 type = atoi(default_param);
7662 const char* text = strchr(default_param, ',');
7663 if ((type & ~message_t::local_flag) >= message_t::MAX_MESSAGE_TYPE || text == NULL) {
7664 return false;
7665 }
7666 welt->get_message()->add_message( text+1, koord::invalid, type,
7667 type & message_t::local_flag ? color_idx_to_rgb(COL_BLACK) : PLAYER_FLAG|player->get_player_nr(), IMG_EMPTY );
7668 }
7669 return false;
7670 }
7671