From 1bdc17f48fc7f5c0c54843a71bee8a0f259527f9 Mon Sep 17 00:00:00 2001 From: ceeac Date: Mon, 3 Apr 2023 08:07:45 +0200 Subject: [PATCH 1/3] Fix -Wreorder-ctor --- gui/replace_frame.cc | 8 ++++---- gui/schedule_list.cc | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/gui/replace_frame.cc b/gui/replace_frame.cc index dfc058f2826..99591942570 100644 --- a/gui/replace_frame.cc +++ b/gui/replace_frame.cc @@ -45,10 +45,10 @@ replace_frame_t::replace_frame_t(convoihandle_t cnv) : gui_frame_t("", NULL), replace_mode(only_this_convoy), depot(false), state(state_replace), replaced_so_far(0), - txt_line_replacing(&buf_line_help), current_convoi(¤t_convoi_pics), scrollx_convoi(¤t_convoi, true, false), - convoy_assembler(this) + convoy_assembler(this), + txt_line_replacing(&buf_line_help) { this->cnv = cnv; target_line=linehandle_t(); @@ -61,10 +61,10 @@ replace_frame_t::replace_frame_t(linehandle_t line) : gui_frame_t("", NULL), replace_mode(all_convoys_of_this_line), depot(false), state(state_replace), replaced_so_far(0), - txt_line_replacing(&buf_line_help), current_convoi(¤t_convoi_pics), scrollx_convoi(¤t_convoi, true, false), - convoy_assembler(this) + convoy_assembler(this), + txt_line_replacing(&buf_line_help) { target_line = line; cnv=convoihandle_t(); diff --git a/gui/schedule_list.cc b/gui/schedule_list.cc index d011cdef4bb..f7ae2b765a5 100644 --- a/gui/schedule_list.cc +++ b/gui/schedule_list.cc @@ -251,8 +251,8 @@ schedule_list_gui_t::schedule_list_gui_t(player_t *player_) : player(player_), lc_preview(0), cont_by_accommo(linehandle_t()), - cont_line_network(linehandle_t()), cont_haltlist(linehandle_t()), + cont_line_network(linehandle_t()), cont_line_capacity_by_catg(linehandle_t(), convoihandle_t()), scrolly_convois(&cont), scroll_halt_waiting(&cont_tab_haltlist, true, true), From d39537cac0af0f43e8e7195564416e3044c15560 Mon Sep 17 00:00:00 2001 From: Markus Pristovsek Date: Thu, 3 Feb 2022 11:54:07 +0000 Subject: [PATCH 2/3] (ceeac) split world terraforming git-svn-id: svn://tron.homeunix.org/simutrans/simutrans/trunk@10443 8aca7d54-2c30-db11-9de9-000461428c89 --- Makefile | 1 + bauer/hausbauer.cc | 2 +- bauer/tunnelbauer.cc | 52 ++- boden/grund.cc | 10 +- cmake/SimutransSourceList.cmake | 1 + player/ai.cc | 4 +- player/ai_goods.cc | 4 +- player/ai_passenger.cc | 2 +- simcity.cc | 2 +- simplan.cc | 2 +- simtool.cc | 14 +- simworld.cc | 674 ++---------------------------- simworld.h | 153 +------ world/terraformer.cc | 697 ++++++++++++++++++++++++++++++++ world/terraformer.h | 143 +++++++ 15 files changed, 935 insertions(+), 826 deletions(-) create mode 100644 world/terraformer.cc create mode 100644 world/terraformer.h diff --git a/Makefile b/Makefile index ea9570cf5e9..6e73a63bd10 100644 --- a/Makefile +++ b/Makefile @@ -617,6 +617,7 @@ SOURCES += vehicle/road_vehicle.cc SOURCES += vehicle/simroadtraffic.cc SOURCES += vehicle/vehicle.cc SOURCES += vehicle/water_vehicle.cc +SOURCES += world/terraformer.cc SOURCES += simunits.cc SOURCES += convoy.cc SOURCES += utils/float32e8_t.cc diff --git a/bauer/hausbauer.cc b/bauer/hausbauer.cc index e32d12ab405..d1b9cf724d1 100644 --- a/bauer/hausbauer.cc +++ b/bauer/hausbauer.cc @@ -534,7 +534,7 @@ void hausbauer_t::remove( player_t *player, const gebaeude_t *gb, bool map_gener gr->calc_image(); } } - welt->set_grid_hgt( newk, new_hgt+corner_nw(new_slope) ); + welt->set_grid_hgt_nocheck( newk, new_hgt+corner_nw(new_slope) ); } } else if (wasser_t* sea = dynamic_cast(gr)) { diff --git a/bauer/tunnelbauer.cc b/bauer/tunnelbauer.cc index 733f48f51dc..d9665046d5a 100644 --- a/bauer/tunnelbauer.cc +++ b/bauer/tunnelbauer.cc @@ -36,6 +36,7 @@ #include "wegbauer.h" #include "../tpl/stringhashtable_tpl.h" #include "../tpl/vector_tpl.h" +#include "../world/terraformer.h" karte_ptr_t tunnel_builder_t::welt; @@ -167,16 +168,15 @@ koord3d tunnel_builder_t::find_end_pos(player_t *player, koord3d pos, koord zv, sint8 hse = pos.z + corner_se(new_slope); sint8 hne = pos.z + corner_ne(new_slope); sint8 hnw = pos.z + corner_nw(new_slope); - karte_t::terraformer_t raise(welt); - raise.add_raise_node(pos.x, pos.y, hsw, hse, hne, hnw); - raise.iterate(true); - if (raise.can_raise_all(player, player->is_public_service())) { - // returned true therefore error reported - return koord3d::invalid; - } + + terraformer_t raise(terraformer_t::raise, welt); + raise.add_node(pos.x, pos.y, hsw, hse, hne, hnw); + raise.generate_affected_tile_list(); + // if we can adjust height here we can build an entrance so don't need checks below return pos; } + if( gr->get_hoehe() < pos.z ){ return koord3d::invalid; } @@ -226,15 +226,21 @@ koord3d tunnel_builder_t::find_end_pos(player_t *player, koord3d pos, koord zv, sint8 hse = pos.z + corner_se(new_slope); sint8 hne = pos.z + corner_ne(new_slope); sint8 hnw = pos.z + corner_nw(new_slope); - karte_t::terraformer_t raise(welt), lower(welt); - raise.add_raise_node(pos.x, pos.y, hsw, hse, hne, hnw); - raise.iterate(false); - lower.add_lower_node(pos.x, pos.y, hsw, hse, hne, hnw); - lower.iterate(false); - if (!player || (raise.can_lower_all(player, player->is_public_service()) || lower.can_lower_all(player, player->is_public_service()))) { - // returned true therefore error reported + + terraformer_t raise(terraformer_t::raise, welt); + terraformer_t lower(terraformer_t::lower, welt); + + raise.add_node(pos.x, pos.y, hsw, hse, hne, hnw); + lower.add_node(pos.x, pos.y, hsw, hse, hne, hnw); + + raise.generate_affected_tile_list(); + lower.generate_affected_tile_list(); + + if (!player || raise.can_raise_all(player, player->is_public_service())!= NULL || lower.can_lower_all(player, player->is_public_service())!=NULL) { + // returned non-null therefore error reported return koord3d::invalid; } + // if we can adjust height here we can build an entrance so don't need checks below return pos; } @@ -397,18 +403,20 @@ const char *tunnel_builder_t::build( player_t *player, koord pos, const tunnel_d int n = 0; - karte_t::terraformer_t raise(welt),lower(welt); - raise.add_raise_node(end.x, end.y, hsw, hse, hne, hnw); - lower.add_lower_node(end.x, end.y, hsw, hse, hne, hnw); - raise.iterate(true); - lower.iterate(false); - err = raise.can_raise_all(player, player ? player->is_public_service() : false); - if (!err) err = lower.can_lower_all(player, player->is_public_service()); + terraformer_t raise(terraformer_t::raise, welt); + terraformer_t lower(terraformer_t::lower, welt); + + raise.add_node(end.x, end.y, hsw, hse, hne, hnw); + lower.add_node(end.x, end.y, hsw, hse, hne, hnw); + + raise.generate_affected_tile_list(); + lower.generate_affected_tile_list(); + if (err) return 0; // TODO: this is rather hackish as 4 seems to come from nowhere but works most of the time // feel free to change if you have a better idea! - n = (raise.raise_all()+lower.lower_all())/4; + n = (raise.apply() + lower.apply()) / 4; player_t::book_construction_costs(player, welt->get_settings().cst_alter_land * n, end.get_2d(), desc->get_waytype()); } diff --git a/boden/grund.cc b/boden/grund.cc index 6cc0d0ad509..61582580380 100644 --- a/boden/grund.cc +++ b/boden/grund.cc @@ -282,7 +282,7 @@ void grund_t::rdwr(loadsave_t *file) else { z_southeast += corner_se(slope); } - welt->set_grid_hgt( k + koord(1,1), z_southeast ); + welt->set_grid_hgt_nocheck( k + koord(1,1), z_southeast ); } if( pos.x == welt->get_size().x-1 ) { sint8 z_east = z; @@ -292,7 +292,7 @@ void grund_t::rdwr(loadsave_t *file) else { z_east += corner_ne(slope); } - welt->set_grid_hgt( k + koord(1,0), z_east ); + welt->set_grid_hgt_nocheck( k + koord(1,0), z_east ); } if( pos.y == welt->get_size().y-1 ) { sint8 z_south = z; @@ -302,7 +302,7 @@ void grund_t::rdwr(loadsave_t *file) else { z_south += corner_sw(slope); } - welt->set_grid_hgt( k + koord(0,1), z_south ); + welt->set_grid_hgt_nocheck( k + koord(0,1), z_south ); } if( get_typ() == grund_t::wasser && z > z_w ) { @@ -311,8 +311,8 @@ void grund_t::rdwr(loadsave_t *file) else { z += corner_nw(slope); } - welt->set_grid_hgt( k, z ); - welt->set_water_hgt( k, z_w ); + welt->set_grid_hgt_nocheck( k, z ); + welt->set_water_hgt_nocheck( k, z_w ); } // loading ways from here on diff --git a/cmake/SimutransSourceList.cmake b/cmake/SimutransSourceList.cmake index bca01918360..1d3a99b77b0 100644 --- a/cmake/SimutransSourceList.cmake +++ b/cmake/SimutransSourceList.cmake @@ -369,4 +369,5 @@ target_sources(simutrans-extended PRIVATE vehicle/rail_vehicle.cc vehicle/road_vehicle.cc vehicle/water_vehicle.cc + world/terraformer.cc ) diff --git a/player/ai.cc b/player/ai.cc index 958a460265f..6673c294bc7 100644 --- a/player/ai.cc +++ b/player/ai.cc @@ -491,13 +491,13 @@ bool ai_t::create_simple_road_transport(koord platz1, koord size1, koord platz2, // ensure is land grund_t* bd = welt->lookup_kartenboden(platz1); if (bd->get_typ() == grund_t::wasser) { - welt->set_water_hgt(platz1, bd->get_hoehe()-1); + welt->set_water_hgt_nocheck(platz1, bd->get_hoehe()-1); welt->access(platz1)->correct_water(); welt->set_climate(platz1, c1, true); } bd = welt->lookup_kartenboden(platz2); if (bd->get_typ() == grund_t::wasser) { - welt->set_water_hgt(platz2, bd->get_hoehe()-1); + welt->set_water_hgt_nocheck(platz2, bd->get_hoehe()-1); welt->access(platz2)->correct_water(); welt->set_climate(platz2, c2, true); } diff --git a/player/ai_goods.cc b/player/ai_goods.cc index 8d10942f00b..448532a5efb 100644 --- a/player/ai_goods.cc +++ b/player/ai_goods.cc @@ -659,7 +659,7 @@ bool ai_goods_t::create_simple_rail_transport() // ensure is land grund_t* bd = welt->lookup_kartenboden(k); if (bd->get_typ() == grund_t::wasser) { - welt->set_water_hgt(k, bd->get_hoehe()-1); + welt->set_water_hgt_nocheck(k, bd->get_hoehe()-1); welt->access(k)->correct_water(); welt->set_climate(k, c, true); } @@ -679,7 +679,7 @@ bool ai_goods_t::create_simple_rail_transport() // ensure is land grund_t* bd = welt->lookup_kartenboden(k); if (bd->get_typ() == grund_t::wasser) { - welt->set_water_hgt(k, bd->get_hoehe()-1); + welt->set_water_hgt_nocheck(k, bd->get_hoehe()-1); welt->access(k)->correct_water(); welt->set_climate(k, c, true); } diff --git a/player/ai_passenger.cc b/player/ai_passenger.cc index 213412e6a2b..965589ff34a 100644 --- a/player/ai_passenger.cc +++ b/player/ai_passenger.cc @@ -493,7 +493,7 @@ halthandle_t ai_passenger_t::build_airport(const stadt_t* city, koord pos, int r // ensure is land grund_t* bd = welt->lookup_kartenboden(pos+koord(j,i)); if (bd->get_typ() == grund_t::wasser) { - welt->set_water_hgt(pos+koord(j,i), bd->get_hoehe()-1); + welt->set_water_hgt_nocheck(pos+koord(j,i), bd->get_hoehe()-1); welt->access(pos+koord(j,i))->correct_water(); welt->set_climate(pos+koord(j,i), c, true); } diff --git a/simcity.cc b/simcity.cc index d670e57f736..260feb1d55c 100644 --- a/simcity.cc +++ b/simcity.cc @@ -5160,7 +5160,7 @@ bool stadt_t::build_road(const koord k, player_t* player_, bool forced, bool map // kartenboden may have changed - also ensure is land bd = welt->lookup_kartenboden(k); if (bd->get_typ() == grund_t::wasser) { - welt->set_water_hgt(k, bd->get_hoehe()-1); + welt->set_water_hgt_nocheck(k, bd->get_hoehe()-1); welt->access(k)->correct_water(); welt->set_climate(k, c, true); bd = welt->lookup_kartenboden(k); diff --git a/simplan.cc b/simplan.cc index 9a93ee31a53..72241bd2a53 100644 --- a/simplan.cc +++ b/simplan.cc @@ -314,7 +314,7 @@ void planquadrat_t::rdwr(loadsave_t *file, koord pos ) else { // other ground must not reset the height boden_hinzufuegen(gr); - welt->set_grid_hgt( pos, hgt ); + welt->set_grid_hgt_nocheck( pos, hgt ); } } } while(gr != NULL); diff --git a/simtool.cc b/simtool.cc index 40484aad598..691ab004821 100644 --- a/simtool.cc +++ b/simtool.cc @@ -1674,7 +1674,7 @@ const char *tool_setslope_t::tool_set_slope_work( player_t *player, koord3d pos, // do not lower tiles when it will be below water level return NOTICE_TILE_FULL; } - welt->set_water_hgt( k, water_table ); + welt->set_water_hgt_nocheck( k, water_table ); water_hgt = water_table; } else if( new_slope == ALL_UP_SLOPE ) { @@ -1770,7 +1770,7 @@ const char *tool_setslope_t::tool_set_slope_work( player_t *player, koord3d pos, gr1->obj_loesche_alle(player); welt->access(k)->kartenboden_setzen( new boden_t(new_pos,new_slope) ); gr1 = welt->lookup_kartenboden(k); - welt->set_water_hgt(k, welt->get_groundwater()-4); + welt->set_water_hgt_nocheck(k, welt->get_groundwater()-4); } else { gr1->set_grund_hang(new_slope); @@ -1832,10 +1832,10 @@ const char *tool_setslope_t::tool_set_slope_work( player_t *player, koord3d pos, // correct the grid height if( gr1->is_water() ) { sint8 grid_hgt = min( water_hgt, welt->lookup_hgt( k ) ); - welt->set_grid_hgt(k, grid_hgt ); + welt->set_grid_hgt_nocheck(k, grid_hgt ); } else { - welt->set_grid_hgt(k, gr1->get_hoehe()+ corner_nw(gr1->get_grund_hang()) ); + welt->set_grid_hgt_nocheck(k, gr1->get_hoehe()+ corner_nw(gr1->get_grund_hang()) ); } minimap_t::get_instance()->calc_map_pixel(k); @@ -2350,7 +2350,7 @@ const char *tool_set_climate_t::do_work( player_t *player, const koord3d &start, } if( ok ) { gr->obj_loesche_alle( NULL ); - welt->set_water_hgt( k, hgt - 1 ); + welt->set_water_hgt_nocheck( k, hgt - 1 ); welt->access(k)->correct_water(); } } @@ -2370,7 +2370,7 @@ const char *tool_set_climate_t::do_work( player_t *player, const koord3d &start, } if( ok ) { gr->obj_loesche_alle( NULL ); - welt->set_water_hgt( k, gr->get_pos().z ); + welt->set_water_hgt_nocheck( k, gr->get_pos().z ); welt->access(k)->correct_water(); welt->set_climate( k, water_climate, true ); minimap_t::get_instance()->calc_map_pixel( k ); @@ -2596,7 +2596,7 @@ const char *tool_change_water_height_t::work( player_t *, koord3d pos ) 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); gr2->set_grund_hang( sneu ); - welt->set_water_hgt(x, y, new_water_height ); + welt->set_water_hgt_nocheck(x, y, new_water_height ); welt->access(x, y)->correct_water(); welt->calc_climate( koord( x, y ), true ); } diff --git a/simworld.cc b/simworld.cc index 1208982c9c5..fd3d5ff9329 100644 --- a/simworld.cc +++ b/simworld.cc @@ -118,9 +118,11 @@ #include "player/ai_passenger.h" #include "player/ai_goods.h" +#include "world/terraformer.h" #include "io/rdwr/adler32_stream.h" #include "dataobj/tabfile.h" // For reload of simuconf.tab to override savegames + #include "pathes.h" @@ -355,7 +357,7 @@ void karte_t::perlin_hoehe_loop( sint16 x_min, sint16 x_max, sint16 y_min, sint1 // loop all tiles koord k(x,y); sint16 const h = perlin_hoehe(&settings, k, koord(0, 0)); - set_grid_hgt( k, (sint8) h); + set_grid_hgt_nocheck( k, (sint8) h); } } } @@ -438,7 +440,7 @@ void karte_t::cleanup_grounds_loop( sint16 x_min, sint16 x_max, sint16 y_min, si } if( max_hgt_nocheck(k) > water_hgt ) { - set_water_hgt(k, groundwater-4); + set_water_hgt_nocheck(k, groundwater-4); } } } @@ -2570,7 +2572,7 @@ void karte_t::create_beaches( int xoff, int yoff ) // if not much nearby water then turn into a beach if( neighbour_water < 4 ) { - set_water_hgt( k, gr->get_hoehe() - 1 ); + set_water_hgt_nocheck( k, gr->get_hoehe() - 1 ); raise_grid_to( ix, iy, gr->get_hoehe() ); raise_grid_to( ix + 1, iy, gr->get_hoehe() ); raise_grid_to( ix, iy + 1, gr->get_hoehe() ); @@ -2771,7 +2773,7 @@ void karte_t::enlarge_map(settings_t const* sets, sint8 const* const h_field) for( sint16 x = (y>old_y) ? 0 : old_x+1; x<=new_size_x; x++ ) { koord k(x,y); sint16 const h = perlin_hoehe(&settings, k, koord(old_x, old_y)); - set_grid_hgt( k, (sint8) h); + set_grid_hgt_nocheck( k, (sint8) h); } ls.set_progress( (y*16)/new_size_y ); } @@ -3241,80 +3243,6 @@ void karte_t::set_scale() } -const char* karte_t::can_lower_plan_to(const player_t *player, sint16 x, sint16 y, sint8 h) const -{ - const planquadrat_t *plan = access(x,y); - - if( plan==NULL ) { - return ""; - } - - if( h < groundwater - 3 ) { - return ""; - } - - const sint8 hmax = plan->get_kartenboden()->get_hoehe(); - if( (hmax == h || hmax == h - 1) && (plan->get_kartenboden()->get_grund_hang() == 0 || is_plan_height_changeable( x, y )) ) { - return NULL; - } - - if( !is_plan_height_changeable(x, y) ) { - return ""; - } - - // tunnel slope below? - grund_t *gr = plan->get_boden_in_hoehe( h - 1 ); - if( !gr ) { - gr = plan->get_boden_in_hoehe( h - 2 ); - } - if( !gr && settings.get_way_height_clearance()==2 ) { - gr = plan->get_boden_in_hoehe( h - 3 ); - } - if (gr && h < gr->get_pos().z + slope_t::max_diff(gr->get_weg_hang()) + settings.get_way_height_clearance()) { - return ""; - } - - // tunnel below? - while(h < hmax) { - if(plan->get_boden_in_hoehe(h)) { - return ""; - } - h ++; - } - - // check allowance by scenario - if (get_scenario()->is_scripted()) { - return get_scenario()->is_work_allowed_here(player, TOOL_LOWER_LAND|GENERAL_TOOL, ignore_wt, plan->get_kartenboden()->get_pos()); - } - - return NULL; -} - - -const char* karte_t::can_raise_plan_to(const player_t *player, sint16 x, sint16 y, sint8 h) const -{ - const planquadrat_t *plan = access(x,y); - if( plan == 0 || !is_plan_height_changeable(x, y) ) { - return ""; - } - - // irgendwo eine Bruecke im Weg? - int hmin = plan->get_kartenboden()->get_hoehe(); - while(h > hmin) { - if(plan->get_boden_in_hoehe(h)) { - return ""; - } - h --; - } - - // check allowance by scenario - if (get_scenario()->is_scripted()) { - return get_scenario()->is_work_allowed_here(player, TOOL_RAISE_LAND|GENERAL_TOOL, ignore_wt, plan->get_kartenboden()->get_pos()); - } - - return NULL; -} - bool karte_t::is_plan_height_changeable(sint16 x, sint16 y) const { @@ -3343,286 +3271,6 @@ bool karte_t::is_plan_height_changeable(sint16 x, sint16 y) const } -bool karte_t::terraformer_t::node_t::comp(const karte_t::terraformer_t::node_t& a, const karte_t::terraformer_t::node_t& b) -{ - int diff = a.x- b.x; - if (diff == 0) { - diff = a.y - b.y; - } - return diff<0; -} - - -void karte_t::terraformer_t::add_node(bool raise, sint16 x, sint16 y, sint8 hsw, sint8 hse, sint8 hne, sint8 hnw) -{ - if (!welt->is_within_limits(x,y)) { - return; - } - node_t test(x, y, hsw, hse, hne, hnw, actual_flag^3); - node_t *other = list.insert_unique_ordered(test, node_t::comp); - - sint8 factor = raise ? +1 : -1; - - if (other) { - for(int i=0; i<4; i++) { - if (factor*other->h[i] < factor*test.h[i]) { - other->h[i] = test.h[i]; - other->changed |= actual_flag^3; - ready = false; - } - } - } - else { - ready = false; - } -} - -void karte_t::terraformer_t::add_raise_node(sint16 x, sint16 y, sint8 hsw, sint8 hse, sint8 hne, sint8 hnw) -{ - add_node(true, x, y, hsw, hse, hne, hnw); -} - -void karte_t::terraformer_t::add_lower_node(sint16 x, sint16 y, sint8 hsw, sint8 hse, sint8 hne, sint8 hnw) -{ - add_node(false, x, y, hsw, hse, hne, hnw); -} - -void karte_t::terraformer_t::iterate(bool raise) -{ - while( !ready) { - actual_flag ^= 3; // flip bits - // clear new_flag bit - FOR(vector_tpl, &i, list) { - i.changed &= actual_flag; - } - // process nodes with actual_flag set - ready = true; - for(uint32 j=0; j < list.get_count(); j++) { - node_t& i = list[j]; - if (i.changed & actual_flag) { - i.changed &= ~ actual_flag; - if (raise) { - welt->prepare_raise(*this, i.x, i.y, i.h[0], i.h[1], i.h[2], i.h[3]); - } - else { - welt->prepare_lower(*this, i.x, i.y, i.h[0], i.h[1], i.h[2], i.h[3]); - } - } - } - } -} - - -const char* karte_t::terraformer_t::can_raise_all(const player_t *player, bool allow_deep_water, bool keep_water) const -{ - const char* err = NULL; - FOR(vector_tpl, const &i, list) { - err = welt->can_raise_to(player, i.x, i.y, keep_water, allow_deep_water, i.h[0], i.h[1], i.h[2], i.h[3]); - if (err) return err; - } - return NULL; -} - -const char* karte_t::terraformer_t::can_lower_all(const player_t *player, bool allow_deep_water) const -{ - const char* err = NULL; - FOR(vector_tpl, const &i, list) { - err = welt->can_lower_to(player, i.x, i.y, i.h[0], i.h[1], i.h[2], i.h[3], allow_deep_water); - if (err) { - return err; - } - } - return NULL; -} - -int karte_t::terraformer_t::raise_all() -{ - int n=0; - FOR(vector_tpl, &i, list) { - n += welt->raise_to(i.x, i.y, i.h[0], i.h[1], i.h[2], i.h[3]); - } - return n; -} - -int karte_t::terraformer_t::lower_all() -{ - int n=0; - FOR(vector_tpl, &i, list) { - n += welt->lower_to(i.x, i.y, i.h[0], i.h[1], i.h[2], i.h[3]); - } - return n; -} - - -const char* karte_t::can_raise_to(const player_t *player, sint16 x, sint16 y, bool keep_water, bool allow_deep_water, sint8 hsw, sint8 hse, sint8 hne, sint8 hnw) const -{ - assert(is_within_limits(x,y)); - grund_t *gr = lookup_kartenboden_nocheck(x,y); - const sint8 water_hgt = get_water_hgt_nocheck(x,y); - - const sint8 max_hgt = max(max(hsw,hse),max(hne,hnw)); - const sint8 min_hgt = min(min(hsw,hse),min(hne,hnw)); - - if( gr->is_water() && keep_water && max_hgt > water_hgt ) { - return ""; - } - - if(gr->is_water() && min_hgt < groundwater && !allow_deep_water) - { - return "Cannot terraform in deep water"; - } - - const char* err = can_raise_plan_to(player, x, y, max_hgt); - - return err; -} - - -void karte_t::prepare_raise(terraformer_t& digger, sint16 x, sint16 y, sint8 hsw, sint8 hse, sint8 hne, sint8 hnw) -{ - assert(is_within_limits(x,y)); - grund_t *gr = lookup_kartenboden_nocheck(x,y); - const sint8 water_hgt = get_water_hgt_nocheck(x,y); - const sint8 h0 = gr->get_hoehe(); - // old height - const sint8 h0_sw = gr->is_water() ? min(water_hgt, lookup_hgt_nocheck(x,y+1) ) : h0 + corner_sw( gr->get_grund_hang() ); - const sint8 h0_se = gr->is_water() ? min(water_hgt, lookup_hgt_nocheck(x+1,y+1) ) : h0 + corner_se( gr->get_grund_hang() ); - const sint8 h0_ne = gr->is_water() ? min(water_hgt, lookup_hgt_nocheck(x+1,y) ) : h0 + corner_ne( gr->get_grund_hang() ); - const sint8 h0_nw = gr->is_water() ? min(water_hgt, lookup_hgt_nocheck(x,y) ) : h0 + corner_nw( gr->get_grund_hang() ); - - // new height - const sint8 hn_sw = max(hsw, h0_sw); - const sint8 hn_se = max(hse, h0_se); - const sint8 hn_ne = max(hne, h0_ne); - const sint8 hn_nw = max(hnw, h0_nw); - // nothing to do? - if( !gr->is_water() && h0_sw >= hsw && h0_se >= hse && h0_ne >= hne && h0_nw >= hnw ) { - return; - } - // calc new height and slope - const sint8 hneu = min( min( hn_sw, hn_se ), min( hn_ne, hn_nw ) ); - const sint8 hmaxneu = max( max( hn_sw, hn_se ), max( hn_ne, hn_nw ) ); - - const uint8 max_hdiff = ground_desc_t::double_grounds ? 2 : 1; - - bool ok = (hmaxneu - hneu <= max_hdiff); // may fail on water tiles since lookup_hgt might be modified from previous raise_to calls - if( !ok && !gr->is_water() ) { - assert(false); - } - - // sw - if (h0_sw < hsw) { - digger.add_raise_node( x - 1, y + 1, hsw - max_hdiff, hsw - max_hdiff, hsw, hsw - max_hdiff ); - } - // s - if (h0_sw < hsw || h0_se < hse) { - const sint8 hs = max( hse, hsw ) - max_hdiff; - digger.add_raise_node( x, y + 1, hs, hs, hse, hsw ); - } - // se - if (h0_se < hse) { - digger.add_raise_node( x + 1, y + 1, hse - max_hdiff, hse - max_hdiff, hse - max_hdiff, hse ); - } - // e - if (h0_se < hse || h0_ne < hne) { - const sint8 he = max( hse, hne ) - max_hdiff; - digger.add_raise_node( x + 1, y, hse, he, he, hne ); - } - // ne - if (h0_ne < hne) { - digger.add_raise_node( x + 1,y - 1, hne, hne - max_hdiff, hne - max_hdiff, hne - max_hdiff ); - } - // n - if (h0_nw < hnw || h0_ne < hne) { - const sint8 hn = max( hnw, hne ) - max_hdiff; - digger.add_raise_node( x, y - 1, hnw, hne, hn, hn ); - } - // nw - if (h0_nw < hnw) { - digger.add_raise_node( x - 1, y - 1, hnw - max_hdiff, hnw, hnw - max_hdiff, hnw - max_hdiff ); - } - // w - if (h0_sw < hsw || h0_nw < hnw) { - const sint8 hw = max( hnw, hsw ) - max_hdiff; - digger.add_raise_node( x - 1, y, hw, hsw, hnw, hw ); - } -} - - -int karte_t::raise_to(sint16 x, sint16 y, sint8 hsw, sint8 hse, sint8 hne, sint8 hnw) -{ - int n=0; - assert(is_within_limits(x,y)); - grund_t *gr = lookup_kartenboden_nocheck(x,y); - const sint8 water_hgt = get_water_hgt_nocheck(x,y); - const sint8 h0 = gr->get_hoehe(); - // old height - const sint8 h0_sw = gr->is_water() ? min(water_hgt, lookup_hgt_nocheck(x,y+1) ) : h0 + corner_sw( gr->get_grund_hang() ); - const sint8 h0_se = gr->is_water() ? min(water_hgt, lookup_hgt_nocheck(x+1,y+1) ) : h0 + corner_se( gr->get_grund_hang() ); - const sint8 h0_ne = gr->is_water() ? min(water_hgt, lookup_hgt_nocheck(x+1,y) ) : h0 + corner_ne( gr->get_grund_hang() ); - const sint8 h0_nw = gr->is_water() ? min(water_hgt, lookup_hgt_nocheck(x,y) ) : h0 + corner_nw( gr->get_grund_hang() ); - - // new height - const sint8 hn_sw = max(hsw, h0_sw); - const sint8 hn_se = max(hse, h0_se); - const sint8 hn_ne = max(hne, h0_ne); - const sint8 hn_nw = max(hnw, h0_nw); - // nothing to do? - if( !gr->is_water() && h0_sw >= hsw && h0_se >= hse && h0_ne >= hne && h0_nw >= hnw ) { - return 0; - } - // calc new height and slope - const sint8 hneu = min( min( hn_sw, hn_se ), min( hn_ne, hn_nw ) ); - const sint8 hmaxneu = max( max( hn_sw, hn_se ), max( hn_ne, hn_nw ) ); - - const uint8 max_hdiff = ground_desc_t::double_grounds ? 2 : 1; - const sint8 disp_hneu = max( hneu, water_hgt ); - const sint8 disp_hn_sw = max( hn_sw, water_hgt ); - const sint8 disp_hn_se = max( hn_se, water_hgt ); - const sint8 disp_hn_ne = max( hn_ne, water_hgt ); - const sint8 disp_hn_nw = max( hn_nw, water_hgt ); - const slope_t::type sneu = encode_corners(disp_hn_sw - disp_hneu, disp_hn_se - disp_hneu, disp_hn_ne - disp_hneu, disp_hn_nw - disp_hneu); - - bool ok = (hmaxneu - hneu <= max_hdiff); // may fail on water tiles since lookup_hgt might be modified from previous raise_to calls - if (!ok && !gr->is_water()) { - assert(false); - } - // change height and slope, for water tiles only if they will become land - if( !gr->is_water() || (hmaxneu > water_hgt || (hneu == water_hgt && hmaxneu == water_hgt) ) ) { - gr->set_pos( koord3d( x, y, disp_hneu ) ); - gr->set_grund_hang( sneu ); - access_nocheck(x,y)->angehoben(); - set_water_hgt(x, y, groundwater-4); - } - - // update north point in grid - set_grid_hgt(x, y, hn_nw); - calc_climate(koord(x,y), true); - if ( x == cached_size.x ) { - // update eastern grid coordinates too if we are in the edge. - set_grid_hgt(x+1, y, hn_ne); - set_grid_hgt(x+1, y+1, hn_se); - } - if ( y == cached_size.y ) { - // update southern grid coordinates too if we are in the edge. - set_grid_hgt(x, y+1, hn_sw); - set_grid_hgt(x+1, y+1, hn_se); - } - - n += hn_sw - h0_sw + hn_se - h0_se + hn_ne - h0_ne + hn_nw - h0_nw; - - lookup_kartenboden_nocheck(x,y)->calc_image(); - if ( (x+1) < cached_size.x ) { - lookup_kartenboden_nocheck(x+1,y)->calc_image(); - } - if ( (y+1) < cached_size.y ) { - lookup_kartenboden_nocheck(x,y+1)->calc_image(); - } - - return n; -} - - // raise height in the hgt-array void karte_t::raise_grid_to(sint16 x, sint16 y, sint8 h) { @@ -3675,16 +3323,15 @@ int karte_t::grid_raise(const player_t *player, koord k, bool allow_deep_water, hsw = hse = hne = hnw = hgt; } - terraformer_t digger(this); - digger.add_raise_node(x, y, hsw, hse, hne, hnw); - digger.iterate(true); + terraformer_t digger(terraformer_t::raise, this); + digger.add_node(x, y, hsw, hse, hne, hnw); + digger.generate_affected_tile_list(); err = digger.can_raise_all(player, allow_deep_water); if (err) { return 0; } - - n = digger.raise_all(); + n = digger.apply(); // force world full redraw, or background could be dirty. set_dirty(); @@ -3697,276 +3344,6 @@ int karte_t::grid_raise(const player_t *player, koord k, bool allow_deep_water, } -void karte_t::prepare_lower(terraformer_t& digger, sint16 x, sint16 y, sint8 hsw, sint8 hse, sint8 hne, sint8 hnw) -{ - assert(is_within_limits(x,y)); - grund_t *gr = lookup_kartenboden_nocheck(x,y); - const sint8 water_hgt = get_water_hgt_nocheck(x,y); - const sint8 h0 = gr->get_hoehe(); - // which corners have to be raised? - const sint8 h0_sw = gr->is_water() ? min( water_hgt, lookup_hgt_nocheck(x,y+1) ) : h0 + corner_sw( gr->get_grund_hang() ); - const sint8 h0_se = gr->is_water() ? min( water_hgt, lookup_hgt_nocheck(x+1,y+1) ) : h0 + corner_se( gr->get_grund_hang() ); - const sint8 h0_ne = gr->is_water() ? min( water_hgt, lookup_hgt_nocheck(x,y+1) ) : h0 + corner_ne( gr->get_grund_hang() ); - const sint8 h0_nw = gr->is_water() ? min( water_hgt, lookup_hgt_nocheck(x,y) ) : h0 + corner_nw( gr->get_grund_hang() ); - - const uint8 max_hdiff = ground_desc_t::double_grounds ? 2 : 1; - - // sw - if (h0_sw > hsw) { - digger.add_lower_node(x - 1, y + 1, hsw + max_hdiff, hsw + max_hdiff, hsw, hsw + max_hdiff); - } - // s - if (h0_se > hse || h0_sw > hsw) { - const sint8 hs = min( hse, hsw ) + max_hdiff; - digger.add_lower_node( x, y + 1, hs, hs, hse, hsw); - } - // se - if (h0_se > hse) { - digger.add_lower_node( x + 1, y + 1, hse + max_hdiff, hse + max_hdiff, hse + max_hdiff, hse); - } - // e - if (h0_se > hse || h0_ne > hne) { - const sint8 he = max( hse, hne ) + max_hdiff; - digger.add_lower_node( x + 1,y, hse, he, he, hne); - } - // ne - if (h0_ne > hne) { - digger.add_lower_node( x + 1, y - 1, hne, hne + max_hdiff, hne + max_hdiff, hne + max_hdiff); - } - // n - if (h0_nw > hnw || h0_ne > hne) { - const sint8 hn = min( hnw, hne ) + max_hdiff; - digger.add_lower_node( x, y - 1, hnw, hne, hn, hn); - } - // nw - if (h0_nw > hnw) { - digger.add_lower_node( x - 1, y - 1, hnw + max_hdiff, hnw, hnw + max_hdiff, hnw + max_hdiff); - } - // w - if (h0_nw > hnw || h0_sw > hsw) { - const sint8 hw = min( hnw, hsw ) + max_hdiff; - digger.add_lower_node( x - 1, y, hw, hsw, hnw, hw); - } -} - -// lower plan -// new heights for each corner given -// only test corners in ctest to avoid infinite loops -const char* karte_t::can_lower_to(const player_t* player, sint16 x, sint16 y, sint8 hsw, sint8 hse, sint8 hne, sint8 hnw, bool allow_deep_water) const -{ - assert(is_within_limits(x,y)); - - const sint8 min_hgt = min(min(hsw,hse),min(hne,hnw)); - - const sint8 hneu = min( min( hsw, hse ), min( hne, hnw ) ); - - if( hneu < get_minimumheight() ) { - return "Maximum tile height difference reached."; - } - - // water heights - // check if need to lower water height for higher neighbouring tiles - for( sint16 i = 0 ; i < 8 ; i++ ) { - const koord neighbour = koord( x, y ) + koord::neighbours[i]; - if( is_within_limits(neighbour) && get_water_hgt_nocheck(neighbour) > hneu ) { - if (!is_plan_height_changeable( neighbour.x, neighbour.y )) { - return ""; - } - } - } - - if(min_hgt < groundwater && !allow_deep_water) - { - return "Cannot terraform in deep water"; - } - - return can_lower_plan_to(player, x, y, hneu ); -} - - -int karte_t::lower_to(sint16 x, sint16 y, sint8 hsw, sint8 hse, sint8 hne, sint8 hnw) -{ - int n=0; - assert(is_within_limits(x,y)); - grund_t *gr = lookup_kartenboden_nocheck(x,y); - const uint8 old_slope = gr->get_grund_hang(); - sint8 water_hgt = get_water_hgt_nocheck(x,y); - const sint8 h0 = gr->get_hoehe(); - // old height - const sint8 h0_sw = gr->is_water() ? min( water_hgt, lookup_hgt_nocheck(x,y+1) ) : h0 + corner_sw( gr->get_grund_hang() ); - const sint8 h0_se = gr->is_water() ? min( water_hgt, lookup_hgt_nocheck(x+1,y+1) ) : h0 + corner_se( gr->get_grund_hang() ); - const sint8 h0_ne = gr->is_water() ? min( water_hgt, lookup_hgt_nocheck(x+1,y) ) : h0 + corner_ne( gr->get_grund_hang() ); - const sint8 h0_nw = gr->is_water() ? min( water_hgt, lookup_hgt_nocheck(x,y) ) : h0 + corner_nw( gr->get_grund_hang() ); - // new height - const sint8 hn_sw = min(hsw, h0_sw); - const sint8 hn_se = min(hse, h0_se); - const sint8 hn_ne = min(hne, h0_ne); - const sint8 hn_nw = min(hnw, h0_nw); - // nothing to do? - if( gr->is_water() ) { - if( h0_nw <= hnw ) { - return 0; - } - } - else { - if( h0_sw <= hsw && h0_se <= hse && h0_ne <= hne && h0_nw <= hnw ) { - return 0; - } - } - - // calc new height and slope - const sint8 hneu = min( min( hn_sw, hn_se ), min( hn_ne, hn_nw ) ); - const sint8 hmaxneu = max( max( hn_sw, hn_se ), max( hn_ne, hn_nw ) ); - - // only slope could have been shores - if( old_slope ) { - // if there are any shore corners, then the new tile must be all water since it is lowered - const bool make_water = - get_water_hgt_nocheck(x,y) <= lookup_hgt_nocheck(x,y) || - get_water_hgt_nocheck(x+1,y) <= lookup_hgt_nocheck(x+1,y) || - get_water_hgt_nocheck(x,y+1) <= lookup_hgt_nocheck(x,y+1) || - get_water_hgt_nocheck(x+1,y+1) <= lookup_hgt_nocheck(x+1,y+1); - if( make_water ) { - sint8 water_table = water_hgt >= hneu ? water_hgt : hneu; - set_water_hgt(x, y, water_table ); - } - } - - if( hneu >= water_hgt ) { - // calculate water table from surrounding tiles - start off with height on this tile - sint8 water_table = water_hgt >= h0 ? water_hgt : groundwater - 4; - - /* we test each corner in turn to see whether it is at the base height of the tile - if it is we then mark the 3 surrounding tiles for that corner for checking - surrounding tiles are indicated by bits going anti-clockwise from - (binary) 00000001 for north-west through to (binary) 10000000 for north - as this is the order of directions used by koord::neighbours[] */ - - uint8 neighbour_flags = 0; - - if( hn_nw == hneu ) { - neighbour_flags |= 0x83; - } - if( hn_ne == hneu ) { - neighbour_flags |= 0xe0; - } - if( hn_se == hneu ) { - neighbour_flags |= 0x38; - } - if( hn_sw == hneu ) { - neighbour_flags |= 0x0e; - } - - for( sint16 i = 0; i < 8 ; i++ ) { - const koord neighbour = koord( x, y ) + koord::neighbours[i]; - - // here we look at the bit in neighbour_flags for this direction - // we shift it i bits to the right and test the least significant bit - - if( is_within_limits( neighbour ) && ((neighbour_flags >> i) & 1) ) { - grund_t *gr2 = lookup_kartenboden_nocheck( neighbour ); - const sint8 water_hgt_neighbour = get_water_hgt_nocheck( neighbour ); - if( gr2 && (water_hgt_neighbour >= gr2->get_hoehe()) && water_hgt_neighbour <= hneu ) { - water_table = max( water_table, water_hgt_neighbour ); - } - } - } - - for( sint16 i = 0; i < 8 ; i++ ) { - const koord neighbour = koord( x, y ) + koord::neighbours[i]; - if( is_within_limits( neighbour ) ) { - grund_t *gr2 = lookup_kartenboden_nocheck( neighbour ); - if( gr2 && gr2->get_hoehe() < water_table ) { - i = 8; - water_table = groundwater - 4; - } - } - } - - // only allow water table to be lowered (except for case of sea level) - // this prevents severe (errors! - if( water_table < get_water_hgt_nocheck(x,y) ) { - water_hgt = water_table; - set_water_hgt(x, y, water_table ); - } - } - - // calc new height and slope - const sint8 disp_hneu = max( hneu, water_hgt ); - const sint8 disp_hn_sw = max( hn_sw, water_hgt ); - const sint8 disp_hn_se = max( hn_se, water_hgt ); - const sint8 disp_hn_ne = max( hn_ne, water_hgt ); - const sint8 disp_hn_nw = max( hn_nw, water_hgt ); - const slope_t::type sneu = encode_corners(disp_hn_sw - disp_hneu, disp_hn_se - disp_hneu, disp_hn_ne - disp_hneu, disp_hn_nw - disp_hneu); - - // change height and slope for land tiles only - if( !gr->is_water() || (hmaxneu > water_hgt) ) { - gr->set_pos( koord3d( x, y, disp_hneu ) ); - gr->set_grund_hang( (slope_t::type)sneu ); - access_nocheck(x,y)->abgesenkt(); - } - // update north point in grid - set_grid_hgt(x, y, hn_nw); - if ( x == cached_size.x ) { - // update eastern grid coordinates too if we are in the edge. - set_grid_hgt(x+1, y, hn_ne); - set_grid_hgt(x+1, y+1, hn_se); - } - if ( y == cached_size.y ) { - // update southern grid coordinates too if we are in the edge. - set_grid_hgt(x, y+1, hn_sw); - set_grid_hgt(x+1, y+1, hn_se); - } - - // water heights - // lower water height for higher neighbouring tiles - // find out how high water is - for( sint16 i = 0; i < 8; i++ ) { - const koord neighbour = koord( x, y ) + koord::neighbours[i]; - if( is_within_limits( neighbour ) ) { - const sint8 water_hgt_neighbour = get_water_hgt_nocheck( neighbour ); - if(water_hgt_neighbour > hneu ) { - if( min_hgt_nocheck( neighbour ) < water_hgt_neighbour ) { - // convert to flat ground before lowering water level - raise_grid_to( neighbour.x, neighbour.y, water_hgt_neighbour ); - raise_grid_to( neighbour.x + 1, neighbour.y, water_hgt_neighbour ); - raise_grid_to( neighbour.x, neighbour.y + 1, water_hgt_neighbour ); - raise_grid_to( neighbour.x + 1, neighbour.y + 1, water_hgt_neighbour ); - } - set_water_hgt( neighbour, hneu ); - access_nocheck(neighbour)->correct_water(); - } - } - } - - calc_climate( koord( x, y ), false ); - for( sint16 i = 0; i < 8; i++ ) { - const koord neighbour = koord( x, y ) + koord::neighbours[i]; - calc_climate( neighbour, false ); - } - - // recalc landscape images - need to extend 2 in each direction - for( sint16 j = y - 2; j <= y + 2; j++ ) { - for( sint16 i = x - 2; i <= x + 2; i++ ) { - if( is_within_limits( i, j ) /*&& (i != x || j != y)*/ ) { - recalc_transitions( koord (i, j ) ); - } - } - } - - n += h0_sw-hn_sw + h0_se-hn_se + h0_ne-hn_ne + h0_nw-hn_nw; - - lookup_kartenboden_nocheck(x,y)->calc_image(); - if( (x+1) < cached_size.x ) { - lookup_kartenboden_nocheck(x+1,y)->calc_image(); - } - if( (y+1) < cached_size.y ) { - lookup_kartenboden_nocheck(x,y+1)->calc_image(); - } - return n; -} - - void karte_t::lower_grid_to(sint16 x, sint16 y, sint8 h) { if(is_within_grid_limits(x,y)) { @@ -4009,16 +3386,16 @@ int karte_t::grid_lower(const player_t *player, koord k, const char*&err) const sint8 hne = hgt + o - scorner_ne( corner_to_lower ) * f; const sint8 hnw = hgt + o - scorner_nw( corner_to_lower ) * f; - terraformer_t digger(this); - digger.add_lower_node(x, y, hsw, hse, hne, hnw); - digger.iterate(false); + terraformer_t digger(terraformer_t::lower, this); + digger.add_node(x, y, hsw, hse, hne, hnw); + digger.generate_affected_tile_list(); err = digger.can_lower_all(player, player->is_public_service()); if (err) { return 0; } - n = digger.lower_all(); + n = digger.apply(); err = NULL; // force world full redraw, or background could be dirty. @@ -4049,31 +3426,32 @@ bool karte_t::flatten_tile(player_t *player, koord k, sint8 hgt, bool keep_water const sint8 max_hgt = old_hgt + slope_t::max_diff(slope); if( max_hgt > hgt ) { - terraformer_t digger(this); - digger.add_lower_node(k.x, k.y, hgt, hgt, hgt, hgt); - digger.iterate(false); + terraformer_t digger(terraformer_t::lower, this); + digger.add_node(k.x, k.y, hgt, hgt, hgt, hgt); + digger.generate_affected_tile_list(); ok = digger.can_lower_all(player, player ? player->is_public_service() : true) == NULL; if (ok && !justcheck) { - n += digger.lower_all(); + n += digger.apply(); } } - if( ok && old_hgt < hgt ) { - terraformer_t digger(this); - digger.add_raise_node(k.x, k.y, hgt, hgt, hgt, hgt); - digger.iterate(true); + if( ok && old_hgt < hgt ) { + terraformer_t digger(terraformer_t::raise, this); + digger.add_node(k.x, k.y, hgt, hgt, hgt, hgt); + digger.generate_affected_tile_list(); ok = digger.can_raise_all(player, keep_water) == NULL; if (ok && !justcheck) { - n += digger.raise_all(); + n += digger.apply(); } } + // was changed => pay for it if(n>0) { - n = (n+3) >> 2; + n = (n+3) / 4; player_t::book_construction_costs(player, n * settings.cst_alter_land, k, ignore_wt); } return ok; @@ -9202,7 +8580,7 @@ void karte_t::rdwr_gamestate(loadsave_t *file, loadingscreen_t *ls) sint32 hgt; file->rdwr_long(hgt); // old height step was 16! - set_grid_hgt(x, y, hgt/16 ); + set_grid_hgt_nocheck(x, y, hgt/16 ); } } } diff --git a/simworld.h b/simworld.h index c523ee14019..222bc1c6104 100644 --- a/simworld.h +++ b/simworld.h @@ -61,6 +61,7 @@ class goods_desc_t; class memory_rw_t; class viewport_t; class loadingscreen_t; +class terraformer_t; #define CHK_RANDS 32 @@ -402,55 +403,7 @@ class karte_t */ interaction_t *eventmanager; - /** - * Checks whether the heights of the corners of the tile at (@p x, @p y) can be raised. - * If the desired height of a corner is lower than its current height, this corner is ignored. - * @param player player who wants to lower - * @param x coordinate - * @param y coordinate - * @param keep_water returns false if water tiles would be raised above water - * @param hsw desired height of sw-corner - * @param hse desired height of se-corner - * @param hne desired height of ne-corner - * @param hnw desired height of nw-corner - * @returns NULL if raise_to operation can be performed, an error message otherwise - */ - const char* can_raise_to(const player_t* player, sint16 x, sint16 y, bool keep_water, bool allow_deep_water, sint8 hsw, sint8 hse, sint8 hne, sint8 hnw) const; - - /** - * Raises heights of the corners of the tile at (@p x, @p y). - * New heights for each corner given. - * @pre can_raise_to should be called before this method. - * @see can_raise_to - * @returns count of full raise operations (4 corners raised one level) - * @note Clear tile, reset water/land type, calc minimap pixel. - */ - int raise_to(sint16 x, sint16 y, sint8 hsw, sint8 hse, sint8 hne, sint8 hnw); - - /** - * Checks whether the heights of the corners of the tile at (@p x, @p y) can be lowered. - * If the desired height of a corner is higher than its current height, this corner is ignored. - * @param player player who wants to lower - * @param x coordinate - * @param y coordinate - * @param hsw desired height of sw-corner - * @param hse desired height of se-corner - * @param hne desired height of ne-corner - * @param hnw desired height of nw-corner - * @returns NULL if lower_to operation can be performed, an error message otherwise - */ - const char* can_lower_to(const player_t* player, sint16 x, sint16 y, sint8 hsw, sint8 hse, sint8 hne, sint8 hnw, bool allow_deep_water) const; - - /** - * Lowers heights of the corners of the tile at (@p x, @p y). - * New heights for each corner given. - * @pre can_lower_to should be called before this method. - * @see can_lower_to - * @returns count of full lower operations (4 corners lowered one level) - * @note Clear tile, reset water/land type, calc minimap pixel. - */ - int lower_to(sint16 x, sint16 y, sint8 hsw, sint8 hse, sint8 hne, sint8 hnw); - +public: /** * Raise grid point (@p x,@p y). Changes grid_hgts only, used during map creation/enlargement. * @see clean_up @@ -463,6 +416,7 @@ class karte_t */ void lower_grid_to(sint16 x, sint16 y, sint8 h); +private: /** * The fractal generation of the map is not perfect. * cleanup_karte() eliminates errors. @@ -1858,14 +1812,17 @@ class karte_t const char* call_work(tool_t *t, player_t *pl, koord3d pos, bool &suspended); /** - * Returns the (x,y) map size. - * @brief Map size. + * Returns the size in tiles of the map. * @note Valid coords are (0..x-1,0..y-1) * @note These values are exactly one less then get_grid_size ones. * @see get_grid_size() */ inline koord const &get_size() const { return cached_grid_size; } + /// Returns the maximum possible index when accessing tiles. + /// Valid tiles are in the range (0..x, 0..y) + inline koord get_max_tile_index() const { return cached_size; } + /** * Maximum size for waiting bars etc. */ @@ -2031,7 +1988,7 @@ class karte_t } -private: +public: /** * @return grund at the bottom (where house will be build) * @note Inline because called very frequently! - nocheck for more speed @@ -2101,18 +2058,6 @@ class karte_t */ extended_version_t load_version; - /** - * Checks if the planquadrat (tile) at coordinate (x,y) - * can be lowered at the specified height. - */ - const char* can_lower_plan_to(const player_t *player, sint16 x, sint16 y, sint8 h) const; - - /** - * Checks if the planquadrat (tile) at coordinate (x,y) - * can be raised at the specified height. - */ - const char* can_raise_plan_to(const player_t *player, sint16 x, sint16 y, sint8 h) const; - /** *Checks if the whole planquadrat (tile) at coordinates (x,y) height can * be changed ( for example, water height can't be changed ). @@ -2135,72 +2080,6 @@ class karte_t bool can_flatten_tile(player_t *player, koord k, sint8 hgt, bool keep_water=false, bool make_underwater_hill=false); bool flatten_tile(player_t *player, koord k, sint8 hgt, bool keep_water=false, bool make_underwater_hill=false, bool justcheck=false); - /** - * Class to manage terraform operations. - * Can be used for raise only or lower only operations, but not mixed. - */ - class terraformer_t { - /// Structure to save terraforming operations - struct node_t { - sint16 x; ///< x-coordinate - sint16 y; ///< y-coordinate - sint8 h[4]; ///< height of corners, order: sw se ne nw - uint8 changed; - - node_t(sint16 x_, sint16 y_, sint8 hsw, sint8 hse, sint8 hne, sint8 hnw, uint8 c) - : x(x_), y(y_), changed(c) { h[0]=hsw; h[1]=hse; h[2]=hne; h[3]=hnw; } - - node_t() : x(-1), y(-1), changed(0) {} - - /// compares position - bool operator== (const node_t& a) const { return (a.x==x) && (a.y==y); } - - /// compares position - static bool comp(const node_t& a, const node_t& b); - }; - - vector_tpl list; ///< list of affected tiles - uint8 actual_flag; ///< internal flag to iterate through list - bool ready; ///< internal flag to signal iteration ready - karte_t* welt; - - void add_node(bool raise, sint16 x, sint16 y, sint8 hsw, sint8 hse, sint8 hne, sint8 hnw); - public: - terraformer_t(karte_t* w) { init(); welt = w; } - - void init() { list.clear(); actual_flag = 1; ready = false; } - - /** - * Add tile to be raised. - */ - void add_raise_node(sint16 x, sint16 y, sint8 hsw, sint8 hse, sint8 hne, sint8 hnw); - - /** - * Add tile to be lowered. - */ - void add_lower_node(sint16 x, sint16 y, sint8 hsw, sint8 hse, sint8 hne, sint8 hnw); - - /** - * Generate list of all tiles that will be affected. - */ - void iterate(bool raise); - - /// Check whether raise operation would succeed - const char* can_raise_all(const player_t *player, bool allow_deep_water, bool keep_water=false) const; - /// Check whether lower operation would succeed - const char* can_lower_all(const player_t *player, bool allow_deep_water) const; - - /// Do the raise operations - int raise_all(); - /// Do the lower operations - int lower_all(); - }; - -private: - /// Internal functions to be used with terraformer_t to propagate terrain changes to neighbouring tiles - void prepare_raise(terraformer_t& digger, sint16 x, sint16 y, sint8 hsw, sint8 hse, sint8 hne, sint8 hnw); - void prepare_lower(terraformer_t& digger, sint16 x, sint16 y, sint8 hsw, sint8 hse, sint8 hne, sint8 hnw); - public: // the convois are also handled each step => thus we keep track of them too @@ -2301,6 +2180,7 @@ class karte_t void step(); /// Tasks undertaken by a server when paused +public: void pause_step(); //private: @@ -2341,12 +2221,13 @@ class karte_t * Sets grid height. * Never set grid_hgts manually, always use this method! */ - void set_grid_hgt(sint16 x, sint16 y, sint8 hgt) { grid_hgts[x + y*(uint32)(cached_grid_size.x+1)] = hgt; } + void set_grid_hgt_nocheck(sint16 x, sint16 y, sint8 hgt) { grid_hgts[x + y*(uint32)(cached_grid_size.x+1)] = hgt; } - inline void set_grid_hgt(koord k, sint8 hgt) { set_grid_hgt(k.x, k.y, hgt); } + inline void set_grid_hgt_nocheck(koord k, sint8 hgt) { set_grid_hgt_nocheck(k.x, k.y, hgt); } +public: -private: +public: /** * @return water height - versions without checks for speed */ @@ -2372,9 +2253,9 @@ class karte_t /** * Sets water height. */ - void set_water_hgt(sint16 x, sint16 y, sint8 hgt) { water_hgts[x + y * (cached_grid_size.x)] = (hgt); } + void set_water_hgt_nocheck(sint16 x, sint16 y, sint8 hgt) { water_hgts[x + y * (cached_grid_size.x)] = (hgt); } - inline void set_water_hgt(koord k, sint8 hgt) { set_water_hgt(k.x, k.y, hgt); } + inline void set_water_hgt_nocheck(koord k, sint8 hgt) { set_water_hgt_nocheck(k.x, k.y, hgt); } /** * Fills array with corner heights of neighbours @@ -2406,7 +2287,7 @@ class karte_t */ void cleanup_grounds_loop(sint16, sint16, sint16, sint16); -private: +public: /** * @return Minimum height of the planquadrats (tile) at i, j. - for speed no checks performed that coordinates are valid */ diff --git a/world/terraformer.cc b/world/terraformer.cc new file mode 100644 index 00000000000..39cb66f70de --- /dev/null +++ b/world/terraformer.cc @@ -0,0 +1,697 @@ +/* + * This file is part of the Simutrans project under the Artistic License. + * (see LICENSE.txt) + */ + +#include "terraformer.h" + +#include "../dataobj/scenario.h" +#include "../descriptor/ground_desc.h" +#include "../simmenu.h" +#include "../simworld.h" + + +terraformer_t::node_t::node_t() : + x(-1), + y(-1), + changed(0) +{ +} + + +terraformer_t::node_t::node_t(sint16 x, sint16 y, sint8 hsw, sint8 hse, sint8 hne, sint8 hnw, uint8 c) : + x(x), + y(y), + hsw(hsw), + hse(hse), + hne(hne), + hnw(hnw), + changed(c) +{ +} + + +bool terraformer_t::node_t::operator==(const terraformer_t::node_t &a) const +{ + return (a.x==x) && (a.y==y); +} + + +terraformer_t::terraformer_t(operation_t op, karte_t *welt) : + actual_flag(1), + ready(false), + op(op), + welt(welt) +{ +} + + +bool terraformer_t::node_t::comp(const terraformer_t::node_t &a, const terraformer_t::node_t &b) +{ + int diff = a.x - b.x; + if (diff == 0) { + diff = a.y - b.y; + } + return diff<0; +} + + +void terraformer_t::add_node(sint16 x, sint16 y, sint8 hsw, sint8 hse, sint8 hne, sint8 hnw) +{ + if (!welt->is_within_limits(x,y)) { + return; + } + + const node_t test(x, y, hsw, hse, hne, hnw, actual_flag^3); + node_t *other = list.insert_unique_ordered(test, node_t::comp); + + const sint8 factor = (op == terraformer_t::raise) ? +1 : -1; + + if (other) { + if (factor*other->hsw < factor*test.hsw) { + other->hsw = test.hsw; + other->changed |= actual_flag ^ 3; + ready = false; + } + + if (factor*other->hse < factor*test.hse) { + other->hse = test.hse; + other->changed |= actual_flag ^ 3; + ready = false; + } + + if (factor*other->hne < factor*test.hne) { + other->hne = test.hne; + other->changed |= actual_flag ^ 3; + ready = false; + } + + if (factor*other->hnw < factor*test.hnw) { + other->hnw = test.hnw; + other->changed |= actual_flag ^ 3; + ready = false; + } + } + else { + ready = false; + } +} + + +void terraformer_t::generate_affected_tile_list() +{ + while( !ready) { + actual_flag ^= 3; // flip bits + // clear new_flag bit + for(node_t& i : list) { + i.changed &= actual_flag; + } + + // process nodes with actual_flag set + ready = true; + for(uint32 j=0; j < list.get_count(); j++) { + node_t& i = list[j]; + if (i.changed & actual_flag) { + i.changed &= ~actual_flag; + if (op == terraformer_t::raise) { + prepare_raise(i); + } + else { + prepare_lower(i); + } + } + } + } +} + + +const char *terraformer_t::can_raise_all(const player_t *player, bool allow_deep_water, bool keep_water) const +{ + assert(op == terraformer_t::raise); + assert(ready); + + for(node_t const& i : list) { + if (const char *err = can_raise_tile_to(i, player, allow_deep_water, keep_water)) { + return err; + } + } + return NULL; +} + + +const char *terraformer_t::can_lower_all(const player_t *player, bool allow_deep_water) const +{ + assert(op == terraformer_t::lower); + assert(ready); + + for(node_t const& i : list) { + if (const char *err = can_lower_tile_to(i, player, allow_deep_water)) { + return err; + } + } + + return NULL; +} + + +int terraformer_t::apply() +{ + assert(ready); + int n = 0; + + if (op == terraformer_t::raise) { + for(node_t const& i : list) { + n += raise_to(i); + } + } + else { + for(node_t const& i : list) { + n += lower_to(i); + } + } + + return n; +} + + +void terraformer_t::prepare_raise(const node_t node) +{ + assert(welt->is_within_limits(node.x,node.y)); + + const grund_t *gr = welt->lookup_kartenboden_nocheck(node.x,node.y); + const sint8 water_hgt = welt->get_water_hgt_nocheck(node.x,node.y); + const sint8 h0 = gr->get_hoehe(); + + // old height + const sint8 h0_sw = gr->is_water() ? min(water_hgt, welt->lookup_hgt_nocheck(node.x, node.y+1) ) : h0 + corner_sw( gr->get_grund_hang() ); + const sint8 h0_se = gr->is_water() ? min(water_hgt, welt->lookup_hgt_nocheck(node.x+1,node.y+1) ) : h0 + corner_se( gr->get_grund_hang() ); + const sint8 h0_ne = gr->is_water() ? min(water_hgt, welt->lookup_hgt_nocheck(node.x+1,node.y ) ) : h0 + corner_ne( gr->get_grund_hang() ); + const sint8 h0_nw = gr->is_water() ? min(water_hgt, welt->lookup_hgt_nocheck(node.x, node.y ) ) : h0 + corner_nw( gr->get_grund_hang() ); + + // new height + const sint8 hn_sw = max(node.hsw, h0_sw); + const sint8 hn_se = max(node.hse, h0_se); + const sint8 hn_ne = max(node.hne, h0_ne); + const sint8 hn_nw = max(node.hnw, h0_nw); + + // nothing to do? + if( !gr->is_water() && h0_sw >= node.hsw && h0_se >= node.hse && h0_ne >= node.hne && h0_nw >= node.hnw ) { + return; + } + + // calc new height and slope + const sint8 hneu = min( min( hn_sw, hn_se ), min( hn_ne, hn_nw ) ); + const sint8 hmaxneu = max( max( hn_sw, hn_se ), max( hn_ne, hn_nw ) ); + + const uint8 max_hdiff = ground_desc_t::double_grounds ? 2 : 1; + + const bool ok = (hmaxneu - hneu <= max_hdiff); // may fail on water tiles since lookup_hgt might be modified from previous raise_to calls + if( !ok && !gr->is_water() ) { + assert(false); + } + + // sw + if (h0_sw < node.hsw) { + add_node( node.x - 1, node.y + 1, node.hsw - max_hdiff, node.hsw - max_hdiff, node.hsw, node.hsw - max_hdiff ); + } + // s + if (h0_sw < node.hsw || h0_se < node.hse) { + const sint8 hs = max( node.hse, node.hsw ) - max_hdiff; + add_node( node.x, node.y + 1, hs, hs, node.hse, node.hsw ); + } + // se + if (h0_se < node.hse) { + add_node( node.x + 1, node.y + 1, node.hse - max_hdiff, node.hse - max_hdiff, node.hse - max_hdiff, node.hse ); + } + // e + if (h0_se < node.hse || h0_ne < node.hne) { + const sint8 he = max( node.hse, node.hne ) - max_hdiff; + add_node( node.x + 1, node.y, node.hse, he, he, node.hne ); + } + // ne + if (h0_ne < node.hne) { + add_node( node.x + 1,node.y - 1, node.hne, node.hne - max_hdiff, node.hne - max_hdiff, node.hne - max_hdiff ); + } + // n + if (h0_nw < node.hnw || h0_ne < node.hne) { + const sint8 hn = max( node.hnw, node.hne ) - max_hdiff; + add_node( node.x, node.y - 1, node.hnw, node.hne, hn, hn ); + } + // nw + if (h0_nw < node.hnw) { + add_node( node.x - 1, node.y - 1, node.hnw - max_hdiff, node.hnw, node.hnw - max_hdiff, node.hnw - max_hdiff ); + } + // w + if (h0_sw < node.hsw || h0_nw < node.hnw) { + const sint8 hw = max( node.hnw, node.hsw ) - max_hdiff; + add_node( node.x - 1, node.y, hw, node.hsw, node.hnw, hw ); + } +} + + +void terraformer_t::prepare_lower(const node_t node) +{ + assert(welt->is_within_limits(node.x,node.y)); + const grund_t *gr = welt->lookup_kartenboden_nocheck(node.x,node.y); + const sint8 water_hgt = welt->get_water_hgt_nocheck(node.x,node.y); + + const sint8 h0 = gr->get_hoehe(); + + // which corners have to be raised? + const sint8 h0_sw = gr->is_water() ? min( water_hgt, welt->lookup_hgt_nocheck(node.x, node.y+1) ) : h0 + corner_sw( gr->get_grund_hang() ); + const sint8 h0_se = gr->is_water() ? min( water_hgt, welt->lookup_hgt_nocheck(node.x+1,node.y+1) ) : h0 + corner_se( gr->get_grund_hang() ); + const sint8 h0_ne = gr->is_water() ? min( water_hgt, welt->lookup_hgt_nocheck(node.x, node.y+1) ) : h0 + corner_ne( gr->get_grund_hang() ); + const sint8 h0_nw = gr->is_water() ? min( water_hgt, welt->lookup_hgt_nocheck(node.x, node.y ) ) : h0 + corner_nw( gr->get_grund_hang() ); + + const uint8 max_hdiff = ground_desc_t::double_grounds ? 2 : 1; + + // sw + if (h0_sw > node.hsw) { + add_node(node.x - 1, node.y + 1, node.hsw + max_hdiff, node.hsw + max_hdiff, node.hsw, node.hsw + max_hdiff); + } + // s + if (h0_se > node.hse || h0_sw > node.hsw) { + const sint8 hs = min( node.hse, node.hsw ) + max_hdiff; + add_node( node.x, node.y + 1, hs, hs, node.hse, node.hsw); + } + // se + if (h0_se > node.hse) { + add_node( node.x + 1, node.y + 1, node.hse + max_hdiff, node.hse + max_hdiff, node.hse + max_hdiff, node.hse); + } + // e + if (h0_se > node.hse || h0_ne > node.hne) { + const sint8 he = max( node.hse, node.hne ) + max_hdiff; + add_node( node.x + 1, node.y, node.hse, he, he, node.hne); + } + // ne + if (h0_ne > node.hne) { + add_node( node.x + 1, node.y - 1, node.hne, node.hne + max_hdiff, node.hne + max_hdiff, node.hne + max_hdiff); + } + // n + if (h0_nw > node.hnw || h0_ne > node.hne) { + const sint8 hn = min( node.hnw, node.hne ) + max_hdiff; + add_node( node.x, node.y - 1, node.hnw, node.hne, hn, hn); + } + // nw + if (h0_nw > node.hnw) { + add_node( node.x - 1, node.y - 1, node.hnw + max_hdiff, node.hnw, node.hnw + max_hdiff, node.hnw + max_hdiff); + } + // w + if (h0_nw > node.hnw || h0_sw > node.hsw) { + const sint8 hw = min( node.hnw, node.hsw ) + max_hdiff; + add_node( node.x - 1, node.y, hw, node.hsw, node.hnw, hw); + } +} + + +const char *terraformer_t::can_raise_tile_to(const node_t &node, const player_t *player, bool allow_deep_water, bool keep_water) const +{ + assert(welt->is_within_limits(node.x,node.y)); + + const grund_t *gr = welt->lookup_kartenboden_nocheck(node.x,node.y); + const sint8 water_hgt = welt->get_water_hgt_nocheck(node.x,node.y); + + const sint8 max_hgt = max(max(node.hsw,node.hse),max(node.hne,node.hnw)); + const sint8 min_hgt = min(min(node.hsw,node.hse),min(node.hne,node.hnw)); + + if( gr->is_water() && keep_water && max_hgt > water_hgt ) { + return ""; + } + + if(gr->is_water() && min_hgt < welt->get_groundwater() && !allow_deep_water) + { + return "Cannot terraform in deep water"; + } + + return can_raise_plan_to(player, node.x, node.y, max_hgt); +} + + +// lower plan +// new heights for each corner given +const char* terraformer_t::can_lower_tile_to(const node_t &node, const player_t *player, bool allow_deep_water) const +{ + assert(welt->is_within_limits(node.x,node.y)); + + const sint8 hneu = min( min( node.hsw, node.hse ), min( node.hne, node.hnw ) ); + + if( hneu < welt->get_minimumheight() ) { + return "Maximum tile height difference reached."; + } + + if(hneu < welt->get_groundwater() && !allow_deep_water) + { + return "Cannot terraform in deep water"; + } + + // water heights + // check if need to lower water height for higher neighbouring tiles + for( sint16 i = 0 ; i < 8 ; i++ ) { + const koord neighbour = koord( node.x, node.y ) + koord::neighbours[i]; + if( welt->is_within_limits(neighbour) && welt->get_water_hgt_nocheck(neighbour) > hneu ) { + if (!welt->is_plan_height_changeable( neighbour.x, neighbour.y )) { + return ""; + } + } + } + + return can_lower_plan_to(player, node.x, node.y, hneu ); +} + + +int terraformer_t::raise_to(const node_t &node) +{ + assert(welt->is_within_limits(node.x,node.y)); + + int n = 0; + grund_t *gr = welt->lookup_kartenboden_nocheck(node.x,node.y); + const sint8 water_hgt = welt->get_water_hgt_nocheck(node.x,node.y); + const sint8 h0 = gr->get_hoehe(); + + // old height + const sint8 h0_sw = gr->is_water() ? min(water_hgt, welt->lookup_hgt_nocheck(node.x, node.y+1) ) : h0 + corner_sw( gr->get_grund_hang() ); + const sint8 h0_se = gr->is_water() ? min(water_hgt, welt->lookup_hgt_nocheck(node.x+1, node.y+1) ) : h0 + corner_se( gr->get_grund_hang() ); + const sint8 h0_ne = gr->is_water() ? min(water_hgt, welt->lookup_hgt_nocheck(node.x+1, node.y ) ) : h0 + corner_ne( gr->get_grund_hang() ); + const sint8 h0_nw = gr->is_water() ? min(water_hgt, welt->lookup_hgt_nocheck(node.x, node.y ) ) : h0 + corner_nw( gr->get_grund_hang() ); + + // new height + const sint8 hn_sw = max(node.hsw, h0_sw); + const sint8 hn_se = max(node.hse, h0_se); + const sint8 hn_ne = max(node.hne, h0_ne); + const sint8 hn_nw = max(node.hnw, h0_nw); + + // nothing to do? + if( !gr->is_water() && h0_sw >= node.hsw && h0_se >= node.hse && h0_ne >= node.hne && h0_nw >= node.hnw ) { + return 0; + } + + // calc new height and slope + const sint8 hneu = min( min( hn_sw, hn_se ), min( hn_ne, hn_nw ) ); + const sint8 hmaxneu = max( max( hn_sw, hn_se ), max( hn_ne, hn_nw ) ); + + const uint8 max_hdiff = ground_desc_t::double_grounds ? 2 : 1; + const sint8 disp_hneu = max( hneu, water_hgt ); + const sint8 disp_hn_sw = max( hn_sw, water_hgt ); + const sint8 disp_hn_se = max( hn_se, water_hgt ); + const sint8 disp_hn_ne = max( hn_ne, water_hgt ); + const sint8 disp_hn_nw = max( hn_nw, water_hgt ); + const slope_t::type sneu = encode_corners(disp_hn_sw - disp_hneu, disp_hn_se - disp_hneu, disp_hn_ne - disp_hneu, disp_hn_nw - disp_hneu); + + const bool ok = (hmaxneu - hneu <= max_hdiff); // may fail on water tiles since lookup_hgt might be modified from previous raise_to calls + if (!ok && !gr->is_water()) { + assert(false); + } + + // change height and slope, for water tiles only if they will become land + if( !gr->is_water() || (hmaxneu > water_hgt || (hneu == water_hgt && hmaxneu == water_hgt) ) ) { + gr->set_pos( koord3d( node.x, node.y, disp_hneu ) ); + gr->set_grund_hang( sneu ); + welt->access_nocheck(node.x,node.y)->angehoben(); + welt->set_water_hgt_nocheck(node.x, node.y, welt->get_groundwater()-4); + } + + // update north point in grid + welt->set_grid_hgt_nocheck(node.x, node.y, hn_nw); + welt->calc_climate(koord(node.x,node.y), true); + + if ( node.x == welt->get_max_tile_index().x ) { + // update eastern grid coordinates too if we are in the edge. + welt->set_grid_hgt_nocheck(node.x+1, node.y, hn_ne); + welt->set_grid_hgt_nocheck(node.x+1, node.y+1, hn_se); + } + + if ( node.y == welt->get_max_tile_index().y ) { + // update southern grid coordinates too if we are in the edge. + welt->set_grid_hgt_nocheck(node.x, node.y+1, hn_sw); + welt->set_grid_hgt_nocheck(node.x+1, node.y+1, hn_se); + } + + n += hn_sw - h0_sw + hn_se - h0_se + hn_ne - h0_ne + hn_nw - h0_nw; + + welt->lookup_kartenboden_nocheck(node.x,node.y)->calc_image(); + + if ( (node.x+1) < welt->get_max_tile_index().x ) { + welt->lookup_kartenboden_nocheck(node.x+1,node.y)->calc_image(); + } + + if ( (node.y+1) < welt->get_max_tile_index().y ) { + welt->lookup_kartenboden_nocheck(node.x,node.y+1)->calc_image(); + } + + return n; +} + + +int terraformer_t::lower_to(const node_t &node) +{ + assert(welt->is_within_limits(node.x,node.y)); + + int n = 0; + grund_t *gr = welt->lookup_kartenboden_nocheck(node.x,node.y); + sint8 water_hgt = welt->get_water_hgt_nocheck(node.x,node.y); + const sint8 h0 = gr->get_hoehe(); + + // old height + const sint8 h0_sw = gr->is_water() ? min( water_hgt, welt->lookup_hgt_nocheck(node.x, node.y+1) ) : h0 + corner_sw( gr->get_grund_hang() ); + const sint8 h0_se = gr->is_water() ? min( water_hgt, welt->lookup_hgt_nocheck(node.x+1, node.y+1) ) : h0 + corner_se( gr->get_grund_hang() ); + const sint8 h0_ne = gr->is_water() ? min( water_hgt, welt->lookup_hgt_nocheck(node.x+1, node.y ) ) : h0 + corner_ne( gr->get_grund_hang() ); + const sint8 h0_nw = gr->is_water() ? min( water_hgt, welt->lookup_hgt_nocheck(node.x, node.y ) ) : h0 + corner_nw( gr->get_grund_hang() ); + + // new height + const sint8 hn_sw = min(node.hsw, h0_sw); + const sint8 hn_se = min(node.hse, h0_se); + const sint8 hn_ne = min(node.hne, h0_ne); + const sint8 hn_nw = min(node.hnw, h0_nw); + + // nothing to do? + if( gr->is_water() ) { + if( h0_nw <= node.hnw ) { + return 0; + } + } + else if( h0_sw <= node.hsw && h0_se <= node.hse && h0_ne <= node.hne && h0_nw <= node.hnw ) { + return 0; + } + + // calc new height and slope + const sint8 hneu = min( min( hn_sw, hn_se ), min( hn_ne, hn_nw ) ); + const sint8 hmaxneu = max( max( hn_sw, hn_se ), max( hn_ne, hn_nw ) ); + + if( hneu >= water_hgt ) { + // calculate water table from surrounding tiles - start off with height on this tile + sint8 water_table = water_hgt >= h0 ? water_hgt : welt->get_groundwater() - 4; + + /* + * we test each corner in turn to see whether it is at the base height of the tile. + * If it is we then mark the 3 surrounding tiles for that corner for checking. + * Surrounding tiles are indicated by bits going anti-clockwise from + * (binary) 00000001 for north-west through to (binary) 10000000 for north + * as this is the order of directions used by koord::neighbours[] + */ + + uint8 neighbour_flags = 0; + + if( hn_nw == hneu ) { + neighbour_flags |= 0x83; + } + if( hn_ne == hneu ) { + neighbour_flags |= 0xe0; + } + if( hn_se == hneu ) { + neighbour_flags |= 0x38; + } + if( hn_sw == hneu ) { + neighbour_flags |= 0x0e; + } + + for( sint16 i = 0; i < 8 ; i++ ) { + const koord neighbour = koord( node.x, node.y ) + koord::neighbours[i]; + + // here we look at the bit in neighbour_flags for this direction + // we shift it i bits to the right and test the least significant bit + + if( welt->is_within_limits( neighbour ) && ((neighbour_flags >> i) & 1) ) { + grund_t *gr2 = welt->lookup_kartenboden_nocheck( neighbour ); + const sint8 water_hgt_neighbour = welt->get_water_hgt_nocheck( neighbour ); + if( gr2 && (water_hgt_neighbour >= gr2->get_hoehe()) && water_hgt_neighbour <= hneu ) { + water_table = max( water_table, water_hgt_neighbour ); + } + } + } + + for( sint16 i = 0; i < 8 ; i++ ) { + const koord neighbour = koord( node.x, node.y ) + koord::neighbours[i]; + if( welt->is_within_limits( neighbour ) ) { + grund_t *gr2 = welt->lookup_kartenboden_nocheck( neighbour ); + if( gr2 && gr2->get_hoehe() < water_table ) { + i = 8; + water_table = welt->get_groundwater() - 4; + } + } + } + + // only allow water table to be lowered (except for case of sea level) + // this prevents severe (errors! + if( water_table < welt->get_water_hgt_nocheck(node.x,node.y) ) { + water_hgt = water_table; + welt->set_water_hgt_nocheck(node.x, node.y, water_table ); + } + } + + // calc new height and slope + const sint8 disp_hneu = max( hneu, water_hgt ); + const sint8 disp_hn_sw = max( hn_sw, water_hgt ); + const sint8 disp_hn_se = max( hn_se, water_hgt ); + const sint8 disp_hn_ne = max( hn_ne, water_hgt ); + const sint8 disp_hn_nw = max( hn_nw, water_hgt ); + const slope_t::type sneu = encode_corners(disp_hn_sw - disp_hneu, disp_hn_se - disp_hneu, disp_hn_ne - disp_hneu, disp_hn_nw - disp_hneu); + + // change height and slope for land tiles only + if( !gr->is_water() || (hmaxneu > water_hgt) ) { + gr->set_pos( koord3d( node.x, node.y, disp_hneu ) ); + gr->set_grund_hang( (slope_t::type)sneu ); + welt->access_nocheck(node.x,node.y)->abgesenkt(); + } + + // update north point in grid + welt->set_grid_hgt_nocheck(node.x, node.y, hn_nw); + if ( node.x == welt->get_max_tile_index().x ) { + // update eastern grid coordinates too if we are in the edge. + welt->set_grid_hgt_nocheck(node.x+1, node.y, hn_ne); + welt->set_grid_hgt_nocheck(node.x+1, node.y+1, hn_se); + } + + if ( node.y == welt->get_max_tile_index().y ) { + // update southern grid coordinates too if we are in the edge. + welt->set_grid_hgt_nocheck(node.x, node.y+1, hn_sw); + welt->set_grid_hgt_nocheck(node.x+1, node.y+1, hn_se); + } + + // water heights + // lower water height for higher neighbouring tiles + // find out how high water is + for( sint16 i = 0; i < 8; i++ ) { + const koord neighbour = koord( node.x, node.y ) + koord::neighbours[i]; + if( welt->is_within_limits( neighbour ) ) { + const sint8 water_hgt_neighbour = welt->get_water_hgt_nocheck( neighbour ); + if(water_hgt_neighbour > hneu ) { + if( welt->min_hgt_nocheck( neighbour ) < water_hgt_neighbour ) { + // convert to flat ground before lowering water level + welt->raise_grid_to( neighbour.x, neighbour.y, water_hgt_neighbour ); + welt->raise_grid_to( neighbour.x + 1, neighbour.y, water_hgt_neighbour ); + welt->raise_grid_to( neighbour.x, neighbour.y + 1, water_hgt_neighbour ); + welt->raise_grid_to( neighbour.x + 1, neighbour.y + 1, water_hgt_neighbour ); + } + + welt->set_water_hgt_nocheck( neighbour, hneu ); + welt->access_nocheck(neighbour)->correct_water(); + } + } + } + + welt->calc_climate( koord( node.x, node.y ), false ); + for( sint16 i = 0; i < 8; i++ ) { + const koord neighbour = koord( node.x, node.y ) + koord::neighbours[i]; + welt->calc_climate( neighbour, false ); + } + + // recalc landscape images - need to extend 2 in each direction + for( sint16 j = node.y - 2; j <= node.y + 2; j++ ) { + for( sint16 i = node.x - 2; i <= node.x + 2; i++ ) { + if( welt->is_within_limits( i, j ) /*&& (i != x || j != y)*/ ) { + welt->recalc_transitions( koord (i, j ) ); + } + } + } + + n += h0_sw-hn_sw + h0_se-hn_se + h0_ne-hn_ne + h0_nw-hn_nw; + + welt->lookup_kartenboden_nocheck(node.x,node.y)->calc_image(); + if( (node.x+1) < welt->get_max_tile_index().x ) { + welt->lookup_kartenboden_nocheck(node.x+1,node.y)->calc_image(); + } + + if( (node.y+1) < welt->get_max_tile_index().y ) { + welt->lookup_kartenboden_nocheck(node.x,node.y+1)->calc_image(); + } + + return n; +} + + +const char *terraformer_t::can_lower_plan_to(const player_t *player, sint16 x, sint16 y, sint8 h) const +{ + const planquadrat_t *plan = welt->access(x,y); + const settings_t &settings = welt->get_settings(); + + if( plan==NULL ) { + return ""; + } + + if( h < welt->get_groundwater() - 3 ) { + return ""; + } + + const sint8 hmax = plan->get_kartenboden()->get_hoehe(); + if( (hmax == h || hmax == h - 1) && (plan->get_kartenboden()->get_grund_hang() == 0 || welt->is_plan_height_changeable( x, y )) ) { + return NULL; + } + + if( !welt->is_plan_height_changeable(x, y) ) { + return ""; + } + + // tunnel slope below? + const grund_t *gr = plan->get_boden_in_hoehe( h - 1 ); + if( !gr ) { + gr = plan->get_boden_in_hoehe( h - 2 ); + } + + if( !gr && settings.get_way_height_clearance()==2 ) { + gr = plan->get_boden_in_hoehe( h - 3 ); + } + + if( gr && h < gr->get_pos().z + slope_t::max_diff( gr->get_weg_hang() ) + settings.get_way_height_clearance() ) { + return ""; + } + + // tunnel below? + while(h < hmax) { + if(plan->get_boden_in_hoehe(h)) { + return ""; + } + h ++; + } + + // check allowance by scenario + if (welt->get_scenario()->is_scripted()) { + return welt->get_scenario()->is_work_allowed_here(player, TOOL_LOWER_LAND|GENERAL_TOOL, ignore_wt, plan->get_kartenboden()->get_pos()); + } + + return NULL; +} + + +const char *terraformer_t::can_raise_plan_to(const player_t *player, sint16 x, sint16 y, sint8 h) const +{ + const planquadrat_t *plan = welt->access(x,y); + if( plan == NULL || !welt->is_plan_height_changeable(x, y) ) { + return ""; + } + + // irgendwo eine Bruecke im Weg? + const sint8 hmin = plan->get_kartenboden()->get_hoehe(); + while(h > hmin) { + if(plan->get_boden_in_hoehe(h)) { + return ""; + } + h --; + } + + // check allowance by scenario + if (welt->get_scenario()->is_scripted()) { + return welt->get_scenario()->is_work_allowed_here(player, TOOL_RAISE_LAND|GENERAL_TOOL, ignore_wt, plan->get_kartenboden()->get_pos()); + } + + return NULL; +} diff --git a/world/terraformer.h b/world/terraformer.h new file mode 100644 index 00000000000..90cc95772c9 --- /dev/null +++ b/world/terraformer.h @@ -0,0 +1,143 @@ +/* + * This file is part of the Simutrans project under the Artistic License. + * (see LICENSE.txt) + */ + +#ifndef WORLD_TERRAFORMER_H +#define WORLD_TERRAFORMER_H + + +#include "../simtypes.h" +#include "../tpl/vector_tpl.h" + + +class karte_t; +class player_t; + + +/** + * Class to manage terraform operations. + * Can be used for raise only or lower only operations, but not mixed. + * + * Usual order of calls: + * 1. add_node() (can be repeated) + * 2. generate_affected_tile_list() + * 3. can_raise_all() / can_lower_all() + * 4. apply() (to actually apply the changes to the terrain; optional) + */ +class terraformer_t +{ +public: + enum operation_t + { + lower = 0, + raise = 1 + }; + +private: + /// Structure to save terraforming operations + struct node_t + { + public: + node_t(); + node_t(sint16 x, sint16 y, sint8 hsw, sint8 hse, sint8 hne, sint8 hnw, uint8 c); + + public: + /// compares position + bool operator==(const node_t &a) const; + + /// compares position + static bool comp(const node_t &a, const node_t &b); + + public: + sint16 x; ///< x-coordinate + sint16 y; ///< y-coordinate + + sint8 hsw, hse, hne, hnw; + + uint8 changed; + }; + +public: + terraformer_t(terraformer_t::operation_t op, karte_t *world); + +public: + /// Add tile to be raised/lowered. + void add_node(sint16 x, sint16 y, sint8 hsw, sint8 hse, sint8 hne, sint8 hnw); + + /// Generate list of all tiles that will be affected. + void generate_affected_tile_list(); + + /// Check whether raise operation would succeed + const char *can_raise_all(const player_t *player, bool allow_deep_water, bool keep_water = false) const; + + /// Check whether lower operation would succeed + const char *can_lower_all(const player_t *player, bool allow_deep_water) const; + + /// Do the raise/lower operations + int apply(); + +private: + /// Internal functions to be used with terraformer_t to propagate terrain changes to neighbouring tiles + void prepare_raise(const node_t node); + void prepare_lower(const node_t node); + + /** + * Checks whether the heights of the corners of the tile at (@p x, @p y) can be raised. + * If the desired height of a corner is lower than its current height, this corner is ignored. + * @param player player who wants to lower + * @param keep_water returns false if water tiles would be raised above water + * @returns NULL if raise_to operation can be performed, an error message otherwise + */ + const char *can_raise_tile_to(const node_t &node, const player_t *player, bool allow_deep_water, bool keep_water) const; + + /** + * Checks whether the heights of the corners of the tile at (@p x, @p y) can be lowered. + * If the desired height of a corner is higher than its current height, this corner is ignored. + * @param player player who wants to lower + * @returns NULL if lower_to operation can be performed, an error message otherwise + */ + const char *can_lower_tile_to(const node_t &node, const player_t *player, bool allow_deep_water) const; + + /** + * Raises heights of the corners of the tile at (@p x, @p y). + * New heights for each corner given. + * @pre can_raise_to should be called before this method. + * @see can_raise_to + * @returns count of full raise operations (4 corners raised one level) + * @note Clear tile, reset water/land type, calc minimap pixel. + */ + int raise_to(const node_t &node); + + /** + * Lowers heights of the corners of the tile at (@p x, @p y). + * New heights for each corner given. + * @pre can_lower_to should be called before this method. + * @see can_lower_to + * @returns count of full lower operations (4 corners lowered one level) + * @note Clear tile, reset water/land type, calc minimap pixel. + */ + int lower_to(const node_t &node); + + /** + * Checks if the planquadrat (tile) at coordinate (x,y) + * can be lowered at the specified height. + */ + const char *can_lower_plan_to(const player_t *player, sint16 x, sint16 y, sint8 h) const; + + /** + * Checks if the planquadrat (tile) at coordinate (x,y) + * can be raised at the specified height. + */ + const char *can_raise_plan_to(const player_t *player, sint16 x, sint16 y, sint8 h) const; + +private: + vector_tpl list; ///< list of affected tiles + uint8 actual_flag; ///< internal flag to iterate through list + bool ready; ///< internal flag to signal that the affected tile list is updated + operation_t op; ///< Raise or lower? + karte_t *welt; +}; + + +#endif From 63d8fd2ed74facc4407f83d102364c1caaff63cb Mon Sep 17 00:00:00 2001 From: ceeac Date: Sat, 29 Apr 2023 11:41:19 +0200 Subject: [PATCH 3/3] Squelch enum related warning --- bauer/vehikelbauer.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bauer/vehikelbauer.cc b/bauer/vehikelbauer.cc index c8631c10659..b762cbea8d5 100644 --- a/bauer/vehikelbauer.cc +++ b/bauer/vehikelbauer.cc @@ -215,7 +215,9 @@ static int compare_freight(const vehicle_desc_t* a, const vehicle_desc_t* b) } static int compare_capacity(const vehicle_desc_t* a, const vehicle_desc_t* b) { return a->get_capacity() - b->get_capacity(); } static int compare_engine(const vehicle_desc_t* a, const vehicle_desc_t* b) { - return (a->get_capacity() + a->get_power() == 0 ? (uint8)vehicle_desc_t::steam : a->get_engine_type()) - (b->get_capacity() + b->get_power() == 0 ? (uint8)vehicle_desc_t::steam : b->get_engine_type()); + const vehicle_desc_t::engine_t a_engine = (a->get_capacity() + a->get_power() == 0) ? vehicle_desc_t::steam : a->get_engine_type(); + const vehicle_desc_t::engine_t b_engine = (b->get_capacity() + b->get_power() == 0) ? vehicle_desc_t::steam : b->get_engine_type(); + return (int)a_engine - (int)b_engine; } static int compare_price(const vehicle_desc_t* a, const vehicle_desc_t* b) { return a->get_base_price() - b->get_base_price(); } static int compare_topspeed(const vehicle_desc_t* a, const vehicle_desc_t* b) { return a->get_topspeed() - b->get_topspeed(); }