diff --git dataobj/settings.cc dataobj/settings.cc index 41c34a306..496ee1c85 100644 --- dataobj/settings.cc +++ dataobj/settings.cc @@ -255,6 +255,8 @@ settings_t::settings_t() : cst_depot_road=-130000; cst_depot_ship=-250000; cst_depot_air=-500000; + allow_merge_distant_halt = false; + cst_multiply_merge_halt=-50000; // alter landscape cst_buy_land=-10000; cst_alter_land=-100000; @@ -644,6 +646,10 @@ void settings_t::rdwr(loadsave_t *file) file->rdwr_longlong(cst_make_public_months); } + if( file->get_version() > 120008 ) { + file->rdwr_longlong(cst_multiply_merge_halt); + } + // wayfinder file->rdwr_long(way_count_straight ); file->rdwr_long(way_count_curve ); @@ -822,6 +828,9 @@ void settings_t::rdwr(loadsave_t *file) file->rdwr_byte(world_maximum_height); file->rdwr_byte(world_minimum_height); } + if( file->get_version() > 120008 ) { + file->rdwr_bool(allow_merge_distant_halt); + } // otherwise the default values of the last one will be used } } @@ -1348,6 +1357,9 @@ void settings_t::parse_simuconf(tabfile_t& simuconf, sint16& disp_width, sint16& cst_depot_road = contents.get_int64("cost_depot_road", cst_depot_road/(-100) ) * -100; cst_depot_ship = contents.get_int64("cost_depot_ship", cst_depot_ship/(-100) ) * -100; + allow_merge_distant_halt = contents.get_int("allow_merge_distant_halt", allow_merge_distant_halt) != 0; + cst_multiply_merge_halt = contents.get_int64("cost_multiply_merge_halt", cst_multiply_merge_halt/(-100) ) * -100; + // alter landscape cst_buy_land = contents.get_int64("cost_buy_land", cst_buy_land/(-100) ) * -100; cst_alter_land = contents.get_int64("cost_alter_land", cst_alter_land/(-100) ) * -100; diff --git dataobj/settings.h dataobj/settings.h index abe837580..101c92b81 100644 --- dataobj/settings.h +++ dataobj/settings.h @@ -302,6 +302,10 @@ public: sint64 cst_depot_ship; sint64 cst_depot_air; + // cost to merge station + bool allow_merge_distant_halt; + sint64 cst_multiply_merge_halt; + // alter landscape sint64 cst_buy_land; sint64 cst_alter_land; @@ -590,6 +594,8 @@ public: bool get_allow_underground_transformers() const { return allow_underground_transformers; } bool get_disable_make_way_public() const { return disable_make_way_public; } + bool get_allow_merge_distant_halt() const { return allow_merge_distant_halt; } + uint16 get_remove_dummy_player_months() const { return remove_dummy_player_months; } uint16 get_unprotect_abandoned_player_months() const { return unprotect_abandoned_player_months; } diff --git gui/settings_stats.cc gui/settings_stats.cc index 9ae6ba70a..f626e4fd0 100644 --- gui/settings_stats.cc +++ gui/settings_stats.cc @@ -236,6 +236,7 @@ void settings_routing_stats_t::init(settings_t const* const sets) INIT_BOOL( "avoid_overcrowding", sets->is_avoid_overcrowding() ); INIT_BOOL( "no_routing_over_overcrowded", sets->is_no_routing_over_overcrowding() ); INIT_NUM( "station_coverage", sets->get_station_coverage(), 1, 8, gui_numberinput_t::AUTOLINEAR, false ); + INIT_BOOL( "allow_merge_distant_halt", sets->get_allow_merge_distant_halt() ); SEPERATOR INIT_NUM( "max_route_steps", sets->get_max_route_steps(), 0, 0x7FFFFFFFul, gui_numberinput_t::POWER2, false ); INIT_NUM( "max_choose_route_steps", sets->get_max_choose_route_steps(), 0, 0x7FFFFFFFul, gui_numberinput_t::POWER2, false ); @@ -264,6 +265,7 @@ void settings_routing_stats_t::read(settings_t* const sets) READ_BOOL_VALUE( sets->avoid_overcrowding ); READ_BOOL_VALUE( sets->no_routing_over_overcrowding ); READ_NUM_VALUE( sets->station_coverage_size ); + READ_BOOL_VALUE( sets->allow_merge_distant_halt ); READ_NUM_VALUE( sets->max_route_steps ); READ_NUM_VALUE( sets->max_choose_route_steps ); READ_NUM_VALUE( sets->max_hops ); diff --git simhalt.cc simhalt.cc index 4d4630417..712a98318 100644 --- simhalt.cc +++ simhalt.cc @@ -224,6 +224,27 @@ koord3d haltestelle_t::get_basis_pos3d() const return tiles.front().grund->get_pos(); } +/* Calculate and set center position of this station + * It is the avarage of all tiles' coordinate weighed by level of the building */ +void haltestelle_t::recalc_center_pos() +{ + koord cent; + sint32 level_sum; + cent = koord(); + level_sum = 0; + FOR(slist_tpl, const& i, tiles) { + if( gebaeude_t* const gb = i.grund->find() ) { + sint16 lv; + lv = gb->get_tile()->get_desc()->get_level() + 1; + cent += gb->get_pos().get_2d() * lv; + level_sum += lv; + } + } + if ( level_sum > 0 ) { + center_pos = cent/level_sum; + } + return; +} /** * Station factory method. Returns handles instead of pointers. @@ -1021,7 +1042,7 @@ void haltestelle_t::verbinde_fabriken() FOR(slist_tpl, const& i, tiles) { koord const p = i.grund->get_pos().get_2d(); - int const cov = welt->get_settings().get_station_coverage(); + uint16 const cov = welt->get_settings().get_station_coverage(); FOR(vector_tpl, const fab, fabrik_t::sind_da_welche(p - koord(cov, cov), p + koord(cov, cov))) { if(!fab_list.is_contained(fab)) { // water factories can only connect to docks @@ -2356,6 +2377,99 @@ void haltestelle_t::make_public_and_join( player_t *player ) } +// merge stop +void haltestelle_t::merge_halt( player_t *player, halthandle_t halt_merged ) +{ + player_t *const public_owner = welt->get_public_player(); + + // process every tile of stop + slist_tpl joining; + FOR(slist_tpl, const& i, tiles) { + grund_t* const gr = i.grund; + gebaeude_t* gb = gr->find(); + if( gb ) { + gb->set_flag(obj_t::dirty); + } + + // search for stops to join, starting with this tile + const planquadrat_t *pl = welt->access(gr->get_pos().get_2d()); + for( uint8 i=0; i < pl->get_boden_count(); i++ ) { + halthandle_t my_halt = pl->get_boden_bei(i)->get_halt(); + if( my_halt.is_bound() && !joining.is_contained(my_halt) ) { + joining.append(my_halt); + } + } + } + + // search for stops merged to + FOR(slist_tpl, const& i, halt_merged->get_tiles()) { + grund_t* const gr = i.grund; + gebaeude_t* gb = gr->find(); + if( gb ) { + gb->set_flag(obj_t::dirty); + } + + const planquadrat_t *pl2 = welt->access(gr->get_pos().get_2d()); + for( uint8 i=0; i < pl2->get_boden_count(); i++ ) { + halthandle_t my_halt = pl2->get_boden_bei(i)->get_halt(); + if( my_halt.is_bound() && !joining.is_contained(my_halt) ) { + joining.append(my_halt); + } + } + } + + // set name to name of first stop + if( !joining.empty() ) { + set_name( joining.front()->get_name()); + } + + while( !joining.empty() ) { + // join this halt with me + halthandle_t halt = joining.remove_first(); + + // now with the second stop + while( halt.is_bound() && halt!=self ) { + // add statistics + for( int month=0; monthfinancial_history[month][type]; + halt->financial_history[month][type] = 0; // to avoid counting twice + } + } + + // we always take the first remaining tile and transfer it => more safe + koord3d t = halt->get_basis_pos3d(); + grund_t *gr = welt->lookup(t); + + // transfer tiles to us + halt->rem_grund(gr); + add_grund(gr); + // and check for existence + if(!halt->existiert_in_welt()) { + // transfer goods + halt->transfer_goods(self); + + // rebuild connections of all linked halts + // otherwise these halts would lose connections and freight might get lost + // (until complete rebuild_connections task is finished) + halt->rebuild_linked_connections(); + + destroy(halt); + } + } + } + + // tell the world of it ... + if( player != public_owner && env_t::networkmode ) { + cbuffer_t buf; + buf.printf( translator::translate("%s at (%i,%i) now merged to %s at (%i,%i)."), halt_merged->get_name(), halt_merged->get_basis_pos().x, halt_merged->get_basis_pos().y, get_name(), get_basis_pos().x, get_basis_pos().y ); + welt->get_message()->add_message( buf, get_basis_pos(), message_t::ai, PLAYER_FLAG|player->get_player_nr(), IMG_EMPTY ); + } + + recalc_station_type(); +} + + void haltestelle_t::transfer_goods(halthandle_t halt) { if (!self.is_bound() || !halt.is_bound()) { @@ -2500,6 +2614,7 @@ void haltestelle_t::recalc_station_type() i.grund->set_halt( self ); } recalc_status(); + recalc_center_pos(); } @@ -2984,7 +3099,7 @@ bool haltestelle_t::add_grund(grund_t *gr, bool relink_factories) // appends this to the ground // after that, the surrounding ground will know of this station bool insert_unsorted = !relink_factories; - int const cov = welt->get_settings().get_station_coverage(); + uint16 const cov = welt->get_settings().get_station_coverage(); for (int y = -cov; y <= cov; y++) { for (int x = -cov; x <= cov; x++) { koord p=pos+koord(x,y); @@ -3119,7 +3234,7 @@ bool haltestelle_t::rem_grund(grund_t *gr) pl->get_kartenboden()->set_flag(grund_t::dirty); } - int const cov = welt->get_settings().get_station_coverage(); + uint16 const cov = welt->get_settings().get_station_coverage(); for (int y = -cov; y <= cov; y++) { for (int x = -cov; x <= cov; x++) { planquadrat_t *pl = welt->access( gr->get_pos().get_2d()+koord(x,y) ); @@ -3343,3 +3458,18 @@ void haltestelle_t::release_factory_links() } fab_list.clear(); } + +/* check if the station given is covered by this station */ +bool haltestelle_t::is_halt_covered(const halthandle_t &halt) const +{ + uint16 const cov = welt->get_settings().get_station_coverage(); + FOR(slist_tpl, const& i, halt->get_tiles()) { + if ( gebaeude_t* const gb = i.grund->find() ) { + if ( koord_distance( gb->get_pos().get_2d(), get_next_pos( gb->get_pos().get_2d() )) <= cov ) + { + return true; + } + } + } + return false; +} diff --git simhalt.h simhalt.h index a0f581630..2e06bfcb4 100644 --- simhalt.h +++ simhalt.h @@ -119,6 +119,7 @@ private: sint32 last_loading_step; koord init_pos; // for halt without grounds, created during game initialisation + koord center_pos; // the avarage of all tiles' coordinate weighed by level of the building /** * Handle for ourselves. Can be used like the 'this' pointer @@ -429,6 +430,8 @@ public: void make_public_and_join( player_t *player ); + void merge_halt( player_t *player, halthandle_t halt_merged ); + vector_tpl const& get_pax_connections() const { return all_links[goods_manager_t::INDEX_PAS].connections; } vector_tpl const& get_mail_connections() const { return all_links[goods_manager_t::INDEX_MAIL].connections; } @@ -594,7 +597,12 @@ public: koord get_init_pos() const { return init_pos; } koord get_basis_pos() const; koord3d get_basis_pos3d() const; + + koord get_center_pos() const { return center_pos; } +private: + void recalc_center_pos(); +public: /* return the closest square that belongs to this halt * @author prissi */ @@ -809,6 +817,10 @@ public: */ static void init_markers(); + /* + * check if it is in the station coverage + */ + bool is_halt_covered (const halthandle_t &halt) const; }; ENUM_BITSET(haltestelle_t::stationtyp) diff --git simmenu.cc simmenu.cc index 1a2d94a25..cc80883c6 100644 --- simmenu.cc +++ simmenu.cc @@ -120,6 +120,7 @@ tool_t *create_general_tool(int toolnr) case TOOL_CHANGE_WATER_HEIGHT: tool = new tool_change_water_height_t(); break; case TOOL_SET_CLIMATE: tool = new tool_set_climate_t(); break; case TOOL_ROTATE_BUILDING: tool = new tool_rotate_building_t(); break; + case TOOL_MERGE_STOP: tool = new tool_merge_stop_t(); break; default: dbg->error("create_general_tool()","cannot satisfy request for general_tool[%i]!",toolnr); return NULL; } diff --git simmenu.h simmenu.h index 9a6f46f95..de3a4ba92 100644 --- simmenu.h +++ simmenu.h @@ -73,6 +73,7 @@ enum { TOOL_CHANGE_WATER_HEIGHT, TOOL_SET_CLIMATE, TOOL_ROTATE_BUILDING, + TOOL_MERGE_STOP, GENERAL_TOOL_COUNT, GENERAL_TOOL = 0x1000 }; diff --git simtool.cc simtool.cc index 2ef8def6a..be17b3846 100644 --- simtool.cc +++ simtool.cc @@ -6200,7 +6200,6 @@ bool tool_daynight_level_t::init( player_t * ) { } - /* make all tiles of this player a public stop * if this player is public, make all connected tiles a public stop */ bool tool_make_stop_public_t::init( player_t * ) @@ -6370,6 +6369,93 @@ const char *tool_make_stop_public_t::work( player_t *player, koord3d p ) } +/* merge stop */ +image_id tool_merge_stop_t::get_marker_image() +{ + return cursor; +} + +uint8 tool_merge_stop_t::is_valid_pos( player_t *player, const koord3d &pos, const char *&error, const koord3d &) +{ + grund_t *bd = welt->lookup(pos); + if (bd==NULL) { + error = ""; + return 0; + } + // check halt ownership + halthandle_t h = haltestelle_t::get_halt(pos,player); + if( h.is_bound() && player != h->get_owner() ) { + error = "Das Feld gehoert\neinem anderen Spieler\n"; + return 0; + } + // check for halt on the tile + if( h.is_bound() && ( bd->is_halt() || (h->get_station_type()&haltestelle_t::dock && bd->is_water()) ) ) { + return 2; + } + error = NOTICE_UNSUITABLE_GROUND; + return 0; +} + +void tool_merge_stop_t::mark_tiles( player_t *player, const koord3d &start, const koord3d &end ) +{ + halt_be_merged_from = halthandle_t(); + halt_be_merged_to = halthandle_t(); + halt_be_merged_from = haltestelle_t::get_halt(start,player); + halt_be_merged_to = haltestelle_t::get_halt(end,player); + sint64 workcost = 0; + if ( welt->get_settings().allow_merge_distant_halt ) { + sint32 dist = (sint32)koord_distance( halt_be_merged_from->get_center_pos(), halt_be_merged_to->get_center_pos() ); + if ( !halt_be_merged_from->is_halt_covered( halt_be_merged_to ) && halt_be_merged_from->get_owner() == player && halt_be_merged_to->get_owner() == player ) { + workcost = -welt->scale_with_month_length( (2 ^ dist) * welt->get_settings().cst_multiply_merge_halt); + } + win_set_static_tooltip( tooltip_with_price("Building costs estimates", workcost) ); + } + else { + if ( halt_be_merged_from->is_halt_covered( halt_be_merged_to ) ) { + win_set_static_tooltip( tooltip_with_price("Building costs estimates", workcost) ); + } + else { + win_set_static_tooltip( "Can not merge" ); + } + } +} + +const char *tool_merge_stop_t::do_work( player_t *player, const koord3d &last_pos, const koord3d &pos) +{ + player_t *const psplayer = welt->get_public_player(); + bool const giveaway = player != psplayer; + halt_be_merged_from = halthandle_t(); + halt_be_merged_to = halthandle_t(); + halt_be_merged_from = haltestelle_t::get_halt(last_pos,player); + halt_be_merged_to = haltestelle_t::get_halt(pos,player); + sint64 workcost = 0; + if ( welt->get_settings().allow_merge_distant_halt ) { + // check funds + sint32 dist = (sint32)koord_distance( halt_be_merged_from->get_center_pos(), halt_be_merged_to->get_center_pos() ); + if ( !halt_be_merged_from->is_halt_covered( halt_be_merged_to ) ) { + workcost = -welt->scale_with_month_length( (2 ^ dist) * welt->get_settings().cst_multiply_merge_halt ); + } + if( giveaway && !player->can_afford(workcost) ) { + return NOTICE_INSUFFICIENT_FUNDS; + } + } + else { + if ( !halt_be_merged_from->is_halt_covered( halt_be_merged_to ) ) { + return "Too far stations!"; + } + } + + if( halt_be_merged_to.is_bound() && halt_be_merged_to->get_owner() == player && + halt_be_merged_from.is_bound() && halt_be_merged_from->get_owner() == player ) { + // merge stop + player_t::book_construction_costs(player, -workcost, pos.get_2d(), ignore_wt); + halt_be_merged_to->merge_halt(player, halt_be_merged_from); + return NULL; + } + + // nothing to do + return NULL; +} bool tool_show_trees_t::init( player_t * ) { diff --git simtool.h simtool.h index a9bb98707..3d4a9a054 100644 --- simtool.h +++ simtool.h @@ -613,6 +613,23 @@ public: }; +/* merge stop */ +class tool_merge_stop_t : public two_click_tool_t { +private: + halthandle_t halt_be_merged_from; + halthandle_t halt_be_merged_to; +public: + tool_merge_stop_t() : two_click_tool_t(TOOL_MERGE_STOP | GENERAL_TOOL) {} + char const* get_tooltip(player_t const*) const OVERRIDE { return translator::translate("merge stop"); } + bool is_init_network_save() const OVERRIDE { return true; } +private: + char const* do_work(player_t*, koord3d const&, koord3d const&) OVERRIDE; + void mark_tiles(player_t*, koord3d const&, koord3d const&) OVERRIDE; + uint8 is_valid_pos(player_t*, koord3d const&, char const*&, koord3d const&) OVERRIDE; + image_id get_marker_image() OVERRIDE; +}; + + // internal tool: show error message at specific coordinate // used for scenario error messages send by server class tool_error_message_t : public tool_t { diff --git simutrans/config/simuconf.tab simutrans/config/simuconf.tab index dc2252a3b..93606916e 100644 --- simutrans/config/simuconf.tab +++ simutrans/config/simuconf.tab @@ -429,6 +429,12 @@ toll_waycost_percentage = 0 #cost_depot_road=1300 #cost_depot_ship=2500 +# if allowed, you can merge station from outside of the station coverage +#allow_merge_distant_halt=1 + +# the cost to merge stations: the actual cost is (cost*2^distance) +#cost_multiply_merge_halt=700 + # other way related stuff #cost_signal=500 #cost_tunnel=10000